diff options
Diffstat (limited to 'hw')
130 files changed, 4826 insertions, 1042 deletions
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index cf346c1d0a..2195b60fac 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,6 +1,6 @@ obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o obj-$(CONFIG_DIGIC) += digic_boards.o -obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o +obj-y += integratorcp.o mainstone.o musicpal.o nseries.o obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-$(CONFIG_ACPI) += virt-acpi-build.o @@ -13,3 +13,5 @@ obj-y += omap1.o omap2.o strongarm.o obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o +obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o +obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 5b969cda1c..bef451b3b2 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -10,6 +10,7 @@ #include "config.h" #include "hw/hw.h" #include "hw/arm/arm.h" +#include "hw/arm/linux-boot-if.h" #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/loader.h" @@ -555,6 +556,20 @@ static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, fw_cfg_add_bytes(fw_cfg, data_key, data, size); } +static int do_arm_linux_init(Object *obj, void *opaque) +{ + if (object_dynamic_cast(obj, TYPE_ARM_LINUX_BOOT_IF)) { + ARMLinuxBootIf *albif = ARM_LINUX_BOOT_IF(obj); + ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_GET_CLASS(obj); + struct arm_boot_info *info = opaque; + + if (albifc->arm_linux_init) { + albifc->arm_linux_init(albif, info->secure_boot); + } + } + return 0; +} + static void arm_load_kernel_notify(Notifier *notifier, void *data) { CPUState *cs; @@ -778,6 +793,12 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) if (info->nb_cpus > 1) { info->write_secondary_boot(cpu, info); } + + /* Notify devices which need to fake up firmware initialization + * that we're doing a direct kernel boot. + */ + object_child_foreach_recursive(object_get_root(), + do_arm_linux_init, info); } info->is_linux = is_linux; @@ -803,3 +824,16 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); } } + +static const TypeInfo arm_linux_boot_if_info = { + .name = TYPE_ARM_LINUX_BOOT_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(ARMLinuxBootIfClass), +}; + +static void arm_linux_boot_register_types(void) +{ + type_register_static(&arm_linux_boot_if_info); +} + +type_init(arm_linux_boot_register_types) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c new file mode 100644 index 0000000000..86fde42e34 --- /dev/null +++ b/hw/arm/fsl-imx25.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2013 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * i.MX25 SOC emulation. + * + * Based on hw/arm/xlnx-zynqmp.c + * + * Copyright (C) 2015 Xilinx Inc + * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/arm/fsl-imx25.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "hw/boards.h" +#include "sysemu/char.h" + +static void fsl_imx25_init(Object *obj) +{ + FslIMX25State *s = FSL_IMX25(obj); + int i; + + object_initialize(&s->cpu, sizeof(s->cpu), "arm926-" TYPE_ARM_CPU); + + object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); + qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); + + object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM); + qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + + for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) { + object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); + qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) { + object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX_GPT); + qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) { + object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); + qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); + } + + object_initialize(&s->fec, sizeof(s->fec), TYPE_IMX_FEC); + qdev_set_parent_bus(DEVICE(&s->fec), sysbus_get_default()); + + for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) { + object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); + qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) { + object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); + qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + } +} + +static void fsl_imx25_realize(DeviceState *dev, Error **errp) +{ + FslIMX25State *s = FSL_IMX25(dev); + uint8_t i; + Error *err = NULL; + + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->avic), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->avic), 0, FSL_IMX25_AVIC_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 0, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 1, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ)); + + object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX25_CCM_ADDR); + + /* Initialize all UARTs */ + for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX25_NUM_UARTS] = { + { FSL_IMX25_UART1_ADDR, FSL_IMX25_UART1_IRQ }, + { FSL_IMX25_UART2_ADDR, FSL_IMX25_UART2_IRQ }, + { FSL_IMX25_UART3_ADDR, FSL_IMX25_UART3_IRQ }, + { FSL_IMX25_UART4_ADDR, FSL_IMX25_UART4_IRQ }, + { FSL_IMX25_UART5_ADDR, FSL_IMX25_UART5_IRQ } + }; + + if (i < MAX_SERIAL_PORTS) { + CharDriverState *chr; + + chr = serial_hds[i]; + + if (!chr) { + char label[20]; + snprintf(label, sizeof(label), "imx31.uart%d", i); + chr = qemu_chr_new(label, "null", NULL); + } + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); + } + + object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + serial_table[i].irq)); + } + + /* Initialize all GPT timers */ + for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } gpt_table[FSL_IMX25_NUM_GPTS] = { + { FSL_IMX25_GPT1_ADDR, FSL_IMX25_GPT1_IRQ }, + { FSL_IMX25_GPT2_ADDR, FSL_IMX25_GPT2_IRQ }, + { FSL_IMX25_GPT3_ADDR, FSL_IMX25_GPT3_IRQ }, + { FSL_IMX25_GPT4_ADDR, FSL_IMX25_GPT4_IRQ } + }; + + s->gpt[i].ccm = DEVICE(&s->ccm); + + object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + gpt_table[i].irq)); + } + + /* Initialize all EPIT timers */ + for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } epit_table[FSL_IMX25_NUM_EPITS] = { + { FSL_IMX25_EPIT1_ADDR, FSL_IMX25_EPIT1_IRQ }, + { FSL_IMX25_EPIT2_ADDR, FSL_IMX25_EPIT2_IRQ } + }; + + s->epit[i].ccm = DEVICE(&s->ccm); + + object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + epit_table[i].irq)); + } + + qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]); + object_property_set_bool(OBJECT(&s->fec), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->fec), 0, FSL_IMX25_FEC_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->fec), 0, + qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX25_FEC_IRQ)); + + + /* Initialize all I2C */ + for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX25_NUM_I2CS] = { + { FSL_IMX25_I2C1_ADDR, FSL_IMX25_I2C1_IRQ }, + { FSL_IMX25_I2C2_ADDR, FSL_IMX25_I2C2_IRQ }, + { FSL_IMX25_I2C3_ADDR, FSL_IMX25_I2C3_IRQ } + }; + + object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + i2c_table[i].irq)); + } + + /* Initialize all GPIOs */ + for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } gpio_table[FSL_IMX25_NUM_GPIOS] = { + { FSL_IMX25_GPIO1_ADDR, FSL_IMX25_GPIO1_IRQ }, + { FSL_IMX25_GPIO2_ADDR, FSL_IMX25_GPIO2_IRQ }, + { FSL_IMX25_GPIO3_ADDR, FSL_IMX25_GPIO3_IRQ }, + { FSL_IMX25_GPIO4_ADDR, FSL_IMX25_GPIO4_IRQ } + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + /* Connect GPIO IRQ to PIC */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + gpio_table[i].irq)); + } + + /* initialize 2 x 16 KB ROM */ + memory_region_init_rom_device(&s->rom[0], NULL, NULL, NULL, + "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR, + &s->rom[0]); + memory_region_init_rom_device(&s->rom[1], NULL, NULL, NULL, + "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM1_ADDR, + &s->rom[1]); + + /* initialize internal RAM (128 KB) */ + memory_region_init_ram(&s->iram, NULL, "imx25.iram", FSL_IMX25_IRAM_SIZE, + &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ADDR, + &s->iram); + vmstate_register_ram_global(&s->iram); + + /* internal RAM (128 KB) is aliased over 128 MB - 128 KB */ + memory_region_init_alias(&s->iram_alias, NULL, "imx25.iram_alias", + &s->iram, 0, FSL_IMX25_IRAM_ALIAS_SIZE); + memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ALIAS_ADDR, + &s->iram_alias); +} + +static void fsl_imx25_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx25_realize; +} + +static const TypeInfo fsl_imx25_type_info = { + .name = TYPE_FSL_IMX25, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslIMX25State), + .instance_init = fsl_imx25_init, + .class_init = fsl_imx25_class_init, +}; + +static void fsl_imx25_register_types(void) +{ + type_register_static(&fsl_imx25_type_info); +} + +type_init(fsl_imx25_register_types) diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c new file mode 100644 index 0000000000..8e1ed4811b --- /dev/null +++ b/hw/arm/fsl-imx31.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2013 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * i.MX31 SOC emulation. + * + * Based on hw/arm/fsl-imx31.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/arm/fsl-imx31.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "hw/boards.h" +#include "sysemu/char.h" + +static void fsl_imx31_init(Object *obj) +{ + FslIMX31State *s = FSL_IMX31(obj); + int i; + + object_initialize(&s->cpu, sizeof(s->cpu), "arm1136-" TYPE_ARM_CPU); + + object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); + qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); + + object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM); + qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + + for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) { + object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); + qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + } + + object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX_GPT); + qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); + + for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) { + object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); + qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) { + object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); + qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + } + + for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) { + object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); + qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + } +} + +static void fsl_imx31_realize(DeviceState *dev, Error **errp) +{ + FslIMX31State *s = FSL_IMX31(dev); + uint16_t i; + Error *err = NULL; + + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->avic), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->avic), 0, FSL_IMX31_AVIC_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 0, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 1, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ)); + + object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX31_CCM_ADDR); + + /* Initialize all UARTS */ + for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX31_NUM_UARTS] = { + { FSL_IMX31_UART1_ADDR, FSL_IMX31_UART1_IRQ }, + { FSL_IMX31_UART2_ADDR, FSL_IMX31_UART2_IRQ }, + }; + + if (i < MAX_SERIAL_PORTS) { + CharDriverState *chr; + + chr = serial_hds[i]; + + if (!chr) { + char label[20]; + snprintf(label, sizeof(label), "imx31.uart%d", i); + chr = qemu_chr_new(label, "null", NULL); + } + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); + } + + object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + serial_table[i].irq)); + } + + s->gpt.ccm = DEVICE(&s->ccm); + + object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt), 0, FSL_IMX31_GPT_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt), 0, + qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX31_GPT_IRQ)); + + /* Initialize all EPIT timers */ + for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } epit_table[FSL_IMX31_NUM_EPITS] = { + { FSL_IMX31_EPIT1_ADDR, FSL_IMX31_EPIT1_IRQ }, + { FSL_IMX31_EPIT2_ADDR, FSL_IMX31_EPIT2_IRQ }, + }; + + s->epit[i].ccm = DEVICE(&s->ccm); + + object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + epit_table[i].irq)); + } + + /* Initialize all I2C */ + for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX31_NUM_I2CS] = { + { FSL_IMX31_I2C1_ADDR, FSL_IMX31_I2C1_IRQ }, + { FSL_IMX31_I2C2_ADDR, FSL_IMX31_I2C2_IRQ }, + { FSL_IMX31_I2C3_ADDR, FSL_IMX31_I2C3_IRQ } + }; + + /* Initialize the I2C */ + object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* Map I2C memory */ + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + /* Connect I2C IRQ to PIC */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + i2c_table[i].irq)); + } + + /* Initialize all GPIOs */ + for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } gpio_table[FSL_IMX31_NUM_GPIOS] = { + { FSL_IMX31_GPIO1_ADDR, FSL_IMX31_GPIO1_IRQ }, + { FSL_IMX31_GPIO2_ADDR, FSL_IMX31_GPIO2_IRQ }, + { FSL_IMX31_GPIO3_ADDR, FSL_IMX31_GPIO3_IRQ } + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), false, "has-edge-sel", + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + /* Connect GPIO IRQ to PIC */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + gpio_table[i].irq)); + } + + /* On a real system, the first 16k is a `secure boot rom' */ + memory_region_init_rom_device(&s->secure_rom, NULL, NULL, NULL, + "imx31.secure_rom", + FSL_IMX31_SECURE_ROM_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX31_SECURE_ROM_ADDR, + &s->secure_rom); + + /* There is also a 16k ROM */ + memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx31.rom", + FSL_IMX31_ROM_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX31_ROM_ADDR, + &s->rom); + + /* initialize internal RAM (16 KB) */ + memory_region_init_ram(&s->iram, NULL, "imx31.iram", FSL_IMX31_IRAM_SIZE, + &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ADDR, + &s->iram); + vmstate_register_ram_global(&s->iram); + + /* internal RAM (16 KB) is aliased over 256 MB - 16 KB */ + memory_region_init_alias(&s->iram_alias, NULL, "imx31.iram_alias", + &s->iram, 0, FSL_IMX31_IRAM_ALIAS_SIZE); + memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ALIAS_ADDR, + &s->iram_alias); +} + +static void fsl_imx31_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx31_realize; +} + +static const TypeInfo fsl_imx31_type_info = { + .name = TYPE_FSL_IMX31, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslIMX31State), + .instance_init = fsl_imx31_init, + .class_init = fsl_imx31_class_init, +}; + +static void fsl_imx31_register_types(void) +{ + type_register_static(&fsl_imx31_type_info); +} + +type_init(fsl_imx31_register_types) diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c new file mode 100644 index 0000000000..c34667f068 --- /dev/null +++ b/hw/arm/imx25_pdk.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2013 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * PDK Board System emulation. + * + * Based on hw/arm/kzm.c + * + * Copyright (c) 2008 OKL and 2011 NICTA + * Written by Hans at OK-Labs + * Updated by Peter Chubb. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/arm/fsl-imx25.h" +#include "hw/boards.h" +#include "qemu/error-report.h" +#include "exec/address-spaces.h" +#include "sysemu/qtest.h" +#include "hw/i2c/i2c.h" + +/* Memory map for PDK Emulation Baseboard: + * 0x00000000-0x7fffffff See i.MX25 SOC fr support + * 0x80000000-0x87ffffff RAM + Alias EMULATED + * 0x90000000-0x9fffffff RAM + Alias EMULATED + * 0xa0000000-0xa7ffffff Flash IGNORED + * 0xa8000000-0xafffffff Flash IGNORED + * 0xb0000000-0xb1ffffff SRAM IGNORED + * 0xb2000000-0xb3ffffff SRAM IGNORED + * 0xb4000000-0xb5ffffff CS4 IGNORED + * 0xb6000000-0xb8000fff Reserved IGNORED + * 0xb8001000-0xb8001fff SDRAM CTRL reg IGNORED + * 0xb8002000-0xb8002fff WEIM CTRL reg IGNORED + * 0xb8003000-0xb8003fff M3IF CTRL reg IGNORED + * 0xb8004000-0xb8004fff EMI CTRL reg IGNORED + * 0xb8005000-0xbaffffff Reserved IGNORED + * 0xbb000000-0xbb000fff NAND flash area buf IGNORED + * 0xbb001000-0xbb0011ff NAND flash reserved IGNORED + * 0xbb001200-0xbb001dff Reserved IGNORED + * 0xbb001e00-0xbb001fff NAN flash CTRL reg IGNORED + * 0xbb012000-0xbfffffff Reserved IGNORED + * 0xc0000000-0xffffffff Reserved IGNORED + */ + +typedef struct IMX25PDK { + FslIMX25State soc; + MemoryRegion ram; + MemoryRegion ram_alias; +} IMX25PDK; + +static struct arm_boot_info imx25_pdk_binfo; + +static void imx25_pdk_init(MachineState *machine) +{ + IMX25PDK *s = g_new0(IMX25PDK, 1); + Error *err = NULL; + unsigned int ram_size; + unsigned int alias_offset; + int i; + + object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX25); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + + object_property_set_bool(OBJECT(&s->soc), true, "realized", &err); + if (err != NULL) { + error_report("%s", error_get_pretty(err)); + exit(1); + } + + /* We need to initialize our memory */ + if (machine->ram_size > (FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE)) { + error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, " + "reduced to %x", machine->ram_size, + FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE); + machine->ram_size = FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE; + } + + memory_region_allocate_system_memory(&s->ram, NULL, "imx25.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), FSL_IMX25_SDRAM0_ADDR, + &s->ram); + + /* initialize the alias memory if any */ + for (i = 0, ram_size = machine->ram_size, alias_offset = 0; + (i < 2) && ram_size; i++) { + unsigned int size; + static const struct { + hwaddr addr; + unsigned int size; + } ram[2] = { + { FSL_IMX25_SDRAM0_ADDR, FSL_IMX25_SDRAM0_SIZE }, + { FSL_IMX25_SDRAM1_ADDR, FSL_IMX25_SDRAM1_SIZE }, + }; + + size = MIN(ram_size, ram[i].size); + + ram_size -= size; + + if (size < ram[i].size) { + memory_region_init_alias(&s->ram_alias, NULL, "ram.alias", + &s->ram, alias_offset, ram[i].size - size); + memory_region_add_subregion(get_system_memory(), + ram[i].addr + size, &s->ram_alias); + } + + alias_offset += ram[i].size; + } + + imx25_pdk_binfo.ram_size = machine->ram_size; + imx25_pdk_binfo.kernel_filename = machine->kernel_filename; + imx25_pdk_binfo.kernel_cmdline = machine->kernel_cmdline; + imx25_pdk_binfo.initrd_filename = machine->initrd_filename; + imx25_pdk_binfo.loader_start = FSL_IMX25_SDRAM0_ADDR; + imx25_pdk_binfo.board_id = 1771, + imx25_pdk_binfo.nb_cpus = 1; + + /* + * We test explicitly for qtest here as it is not done (yet?) in + * arm_load_kernel(). Without this the "make check" command would + * fail. + */ + if (!qtest_enabled()) { + arm_load_kernel(&s->soc.cpu, &imx25_pdk_binfo); + } else { + /* + * This I2C device doesn't exist on the real board. + * We add it here (only on qtest usage) to be able to do a bit + * of simple qtest. See "make check" for details. + */ + i2c_create_slave((I2CBus *)qdev_get_child_bus(DEVICE(&s->soc.i2c[0]), + "i2c"), + "ds1338", 0x68); + } +} + +static QEMUMachine imx25_pdk_machine = { + .name = "imx25_pdk", + .desc = "ARM i.MX25 PDK board (ARM926)", + .init = imx25_pdk_init, +}; + +static void imx25_pdk_machine_init(void) +{ + qemu_register_machine(&imx25_pdk_machine); +} + +machine_init(imx25_pdk_machine_init) diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index d7af230925..241b1d7819 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -13,131 +13,130 @@ * i.MX31 SoC */ -#include "hw/sysbus.h" +#include "hw/arm/fsl-imx31.h" +#include "hw/boards.h" +#include "qemu/error-report.h" #include "exec/address-spaces.h" -#include "hw/hw.h" -#include "hw/arm/arm.h" -#include "hw/devices.h" #include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" +#include "hw/devices.h" #include "hw/char/serial.h" -#include "hw/intc/imx_avic.h" -#include "hw/arm/imx.h" - - /* Memory map for Kzm Emulation Baseboard: - * 0x00000000-0x00003fff 16k secure ROM IGNORED - * 0x00004000-0x00407fff Reserved IGNORED - * 0x00404000-0x00407fff ROM IGNORED - * 0x00408000-0x0fffffff Reserved IGNORED - * 0x10000000-0x1fffbfff RAM aliasing IGNORED - * 0x1fffc000-0x1fffffff RAM EMULATED - * 0x20000000-0x2fffffff Reserved IGNORED - * 0x30000000-0x7fffffff I.MX31 Internal Register Space - * 0x43f00000 IO_AREA0 - * 0x43f90000 UART1 EMULATED - * 0x43f94000 UART2 EMULATED - * 0x68000000 AVIC EMULATED - * 0x53f80000 CCM EMULATED - * 0x53f94000 PIT 1 EMULATED - * 0x53f98000 PIT 2 EMULATED - * 0x53f90000 GPT EMULATED - * 0x80000000-0x87ffffff RAM EMULATED - * 0x88000000-0x8fffffff RAM Aliasing EMULATED - * 0xa0000000-0xafffffff NAND Flash IGNORED - * 0xb0000000-0xb3ffffff Unavailable IGNORED - * 0xb4000000-0xb4000fff 8-bit free space IGNORED - * 0xb4001000-0xb400100f Board control IGNORED - * 0xb4001003 DIP switch - * 0xb4001010-0xb400101f 7-segment LED IGNORED - * 0xb4001020-0xb400102f LED IGNORED - * 0xb4001030-0xb400103f LED IGNORED - * 0xb4001040-0xb400104f FPGA, UART EMULATED - * 0xb4001050-0xb400105f FPGA, UART EMULATED - * 0xb4001060-0xb40fffff FPGA IGNORED - * 0xb6000000-0xb61fffff LAN controller EMULATED - * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED - * 0xb6300000-0xb7ffffff Free IGNORED - * 0xb8000000-0xb8004fff Memory control registers IGNORED - * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED - * 0xc4000000-0xffffffff Reserved IGNORED - */ - -#define KZM_RAMADDRESS (0x80000000) -#define KZM_FPGA (0xb4001040) +#include "sysemu/qtest.h" + +/* Memory map for Kzm Emulation Baseboard: + * 0x00000000-0x7fffffff See i.MX31 SOC for support + * 0x80000000-0x8fffffff RAM EMULATED + * 0x90000000-0x9fffffff RAM EMULATED + * 0xa0000000-0xafffffff Flash IGNORED + * 0xb0000000-0xb3ffffff Unavailable IGNORED + * 0xb4000000-0xb4000fff 8-bit free space IGNORED + * 0xb4001000-0xb400100f Board control IGNORED + * 0xb4001003 DIP switch + * 0xb4001010-0xb400101f 7-segment LED IGNORED + * 0xb4001020-0xb400102f LED IGNORED + * 0xb4001030-0xb400103f LED IGNORED + * 0xb4001040-0xb400104f FPGA, UART EMULATED + * 0xb4001050-0xb400105f FPGA, UART EMULATED + * 0xb4001060-0xb40fffff FPGA IGNORED + * 0xb6000000-0xb61fffff LAN controller EMULATED + * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED + * 0xb6300000-0xb7ffffff Free IGNORED + * 0xb8000000-0xb8004fff Memory control registers IGNORED + * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED + * 0xc4000000-0xffffffff Reserved IGNORED + */ + +typedef struct IMX31KZM { + FslIMX31State soc; + MemoryRegion ram; + MemoryRegion ram_alias; +} IMX31KZM; + +#define KZM_RAM_ADDR (FSL_IMX31_SDRAM0_ADDR) +#define KZM_FPGA_ADDR (FSL_IMX31_CS4_ADDR + 0x1040) +#define KZM_LAN9118_ADDR (FSL_IMX31_CS5_ADDR) static struct arm_boot_info kzm_binfo = { - .loader_start = KZM_RAMADDRESS, + .loader_start = KZM_RAM_ADDR, .board_id = 1722, }; static void kzm_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; - const char *cpu_model = machine->cpu_model; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - ARMCPU *cpu; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *ram_alias = g_new(MemoryRegion, 1); - DeviceState *dev; - DeviceState *ccm; - - if (!cpu_model) { - cpu_model = "arm1136"; - } - - cpu = cpu_arm_init(cpu_model); - if (!cpu) { - fprintf(stderr, "Unable to find CPU definition\n"); + IMX31KZM *s = g_new0(IMX31KZM, 1); + Error *err = NULL; + unsigned int ram_size; + unsigned int alias_offset; + unsigned int i; + + object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX31); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + + object_property_set_bool(OBJECT(&s->soc), true, "realized", &err); + if (err != NULL) { + error_report("%s", error_get_pretty(err)); exit(1); } - /* On a real system, the first 16k is a `secure boot rom' */ - - memory_region_allocate_system_memory(ram, NULL, "kzm.ram", ram_size); - memory_region_add_subregion(address_space_mem, KZM_RAMADDRESS, ram); - - memory_region_init_alias(ram_alias, NULL, "ram.alias", ram, 0, ram_size); - memory_region_add_subregion(address_space_mem, 0x88000000, ram_alias); - - memory_region_init_ram(sram, NULL, "kzm.sram", 0x4000, &error_abort); - memory_region_add_subregion(address_space_mem, 0x1FFFC000, sram); - - dev = sysbus_create_varargs(TYPE_IMX_AVIC, 0x68000000, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ), - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ), - NULL); - - imx_serial_create(0, 0x43f90000, qdev_get_gpio_in(dev, 45)); - imx_serial_create(1, 0x43f94000, qdev_get_gpio_in(dev, 32)); - - ccm = sysbus_create_simple(TYPE_IMX_CCM, 0x53f80000, NULL); + /* Check the amount of memory is compatible with the SOC */ + if (machine->ram_size > (FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE)) { + error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, " + "reduced to %x", machine->ram_size, + FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE); + machine->ram_size = FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE; + } - imx_timerp_create(0x53f94000, qdev_get_gpio_in(dev, 28), ccm); - imx_timerp_create(0x53f98000, qdev_get_gpio_in(dev, 27), ccm); - imx_timerg_create(0x53f90000, qdev_get_gpio_in(dev, 29), ccm); + memory_region_allocate_system_memory(&s->ram, NULL, "kzm.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), FSL_IMX31_SDRAM0_ADDR, + &s->ram); + + /* initialize the alias memory if any */ + for (i = 0, ram_size = machine->ram_size, alias_offset = 0; + (i < 2) && ram_size; i++) { + unsigned int size; + static const struct { + hwaddr addr; + unsigned int size; + } ram[2] = { + { FSL_IMX31_SDRAM0_ADDR, FSL_IMX31_SDRAM0_SIZE }, + { FSL_IMX31_SDRAM1_ADDR, FSL_IMX31_SDRAM1_SIZE }, + }; + + size = MIN(ram_size, ram[i].size); + + ram_size -= size; + + if (size < ram[i].size) { + memory_region_init_alias(&s->ram_alias, NULL, "ram.alias", + &s->ram, alias_offset, ram[i].size - size); + memory_region_add_subregion(get_system_memory(), + ram[i].addr + size, &s->ram_alias); + } + + alias_offset += ram[i].size; + } if (nd_table[0].used) { - lan9118_init(&nd_table[0], 0xb6000000, qdev_get_gpio_in(dev, 52)); + lan9118_init(&nd_table[0], KZM_LAN9118_ADDR, + qdev_get_gpio_in(DEVICE(&s->soc.avic), 52)); } if (serial_hds[2]) { /* touchscreen */ - serial_mm_init(address_space_mem, KZM_FPGA+0x10, 0, - qdev_get_gpio_in(dev, 52), - 14745600, serial_hds[2], - DEVICE_NATIVE_ENDIAN); + serial_mm_init(get_system_memory(), KZM_FPGA_ADDR+0x10, 0, + qdev_get_gpio_in(DEVICE(&s->soc.avic), 52), + 14745600, serial_hds[2], DEVICE_NATIVE_ENDIAN); } - kzm_binfo.ram_size = ram_size; - kzm_binfo.kernel_filename = kernel_filename; - kzm_binfo.kernel_cmdline = kernel_cmdline; - kzm_binfo.initrd_filename = initrd_filename; + kzm_binfo.ram_size = machine->ram_size; + kzm_binfo.kernel_filename = machine->kernel_filename; + kzm_binfo.kernel_cmdline = machine->kernel_cmdline; + kzm_binfo.initrd_filename = machine->initrd_filename; kzm_binfo.nb_cpus = 1; - arm_load_kernel(cpu, &kzm_binfo); + + if (!qtest_enabled()) { + arm_load_kernel(&s->soc.cpu, &kzm_binfo); + } } static QEMUMachine kzm_machine = { diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index de2b289257..8873f9427c 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -258,8 +258,7 @@ static struct omap_mpu_timer_s *omap_mpu_timer_init(MemoryRegion *system_memory, hwaddr base, qemu_irq irq, omap_clk clk) { - struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) - g_malloc0(sizeof(struct omap_mpu_timer_s)); + struct omap_mpu_timer_s *s = g_new0(struct omap_mpu_timer_s, 1); s->irq = irq; s->clk = clk; @@ -388,8 +387,7 @@ static struct omap_watchdog_timer_s *omap_wd_timer_init(MemoryRegion *memory, hwaddr base, qemu_irq irq, omap_clk clk) { - struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) - g_malloc0(sizeof(struct omap_watchdog_timer_s)); + struct omap_watchdog_timer_s *s = g_new0(struct omap_watchdog_timer_s, 1); s->timer.irq = irq; s->timer.clk = clk; @@ -495,8 +493,7 @@ static struct omap_32khz_timer_s *omap_os_timer_init(MemoryRegion *memory, hwaddr base, qemu_irq irq, omap_clk clk) { - struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) - g_malloc0(sizeof(struct omap_32khz_timer_s)); + struct omap_32khz_timer_s *s = g_new0(struct omap_32khz_timer_s, 1); s->timer.irq = irq; s->timer.clk = clk; @@ -1236,8 +1233,7 @@ static struct omap_tipb_bridge_s *omap_tipb_bridge_init( MemoryRegion *memory, hwaddr base, qemu_irq abort_irq, omap_clk clk) { - struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) - g_malloc0(sizeof(struct omap_tipb_bridge_s)); + struct omap_tipb_bridge_s *s = g_new0(struct omap_tipb_bridge_s, 1); s->abort = abort_irq; omap_tipb_bridge_reset(s); @@ -2099,8 +2095,7 @@ static struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *memory, qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup, omap_clk clk) { - struct omap_mpuio_s *s = (struct omap_mpuio_s *) - g_malloc0(sizeof(struct omap_mpuio_s)); + struct omap_mpuio_s *s = g_new0(struct omap_mpuio_s, 1); s->irq = gpio_int; s->kbd_irq = kbd_int; @@ -2292,8 +2287,7 @@ static struct omap_uwire_s *omap_uwire_init(MemoryRegion *system_memory, qemu_irq dma, omap_clk clk) { - struct omap_uwire_s *s = (struct omap_uwire_s *) - g_malloc0(sizeof(struct omap_uwire_s)); + struct omap_uwire_s *s = g_new0(struct omap_uwire_s, 1); s->txirq = txirq; s->rxirq = rxirq; @@ -2932,8 +2926,7 @@ static struct omap_rtc_s *omap_rtc_init(MemoryRegion *system_memory, qemu_irq timerirq, qemu_irq alarmirq, omap_clk clk) { - struct omap_rtc_s *s = (struct omap_rtc_s *) - g_malloc0(sizeof(struct omap_rtc_s)); + struct omap_rtc_s *s = g_new0(struct omap_rtc_s, 1); s->irq = timerirq; s->alarm = alarmirq; @@ -3468,8 +3461,7 @@ static struct omap_mcbsp_s *omap_mcbsp_init(MemoryRegion *system_memory, qemu_irq txirq, qemu_irq rxirq, qemu_irq *dma, omap_clk clk) { - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) - g_malloc0(sizeof(struct omap_mcbsp_s)); + struct omap_mcbsp_s *s = g_new0(struct omap_mcbsp_s, 1); s->txirq = txirq; s->rxirq = rxirq; @@ -3648,8 +3640,7 @@ static void omap_lpg_clk_update(void *opaque, int line, int on) static struct omap_lpg_s *omap_lpg_init(MemoryRegion *system_memory, hwaddr base, omap_clk clk) { - struct omap_lpg_s *s = (struct omap_lpg_s *) - g_malloc0(sizeof(struct omap_lpg_s)); + struct omap_lpg_s *s = g_new0(struct omap_lpg_s, 1); s->tm = timer_new_ms(QEMU_CLOCK_VIRTUAL, omap_lpg_tick, s); @@ -3853,8 +3844,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, const char *core) { int i; - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) - g_malloc0(sizeof(struct omap_mpu_state_s)); + struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1); qemu_irq dma_irqs[6]; DriveInfo *dinfo; SysBusDevice *busdev; diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index e39b317290..1ee2d610f7 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -596,8 +596,7 @@ static const MemoryRegionOps omap_eac_ops = { static struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta, qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) { - struct omap_eac_s *s = (struct omap_eac_s *) - g_malloc0(sizeof(struct omap_eac_s)); + struct omap_eac_s *s = g_new0(struct omap_eac_s, 1); s->irq = irq; s->codec.rxdrq = *drq ++; @@ -788,8 +787,7 @@ static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, hwaddr channel_base, qemu_irq irq, omap_clk clk, CharDriverState *chr) { - struct omap_sti_s *s = (struct omap_sti_s *) - g_malloc0(sizeof(struct omap_sti_s)); + struct omap_sti_s *s = g_new0(struct omap_sti_s, 1); s->irq = irq; omap_sti_reset(s); @@ -1806,8 +1804,7 @@ static struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta, qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int, struct omap_mpu_state_s *mpu) { - struct omap_prcm_s *s = (struct omap_prcm_s *) - g_malloc0(sizeof(struct omap_prcm_s)); + struct omap_prcm_s *s = g_new0(struct omap_prcm_s, 1); s->irq[0] = mpu_int; s->irq[1] = dsp_int; @@ -2185,8 +2182,7 @@ static void omap_sysctl_reset(struct omap_sysctl_s *s) static struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta, omap_clk iclk, struct omap_mpu_state_s *mpu) { - struct omap_sysctl_s *s = (struct omap_sysctl_s *) - g_malloc0(sizeof(struct omap_sysctl_s)); + struct omap_sysctl_s *s = g_new0(struct omap_sysctl_s, 1); s->mpu = mpu; omap_sysctl_reset(s); @@ -2248,8 +2244,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, unsigned long sdram_size, const char *core) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) - g_malloc0(sizeof(struct omap_mpu_state_s)); + struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1); qemu_irq dma_irqs[4]; DriveInfo *dinfo; int i; diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index ec353f79c4..ec56b6172e 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1731,8 +1731,7 @@ static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem, hwaddr base, qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma) { - PXA2xxI2SState *s = (PXA2xxI2SState *) - g_malloc0(sizeof(PXA2xxI2SState)); + PXA2xxI2SState *s = g_new0(PXA2xxI2SState, 1); s->irq = irq; s->rx_dma = rx_dma; @@ -2061,7 +2060,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, PXA2xxState *s; int i; DriveInfo *dinfo; - s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState)); + s = g_new0(PXA2xxState, 1); if (revision && strncmp(revision, "pxa27", 5)) { fprintf(stderr, "Machine requires a PXA27x processor.\n"); @@ -2157,7 +2156,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); for (i = 0; pxa27x_ssp[i].io_base; i ++); - s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i); + s->ssp = g_new0(SSIBus *, i); for (i = 0; pxa27x_ssp[i].io_base; i ++) { DeviceState *dev; dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa27x_ssp[i].io_base, @@ -2202,7 +2201,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) int i; DriveInfo *dinfo; - s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState)); + s = g_new0(PXA2xxState, 1); s->cpu = cpu_arm_init("pxa255"); if (s->cpu == NULL) { @@ -2290,7 +2289,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); for (i = 0; pxa255_ssp[i].io_base; i ++); - s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i); + s->ssp = g_new0(SSIBus *, i); for (i = 0; pxa255_ssp[i].io_base; i ++) { DeviceState *dev; dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa255_ssp[i].io_base, diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index 5bf032a637..fdb1010386 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -1060,10 +1060,6 @@ static VMStateDescription vmstate_spitz_kbd = { }, }; -static Property spitz_keyboard_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void spitz_keyboard_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1071,7 +1067,6 @@ static void spitz_keyboard_class_init(ObjectClass *klass, void *data) k->init = spitz_keyboard_init; dc->vmsd = &vmstate_spitz_kbd; - dc->props = spitz_keyboard_properties; } static const TypeInfo spitz_keyboard_info = { diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index cb515ec765..ca4628b0fc 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -675,7 +675,7 @@ static int stellaris_sys_init(uint32_t base, qemu_irq irq, { ssys_state *s; - s = (ssys_state *)g_malloc0(sizeof(ssys_state)); + s = g_new0(ssys_state, 1); s->irq = irq; s->board = board; /* Most devices come preprogrammed with a MAC address in the user data. */ diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index da9fc1d51b..9624ecb586 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -1588,7 +1588,7 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem, StrongARMState *s; int i; - s = g_malloc0(sizeof(StrongARMState)); + s = g_new0(StrongARMState, 1); if (!rev) { rev = "sa1110-b5"; diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index da217884e6..0f8bf1ca5c 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -541,7 +541,7 @@ static void vexpress_common_init(MachineState *machine) { VexpressMachineState *vms = VEXPRESS_MACHINE(machine); VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine); - VEDBoardInfo *daughterboard = vmc->daughterboard;; + VEDBoardInfo *daughterboard = vmc->daughterboard; DeviceState *dev, *sysctl, *pl041; qemu_irq pic[64]; uint32_t sys_id; @@ -762,7 +762,7 @@ static void vexpress_a9_class_init(ObjectClass *oc, void *data) mc->name = TYPE_VEXPRESS_A9_MACHINE; mc->desc = "ARM Versatile Express for Cortex-A9"; - vmc->daughterboard = &a9_daughterboard;; + vmc->daughterboard = &a9_daughterboard; } static void vexpress_a15_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index f365140319..9088248c3a 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -159,7 +159,8 @@ static void acpi_dsdt_add_virtio(Aml *scope, } } -static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) +static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq, + bool use_highmem) { Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf; int i, bus_no; @@ -234,6 +235,17 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq) AML_ENTIRE_RANGE, 0x0000, 0x0000, size_pio - 1, base_pio, size_pio)); + if (use_highmem) { + hwaddr base_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].base; + hwaddr size_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].size; + + aml_append(rbuf, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, + base_mmio_high, base_mmio_high, 0x0000, + size_mmio_high)); + } + aml_append(method, aml_name_decl("RBUF", rbuf)); aml_append(method, aml_return(rbuf)); aml_append(dev, method); @@ -510,7 +522,8 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]); acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO], (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS); - acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE)); + acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE), + guest_info->use_highmem); aml_append(dsdt, scope); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index d5a84175c9..e9324f56bd 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -50,6 +50,7 @@ #include "hw/arm/fdt.h" #include "hw/intc/arm_gic_common.h" #include "kvm_arm.h" +#include "hw/smbios/smbios.h" /* Number of external interrupt lines to configure the GIC with */ #define NUM_IRQS 256 @@ -79,6 +80,7 @@ typedef struct { typedef struct { MachineState parent; bool secure; + bool highmem; } VirtMachineState; #define TYPE_VIRT_MACHINE "virt" @@ -119,6 +121,8 @@ static const MemMapEntry a15memmap[] = { [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 }, + /* Second PCIe window, 512GB wide at the 512GB boundary */ + [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL }, }; static const int a15irqmap[] = { @@ -284,9 +288,32 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) { int cpu; + int addr_cells = 1; + + /* + * From Documentation/devicetree/bindings/arm/cpus.txt + * On ARM v8 64-bit systems value should be set to 2, + * that corresponds to the MPIDR_EL1 register size. + * If MPIDR_EL1[63:32] value is equal to 0 on all CPUs + * in the system, #address-cells can be set to 1, since + * MPIDR_EL1[63:32] bits are not used for CPUs + * identification. + * + * Here we actually don't know whether our system is 32- or 64-bit one. + * The simplest way to go is to examine affinity IDs of all our CPUs. If + * at least one of them has Aff3 populated, we set #address-cells to 2. + */ + for (cpu = 0; cpu < vbi->smp_cpus; cpu++) { + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); + + if (armcpu->mp_affinity & ARM_AFF3_MASK) { + addr_cells = 2; + break; + } + } qemu_fdt_add_subnode(vbi->fdt, "/cpus"); - qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", addr_cells); qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0); for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) { @@ -303,7 +330,14 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) "enable-method", "psci"); } - qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", armcpu->mp_affinity); + if (addr_cells == 2) { + qemu_fdt_setprop_u64(vbi->fdt, nodename, "reg", + armcpu->mp_affinity); + } else { + qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", + armcpu->mp_affinity); + } + g_free(nodename); } } @@ -362,7 +396,7 @@ static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic) fdt_add_v2m_gic_node(vbi); } -static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic) +static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, bool secure) { /* We create a standalone GIC v2 */ DeviceState *gicdev; @@ -379,6 +413,9 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic) * interrupts; there are always 32 of the former (mandated by GIC spec). */ qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32); + if (!kvm_irqchip_in_kernel()) { + qdev_prop_set_bit(gicdev, "has-security-extensions", secure); + } qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); sysbus_mmio_map(gicbusdev, 0, vbi->memmap[VIRT_GIC_DIST].base); @@ -666,10 +703,13 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle, 0x7 /* PCI irq */); } -static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic) +static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, + bool use_highmem) { hwaddr base_mmio = vbi->memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = vbi->memmap[VIRT_PCIE_MMIO].size; + hwaddr base_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].base; + hwaddr size_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].size; hwaddr base_pio = vbi->memmap[VIRT_PCIE_PIO].base; hwaddr size_pio = vbi->memmap[VIRT_PCIE_PIO].size; hwaddr base_ecam = vbi->memmap[VIRT_PCIE_ECAM].base; @@ -706,6 +746,16 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic) mmio_reg, base_mmio, size_mmio); memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias); + if (use_highmem) { + /* Map high MMIO space */ + MemoryRegion *high_mmio_alias = g_new0(MemoryRegion, 1); + + memory_region_init_alias(high_mmio_alias, OBJECT(dev), "pcie-mmio-high", + mmio_reg, base_mmio_high, size_mmio_high); + memory_region_add_subregion(get_system_memory(), base_mmio_high, + high_mmio_alias); + } + /* Map IO port space */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); @@ -727,11 +777,23 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic) qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base_ecam, 2, size_ecam); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", - 1, FDT_PCI_RANGE_IOPORT, 2, 0, - 2, base_pio, 2, size_pio, - 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, - 2, base_mmio, 2, size_mmio); + + if (use_highmem) { + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", + 1, FDT_PCI_RANGE_IOPORT, 2, 0, + 2, base_pio, 2, size_pio, + 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, + 2, base_mmio, 2, size_mmio, + 1, FDT_PCI_RANGE_MMIO_64BIT, + 2, base_mmio_high, + 2, base_mmio_high, 2, size_mmio_high); + } else { + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", + 1, FDT_PCI_RANGE_IOPORT, 2, 0, + 2, base_pio, 2, size_pio, + 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, + 2, base_mmio, 2, size_mmio); + } qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1); create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename); @@ -788,12 +850,37 @@ static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) return board->fdt; } +static void virt_build_smbios(VirtGuestInfo *guest_info) +{ + FWCfgState *fw_cfg = guest_info->fw_cfg; + uint8_t *smbios_tables, *smbios_anchor; + size_t smbios_tables_len, smbios_anchor_len; + + if (!fw_cfg) { + return; + } + + smbios_set_defaults("QEMU", "QEMU Virtual Machine", + "1.0", false, true, SMBIOS_ENTRY_POINT_30); + + smbios_get_tables(NULL, 0, &smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len); + + if (smbios_anchor) { + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables", + smbios_tables, smbios_tables_len); + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor", + smbios_anchor, smbios_anchor_len); + } +} + static void virt_guest_info_machine_done(Notifier *notifier, void *data) { VirtGuestInfoState *guest_info_state = container_of(notifier, VirtGuestInfoState, machine_done); virt_acpi_setup(&guest_info_state->info); + virt_build_smbios(&guest_info_state->info); } static void machvirt_init(MachineState *machine) @@ -883,13 +970,13 @@ static void machvirt_init(MachineState *machine) create_flash(vbi); - create_gic(vbi, pic); + create_gic(vbi, pic, vms->secure); create_uart(vbi, pic); create_rtc(vbi, pic); - create_pcie(vbi, pic); + create_pcie(vbi, pic, vms->highmem); /* Create mmio transports, so the user can create virtio backends * (which will be automatically plugged in to the transports). If @@ -904,6 +991,7 @@ static void machvirt_init(MachineState *machine) guest_info->fw_cfg = fw_cfg_find(); guest_info->memmap = vbi->memmap; guest_info->irqmap = vbi->irqmap; + guest_info->use_highmem = vms->highmem; guest_info_state->machine_done.notify = virt_guest_info_machine_done; qemu_add_machine_init_done_notifier(&guest_info_state->machine_done); @@ -941,18 +1029,44 @@ static void virt_set_secure(Object *obj, bool value, Error **errp) vms->secure = value; } +static bool virt_get_highmem(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + return vms->highmem; +} + +static void virt_set_highmem(Object *obj, bool value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + vms->highmem = value; +} + static void virt_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); - /* EL3 is enabled by default on virt */ - vms->secure = true; + /* EL3 is disabled by default on virt: this makes us consistent + * between KVM and TCG for this board, and it also allows us to + * boot UEFI blobs which assume no TrustZone support. + */ + vms->secure = false; object_property_add_bool(obj, "secure", virt_get_secure, virt_set_secure, NULL); object_property_set_description(obj, "secure", "Set on/off to enable/disable the ARM " "Security Extensions (TrustZone)", NULL); + + /* High memory is enabled by default */ + vms->highmem = true; + object_property_add_bool(obj, "highmem", virt_get_highmem, + virt_set_highmem, NULL); + object_property_set_description(obj, "highmem", + "Set on/off to enable/disable using " + "physical address space above 32 bits", + NULL); } static void virt_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 388baef76e..21855420dd 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -28,6 +28,10 @@ #define GIC_DIST_ADDR 0xf9010000 #define GIC_CPU_ADDR 0xf9020000 +#define SATA_INTR 133 +#define SATA_ADDR 0xFD0C0000 +#define SATA_NUM_PORTS 2 + static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = { 0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000, }; @@ -90,6 +94,9 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_CADENCE_UART); qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); } + + object_initialize(&s->sata, sizeof(s->sata), TYPE_SYSBUS_AHCI); + qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default()); } static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) @@ -121,7 +128,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_APU_CPUS); object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS); @@ -162,16 +169,11 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) g_free(name); object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR, - "reset-cbar", &err); - if (err) { - error_propagate((errp), (err)); - return; - } - + "reset-cbar", &error_abort); object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } @@ -200,16 +202,11 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) g_free(name); object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs", - &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - + &error_abort); object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } } @@ -232,7 +229,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem[i]), 0, gem_addr[i]); @@ -243,13 +240,24 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, uart_addr[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, gic_spi[uart_intr[i]]); } + + object_property_set_int(OBJECT(&s->sata), SATA_NUM_PORTS, "num-ports", + &error_abort); + object_property_set_bool(OBJECT(&s->sata), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, SATA_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]); } static Property xlnx_zynqmp_props[] = { diff --git a/hw/audio/fmopl.c b/hw/audio/fmopl.c index adcef2d3b9..81c0c1be20 100644 --- a/hw/audio/fmopl.c +++ b/hw/audio/fmopl.c @@ -1177,7 +1177,7 @@ void OPLResetChip(FM_OPL *OPL) OPLWriteReg(OPL,0x03,0); /* Timer2 */ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); - /* reset OPerator paramater */ + /* reset operator parameter */ for( c = 0 ; c < OPL->max_ch ; c++ ) { OPL_CH *CH = &OPL->P_CH[c]; diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 40d4880326..5da41b23cf 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -805,7 +805,7 @@ static int nvme_init(PCIDevice *pci_dev) n->num_namespaces = 1; n->num_queues = 64; - n->reg_size = 1 << qemu_fls(0x1004 + 2 * (n->num_queues + 1) * 4); + n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4); n->ns_size = bs_size / (uint64_t)n->num_namespaces; n->namespaces = g_new0(NvmeNamespace, n->num_namespaces); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 1556c9cf5a..f9301ae541 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -731,7 +731,7 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); - if (__virtio_has_feature(features, VIRTIO_F_VERSION_1)) { + if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) { if (s->conf.scsi) { error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0"); return 0; @@ -782,10 +782,11 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) * * s->blk would erroneously be placed in writethrough mode. */ - if (!virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) { aio_context_acquire(blk_get_aio_context(s->blk)); blk_set_enable_write_cache(s->blk, - virtio_has_feature(vdev, VIRTIO_BLK_F_WCE)); + virtio_vdev_has_feature(vdev, + VIRTIO_BLK_F_WCE)); aio_context_release(blk_get_aio_context(s->blk)); } } diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 267d8a8c70..36d7398f4f 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -24,7 +24,6 @@ #include <stdarg.h> #include <string.h> #include <unistd.h> -#include <signal.h> #include <inttypes.h> #include <time.h> #include <fcntl.h> diff --git a/hw/bt/hci.c b/hw/bt/hci.c index 7ea3dc6b70..3fec435378 100644 --- a/hw/bt/hci.c +++ b/hw/bt/hci.c @@ -1151,8 +1151,7 @@ static void bt_hci_reset(struct bt_hci_s *hci) hci->event_mask[7] = 0x00; hci->device.inquiry_scan = 0; hci->device.page_scan = 0; - if (hci->device.lmp_name) - g_free((void *) hci->device.lmp_name); + g_free((void *) hci->device.lmp_name); hci->device.lmp_name = NULL; hci->device.class[0] = 0x00; hci->device.class[1] = 0x00; @@ -1829,8 +1828,7 @@ static void bt_submit_hci(struct HCIInfo *info, case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME): LENGTH_CHECK(change_local_name); - if (hci->device.lmp_name) - g_free((void *) hci->device.lmp_name); + g_free((void *) hci->device.lmp_name); hci->device.lmp_name = g_strndup(PARAM(change_local_name, name), sizeof(PARAM(change_local_name, name))); bt_hci_event_complete_status(hci, HCI_SUCCESS); @@ -2231,8 +2229,7 @@ static void bt_hci_done(struct HCIInfo *info) bt_device_done(&hci->device); - if (hci->device.lmp_name) - g_free((void *) hci->device.lmp_name); + g_free((void *) hci->device.lmp_name); /* Be gentle and send DISCONNECT to all connected peers and those * currently waiting for us to accept or reject a connection request. diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 7614e5860f..215f9622c9 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -234,10 +234,8 @@ static int fifo_empty_elements_number(Exynos4210UartFIFO *q) static void fifo_reset(Exynos4210UartFIFO *q) { - if (q->data != NULL) { - g_free(q->data); - q->data = NULL; - } + g_free(q->data); + q->data = NULL; q->data = (uint8_t *)g_malloc0(q->size); diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 801156db2d..e8f32c46c2 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -21,7 +21,6 @@ #include "hw/char/imx_serial.h" #include "sysemu/sysemu.h" #include "sysemu/char.h" -#include "hw/arm/imx.h" //#define DEBUG_SERIAL 1 #ifdef DEBUG_SERIAL @@ -334,40 +333,6 @@ static void imx_serial_init(Object *obj) sysbus_init_irq(sbd, &s->irq); } -void imx_serial_create(int uart, const hwaddr addr, qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *bus; - CharDriverState *chr; - const char chr_name[] = "serial"; - char label[ARRAY_SIZE(chr_name) + 1]; - - dev = qdev_create(NULL, TYPE_IMX_SERIAL); - - if (uart >= MAX_SERIAL_PORTS) { - hw_error("Cannot assign uart %d: QEMU supports only %d ports\n", - uart, MAX_SERIAL_PORTS); - } - chr = serial_hds[uart]; - if (!chr) { - snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, uart); - chr = qemu_chr_new(label, "null", NULL); - if (!(chr)) { - hw_error("Can't assign serial port to imx-uart%d.\n", uart); - } - } - - qdev_prop_set_chr(dev, "chardev", chr); - bus = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - if (addr != (hwaddr)-1) { - sysbus_mmio_map(bus, 0, addr); - } - sysbus_connect_irq(bus, 0, irq); - -} - - static Property imx_serial_properties[] = { DEFINE_PROP_CHR("chardev", IMXSerialState, chr), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 88f20943e4..278ce36cb0 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -55,8 +55,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base, qemu_irq txdma, qemu_irq rxdma, const char *label, CharDriverState *chr) { - struct omap_uart_s *s = (struct omap_uart_s *) - g_malloc0(sizeof(struct omap_uart_s)); + struct omap_uart_s *s = g_new0(struct omap_uart_s, 1); s->base = base; s->fclk = fclk; diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index bc56f5d520..be97058712 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -76,7 +76,7 @@ static VirtIOSerialPort *find_port_by_name(char *name) static bool use_multiport(VirtIOSerial *vser) { VirtIODevice *vdev = VIRTIO_DEVICE(vser); - return virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT); + return virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT); } static size_t write_to_port(VirtIOSerialPort *port, diff --git a/hw/core/loader.c b/hw/core/loader.c index 216eeeb917..a96a74e579 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -594,8 +594,7 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, ret = hdr->ih_size; out: - if (data) - g_free(data); + g_free(data); close(fd); return ret; } diff --git a/hw/core/machine.c b/hw/core/machine.c index ac4654e9dd..51ed6b2e05 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -226,6 +226,20 @@ static void machine_set_usb(Object *obj, bool value, Error **errp) ms->usb_disabled = !value; } +static bool machine_get_igd_gfx_passthru(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->igd_gfx_passthru; +} + +static void machine_set_igd_gfx_passthru(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->igd_gfx_passthru = value; +} + static char *machine_get_firmware(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -388,6 +402,12 @@ static void machine_initfn(Object *obj) object_property_set_description(obj, "usb", "Set on/off to enable/disable usb", NULL); + object_property_add_bool(obj, "igd-passthru", + machine_get_igd_gfx_passthru, + machine_set_igd_gfx_passthru, NULL); + object_property_set_description(obj, "igd-passthru", + "Set on/off to enable/disable igd passthrou", + NULL); object_property_add_str(obj, "firmware", machine_get_firmware, machine_set_firmware, NULL); diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 04fd80a4de..33e245e12f 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -422,9 +422,7 @@ static void set_string(Object *obj, Visitor *v, void *opaque, error_propagate(errp, local_err); return; } - if (*ptr) { - g_free(*ptr); - } + g_free(*ptr); *ptr = str; } diff --git a/hw/core/qdev.c b/hw/core/qdev.c index b2f404a765..a24493a48d 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -506,7 +506,7 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n) return ret; } -/* disconnect a GPIO ouput, returning the disconnected input (if any) */ +/* disconnect a GPIO output, returning the disconnected input (if any) */ static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev, const char *name, int n) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 58ac02e610..94e8cc1a66 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -52,10 +52,23 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) SysBusDevice *busdev; int i; Error *err = NULL; + bool has_el3; + Object *cpuobj; gicdev = DEVICE(&s->gic); qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); + + if (!kvm_irqchip_in_kernel()) { + /* Make the GIC's TZ support match the CPUs. We assume that + * either all the CPUs have TZ, or none do. + */ + cpuobj = OBJECT(qemu_get_cpu(0)); + has_el3 = object_property_find(cpuobj, "has_el3", NULL) && + object_property_get_bool(cpuobj, "has_el3", &error_abort); + qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3); + } + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index c09358c6e7..869818cd8a 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -49,6 +49,8 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) *wdtbusdev; Error *err = NULL; int i; + bool has_el3; + Object *cpuobj; scudev = DEVICE(&s->scu); qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu); @@ -62,6 +64,15 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) gicdev = DEVICE(&s->gic); qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); + + /* Make the GIC's TZ support match the CPUs. We assume that + * either all the CPUs have TZ, or none do. + */ + cpuobj = OBJECT(qemu_get_cpu(0)); + has_el3 = object_property_find(cpuobj, "has_el3", NULL) && + object_property_get_bool(cpuobj, "has_el3", &error_abort); + qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3); + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 3cae480fa2..2e8c2c74fb 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -138,7 +138,7 @@ static void tempsensor_clkedge(struct tempsensor_t *s, s->count = 16; if ((s->regs[0] & 0xff) == 0) { - /* 25 degrees celcius. */ + /* 25 degrees celsius. */ s->shiftreg = 0x0b9f; } else if ((s->regs[0] & 0xff) == 0xff) { /* Sensor ID, 0x8100 LM70. */ diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 603ef50568..45239e836a 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1354,9 +1354,7 @@ static void exynos4210_fimd_reset(DeviceState *d) fimd_update_get_alpha(s, w); } - if (s->ifb != NULL) { - g_free(s->ifb); - } + g_free(s->ifb); s->ifb = NULL; exynos4210_fimd_invalidate(s); diff --git a/hw/display/omap_dss.c b/hw/display/omap_dss.c index f1fef2767e..b1c7af5819 100644 --- a/hw/display/omap_dss.c +++ b/hw/display/omap_dss.c @@ -1051,8 +1051,7 @@ struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta, omap_clk fck1, omap_clk fck2, omap_clk ck54m, omap_clk ick1, omap_clk ick2) { - struct omap_dss_s *s = (struct omap_dss_s *) - g_malloc0(sizeof(struct omap_dss_s)); + struct omap_dss_s *s = g_new0(struct omap_dss_s, 1); s->irq = irq; s->drq = drq; diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c index a7c6cd79b9..678f9a1b42 100644 --- a/hw/display/omap_lcdc.c +++ b/hw/display/omap_lcdc.c @@ -403,8 +403,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, struct omap_dma_lcd_channel_s *dma, omap_clk clk) { - struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) - g_malloc0(sizeof(struct omap_lcd_panel_s)); + struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1); s->irq = irq; s->dma = dma; diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index a542087fcc..7e4ef1ed04 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -159,7 +159,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) /* * use ssd.lock to protect render_update_cookie_num. * qxl_render_update is called by io thread or vcpu thread, and the completion - * callbacks are called by spice_server thread, defering to bh called from the + * callbacks are called by spice_server thread, deferring to bh called from the * io thread. */ void qxl_render_update(PCIQXLDevice *qxl) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index 97c57a03c0..db68730990 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -1626,8 +1626,7 @@ struct soc_dma_s *omap_dma_init(hwaddr base, qemu_irq *irqs, enum omap_dma_model model) { int num_irqs, memsize, i; - struct omap_dma_s *s = (struct omap_dma_s *) - g_malloc0(sizeof(struct omap_dma_s)); + struct omap_dma_s *s = g_new0(struct omap_dma_s, 1); if (model <= omap_dma_3_1) { num_irqs = 6; @@ -2061,8 +2060,7 @@ struct soc_dma_s *omap_dma4_init(hwaddr base, qemu_irq *irqs, int chans, omap_clk iclk, omap_clk fclk) { int i; - struct omap_dma_s *s = (struct omap_dma_s *) - g_malloc0(sizeof(struct omap_dma_s)); + struct omap_dma_s *s = g_new0(struct omap_dma_s, 1); s->model = omap_dma_4; s->chans = chans; diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index cf842a3cc7..b1cfa11356 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -133,7 +133,7 @@ struct XilinxAXIDMA { }; /* - * Helper calls to extract info from desriptors and other trivial + * Helper calls to extract info from descriptors and other trivial * state from regs. */ static inline int stream_desc_sof(struct SDesc *d) diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index 1abcf17988..52233f7e2f 100644 --- a/hw/gpio/Makefile.objs +++ b/hw/gpio/Makefile.objs @@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o common-obj-$(CONFIG_E500) += mpc8xxx.o obj-$(CONFIG_OMAP) += omap_gpio.o +obj-$(CONFIG_IMX) += imx_gpio.o diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c new file mode 100644 index 0000000000..d56ffcd8d7 --- /dev/null +++ b/hw/gpio/imx_gpio.c @@ -0,0 +1,340 @@ +/* + * i.MX processors GPIO emulation. + * + * Copyright (C) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/gpio/imx_gpio.h" + +#ifndef DEBUG_IMX_GPIO +#define DEBUG_IMX_GPIO 0 +#endif + +typedef enum IMXGPIOLevel { + IMX_GPIO_LEVEL_LOW = 0, + IMX_GPIO_LEVEL_HIGH = 1, +} IMXGPIOLevel; + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_GPIO) { \ + fprintf(stderr, "%s: " fmt , __func__, ##args); \ + } \ + } while (0) + +static const char *imx_gpio_reg_name(uint32_t reg) +{ + switch (reg) { + case DR_ADDR: + return "DR"; + case GDIR_ADDR: + return "GDIR"; + case PSR_ADDR: + return "PSR"; + case ICR1_ADDR: + return "ICR1"; + case ICR2_ADDR: + return "ICR2"; + case IMR_ADDR: + return "IMR"; + case ISR_ADDR: + return "ISR"; + case EDGE_SEL_ADDR: + return "EDGE_SEL"; + default: + return "[?]"; + } +} + +static void imx_gpio_update_int(IMXGPIOState *s) +{ + qemu_set_irq(s->irq, (s->isr & s->imr) ? 1 : 0); +} + +static void imx_gpio_set_int_line(IMXGPIOState *s, int line, IMXGPIOLevel level) +{ + /* if this signal isn't configured as an input signal, nothing to do */ + if (!extract32(s->gdir, line, 1)) { + return; + } + + /* When set, EDGE_SEL overrides the ICR config */ + if (extract32(s->edge_sel, line, 1)) { + /* we detect interrupt on rising and falling edge */ + if (extract32(s->psr, line, 1) != level) { + /* level changed */ + s->isr = deposit32(s->isr, line, 1, 1); + } + } else if (extract64(s->icr, 2*line + 1, 1)) { + /* interrupt is edge sensitive */ + if (extract32(s->psr, line, 1) != level) { + /* level changed */ + if (extract64(s->icr, 2*line, 1) != level) { + s->isr = deposit32(s->isr, line, 1, 1); + } + } + } else { + /* interrupt is level sensitive */ + if (extract64(s->icr, 2*line, 1) == level) { + s->isr = deposit32(s->isr, line, 1, 1); + } + } +} + +static void imx_gpio_set(void *opaque, int line, int level) +{ + IMXGPIOState *s = IMX_GPIO(opaque); + IMXGPIOLevel imx_level = level ? IMX_GPIO_LEVEL_HIGH : IMX_GPIO_LEVEL_LOW; + + imx_gpio_set_int_line(s, line, imx_level); + + /* this is an input signal, so set PSR */ + s->psr = deposit32(s->psr, line, 1, imx_level); + + imx_gpio_update_int(s); +} + +static void imx_gpio_set_all_int_lines(IMXGPIOState *s) +{ + int i; + + for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) { + IMXGPIOLevel imx_level = extract32(s->psr, i, 1); + imx_gpio_set_int_line(s, i, imx_level); + } + + imx_gpio_update_int(s); +} + +static inline void imx_gpio_set_all_output_lines(IMXGPIOState *s) +{ + int i; + + for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) { + /* + * if the line is set as output, then forward the line + * level to its user. + */ + if (extract32(s->gdir, i, 1) && s->output[i]) { + qemu_set_irq(s->output[i], extract32(s->dr, i, 1)); + } + } +} + +static uint64_t imx_gpio_read(void *opaque, hwaddr offset, unsigned size) +{ + IMXGPIOState *s = IMX_GPIO(opaque); + uint32_t reg_value = 0; + + switch (offset) { + case DR_ADDR: + /* + * depending on the "line" configuration, the bit values + * are coming either from DR or PSR + */ + reg_value = (s->dr & s->gdir) | (s->psr & ~s->gdir); + break; + + case GDIR_ADDR: + reg_value = s->gdir; + break; + + case PSR_ADDR: + reg_value = s->psr & ~s->gdir; + break; + + case ICR1_ADDR: + reg_value = extract64(s->icr, 0, 32); + break; + + case ICR2_ADDR: + reg_value = extract64(s->icr, 32, 32); + break; + + case IMR_ADDR: + reg_value = s->imr; + break; + + case ISR_ADDR: + reg_value = s->isr; + break; + + case EDGE_SEL_ADDR: + if (s->has_edge_sel) { + reg_value = s->edge_sel; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: EDGE_SEL register not " + "present on this version of GPIO device\n", + TYPE_IMX_GPIO, __func__); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad register at offset %d\n", + TYPE_IMX_GPIO, __func__, (int)offset); + break; + } + + DPRINTF("(%s) = 0x%"PRIx32"\n", imx_gpio_reg_name(offset), reg_value); + + return reg_value; +} + +static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMXGPIOState *s = IMX_GPIO(opaque); + + DPRINTF("(%s, value = 0x%"PRIx32")\n", imx_gpio_reg_name(offset), + (uint32_t)value); + + switch (offset) { + case DR_ADDR: + s->dr = value; + imx_gpio_set_all_output_lines(s); + break; + + case GDIR_ADDR: + s->gdir = value; + imx_gpio_set_all_output_lines(s); + imx_gpio_set_all_int_lines(s); + break; + + case ICR1_ADDR: + s->icr = deposit64(s->icr, 0, 32, value); + imx_gpio_set_all_int_lines(s); + break; + + case ICR2_ADDR: + s->icr = deposit64(s->icr, 32, 32, value); + imx_gpio_set_all_int_lines(s); + break; + + case IMR_ADDR: + s->imr = value; + imx_gpio_update_int(s); + break; + + case ISR_ADDR: + s->isr |= ~value; + imx_gpio_set_all_int_lines(s); + break; + + case EDGE_SEL_ADDR: + if (s->has_edge_sel) { + s->edge_sel = value; + imx_gpio_set_all_int_lines(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: EDGE_SEL register not " + "present on this version of GPIO device\n", + TYPE_IMX_GPIO, __func__); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad register at offset %d\n", + TYPE_IMX_GPIO, __func__, (int)offset); + break; + } + + return; +} + +static const MemoryRegionOps imx_gpio_ops = { + .read = imx_gpio_read, + .write = imx_gpio_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_imx_gpio = { + .name = TYPE_IMX_GPIO, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(dr, IMXGPIOState), + VMSTATE_UINT32(gdir, IMXGPIOState), + VMSTATE_UINT32(psr, IMXGPIOState), + VMSTATE_UINT64(icr, IMXGPIOState), + VMSTATE_UINT32(imr, IMXGPIOState), + VMSTATE_UINT32(isr, IMXGPIOState), + VMSTATE_BOOL(has_edge_sel, IMXGPIOState), + VMSTATE_UINT32(edge_sel, IMXGPIOState), + VMSTATE_END_OF_LIST() + } +}; + +static Property imx_gpio_properties[] = { + DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void imx_gpio_reset(DeviceState *dev) +{ + IMXGPIOState *s = IMX_GPIO(dev); + + s->dr = 0; + s->gdir = 0; + s->psr = 0; + s->icr = 0; + s->imr = 0; + s->isr = 0; + s->edge_sel = 0; + + imx_gpio_set_all_output_lines(s); + imx_gpio_update_int(s); +} + +static void imx_gpio_realize(DeviceState *dev, Error **errp) +{ + IMXGPIOState *s = IMX_GPIO(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpio_ops, s, + TYPE_IMX_GPIO, IMX_GPIO_MEM_SIZE); + + qdev_init_gpio_in(DEVICE(s), imx_gpio_set, IMX_GPIO_PIN_COUNT); + qdev_init_gpio_out(DEVICE(s), s->output, IMX_GPIO_PIN_COUNT); + + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); +} + +static void imx_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx_gpio_realize; + dc->reset = imx_gpio_reset; + dc->props = imx_gpio_properties; + dc->vmsd = &vmstate_imx_gpio; + dc->desc = "i.MX GPIO controller"; +} + +static const TypeInfo imx_gpio_info = { + .name = TYPE_IMX_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXGPIOState), + .class_init = imx_gpio_class_init, +}; + +static void imx_gpio_register_types(void) +{ + type_register_static(&imx_gpio_info); +} + +type_init(imx_gpio_register_types) diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index d92f8cfbae..3c538985ee 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -710,8 +710,8 @@ static int omap2_gpio_init(SysBusDevice *sbd) } else { s->modulecount = 6; } - s->modules = g_malloc0(s->modulecount * sizeof(struct omap2_gpio_s)); - s->handler = g_malloc0(s->modulecount * 32 * sizeof(qemu_irq)); + s->modules = g_new0(struct omap2_gpio_s, s->modulecount); + s->handler = g_new0(qemu_irq, s->modulecount * 32); qdev_init_gpio_in(dev, omap2_gpio_set, s->modulecount * 32); qdev_init_gpio_out(dev, s->handler, s->modulecount * 32); for (i = 0; i < s->modulecount; i++) { diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c index 24a77272d7..aa8ee5fa0e 100644 --- a/hw/gpio/zaurus.c +++ b/hw/gpio/zaurus.c @@ -235,10 +235,6 @@ static const VMStateDescription vmstate_scoop_regs = { }, }; -static Property scoop_sysbus_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void scoop_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -247,7 +243,6 @@ static void scoop_sysbus_class_init(ObjectClass *klass, void *data) k->init = scoop_init; dc->desc = "Scoop2 Sharp custom ASIC"; dc->vmsd = &vmstate_scoop_regs; - dc->props = scoop_sysbus_properties; } static const TypeInfo scoop_sysbus_info = { diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs index 0f130608c1..aeb8f38d70 100644 --- a/hw/i2c/Makefile.objs +++ b/hw/i2c/Makefile.objs @@ -4,4 +4,5 @@ common-obj-$(CONFIG_ACPI_X86) += smbus_ich9.o common-obj-$(CONFIG_APM) += pm_smbus.o common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o +common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o obj-$(CONFIG_OMAP) += omap_i2c.o diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c new file mode 100644 index 0000000000..8474872e07 --- /dev/null +++ b/hw/i2c/imx_i2c.c @@ -0,0 +1,334 @@ +/* + * i.MX I2C Bus Serial Interface Emulation + * + * Copyright (C) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "hw/i2c/imx_i2c.h" +#include "hw/i2c/i2c.h" + +#ifndef IMX_I2C_DEBUG +#define IMX_I2C_DEBUG 0 +#endif + +#if IMX_I2C_DEBUG +#define DPRINT(fmt, args...) \ + do { fprintf(stderr, "%s: "fmt, __func__, ## args); } while (0) + +static const char *imx_i2c_get_regname(unsigned offset) +{ + switch (offset) { + case IADR_ADDR: + return "IADR"; + case IFDR_ADDR: + return "IFDR"; + case I2CR_ADDR: + return "I2CR"; + case I2SR_ADDR: + return "I2SR"; + case I2DR_ADDR: + return "I2DR"; + default: + return "[?]"; + } +} +#else +#define DPRINT(fmt, args...) do { } while (0) +#endif + +static inline bool imx_i2c_is_enabled(IMXI2CState *s) +{ + return s->i2cr & I2CR_IEN; +} + +static inline bool imx_i2c_interrupt_is_enabled(IMXI2CState *s) +{ + return s->i2cr & I2CR_IIEN; +} + +static inline bool imx_i2c_is_master(IMXI2CState *s) +{ + return s->i2cr & I2CR_MSTA; +} + +static void imx_i2c_reset(DeviceState *dev) +{ + IMXI2CState *s = IMX_I2C(dev); + + if (s->address != ADDR_RESET) { + i2c_end_transfer(s->bus); + } + + s->address = ADDR_RESET; + s->iadr = IADR_RESET; + s->ifdr = IFDR_RESET; + s->i2cr = I2CR_RESET; + s->i2sr = I2SR_RESET; + s->i2dr_read = I2DR_RESET; + s->i2dr_write = I2DR_RESET; +} + +static inline void imx_i2c_raise_interrupt(IMXI2CState *s) +{ + /* + * raise an interrupt if the device is enabled and it is configured + * to generate some interrupts. + */ + if (imx_i2c_is_enabled(s) && imx_i2c_interrupt_is_enabled(s)) { + s->i2sr |= I2SR_IIF; + qemu_irq_raise(s->irq); + } +} + +static uint64_t imx_i2c_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint16_t value; + IMXI2CState *s = IMX_I2C(opaque); + + switch (offset) { + case IADR_ADDR: + value = s->iadr; + break; + case IFDR_ADDR: + value = s->ifdr; + break; + case I2CR_ADDR: + value = s->i2cr; + break; + case I2SR_ADDR: + value = s->i2sr; + break; + case I2DR_ADDR: + value = s->i2dr_read; + + if (imx_i2c_is_master(s)) { + int ret = 0xff; + + if (s->address == ADDR_RESET) { + /* something is wrong as the address is not set */ + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Trying to read " + "without specifying the slave address\n", + TYPE_IMX_I2C, __func__); + } else if (s->i2cr & I2CR_MTX) { + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Trying to read " + "but MTX is set\n", TYPE_IMX_I2C, __func__); + } else { + /* get the next byte */ + ret = i2c_recv(s->bus); + + if (ret >= 0) { + imx_i2c_raise_interrupt(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: read failed " + "for device 0x%02x\n", TYPE_IMX_I2C, + __func__, s->address); + ret = 0xff; + } + } + + s->i2dr_read = ret; + } else { + qemu_log_mask(LOG_UNIMP, "%s[%s]: slave mode not implemented\n", + TYPE_IMX_I2C, __func__); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", + TYPE_IMX_I2C, __func__, s->address); + value = 0; + break; + } + + DPRINT("read %s [0x%02x] -> 0x%02x\n", imx_i2c_get_regname(offset), + (unsigned int)offset, value); + + return (uint64_t)value; +} + +static void imx_i2c_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMXI2CState *s = IMX_I2C(opaque); + + DPRINT("write %s [0x%02x] <- 0x%02x\n", imx_i2c_get_regname(offset), + (unsigned int)offset, (int)value); + + value &= 0xff; + + switch (offset) { + case IADR_ADDR: + s->iadr = value & IADR_MASK; + /* i2c_set_slave_address(s->bus, (uint8_t)s->iadr); */ + break; + case IFDR_ADDR: + s->ifdr = value & IFDR_MASK; + break; + case I2CR_ADDR: + if (imx_i2c_is_enabled(s) && ((value & I2CR_IEN) == 0)) { + /* This is a soft reset. IADR is preserved during soft resets */ + uint16_t iadr = s->iadr; + imx_i2c_reset(DEVICE(s)); + s->iadr = iadr; + } else { /* normal write */ + s->i2cr = value & I2CR_MASK; + + if (imx_i2c_is_master(s)) { + /* set the bus to busy */ + s->i2sr |= I2SR_IBB; + } else { /* slave mode */ + /* bus is not busy anymore */ + s->i2sr &= ~I2SR_IBB; + + /* + * if we unset the master mode then it ends the ongoing + * transfer if any + */ + if (s->address != ADDR_RESET) { + i2c_end_transfer(s->bus); + s->address = ADDR_RESET; + } + } + + if (s->i2cr & I2CR_RSTA) { /* Restart */ + /* if this is a restart then it ends the ongoing transfer */ + if (s->address != ADDR_RESET) { + i2c_end_transfer(s->bus); + s->address = ADDR_RESET; + s->i2cr &= ~I2CR_RSTA; + } + } + } + break; + case I2SR_ADDR: + /* + * if the user writes 0 to IIF then lower the interrupt and + * reset the bit + */ + if ((s->i2sr & I2SR_IIF) && !(value & I2SR_IIF)) { + s->i2sr &= ~I2SR_IIF; + qemu_irq_lower(s->irq); + } + + /* + * if the user writes 0 to IAL, reset the bit + */ + if ((s->i2sr & I2SR_IAL) && !(value & I2SR_IAL)) { + s->i2sr &= ~I2SR_IAL; + } + + break; + case I2DR_ADDR: + /* if the device is not enabled, nothing to do */ + if (!imx_i2c_is_enabled(s)) { + break; + } + + s->i2dr_write = value & I2DR_MASK; + + if (imx_i2c_is_master(s)) { + /* If this is the first write cycle then it is the slave addr */ + if (s->address == ADDR_RESET) { + if (i2c_start_transfer(s->bus, extract32(s->i2dr_write, 1, 7), + extract32(s->i2dr_write, 0, 1))) { + /* if non zero is returned, the adress is not valid */ + s->i2sr |= I2SR_RXAK; + } else { + s->address = s->i2dr_write; + s->i2sr &= ~I2SR_RXAK; + imx_i2c_raise_interrupt(s); + } + } else { /* This is a normal data write */ + if (i2c_send(s->bus, s->i2dr_write)) { + /* if the target return non zero then end the transfer */ + s->i2sr |= I2SR_RXAK; + s->address = ADDR_RESET; + i2c_end_transfer(s->bus); + } else { + s->i2sr &= ~I2SR_RXAK; + imx_i2c_raise_interrupt(s); + } + } + } else { + qemu_log_mask(LOG_UNIMP, "%s[%s]: slave mode not implemented\n", + TYPE_IMX_I2C, __func__); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", + TYPE_IMX_I2C, __func__, s->address); + break; + } +} + +static const MemoryRegionOps imx_i2c_ops = { + .read = imx_i2c_read, + .write = imx_i2c_write, + .valid.min_access_size = 1, + .valid.max_access_size = 2, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription imx_i2c_vmstate = { + .name = TYPE_IMX_I2C, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(address, IMXI2CState), + VMSTATE_UINT16(iadr, IMXI2CState), + VMSTATE_UINT16(ifdr, IMXI2CState), + VMSTATE_UINT16(i2cr, IMXI2CState), + VMSTATE_UINT16(i2sr, IMXI2CState), + VMSTATE_UINT16(i2dr_read, IMXI2CState), + VMSTATE_UINT16(i2dr_write, IMXI2CState), + VMSTATE_END_OF_LIST() + } +}; + +static void imx_i2c_realize(DeviceState *dev, Error **errp) +{ + IMXI2CState *s = IMX_I2C(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &imx_i2c_ops, s, TYPE_IMX_I2C, + IMX_I2C_MEM_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + s->bus = i2c_init_bus(DEVICE(dev), "i2c"); +} + +static void imx_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &imx_i2c_vmstate; + dc->reset = imx_i2c_reset; + dc->realize = imx_i2c_realize; +} + +static const TypeInfo imx_i2c_type_info = { + .name = TYPE_IMX_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXI2CState), + .class_init = imx_i2c_class_init, +}; + +static void imx_i2c_register_types(void) +{ + type_register_static(&imx_i2c_type_info); +} + +type_init(imx_i2c_register_types) diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index ebd1015a08..c250deb848 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -7,6 +7,7 @@ obj-$(CONFIG_XEN) += ../xenpv/ xen/ obj-y += kvmvapic.o obj-y += acpi-build.o +obj-y += pci-assign-load-rom.o gen-hex-y += hw/i386/acpi-dsdt.hex gen-hex-y += hw/i386/q35-acpi-dsdt.hex diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c index 74d22f4fd2..b1beaa66b2 100644 --- a/hw/i386/kvm/pci-assign.c +++ b/hw/i386/kvm/pci-assign.c @@ -37,6 +37,7 @@ #include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "kvm_i386.h" +#include "hw/pci/pci-assign.h" #define MSIX_PAGE_SIZE 0x1000 @@ -48,17 +49,6 @@ #define IORESOURCE_PREFETCH 0x00002000 /* No side effects */ #define IORESOURCE_MEM_64 0x00100000 -//#define DEVICE_ASSIGNMENT_DEBUG - -#ifdef DEVICE_ASSIGNMENT_DEBUG -#define DEBUG(fmt, ...) \ - do { \ - fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \ - } while (0) -#else -#define DEBUG(fmt, ...) -#endif - typedef struct PCIRegion { int type; /* Memory or port I/O */ int valid; @@ -1896,73 +1886,15 @@ static void assign_register_types(void) type_init(assign_register_types) -/* - * Scan the assigned devices for the devices that have an option ROM, and then - * load the corresponding ROM data to RAM. If an error occurs while loading an - * option ROM, we just ignore that option ROM and continue with the next one. - */ static void assigned_dev_load_option_rom(AssignedDevice *dev) { - char name[32], rom_file[64]; - FILE *fp; - uint8_t val; - struct stat st; - void *ptr; - - /* If loading ROM from file, pci handles it */ - if (dev->dev.romfile || !dev->dev.rom_bar) { - return; - } + int size = 0; - snprintf(rom_file, sizeof(rom_file), - "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom", - dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function); + pci_assign_dev_load_option_rom(&dev->dev, OBJECT(dev), &size, + dev->host.domain, dev->host.bus, + dev->host.slot, dev->host.function); - if (stat(rom_file, &st)) { - return; - } - - if (access(rom_file, F_OK)) { - error_report("pci-assign: Insufficient privileges for %s", rom_file); - return; - } - - /* Write "1" to the ROM file to enable it */ - fp = fopen(rom_file, "r+"); - if (fp == NULL) { - return; + if (!size) { + error_report("pci-assign: Invalid ROM."); } - val = 1; - if (fwrite(&val, 1, 1, fp) != 1) { - goto close_rom; - } - fseek(fp, 0, SEEK_SET); - - snprintf(name, sizeof(name), "%s.rom", - object_get_typename(OBJECT(dev))); - memory_region_init_ram(&dev->dev.rom, OBJECT(dev), name, st.st_size, - &error_abort); - vmstate_register_ram(&dev->dev.rom, &dev->dev.qdev); - ptr = memory_region_get_ram_ptr(&dev->dev.rom); - memset(ptr, 0xff, st.st_size); - - if (!fread(ptr, 1, st.st_size, fp)) { - error_report("pci-assign: Cannot read from host %s", rom_file); - error_printf("Device option ROM contents are probably invalid " - "(check dmesg).\nSkip option ROM probe with rombar=0, " - "or load from file with romfile=\n"); - goto close_rom; - } - - pci_register_bar(&dev->dev, PCI_ROM_SLOT, 0, &dev->dev.rom); - dev->dev.has_rom = true; -close_rom: - /* Write "0" to disable ROM */ - fseek(fp, 0, SEEK_SET); - val = 0; - if (!fwrite(&val, 1, 1, fp)) { - DEBUG("%s\n", "Failed to disable pci-sysfs rom file"); - } - fclose(fp); } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 16d06c6bf8..56aecced7b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1412,7 +1412,13 @@ FWCfgState *pc_memory_init(PCMachineState *pcms, if (guest_info->has_reserved_memory && pcms->hotplug_memory.base) { uint64_t *val = g_malloc(sizeof(*val)); - *val = cpu_to_le64(ROUND_UP(pcms->hotplug_memory.base, 0x1ULL << 30)); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + uint64_t res_mem_end = pcms->hotplug_memory.base; + + if (!pcmc->broken_reserved_end) { + res_mem_end += memory_region_size(&pcms->hotplug_memory.mr); + } + *val = cpu_to_le64(ROUND_UP(res_mem_end, 0x1ULL << 30)); fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 95584676e2..3f925b26db 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -50,7 +50,8 @@ #include "cpu.h" #include "qemu/error-report.h" #ifdef CONFIG_XEN -# include <xen/hvm/hvm_info_table.h> +#include <xen/hvm/hvm_info_table.h> +#include "hw/xen/xen_pt.h" #endif #include "migration/migration.h" @@ -76,7 +77,8 @@ static bool has_reserved_memory = true; static bool kvmclock_enabled = true; /* PC hardware initialisation */ -static void pc_init1(MachineState *machine) +static void pc_init1(MachineState *machine, + const char *host_type, const char *pci_type) { PCMachineState *pcms = PC_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); @@ -134,9 +136,7 @@ static void pc_init1(MachineState *machine) pcms->below_4g_mem_size = machine->ram_size; } - if (xen_enabled() && xen_hvm_init(&pcms->below_4g_mem_size, - &pcms->above_4g_mem_size, - &ram_memory) != 0) { + if (xen_enabled() && xen_hvm_init(pcms, &ram_memory) != 0) { fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); exit(1); } @@ -173,7 +173,8 @@ static void pc_init1(MachineState *machine) MachineClass *mc = MACHINE_GET_CLASS(machine); /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", - mc->name, smbios_legacy_mode, smbios_uuid_encoded); + mc->name, smbios_legacy_mode, smbios_uuid_encoded, + SMBIOS_ENTRY_POINT_21); } /* allocate ram and load rom/bios */ @@ -195,7 +196,9 @@ static void pc_init1(MachineState *machine) } if (pci_enabled) { - pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi, + pci_bus = i440fx_init(host_type, + pci_type, + &i440fx_state, &piix3_devfn, &isa_bus, gsi, system_memory, system_io, machine->ram_size, pcms->below_4g_mem_size, pcms->above_4g_mem_size, @@ -413,15 +416,25 @@ static void pc_init_isa(MachineState *machine) } x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI); enable_compat_apic_id_mode(); - pc_init1(machine); + pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE); } #ifdef CONFIG_XEN +static void pc_xen_hvm_init_pci(MachineState *machine) +{ + const char *pci_type = has_igd_gfx_passthru ? + TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE : TYPE_I440FX_PCI_DEVICE; + + pc_init1(machine, + TYPE_I440FX_PCI_HOST_BRIDGE, + pci_type); +} + static void pc_xen_hvm_init(MachineState *machine) { PCIBus *bus; - pc_init1(machine); + pc_xen_hvm_init_pci(machine); bus = pci_find_primary_bus(); if (bus != NULL) { @@ -437,7 +450,8 @@ static void pc_xen_hvm_init(MachineState *machine) if (compat) { \ compat(machine); \ } \ - pc_init1(machine); \ + pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ + TYPE_I440FX_PCI_DEVICE); \ } \ DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) @@ -450,7 +464,9 @@ static void pc_i440fx_machine_options(MachineClass *m) static void pc_i440fx_2_4_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_machine_options(m); + pcmc->broken_reserved_end = true; m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; m->alias = "pc"; @@ -877,6 +893,118 @@ static void pc_i440fx_0_10_machine_options(MachineClass *m) DEFINE_I440FX_MACHINE(v0_10, "pc-0.10", pc_compat_0_13, pc_i440fx_0_10_machine_options); +typedef struct { + uint16_t gpu_device_id; + uint16_t pch_device_id; + uint8_t pch_revision_id; +} IGDDeviceIDInfo; + +/* In real world different GPU should have different PCH. But actually + * the different PCH DIDs likely map to different PCH SKUs. We do the + * same thing for the GPU. For PCH, the different SKUs are going to be + * all the same silicon design and implementation, just different + * features turn on and off with fuses. The SW interfaces should be + * consistent across all SKUs in a given family (eg LPT). But just same + * features may not be supported. + * + * Most of these different PCH features probably don't matter to the + * Gfx driver, but obviously any difference in display port connections + * will so it should be fine with any PCH in case of passthrough. + * + * So currently use one PCH version, 0x8c4e, to cover all HSW(Haswell) + * scenarios, 0x9cc3 for BDW(Broadwell). + */ +static const IGDDeviceIDInfo igd_combo_id_infos[] = { + /* HSW Classic */ + {0x0402, 0x8c4e, 0x04}, /* HSWGT1D, HSWD_w7 */ + {0x0406, 0x8c4e, 0x04}, /* HSWGT1M, HSWM_w7 */ + {0x0412, 0x8c4e, 0x04}, /* HSWGT2D, HSWD_w7 */ + {0x0416, 0x8c4e, 0x04}, /* HSWGT2M, HSWM_w7 */ + {0x041E, 0x8c4e, 0x04}, /* HSWGT15D, HSWD_w7 */ + /* HSW ULT */ + {0x0A06, 0x8c4e, 0x04}, /* HSWGT1UT, HSWM_w7 */ + {0x0A16, 0x8c4e, 0x04}, /* HSWGT2UT, HSWM_w7 */ + {0x0A26, 0x8c4e, 0x06}, /* HSWGT3UT, HSWM_w7 */ + {0x0A2E, 0x8c4e, 0x04}, /* HSWGT3UT28W, HSWM_w7 */ + {0x0A1E, 0x8c4e, 0x04}, /* HSWGT2UX, HSWM_w7 */ + {0x0A0E, 0x8c4e, 0x04}, /* HSWGT1ULX, HSWM_w7 */ + /* HSW CRW */ + {0x0D26, 0x8c4e, 0x04}, /* HSWGT3CW, HSWM_w7 */ + {0x0D22, 0x8c4e, 0x04}, /* HSWGT3CWDT, HSWD_w7 */ + /* HSW Server */ + {0x041A, 0x8c4e, 0x04}, /* HSWSVGT2, HSWD_w7 */ + /* HSW SRVR */ + {0x040A, 0x8c4e, 0x04}, /* HSWSVGT1, HSWD_w7 */ + /* BSW */ + {0x1606, 0x9cc3, 0x03}, /* BDWULTGT1, BDWM_w7 */ + {0x1616, 0x9cc3, 0x03}, /* BDWULTGT2, BDWM_w7 */ + {0x1626, 0x9cc3, 0x03}, /* BDWULTGT3, BDWM_w7 */ + {0x160E, 0x9cc3, 0x03}, /* BDWULXGT1, BDWM_w7 */ + {0x161E, 0x9cc3, 0x03}, /* BDWULXGT2, BDWM_w7 */ + {0x1602, 0x9cc3, 0x03}, /* BDWHALOGT1, BDWM_w7 */ + {0x1612, 0x9cc3, 0x03}, /* BDWHALOGT2, BDWM_w7 */ + {0x1622, 0x9cc3, 0x03}, /* BDWHALOGT3, BDWM_w7 */ + {0x162B, 0x9cc3, 0x03}, /* BDWHALO28W, BDWM_w7 */ + {0x162A, 0x9cc3, 0x03}, /* BDWGT3WRKS, BDWM_w7 */ + {0x162D, 0x9cc3, 0x03}, /* BDWGT3SRVR, BDWM_w7 */ +}; + +static void isa_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->desc = "ISA bridge faked to support IGD PT"; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->class_id = PCI_CLASS_BRIDGE_ISA; +}; + +static TypeInfo isa_bridge_info = { + .name = "igd-passthrough-isa-bridge", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = isa_bridge_class_init, +}; + +static void pt_graphics_register_types(void) +{ + type_register_static(&isa_bridge_info); +} +type_init(pt_graphics_register_types) + +void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id) +{ + struct PCIDevice *bridge_dev; + int i, num; + uint16_t pch_dev_id = 0xffff; + uint8_t pch_rev_id; + + num = ARRAY_SIZE(igd_combo_id_infos); + for (i = 0; i < num; i++) { + if (gpu_dev_id == igd_combo_id_infos[i].gpu_device_id) { + pch_dev_id = igd_combo_id_infos[i].pch_device_id; + pch_rev_id = igd_combo_id_infos[i].pch_revision_id; + } + } + + if (pch_dev_id == 0xffff) { + return; + } + + /* Currently IGD drivers always need to access PCH by 1f.0. */ + bridge_dev = pci_create_simple(bus, PCI_DEVFN(0x1f, 0), + "igd-passthrough-isa-bridge"); + + /* + * Note that vendor id is always PCI_VENDOR_ID_INTEL. + */ + if (!bridge_dev) { + fprintf(stderr, "set igd-passthrough-isa-bridge failed!\n"); + return; + } + pci_config_set_device_id(bridge_dev->config, pch_dev_id); + pci_config_set_revision(bridge_dev->config, pch_rev_id); +} static void isapc_machine_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index c07d65bc46..11601ab005 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -125,9 +125,7 @@ static void pc_q35_init(MachineState *machine) pcms->below_4g_mem_size = machine->ram_size; } - if (xen_enabled() && xen_hvm_init(&pcms->below_4g_mem_size, - &pcms->above_4g_mem_size, - &ram_memory) != 0) { + if (xen_enabled() && xen_hvm_init(pcms, &ram_memory) != 0) { fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); exit(1); } @@ -165,7 +163,8 @@ static void pc_q35_init(MachineState *machine) if (smbios_defaults) { /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", - mc->name, smbios_legacy_mode, smbios_uuid_encoded); + mc->name, smbios_legacy_mode, smbios_uuid_encoded, + SMBIOS_ENTRY_POINT_21); } /* allocate ram and load rom/bios */ @@ -369,7 +368,9 @@ static void pc_q35_machine_options(MachineClass *m) static void pc_q35_2_4_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_machine_options(m); + pcmc->broken_reserved_end = true; m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; m->no_floppy = 1; diff --git a/hw/i386/pci-assign-load-rom.c b/hw/i386/pci-assign-load-rom.c new file mode 100644 index 0000000000..34a3a7ed7f --- /dev/null +++ b/hw/i386/pci-assign-load-rom.c @@ -0,0 +1,91 @@ +/* + * This is splited from hw/i386/kvm/pci-assign.c + */ +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "qemu/error-report.h" +#include "ui/console.h" +#include "hw/loader.h" +#include "monitor/monitor.h" +#include "qemu/range.h" +#include "sysemu/sysemu.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci-assign.h" + +/* + * Scan the assigned devices for the devices that have an option ROM, and then + * load the corresponding ROM data to RAM. If an error occurs while loading an + * option ROM, we just ignore that option ROM and continue with the next one. + */ +void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, + int *size, unsigned int domain, + unsigned int bus, unsigned int slot, + unsigned int function) +{ + char name[32], rom_file[64]; + FILE *fp; + uint8_t val; + struct stat st; + void *ptr = NULL; + + /* If loading ROM from file, pci handles it */ + if (dev->romfile || !dev->rom_bar) { + return NULL; + } + + snprintf(rom_file, sizeof(rom_file), + "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom", + domain, bus, slot, function); + + if (stat(rom_file, &st)) { + return NULL; + } + + if (access(rom_file, F_OK)) { + error_report("pci-assign: Insufficient privileges for %s", rom_file); + return NULL; + } + + /* Write "1" to the ROM file to enable it */ + fp = fopen(rom_file, "r+"); + if (fp == NULL) { + return NULL; + } + val = 1; + if (fwrite(&val, 1, 1, fp) != 1) { + goto close_rom; + } + fseek(fp, 0, SEEK_SET); + + snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner)); + memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort); + vmstate_register_ram(&dev->rom, &dev->qdev); + ptr = memory_region_get_ram_ptr(&dev->rom); + memset(ptr, 0xff, st.st_size); + + if (!fread(ptr, 1, st.st_size, fp)) { + error_report("pci-assign: Cannot read from host %s", rom_file); + error_printf("Device option ROM contents are probably invalid " + "(check dmesg).\nSkip option ROM probe with rombar=0, " + "or load from file with romfile=\n"); + goto close_rom; + } + + pci_register_bar(dev, PCI_ROM_SLOT, 0, &dev->rom); + dev->has_rom = true; + *size = st.st_size; +close_rom: + /* Write "0" to disable ROM */ + fseek(fp, 0, SEEK_SET); + val = 0; + if (!fwrite(&val, 1, 1, fp)) { + DEBUG("%s\n", "Failed to disable pci-sysfs rom file"); + } + fclose(fp); + + return ptr; +} diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 28b324a6f4..ee45f038b8 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -23,8 +23,6 @@ * THE SOFTWARE. */ -#include <assert.h> - #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/ide.h" diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 48749c1dc1..44f6e27398 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -25,7 +25,6 @@ #include <hw/pci/msi.h> #include <hw/i386/pc.h> #include <hw/pci/pci.h> -#include <hw/sysbus.h> #include "qemu/error-report.h" #include "sysemu/block-backend.h" @@ -122,9 +121,9 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) { - AHCIPCIState *d = container_of(s, AHCIPCIState, ahci); - PCIDevice *pci_dev = - (PCIDevice *)object_dynamic_cast(OBJECT(d), TYPE_PCI_DEVICE); + DeviceState *dev_state = s->container; + PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), + TYPE_PCI_DEVICE); DPRINTF(0, "raise irq\n"); @@ -137,9 +136,9 @@ static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev) { - AHCIPCIState *d = container_of(s, AHCIPCIState, ahci); - PCIDevice *pci_dev = - (PCIDevice *)object_dynamic_cast(OBJECT(d), TYPE_PCI_DEVICE); + DeviceState *dev_state = s->container; + PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), + TYPE_PCI_DEVICE); DPRINTF(0, "lower irq\n"); @@ -877,7 +876,7 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, limit - sglist->size)); if (sglist->size > INT32_MAX) { error_report("AHCI Physical Region Descriptor Table describes " - "more than 2 GiB.\n"); + "more than 2 GiB."); qemu_sglist_destroy(sglist); r = -1; goto out; @@ -1437,6 +1436,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) s->as = as; s->ports = ports; s->dev = g_new0(AHCIDevice, ports); + s->container = qdev; ahci_reg_init(s); /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s, @@ -1625,18 +1625,6 @@ const VMStateDescription vmstate_ahci = { }, }; -#define TYPE_SYSBUS_AHCI "sysbus-ahci" -#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI) - -typedef struct SysbusAHCIState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - AHCIState ahci; - uint32_t num_ports; -} SysbusAHCIState; - static const VMStateDescription vmstate_sysbus_ahci = { .name = "sysbus-ahci", .fields = (VMStateField[]) { diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 79a463d93c..c9b3805415 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -24,6 +24,8 @@ #ifndef HW_IDE_AHCI_H #define HW_IDE_AHCI_H +#include <hw/sysbus.h> + #define AHCI_MEM_BAR_SIZE 0x1000 #define AHCI_MAX_PORTS 32 #define AHCI_MAX_SG 168 /* hardware max is 64K */ @@ -285,6 +287,8 @@ struct AHCIDevice { }; typedef struct AHCIState { + DeviceState *container; + AHCIDevice *dev; AHCIControlRegs control_regs; MemoryRegion mem; @@ -369,4 +373,16 @@ void ahci_reset(AHCIState *s); void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd); +#define TYPE_SYSBUS_AHCI "sysbus-ahci" +#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI) + +typedef struct SysbusAHCIState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + AHCIState ahci; + uint32_t num_ports; +} SysbusAHCIState; + #endif /* HW_IDE_AHCI_H */ diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_input.c index 0609e80868..4d86c04e51 100644 --- a/hw/input/stellaris_input.c +++ b/hw/input/stellaris_input.c @@ -69,14 +69,14 @@ static const VMStateDescription vmstate_stellaris_gamepad = { } }; -/* Returns an array 5 ouput slots. */ +/* Returns an array of 5 output slots. */ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) { gamepad_state *s; int i; - s = (gamepad_state *)g_malloc0(sizeof (gamepad_state)); - s->buttons = (gamepad_button *)g_malloc0(n * sizeof (gamepad_button)); + s = g_new0(gamepad_state, 1); + s->buttons = g_new0(gamepad_button, n); for (i = 0; i < n; i++) { s->buttons[i].irq = irq[i]; s->buttons[i].keycode = keycode[i]; diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index a8c5d19448..8bad132d5a 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -219,15 +219,99 @@ static uint16_t gic_get_current_pending_irq(GICState *s, int cpu, return pending_irq; } -static void gic_set_running_irq(GICState *s, int cpu, int irq) +static int gic_get_group_priority(GICState *s, int cpu, int irq) { - s->running_irq[cpu] = irq; - if (irq == 1023) { - s->running_priority[cpu] = 0x100; + /* Return the group priority of the specified interrupt + * (which is the top bits of its priority, with the number + * of bits masked determined by the applicable binary point register). + */ + int bpr; + uint32_t mask; + + if (gic_has_groups(s) && + !(s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) && + GIC_TEST_GROUP(irq, (1 << cpu))) { + bpr = s->abpr[cpu]; } else { - s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu); + bpr = s->bpr[cpu]; } - gic_update(s); + + /* a BPR of 0 means the group priority bits are [7:1]; + * a BPR of 1 means they are [7:2], and so on down to + * a BPR of 7 meaning no group priority bits at all. + */ + mask = ~0U << ((bpr & 7) + 1); + + return GIC_GET_PRIORITY(irq, cpu) & mask; +} + +static void gic_activate_irq(GICState *s, int cpu, int irq) +{ + /* Set the appropriate Active Priority Register bit for this IRQ, + * and update the running priority. + */ + int prio = gic_get_group_priority(s, cpu, irq); + int preemption_level = prio >> (GIC_MIN_BPR + 1); + int regno = preemption_level / 32; + int bitno = preemption_level % 32; + + if (gic_has_groups(s) && GIC_TEST_GROUP(irq, (1 << cpu))) { + s->nsapr[regno][cpu] &= (1 << bitno); + } else { + s->apr[regno][cpu] &= (1 << bitno); + } + + s->running_priority[cpu] = prio; + GIC_SET_ACTIVE(irq, 1 << cpu); +} + +static int gic_get_prio_from_apr_bits(GICState *s, int cpu) +{ + /* Recalculate the current running priority for this CPU based + * on the set bits in the Active Priority Registers. + */ + int i; + for (i = 0; i < GIC_NR_APRS; i++) { + uint32_t apr = s->apr[i][cpu] | s->nsapr[i][cpu]; + if (!apr) { + continue; + } + return (i * 32 + ctz32(apr)) << (GIC_MIN_BPR + 1); + } + return 0x100; +} + +static void gic_drop_prio(GICState *s, int cpu, int group) +{ + /* Drop the priority of the currently active interrupt in the + * specified group. + * + * Note that we can guarantee (because of the requirement to nest + * GICC_IAR reads [which activate an interrupt and raise priority] + * with GICC_EOIR writes [which drop the priority for the interrupt]) + * that the interrupt we're being called for is the highest priority + * active interrupt, meaning that it has the lowest set bit in the + * APR registers. + * + * If the guest does not honour the ordering constraints then the + * behaviour of the GIC is UNPREDICTABLE, which for us means that + * the values of the APR registers might become incorrect and the + * running priority will be wrong, so interrupts that should preempt + * might not do so, and interrupts that should not preempt might do so. + */ + int i; + + for (i = 0; i < GIC_NR_APRS; i++) { + uint32_t *papr = group ? &s->nsapr[i][cpu] : &s->apr[i][cpu]; + if (!*papr) { + continue; + } + /* Clear lowest set bit */ + *papr &= *papr - 1; + break; + } + + s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu); } uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) @@ -239,7 +323,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) * for the case where this GIC supports grouping and the pending interrupt * is in the wrong group. */ - irq = gic_get_current_pending_irq(s, cpu, attrs);; + irq = gic_get_current_pending_irq(s, cpu, attrs); if (irq >= GIC_MAXIRQ) { DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq); @@ -250,7 +334,6 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq); return 1023; } - s->last_active[irq][cpu] = s->running_irq[cpu]; if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { /* Clear pending flags for both level and edge triggered interrupts. @@ -281,7 +364,8 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) } } - gic_set_running_irq(s, cpu, irq); + gic_activate_irq(s, cpu, irq); + gic_update(s); DPRINTF("ACK %d\n", irq); return ret; } @@ -411,8 +495,9 @@ static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs) void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) { - int update = 0; int cm = 1 << cpu; + int group; + DPRINTF("EOI %d\n", irq); if (irq >= s->num_irq) { /* This handles two cases: @@ -425,8 +510,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) */ return; } - if (s->running_irq[cpu] == 1023) + if (s->running_priority[cpu] == 0x100) { return; /* No active IRQ. */ + } if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { /* Mark level triggered interrupts as pending if they are still @@ -435,11 +521,12 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) { DPRINTF("Set %d pending mask %x\n", irq, cm); GIC_SET_PENDING(irq, cm); - update = 1; } } - if (s->security_extn && !attrs.secure && !GIC_TEST_GROUP(irq, cm)) { + group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); + + if (s->security_extn && !attrs.secure && !group) { DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq); return; } @@ -449,23 +536,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) * i.e. go ahead and complete the irq anyway. */ - if (irq != s->running_irq[cpu]) { - /* Complete an IRQ that is not currently running. */ - int tmp = s->running_irq[cpu]; - while (s->last_active[tmp][cpu] != 1023) { - if (s->last_active[tmp][cpu] == irq) { - s->last_active[tmp][cpu] = s->last_active[irq][cpu]; - break; - } - tmp = s->last_active[tmp][cpu]; - } - if (update) { - gic_update(s); - } - } else { - /* Complete the current running IRQ. */ - gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]); - } + gic_drop_prio(s, cpu, group); + GIC_CLEAR_ACTIVE(irq, cm); + gic_update(s); } static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) @@ -922,6 +995,68 @@ static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data, } } +static inline uint32_t gic_apr_ns_view(GICState *s, int cpu, int regno) +{ + /* Return the Nonsecure view of GICC_APR<regno>. This is the + * second half of GICC_NSAPR. + */ + switch (GIC_MIN_BPR) { + case 0: + if (regno < 2) { + return s->nsapr[regno + 2][cpu]; + } + break; + case 1: + if (regno == 0) { + return s->nsapr[regno + 1][cpu]; + } + break; + case 2: + if (regno == 0) { + return extract32(s->nsapr[0][cpu], 16, 16); + } + break; + case 3: + if (regno == 0) { + return extract32(s->nsapr[0][cpu], 8, 8); + } + break; + default: + g_assert_not_reached(); + } + return 0; +} + +static inline void gic_apr_write_ns_view(GICState *s, int cpu, int regno, + uint32_t value) +{ + /* Write the Nonsecure view of GICC_APR<regno>. */ + switch (GIC_MIN_BPR) { + case 0: + if (regno < 2) { + s->nsapr[regno + 2][cpu] = value; + } + break; + case 1: + if (regno == 0) { + s->nsapr[regno + 1][cpu] = value; + } + break; + case 2: + if (regno == 0) { + s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 16, 16, value); + } + break; + case 3: + if (regno == 0) { + s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 8, 8, value); + } + break; + default: + g_assert_not_reached(); + } +} + static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, uint64_t *data, MemTxAttrs attrs) { @@ -962,8 +1097,31 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, } break; case 0xd0: case 0xd4: case 0xd8: case 0xdc: - *data = s->apr[(offset - 0xd0) / 4][cpu]; + { + int regno = (offset - 0xd0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2) { + *data = 0; + } else if (s->security_extn && !attrs.secure) { + /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */ + *data = gic_apr_ns_view(s, regno, cpu); + } else { + *data = s->apr[regno][cpu]; + } break; + } + case 0xe0: case 0xe4: case 0xe8: case 0xec: + { + int regno = (offset - 0xe0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2 || !gic_has_groups(s) || + (s->security_extn && !attrs.secure)) { + *data = 0; + } else { + *data = s->nsapr[regno][cpu]; + } + break; + } default: qemu_log_mask(LOG_GUEST_ERROR, "gic_cpu_read: Bad offset %x\n", (int)offset); @@ -1001,8 +1159,33 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, } break; case 0xd0: case 0xd4: case 0xd8: case 0xdc: - qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n"); + { + int regno = (offset - 0xd0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2) { + return MEMTX_OK; + } + if (s->security_extn && !attrs.secure) { + /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */ + gic_apr_write_ns_view(s, regno, cpu, value); + } else { + s->apr[regno][cpu] = value; + } break; + } + case 0xe0: case 0xe4: case 0xe8: case 0xec: + { + int regno = (offset - 0xe0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2) { + return MEMTX_OK; + } + if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) { + return MEMTX_OK; + } + s->nsapr[regno][cpu] = value; + break; + } default: qemu_log_mask(LOG_GUEST_ERROR, "gic_cpu_write: Bad offset %x\n", (int)offset); diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index fe64b51cff..9c82b97293 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -19,6 +19,7 @@ */ #include "gic_internal.h" +#include "hw/arm/linux-boot-if.h" static void gic_pre_save(void *opaque) { @@ -59,8 +60,8 @@ static const VMStateDescription vmstate_gic_irq_state = { static const VMStateDescription vmstate_gic = { .name = "arm_gic", - .version_id = 10, - .minimum_version_id = 10, + .version_id = 12, + .minimum_version_id = 12, .pre_save = gic_pre_save, .post_load = gic_post_load, .fields = (VMStateField[]) { @@ -71,15 +72,14 @@ static const VMStateDescription vmstate_gic = { VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ), VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU), VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL), - VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU), VMSTATE_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU), VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU), - VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU), VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU), VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU), VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU), VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU), VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU), + VMSTATE_UINT32_2DARRAY(nsapr, GICState, GIC_NR_APRS, GIC_NCPU), VMSTATE_END_OF_LIST() } }; @@ -165,21 +165,35 @@ static void arm_gic_common_reset(DeviceState *dev) { GICState *s = ARM_GIC_COMMON(dev); int i, j; + int resetprio; + + /* If we're resetting a TZ-aware GIC as if secure firmware + * had set it up ready to start a kernel in non-secure, + * we need to set interrupt priorities to a "zero for the + * NS view" value. This is particularly critical for the + * priority_mask[] values, because if they are zero then NS + * code cannot ever rewrite the priority to anything else. + */ + if (s->security_extn && s->irq_reset_nonsecure) { + resetprio = 0x80; + } else { + resetprio = 0; + } + memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); for (i = 0 ; i < s->num_cpu; i++) { if (s->revision == REV_11MPCORE) { s->priority_mask[i] = 0xf0; } else { - s->priority_mask[i] = 0; + s->priority_mask[i] = resetprio; } s->current_pending[i] = 1023; - s->running_irq[i] = 1023; s->running_priority[i] = 0x100; s->cpu_ctlr[i] = 0; s->bpr[i] = GIC_MIN_BPR; s->abpr[i] = GIC_MIN_ABPR; for (j = 0; j < GIC_INTERNAL; j++) { - s->priority1[j][i] = 0; + s->priority1[j][i] = resetprio; } for (j = 0; j < GIC_NR_SGIS; j++) { s->sgi_pending[j][i] = 0; @@ -191,7 +205,7 @@ static void arm_gic_common_reset(DeviceState *dev) } for (i = 0; i < ARRAY_SIZE(s->priority2); i++) { - s->priority2[i] = 0; + s->priority2[i] = resetprio; } for (i = 0; i < GIC_MAXIRQ; i++) { @@ -202,9 +216,32 @@ static void arm_gic_common_reset(DeviceState *dev) s->irq_target[i] = 0; } } + if (s->security_extn && s->irq_reset_nonsecure) { + for (i = 0; i < GIC_MAXIRQ; i++) { + GIC_SET_GROUP(i, ALL_CPU_MASK); + } + } + s->ctlr = 0; } +static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, + bool secure_boot) +{ + GICState *s = ARM_GIC_COMMON(obj); + + if (s->security_extn && !secure_boot) { + /* We're directly booting a kernel into NonSecure. If this GIC + * implements the security extensions then we must configure it + * to have all the interrupts be NonSecure (this is a job that + * is done by the Secure boot firmware in real hardware, and in + * this mode QEMU is acting as a minimalist firmware-and-bootloader + * equivalent). + */ + s->irq_reset_nonsecure = true; + } +} + static Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1), DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32), @@ -221,11 +258,13 @@ static Property arm_gic_common_properties[] = { static void arm_gic_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass); dc->reset = arm_gic_common_reset; dc->realize = arm_gic_common_realize; dc->props = arm_gic_common_properties; dc->vmsd = &vmstate_gic; + albifc->arm_linux_init = arm_gic_common_linux_init; } static const TypeInfo arm_gic_common_type = { @@ -235,6 +274,10 @@ static const TypeInfo arm_gic_common_type = { .class_size = sizeof(ARMGICCommonClass), .class_init = arm_gic_common_class_init, .abstract = true, + .interfaces = (InterfaceInfo []) { + { TYPE_ARM_LINUX_BOOT_IF }, + { }, + }, }; static void register_types(void) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index e13b729e1d..3ec8408618 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -185,26 +185,25 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) return cpu->midr; case 0xd04: /* Interrupt Control State. */ /* VECTACTIVE */ - val = s->gic.running_irq[0]; + cpu = ARM_CPU(current_cpu); + val = cpu->env.v7m.exception; if (val == 1023) { val = 0; } else if (val >= 32) { val -= 16; } - /* RETTOBASE */ - if (s->gic.running_irq[0] == 1023 - || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) { - val |= (1 << 11); - } /* VECTPENDING */ if (s->gic.current_pending[0] != 1023) val |= (s->gic.current_pending[0] << 12); - /* ISRPENDING */ + /* ISRPENDING and RETTOBASE */ for (irq = 32; irq < s->num_irq; irq++) { if (s->gic.irq_state[irq].pending) { val |= (1 << 22); break; } + if (irq != cpu->env.v7m.exception && s->gic.irq_state[irq].active) { + val |= (1 << 11); + } } /* PENDSTSET */ if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending) diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index b471e7a41e..48714f96a3 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -228,6 +228,8 @@ static int kvm_s390_add_adapter_routes(S390FLICState *fs, routes->gsi[i] = ret; routes->adapter.ind_offset++; } + kvm_irqchip_commit_routes(kvm_state); + /* Restore passed-in structure to original state. */ routes->adapter.ind_offset = ind_offset; return 0; diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 924b1ae3c9..67881c7109 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -739,7 +739,7 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi) } /* - * Allocate block of consequtive IRQs, returns a number of the first. + * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block. * If align==true, aligns the first IRQ number to num. */ int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align) diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index f4dcacd862..86b2c0bcb7 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -252,9 +252,7 @@ void mips_r4k_init(MachineState *machine) fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n", bios_name); } - if (filename) { - g_free(filename); - } + g_free(filename); if (kernel_filename) { loaderparams.ram_size = ram_size; diff --git a/hw/misc/omap_clk.c b/hw/misc/omap_clk.c index 80a3c50e1e..73d4f8becd 100644 --- a/hw/misc/omap_clk.c +++ b/hw/misc/omap_clk.c @@ -1239,7 +1239,7 @@ void omap_clk_init(struct omap_mpu_state_s *mpu) for (i = onchip_clks, count = 0; *i; i ++) if ((*i)->flags & flag) count ++; - mpu->clks = (struct clk *) g_malloc0(sizeof(struct clk) * (count + 1)); + mpu->clks = g_new0(struct clk, count + 1); for (i = onchip_clks, j = mpu->clks; *i; i ++) if ((*i)->flags & flag) { memcpy(j, *i, sizeof(struct clk)); diff --git a/hw/misc/omap_gpmc.c b/hw/misc/omap_gpmc.c index 74fc91c8e9..8960f1bf16 100644 --- a/hw/misc/omap_gpmc.c +++ b/hw/misc/omap_gpmc.c @@ -826,8 +826,7 @@ struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu, qemu_irq irq, qemu_irq drq) { int cs; - struct omap_gpmc_s *s = (struct omap_gpmc_s *) - g_malloc0(sizeof(struct omap_gpmc_s)); + struct omap_gpmc_s *s = g_new0(struct omap_gpmc_s, 1); memory_region_init_io(&s->iomem, NULL, &omap_gpmc_ops, s, "omap-gpmc", 0x1000); memory_region_add_subregion(get_system_memory(), base, &s->iomem); diff --git a/hw/misc/omap_sdrc.c b/hw/misc/omap_sdrc.c index 3de0c0e9d0..bca25307b5 100644 --- a/hw/misc/omap_sdrc.c +++ b/hw/misc/omap_sdrc.c @@ -157,8 +157,7 @@ static const MemoryRegionOps omap_sdrc_ops = { struct omap_sdrc_s *omap_sdrc_init(MemoryRegion *sysmem, hwaddr base) { - struct omap_sdrc_s *s = (struct omap_sdrc_s *) - g_malloc0(sizeof(struct omap_sdrc_s)); + struct omap_sdrc_s *s = g_new0(struct omap_sdrc_s, 1); omap_sdrc_reset(s); diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 98801739ef..64d044923c 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -19,6 +19,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o common-obj-$(CONFIG_MIPSNET) += mipsnet.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o +common-obj-$(CONFIG_IMX_FEC) += imx_fec.o common-obj-$(CONFIG_CADENCE) += cadence_gem.o common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 494a346cf6..1127223cfd 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -951,7 +951,7 @@ static void gem_phy_reset(CadenceGEMState *s) s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00; s->phy_regs[PHY_REG_EXTSTAT] = 0x3000; s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078; - s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00; + s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0x7C00; s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60; s->phy_regs[PHY_REG_LED] = 0x4100; s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A; diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 0f5cf4477b..04bb41da64 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -353,7 +353,7 @@ static ssize_t etsec_receive(NetClientState *nc, etsec->need_flush = false; ret = etsec_rx_ring_write(etsec, buf, size); if (ret == 0) { - /* The packet will be queued, let's flush it when buffer is avilable + /* The packet will be queued, let's flush it when buffer is available * again. */ etsec->need_flush = true; } diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index 68e7b6d16b..0a5c6cfe3f 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -464,9 +464,7 @@ static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size) etsec->rx_fcb_size = 0; } - if (etsec->rx_buffer != NULL) { - g_free(etsec->rx_buffer); - } + g_free(etsec->rx_buffer); /* Do not copy the frame for now */ etsec->rx_buffer = (uint8_t *)buf; diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c new file mode 100644 index 0000000000..725f3fa335 --- /dev/null +++ b/hw/net/imx_fec.c @@ -0,0 +1,709 @@ +/* + * i.MX Fast Ethernet Controller emulation. + * + * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net> + * + * Based on Coldfire Fast Ethernet Controller emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/net/imx_fec.h" +#include "sysemu/dma.h" + +/* For crc32 */ +#include <zlib.h> + +#ifndef IMX_FEC_DEBUG +#define IMX_FEC_DEBUG 0 +#endif + +#ifndef IMX_PHY_DEBUG +#define IMX_PHY_DEBUG 0 +#endif + +#if IMX_FEC_DEBUG +#define FEC_PRINTF(fmt, ...) \ + do { fprintf(stderr, "%s[%s]: " fmt , TYPE_IMX_FEC, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define FEC_PRINTF(fmt, ...) do {} while (0) +#endif + +#if IMX_PHY_DEBUG +#define PHY_PRINTF(fmt, ...) \ + do { fprintf(stderr, "%s.phy[%s]: " fmt , TYPE_IMX_FEC, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define PHY_PRINTF(fmt, ...) do {} while (0) +#endif + +static const VMStateDescription vmstate_imx_fec = { + .name = TYPE_IMX_FEC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irq_state, IMXFECState), + VMSTATE_UINT32(eir, IMXFECState), + VMSTATE_UINT32(eimr, IMXFECState), + VMSTATE_UINT32(rx_enabled, IMXFECState), + VMSTATE_UINT32(rx_descriptor, IMXFECState), + VMSTATE_UINT32(tx_descriptor, IMXFECState), + VMSTATE_UINT32(ecr, IMXFECState), + VMSTATE_UINT32(mmfr, IMXFECState), + VMSTATE_UINT32(mscr, IMXFECState), + VMSTATE_UINT32(mibc, IMXFECState), + VMSTATE_UINT32(rcr, IMXFECState), + VMSTATE_UINT32(tcr, IMXFECState), + VMSTATE_UINT32(tfwr, IMXFECState), + VMSTATE_UINT32(frsr, IMXFECState), + VMSTATE_UINT32(erdsr, IMXFECState), + VMSTATE_UINT32(etdsr, IMXFECState), + VMSTATE_UINT32(emrbr, IMXFECState), + VMSTATE_UINT32(miigsk_cfgr, IMXFECState), + VMSTATE_UINT32(miigsk_enr, IMXFECState), + + VMSTATE_UINT32(phy_status, IMXFECState), + VMSTATE_UINT32(phy_control, IMXFECState), + VMSTATE_UINT32(phy_advertise, IMXFECState), + VMSTATE_UINT32(phy_int, IMXFECState), + VMSTATE_UINT32(phy_int_mask, IMXFECState), + VMSTATE_END_OF_LIST() + } +}; + +#define PHY_INT_ENERGYON (1 << 7) +#define PHY_INT_AUTONEG_COMPLETE (1 << 6) +#define PHY_INT_FAULT (1 << 5) +#define PHY_INT_DOWN (1 << 4) +#define PHY_INT_AUTONEG_LP (1 << 3) +#define PHY_INT_PARFAULT (1 << 2) +#define PHY_INT_AUTONEG_PAGE (1 << 1) + +static void imx_fec_update(IMXFECState *s); + +/* + * The MII phy could raise a GPIO to the processor which in turn + * could be handled as an interrpt by the OS. + * For now we don't handle any GPIO/interrupt line, so the OS will + * have to poll for the PHY status. + */ +static void phy_update_irq(IMXFECState *s) +{ + imx_fec_update(s); +} + +static void phy_update_link(IMXFECState *s) +{ + /* Autonegotiation status mirrors link status. */ + if (qemu_get_queue(s->nic)->link_down) { + PHY_PRINTF("link is down\n"); + s->phy_status &= ~0x0024; + s->phy_int |= PHY_INT_DOWN; + } else { + PHY_PRINTF("link is up\n"); + s->phy_status |= 0x0024; + s->phy_int |= PHY_INT_ENERGYON; + s->phy_int |= PHY_INT_AUTONEG_COMPLETE; + } + phy_update_irq(s); +} + +static void imx_fec_set_link(NetClientState *nc) +{ + phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); +} + +static void phy_reset(IMXFECState *s) +{ + s->phy_status = 0x7809; + s->phy_control = 0x3000; + s->phy_advertise = 0x01e1; + s->phy_int_mask = 0; + s->phy_int = 0; + phy_update_link(s); +} + +static uint32_t do_phy_read(IMXFECState *s, int reg) +{ + uint32_t val; + + if (reg > 31) { + /* we only advertise one phy */ + return 0; + } + + switch (reg) { + case 0: /* Basic Control */ + val = s->phy_control; + break; + case 1: /* Basic Status */ + val = s->phy_status; + break; + case 2: /* ID1 */ + val = 0x0007; + break; + case 3: /* ID2 */ + val = 0xc0d1; + break; + case 4: /* Auto-neg advertisement */ + val = s->phy_advertise; + break; + case 5: /* Auto-neg Link Partner Ability */ + val = 0x0f71; + break; + case 6: /* Auto-neg Expansion */ + val = 1; + break; + case 29: /* Interrupt source. */ + val = s->phy_int; + s->phy_int = 0; + phy_update_irq(s); + break; + case 30: /* Interrupt mask */ + val = s->phy_int_mask; + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "%s.phy[%s]: reg %d not implemented\n", + TYPE_IMX_FEC, __func__, reg); + val = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", + TYPE_IMX_FEC, __func__, reg); + val = 0; + break; + } + + PHY_PRINTF("read 0x%04x @ %d\n", val, reg); + + return val; +} + +static void do_phy_write(IMXFECState *s, int reg, uint32_t val) +{ + PHY_PRINTF("write 0x%04x @ %d\n", val, reg); + + if (reg > 31) { + /* we only advertise one phy */ + return; + } + + switch (reg) { + case 0: /* Basic Control */ + if (val & 0x8000) { + phy_reset(s); + } else { + s->phy_control = val & 0x7980; + /* Complete autonegotiation immediately. */ + if (val & 0x1000) { + s->phy_status |= 0x0020; + } + } + break; + case 4: /* Auto-neg advertisement */ + s->phy_advertise = (val & 0x2d7f) | 0x80; + break; + case 30: /* Interrupt mask */ + s->phy_int_mask = val & 0xff; + phy_update_irq(s); + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "%s.phy[%s]: reg %d not implemented\n", + TYPE_IMX_FEC, __func__, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s.phy[%s]: Bad address at offset %d\n", + TYPE_IMX_FEC, __func__, reg); + break; + } +} + +static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) +{ + dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); +} + +static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr) +{ + dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); +} + +static void imx_fec_update(IMXFECState *s) +{ + uint32_t active; + uint32_t changed; + + active = s->eir & s->eimr; + changed = active ^ s->irq_state; + if (changed) { + qemu_set_irq(s->irq, active); + } + s->irq_state = active; +} + +static void imx_fec_do_tx(IMXFECState *s) +{ + int frame_size = 0; + uint8_t frame[FEC_MAX_FRAME_SIZE]; + uint8_t *ptr = frame; + uint32_t addr = s->tx_descriptor; + + while (1) { + IMXFECBufDesc bd; + int len; + + imx_fec_read_bd(&bd, addr); + FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n", + addr, bd.flags, bd.length, bd.data); + if ((bd.flags & FEC_BD_R) == 0) { + /* Run out of descriptors to transmit. */ + break; + } + len = bd.length; + if (frame_size + len > FEC_MAX_FRAME_SIZE) { + len = FEC_MAX_FRAME_SIZE - frame_size; + s->eir |= FEC_INT_BABT; + } + dma_memory_read(&address_space_memory, bd.data, ptr, len); + ptr += len; + frame_size += len; + if (bd.flags & FEC_BD_L) { + /* Last buffer in frame. */ + qemu_send_packet(qemu_get_queue(s->nic), frame, len); + ptr = frame; + frame_size = 0; + s->eir |= FEC_INT_TXF; + } + s->eir |= FEC_INT_TXB; + bd.flags &= ~FEC_BD_R; + /* Write back the modified descriptor. */ + imx_fec_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if ((bd.flags & FEC_BD_W) != 0) { + addr = s->etdsr; + } else { + addr += 8; + } + } + + s->tx_descriptor = addr; + + imx_fec_update(s); +} + +static void imx_fec_enable_rx(IMXFECState *s) +{ + IMXFECBufDesc bd; + uint32_t tmp; + + imx_fec_read_bd(&bd, s->rx_descriptor); + + tmp = ((bd.flags & FEC_BD_E) != 0); + + if (!tmp) { + FEC_PRINTF("RX buffer full\n"); + } else if (!s->rx_enabled) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + + s->rx_enabled = tmp; +} + +static void imx_fec_reset(DeviceState *d) +{ + IMXFECState *s = IMX_FEC(d); + + /* Reset the FEC */ + s->eir = 0; + s->eimr = 0; + s->rx_enabled = 0; + s->ecr = 0; + s->mscr = 0; + s->mibc = 0xc0000000; + s->rcr = 0x05ee0001; + s->tcr = 0; + s->tfwr = 0; + s->frsr = 0x500; + s->miigsk_cfgr = 0; + s->miigsk_enr = 0x6; + + /* We also reset the PHY */ + phy_reset(s); +} + +static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size) +{ + IMXFECState *s = IMX_FEC(opaque); + + FEC_PRINTF("reading from @ 0x%03x\n", (int)addr); + + switch (addr & 0x3ff) { + case 0x004: + return s->eir; + case 0x008: + return s->eimr; + case 0x010: + return s->rx_enabled ? (1 << 24) : 0; /* RDAR */ + case 0x014: + return 0; /* TDAR */ + case 0x024: + return s->ecr; + case 0x040: + return s->mmfr; + case 0x044: + return s->mscr; + case 0x064: + return s->mibc; /* MIBC */ + case 0x084: + return s->rcr; + case 0x0c4: + return s->tcr; + case 0x0e4: /* PALR */ + return (s->conf.macaddr.a[0] << 24) + | (s->conf.macaddr.a[1] << 16) + | (s->conf.macaddr.a[2] << 8) + | s->conf.macaddr.a[3]; + break; + case 0x0e8: /* PAUR */ + return (s->conf.macaddr.a[4] << 24) + | (s->conf.macaddr.a[5] << 16) + | 0x8808; + case 0x0ec: + return 0x10000; /* OPD */ + case 0x118: + return 0; + case 0x11c: + return 0; + case 0x120: + return 0; + case 0x124: + return 0; + case 0x144: + return s->tfwr; + case 0x14c: + return 0x600; + case 0x150: + return s->frsr; + case 0x180: + return s->erdsr; + case 0x184: + return s->etdsr; + case 0x188: + return s->emrbr; + case 0x300: + return s->miigsk_cfgr; + case 0x308: + return s->miigsk_enr; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", + TYPE_IMX_FEC, __func__, (int)addr); + return 0; + } +} + +static void imx_fec_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + IMXFECState *s = IMX_FEC(opaque); + + FEC_PRINTF("writing 0x%08x @ 0x%03x\n", (int)value, (int)addr); + + switch (addr & 0x3ff) { + case 0x004: /* EIR */ + s->eir &= ~value; + break; + case 0x008: /* EIMR */ + s->eimr = value; + break; + case 0x010: /* RDAR */ + if ((s->ecr & FEC_EN) && !s->rx_enabled) { + imx_fec_enable_rx(s); + } + break; + case 0x014: /* TDAR */ + if (s->ecr & FEC_EN) { + imx_fec_do_tx(s); + } + break; + case 0x024: /* ECR */ + s->ecr = value; + if (value & FEC_RESET) { + imx_fec_reset(DEVICE(s)); + } + if ((s->ecr & FEC_EN) == 0) { + s->rx_enabled = 0; + } + break; + case 0x040: /* MMFR */ + /* store the value */ + s->mmfr = value; + if (extract32(value, 28, 1)) { + do_phy_write(s, extract32(value, 18, 9), extract32(value, 0, 16)); + } else { + s->mmfr = do_phy_read(s, extract32(value, 18, 9)); + } + /* raise the interrupt as the PHY operation is done */ + s->eir |= FEC_INT_MII; + break; + case 0x044: /* MSCR */ + s->mscr = value & 0xfe; + break; + case 0x064: /* MIBC */ + /* TODO: Implement MIB. */ + s->mibc = (value & 0x80000000) ? 0xc0000000 : 0; + break; + case 0x084: /* RCR */ + s->rcr = value & 0x07ff003f; + /* TODO: Implement LOOP mode. */ + break; + case 0x0c4: /* TCR */ + /* We transmit immediately, so raise GRA immediately. */ + s->tcr = value; + if (value & 1) { + s->eir |= FEC_INT_GRA; + } + break; + case 0x0e4: /* PALR */ + s->conf.macaddr.a[0] = value >> 24; + s->conf.macaddr.a[1] = value >> 16; + s->conf.macaddr.a[2] = value >> 8; + s->conf.macaddr.a[3] = value; + break; + case 0x0e8: /* PAUR */ + s->conf.macaddr.a[4] = value >> 24; + s->conf.macaddr.a[5] = value >> 16; + break; + case 0x0ec: /* OPDR */ + break; + case 0x118: /* IAUR */ + case 0x11c: /* IALR */ + case 0x120: /* GAUR */ + case 0x124: /* GALR */ + /* TODO: implement MAC hash filtering. */ + break; + case 0x144: /* TFWR */ + s->tfwr = value & 3; + break; + case 0x14c: /* FRBR */ + /* FRBR writes ignored. */ + break; + case 0x150: /* FRSR */ + s->frsr = (value & 0x3fc) | 0x400; + break; + case 0x180: /* ERDSR */ + s->erdsr = value & ~3; + s->rx_descriptor = s->erdsr; + break; + case 0x184: /* ETDSR */ + s->etdsr = value & ~3; + s->tx_descriptor = s->etdsr; + break; + case 0x188: /* EMRBR */ + s->emrbr = value & 0x7f0; + break; + case 0x300: /* MIIGSK_CFGR */ + s->miigsk_cfgr = value & 0x53; + break; + case 0x308: /* MIIGSK_ENR */ + s->miigsk_enr = (value & 0x2) ? 0x6 : 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", + TYPE_IMX_FEC, __func__, (int)addr); + break; + } + + imx_fec_update(s); +} + +static int imx_fec_can_receive(NetClientState *nc) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + + return s->rx_enabled; +} + +static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, + size_t len) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + IMXFECBufDesc bd; + uint32_t flags = 0; + uint32_t addr; + uint32_t crc; + uint32_t buf_addr; + uint8_t *crc_ptr; + unsigned int buf_len; + size_t size = len; + + FEC_PRINTF("len %d\n", (int)size); + + if (!s->rx_enabled) { + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Unexpected packet\n", + TYPE_IMX_FEC, __func__); + return 0; + } + + /* 4 bytes for the CRC. */ + size += 4; + crc = cpu_to_be32(crc32(~0, buf, size)); + crc_ptr = (uint8_t *) &crc; + + /* Huge frames are truncted. */ + if (size > FEC_MAX_FRAME_SIZE) { + size = FEC_MAX_FRAME_SIZE; + flags |= FEC_BD_TR | FEC_BD_LG; + } + + /* Frames larger than the user limit just set error flags. */ + if (size > (s->rcr >> 16)) { + flags |= FEC_BD_LG; + } + + addr = s->rx_descriptor; + while (size > 0) { + imx_fec_read_bd(&bd, addr); + if ((bd.flags & FEC_BD_E) == 0) { + /* No descriptors available. Bail out. */ + /* + * FIXME: This is wrong. We should probably either + * save the remainder for when more RX buffers are + * available, or flag an error. + */ + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Lost end of frame\n", + TYPE_IMX_FEC, __func__); + break; + } + buf_len = (size <= s->emrbr) ? size : s->emrbr; + bd.length = buf_len; + size -= buf_len; + FEC_PRINTF("rx_bd %x length %d\n", addr, bd.length); + /* The last 4 bytes are the CRC. */ + if (size < 4) { + buf_len += size - 4; + } + buf_addr = bd.data; + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + buf += buf_len; + if (size < 4) { + dma_memory_write(&address_space_memory, buf_addr + buf_len, + crc_ptr, 4 - size); + crc_ptr += 4 - size; + } + bd.flags &= ~FEC_BD_E; + if (size == 0) { + /* Last buffer in frame. */ + bd.flags |= flags | FEC_BD_L; + FEC_PRINTF("rx frame flags %04x\n", bd.flags); + s->eir |= FEC_INT_RXF; + } else { + s->eir |= FEC_INT_RXB; + } + imx_fec_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if ((bd.flags & FEC_BD_W) != 0) { + addr = s->erdsr; + } else { + addr += 8; + } + } + s->rx_descriptor = addr; + imx_fec_enable_rx(s); + imx_fec_update(s); + return len; +} + +static const MemoryRegionOps imx_fec_ops = { + .read = imx_fec_read, + .write = imx_fec_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void imx_fec_cleanup(NetClientState *nc) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + + s->nic = NULL; +} + +static NetClientInfo net_imx_fec_info = { + .type = NET_CLIENT_OPTIONS_KIND_NIC, + .size = sizeof(NICState), + .can_receive = imx_fec_can_receive, + .receive = imx_fec_receive, + .cleanup = imx_fec_cleanup, + .link_status_changed = imx_fec_set_link, +}; + + +static void imx_fec_realize(DeviceState *dev, Error **errp) +{ + IMXFECState *s = IMX_FEC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx_fec_ops, s, + TYPE_IMX_FEC, 0x400); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); + + s->conf.peers.ncs[0] = nd_table[0].netdev; + + s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf, + object_get_typename(OBJECT(dev)), DEVICE(dev)->id, + s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} + +static Property imx_fec_properties[] = { + DEFINE_NIC_PROPERTIES(IMXFECState, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void imx_fec_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_imx_fec; + dc->reset = imx_fec_reset; + dc->props = imx_fec_properties; + dc->realize = imx_fec_realize; +} + +static const TypeInfo imx_fec_info = { + .name = TYPE_IMX_FEC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXFECState), + .class_init = imx_fec_class_init, +}; + +static void imx_fec_register_types(void) +{ + type_register_static(&imx_fec_info); +} + +type_init(imx_fec_register_types) diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 17e7199f70..18b064463a 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -44,7 +44,6 @@ typedef struct ISANE2000State { static NetClientInfo net_ne2000_isa_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = ne2000_can_receive, .receive = ne2000_receive, }; diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 3492db3663..53c704ad41 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -165,15 +165,6 @@ static int ne2000_buffer_full(NE2000State *s) return 0; } -int ne2000_can_receive(NetClientState *nc) -{ - NE2000State *s = qemu_get_nic_opaque(nc); - - if (s->cmd & E8390_STOP) - return 1; - return !ne2000_buffer_full(s); -} - #define MIN_BUF_SIZE 60 ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) @@ -705,7 +696,6 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size) static NetClientInfo net_ne2000_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = ne2000_can_receive, .receive = ne2000_receive, }; diff --git a/hw/net/ne2000.h b/hw/net/ne2000.h index e500306aac..d022b28fc2 100644 --- a/hw/net/ne2000.h +++ b/hw/net/ne2000.h @@ -34,7 +34,6 @@ typedef struct NE2000State { void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size); extern const VMStateDescription vmstate_ne2000; void ne2000_reset(NE2000State *s); -int ne2000_can_receive(NetClientState *nc); ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_); #endif diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 47d080fd33..7e7bda4987 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -265,9 +265,7 @@ err_bad_io: err_no_mem: err_bad_attr: for (i = 0; i < ROCKER_TX_FRAGS_MAX; i++) { - if (iov[i].iov_base) { - g_free(iov[i].iov_base); - } + g_free(iov[i].iov_base); } return err; diff --git a/hw/net/rocker/rocker_desc.c b/hw/net/rocker/rocker_desc.c index 9d896fe470..b5c0b4a241 100644 --- a/hw/net/rocker/rocker_desc.c +++ b/hw/net/rocker/rocker_desc.c @@ -136,9 +136,7 @@ bool desc_ring_set_size(DescRing *ring, uint32_t size) } for (i = 0; i < ring->size; i++) { - if (ring->info[i].buf) { - g_free(ring->info[i].buf); - } + g_free(ring->info[i].buf); } ring->size = size; @@ -360,9 +358,7 @@ DescRing *desc_ring_alloc(Rocker *r, int index) void desc_ring_free(DescRing *ring) { - if (ring->info) { - g_free(ring->info); - } + g_free(ring->info); g_free(ring); } diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index edbb61ccf3..b0d6c40f58 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -43,7 +43,7 @@ * Added rx/tx buffer reset when enabling rx/tx operation * * 2010-Feb-04 Frediano Ziglio: Rewrote timer support using QEMU timer only - * when strictly needed (required for for + * when strictly needed (required for * Darwin) * 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading */ @@ -56,6 +56,7 @@ #include "sysemu/dma.h" #include "qemu/timer.h" #include "net/net.h" +#include "net/eth.h" #include "hw/loader.h" #include "sysemu/sysemu.h" #include "qemu/iov.h" @@ -72,11 +73,8 @@ #define MOD2(input, size) \ ( ( input ) & ( size - 1 ) ) -#define ETHER_ADDR_LEN 6 #define ETHER_TYPE_LEN 2 -#define ETH_HLEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) -#define ETH_P_IP 0x0800 /* Internet Protocol packet */ -#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_HLEN (ETH_ALEN * 2 + ETHER_TYPE_LEN) #define ETH_MTU 1500 #define VLAN_TCI_LEN 2 @@ -1016,8 +1014,8 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t /* write VLAN info to descriptor variables. */ if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *) - &buf[ETHER_ADDR_LEN * 2]) == ETH_P_8021Q) { - dot1q_buf = &buf[ETHER_ADDR_LEN * 2]; + &buf[ETH_ALEN * 2]) == ETH_P_VLAN) { + dot1q_buf = &buf[ETH_ALEN * 2]; size -= VLAN_HLEN; /* if too small buffer, use the tailroom added duing expansion */ if (size < MIN_BUF_SIZE) { @@ -1058,10 +1056,10 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t /* receive/copy to target memory */ if (dot1q_buf) { - pci_dma_write(d, rx_addr, buf, 2 * ETHER_ADDR_LEN); - pci_dma_write(d, rx_addr + 2 * ETHER_ADDR_LEN, - buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN, - size - 2 * ETHER_ADDR_LEN); + pci_dma_write(d, rx_addr, buf, 2 * ETH_ALEN); + pci_dma_write(d, rx_addr + 2 * ETH_ALEN, + buf + 2 * ETH_ALEN + VLAN_HLEN, + size - 2 * ETH_ALEN); } else { pci_dma_write(d, rx_addr, buf, size); } @@ -1148,7 +1146,9 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t /* if receiver buffer is empty then avail == 0 */ - if (avail != 0 && size + 8 >= avail) +#define RX_ALIGN(x) (((x) + 3) & ~0x3) + + if (avail != 0 && RX_ALIGN(size + 8) >= avail) { DPRINTF("rx overflow: rx buffer length %d head 0x%04x " "read 0x%04x === available 0x%04x need 0x%04x\n", @@ -1157,7 +1157,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t s->IntrStatus |= RxOverflow; ++s->RxMissed; rtl8139_update_irq(s); - return size_; + return 0; } packet_header |= RxStatusOK; @@ -1176,7 +1176,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t rtl8139_write_buffer(s, (uint8_t *)&val, 4); /* correct buffer write pointer */ - s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize); + s->RxBufAddr = MOD2(RX_ALIGN(s->RxBufAddr), s->RxBufferSize); /* now we can signal we have received something */ @@ -1783,12 +1783,12 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, return; } - if (dot1q_buf && size >= ETHER_ADDR_LEN * 2) { + if (dot1q_buf && size >= ETH_ALEN * 2) { iov = (struct iovec[3]) { - { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 }, + { .iov_base = buf, .iov_len = ETH_ALEN * 2 }, { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN }, - { .iov_base = buf + ETHER_ADDR_LEN * 2, - .iov_len = size - ETHER_ADDR_LEN * 2 }, + { .iov_base = buf + ETH_ALEN * 2, + .iov_len = size - ETH_ALEN * 2 }, }; memcpy(vlan_iov, iov, sizeof(vlan_iov)); @@ -1868,64 +1868,12 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) } /* structures and macros for task offloading */ -typedef struct ip_header -{ - uint8_t ip_ver_len; /* version and header length */ - uint8_t ip_tos; /* type of service */ - uint16_t ip_len; /* total length */ - uint16_t ip_id; /* identification */ - uint16_t ip_off; /* fragment offset field */ - uint8_t ip_ttl; /* time to live */ - uint8_t ip_p; /* protocol */ - uint16_t ip_sum; /* checksum */ - uint32_t ip_src,ip_dst; /* source and dest address */ -} ip_header; - -#define IP_HEADER_VERSION_4 4 -#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf) -#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2) - -typedef struct tcp_header -{ - uint16_t th_sport; /* source port */ - uint16_t th_dport; /* destination port */ - uint32_t th_seq; /* sequence number */ - uint32_t th_ack; /* acknowledgement number */ - uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */ - uint16_t th_win; /* window */ - uint16_t th_sum; /* checksum */ - uint16_t th_urp; /* urgent pointer */ -} tcp_header; - -typedef struct udp_header -{ - uint16_t uh_sport; /* source port */ - uint16_t uh_dport; /* destination port */ - uint16_t uh_ulen; /* udp length */ - uint16_t uh_sum; /* udp checksum */ -} udp_header; - -typedef struct ip_pseudo_header -{ - uint32_t ip_src; - uint32_t ip_dst; - uint8_t zeros; - uint8_t ip_proto; - uint16_t ip_payload; -} ip_pseudo_header; - -#define IP_PROTO_TCP 6 -#define IP_PROTO_UDP 17 - #define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2) #define TCP_FLAGS_ONLY(flags) ((flags)&0x3f) #define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags)) #define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off))) -#define TCP_FLAG_FIN 0x01 -#define TCP_FLAG_PUSH 0x08 - /* produces ones' complement sum of data */ static uint16_t ones_complement_sum(uint8_t *data, size_t len) { @@ -2134,7 +2082,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) bswap16(txdw1 & CP_TX_VLAN_TAG_MASK)); dot1q_buffer = (uint16_t *) dot1q_buffer_space; - dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q); + dot1q_buffer[0] = cpu_to_be16(ETH_P_VLAN); /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */ dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK); } else { @@ -2151,12 +2099,12 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) DPRINTF("+++ C+ mode offloaded task checksum\n"); /* Large enough for Ethernet and IP headers? */ - if (saved_size < ETH_HLEN + sizeof(ip_header)) { + if (saved_size < ETH_HLEN + sizeof(struct ip_header)) { goto skip_offload; } /* ip packet header */ - ip_header *ip = NULL; + struct ip_header *ip = NULL; int hlen = 0; uint8_t ip_protocol = 0; uint16_t ip_data_len = 0; @@ -2172,11 +2120,15 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) DPRINTF("+++ C+ mode has IP packet\n"); - /* not aligned */ + /* Note on memory alignment: eth_payload_data is 16-bit aligned + * since saved_buffer is allocated with g_malloc() and ETH_HLEN is + * even. 32-bit accesses must use ldl/stl wrappers to avoid + * unaligned accesses. + */ eth_payload_data = saved_buffer + ETH_HLEN; eth_payload_len = saved_size - ETH_HLEN; - ip = (ip_header*)eth_payload_data; + ip = (struct ip_header*)eth_payload_data; if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { DPRINTF("+++ C+ mode packet has bad IP version %d " @@ -2185,8 +2137,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) goto skip_offload; } - hlen = IP_HEADER_LENGTH(ip); - if (hlen < sizeof(ip_header) || hlen > eth_payload_len) { + hlen = IP_HDR_GET_LEN(ip); + if (hlen < sizeof(struct ip_header) || hlen > eth_payload_len) { goto skip_offload; } @@ -2269,7 +2221,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) } DPRINTF("+++ C+ mode TSO TCP seqno %08x\n", - be32_to_cpu(p_tcp_hdr->th_seq)); + ldl_be_p(&p_tcp_hdr->th_seq)); /* add 4 TCP pseudoheader fields */ /* copy IP source and destination fields */ @@ -2287,7 +2239,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) /* keep PUSH and FIN flags only for the last frame */ if (!is_last_frame) { - TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN); + TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TH_PUSH | TH_FIN); } /* recalculate TCP checksum */ @@ -2325,7 +2277,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) 0, (uint8_t *) dot1q_buffer); /* add transferred count to TCP sequence number */ - p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq)); + stl_be_p(&p_tcp_hdr->th_seq, + chunk_size + ldl_be_p(&p_tcp_hdr->th_seq)); ++send_count; } @@ -3438,10 +3391,8 @@ static void pci_rtl8139_uninit(PCIDevice *dev) { RTL8139State *s = RTL8139(dev); - if (s->cplus_txbuffer) { - g_free(s->cplus_txbuffer); - s->cplus_txbuffer = NULL; - } + g_free(s->cplus_txbuffer); + s->cplus_txbuffer = NULL; timer_del(s->timer); timer_free(s->timer); qemu_del_nic(s->nic); diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 5c1d11f517..1d76b94c84 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -197,7 +197,7 @@ static int vhost_net_set_vnet_endian(VirtIODevice *dev, NetClientState *peer, { int r = 0; - if (virtio_has_feature(dev, VIRTIO_F_VERSION_1) || + if (virtio_vdev_has_feature(dev, VIRTIO_F_VERSION_1) || (virtio_legacy_is_cross_endian(dev) && !virtio_is_big_endian(dev))) { r = qemu_set_vnet_le(peer, set); if (r) { diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 8d28e45afc..f72eebff2b 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -86,8 +86,8 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) memcpy(&netcfg, config, n->config_size); - if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) && - !virtio_has_feature(vdev, VIRTIO_F_VERSION_1) && + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) && + !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) && memcmp(netcfg.mac, n->mac, ETH_ALEN)) { memcpy(n->mac, netcfg.mac, ETH_ALEN); qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); @@ -304,7 +304,7 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) info->multicast_table = str_list; info->vlan_table = get_vlan_table(n); - if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) { info->vlan = RX_STATE_ALL; } else if (!info->vlan_table) { info->vlan = RX_STATE_NONE; @@ -529,13 +529,13 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) int i; virtio_net_set_multiqueue(n, - __virtio_has_feature(features, VIRTIO_NET_F_MQ)); + virtio_has_feature(features, VIRTIO_NET_F_MQ)); virtio_net_set_mrg_rx_bufs(n, - __virtio_has_feature(features, - VIRTIO_NET_F_MRG_RXBUF), - __virtio_has_feature(features, - VIRTIO_F_VERSION_1)); + virtio_has_feature(features, + VIRTIO_NET_F_MRG_RXBUF), + virtio_has_feature(features, + VIRTIO_F_VERSION_1)); if (n->has_vnet_hdr) { n->curr_guest_offloads = @@ -552,7 +552,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) vhost_net_ack_features(get_vhost_net(nc->peer), features); } - if (__virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { + if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { memset(n->vlans, 0, MAX_VLAN >> 3); } else { memset(n->vlans, 0xff, MAX_VLAN >> 3); @@ -599,7 +599,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, uint64_t offloads; size_t s; - if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { return VIRTIO_NET_ERR; } @@ -1449,7 +1449,7 @@ static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) } } - if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { qemu_put_be64(f, n->curr_guest_offloads); } } @@ -1475,7 +1475,8 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, n->vqs[0].tx_waiting = qemu_get_be32(f); virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f), - virtio_has_feature(vdev, VIRTIO_F_VERSION_1)); + virtio_vdev_has_feature(vdev, + VIRTIO_F_VERSION_1)); if (version_id >= 3) n->status = qemu_get_be16(f); @@ -1558,7 +1559,7 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, } } - if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { n->curr_guest_offloads = qemu_get_be64(f); } else { n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); @@ -1585,8 +1586,8 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_subqueue(n->nic, i)->link_down = link_down; } - if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && - virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && + virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { n->announce_counter = SELF_ANNOUNCE_ROUNDS; timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); } diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 071feebf15..04159c8222 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1988,7 +1988,6 @@ static void vmxnet3_set_link_status(NetClientState *nc) static NetClientInfo net_vmxnet3_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = vmxnet3_can_receive, .receive = vmxnet3_receive, .link_status_changed = vmxnet3_set_link_status, }; diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index d7cbfc1033..0da16b44f4 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -24,7 +24,6 @@ #include <stdarg.h> #include <string.h> #include <unistd.h> -#include <signal.h> #include <inttypes.h> #include <fcntl.h> #include <errno.h> diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 88481b78c4..658f8c4ff1 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -187,9 +187,7 @@ static void fw_cfg_bootsplash(FWCfgState *s) g_free(filename); return; } - if (boot_splash_filedata != NULL) { - g_free(boot_splash_filedata); - } + g_free(boot_splash_filedata); boot_splash_filedata = (uint8_t *)file_data; boot_splash_filedata_size = file_size; diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index 1cb25f3fa6..1fb71c8081 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -40,7 +40,6 @@ * http://download.intel.com/design/chipsets/datashts/29054901.pdf */ -#define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost" #define I440FX_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE) @@ -95,7 +94,6 @@ typedef struct PIIX3State { #define PIIX3_PCI_DEVICE(obj) \ OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE) -#define TYPE_I440FX_PCI_DEVICE "i440FX" #define I440FX_PCI_DEVICE(obj) \ OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) @@ -305,7 +303,8 @@ static void i440fx_realize(PCIDevice *dev, Error **errp) dev->config[I440FX_SMRAM] = 0x02; } -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, +PCIBus *i440fx_init(const char *host_type, const char *pci_type, + PCII440FXState **pi440fx_state, int *piix3_devfn, ISABus **isa_bus, qemu_irq *pic, MemoryRegion *address_space_mem, @@ -325,7 +324,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, unsigned i; I440FXState *i440fx; - dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE); + dev = qdev_create(NULL, host_type); s = PCI_HOST_BRIDGE(dev); b = pci_bus_new(dev, NULL, pci_address_space, address_space_io, 0, TYPE_PCI_BUS); @@ -333,7 +332,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL); qdev_init_nofail(dev); - d = pci_create_simple(b, 0, TYPE_I440FX_PCI_DEVICE); + d = pci_create_simple(b, 0, pci_type); *pi440fx_state = I440FX_PCI_DEVICE(d); f = *pi440fx_state; f->system_memory = address_space_mem; @@ -740,6 +739,90 @@ static const TypeInfo i440fx_info = { .class_init = i440fx_class_init, }; +/* IGD Passthrough Host Bridge. */ +typedef struct { + uint8_t offset; + uint8_t len; +} IGDHostInfo; + +/* Here we just expose minimal host bridge offset subset. */ +static const IGDHostInfo igd_host_bridge_infos[] = { + {0x08, 2}, /* revision id */ + {0x2c, 2}, /* sybsystem vendor id */ + {0x2e, 2}, /* sybsystem id */ + {0x50, 2}, /* SNB: processor graphics control register */ + {0x52, 2}, /* processor graphics control register */ + {0xa4, 4}, /* SNB: graphics base of stolen memory */ + {0xa8, 4}, /* SNB: base of GTT stolen memory */ +}; + +static int host_pci_config_read(int pos, int len, uint32_t val) +{ + char path[PATH_MAX]; + int config_fd; + ssize_t size = sizeof(path); + /* Access real host bridge. */ + int rc = snprintf(path, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", + 0, 0, 0, 0, "config"); + + if (rc >= size || rc < 0) { + return -ENODEV; + } + + config_fd = open(path, O_RDWR); + if (config_fd < 0) { + return -ENODEV; + } + + if (lseek(config_fd, pos, SEEK_SET) != pos) { + return -errno; + } + do { + rc = read(config_fd, (uint8_t *)&val, len); + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (rc != len) { + return -errno; + } + + return 0; +} + +static int igd_pt_i440fx_initfn(struct PCIDevice *pci_dev) +{ + uint32_t val = 0; + int rc, i, num; + int pos, len; + + num = ARRAY_SIZE(igd_host_bridge_infos); + for (i = 0; i < num; i++) { + pos = igd_host_bridge_infos[i].offset; + len = igd_host_bridge_infos[i].len; + rc = host_pci_config_read(pos, len, val); + if (rc) { + return -ENODEV; + } + pci_default_write_config(pci_dev, pos, val, len); + } + + return 0; +} + +static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = igd_pt_i440fx_initfn; + dc->desc = "IGD Passthrough Host bridge"; +} + +static const TypeInfo igd_passthrough_i440fx_info = { + .name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE, + .parent = TYPE_I440FX_PCI_DEVICE, + .instance_size = sizeof(PCII440FXState), + .class_init = igd_passthrough_i440fx_class_init, +}; + static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { @@ -781,6 +864,7 @@ static const TypeInfo i440fx_pcihost_info = { static void i440fx_register_types(void) { type_register_static(&i440fx_info); + type_register_static(&igd_passthrough_i440fx_info); type_register_static(&piix3_pci_type_info); type_register_static(&piix3_info); type_register_static(&piix3_xen_info); diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index c63f45d217..988907bfa0 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -328,9 +328,7 @@ static void raven_realize(PCIDevice *d, Error **errp) if (bios_size < 0 || bios_size > BIOS_SIZE) { hw_error("qemu: could not load bios image '%s'\n", s->bios_name); } - if (filename) { - g_free(filename); - } + g_free(filename); } } diff --git a/hw/pci/msix.c b/hw/pci/msix.c index 7716bf3649..2fdada4e8f 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -314,9 +314,7 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, bar_size = bar_pba_offset + bar_pba_size; } - if (bar_size & (bar_size - 1)) { - bar_size = 1 << qemu_fls(bar_size); - } + bar_size = pow2ceil(bar_size); name = g_strdup_printf("%s-msix", dev->name); memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, bar_size); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 9f57aeaeba..ccea628209 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1154,16 +1154,16 @@ static void pci_update_mappings(PCIDevice *d) /* now do the real mapping */ if (r->addr != PCI_BAR_UNMAPPED) { trace_pci_update_mappings_del(d, pci_bus_num(d->bus), - PCI_FUNC(d->devfn), PCI_SLOT(d->devfn), + PCI_FUNC(d->devfn), i, r->addr, r->size); memory_region_del_subregion(r->address_space, r->memory); } r->addr = new_addr; if (r->addr != PCI_BAR_UNMAPPED) { trace_pci_update_mappings_add(d, pci_bus_num(d->bus), - PCI_FUNC(d->devfn), PCI_SLOT(d->devfn), + PCI_FUNC(d->devfn), i, r->addr, r->size); memory_region_add_subregion_overlap(r->address_space, r->addr, r->memory, 1); @@ -2071,9 +2071,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, g_free(path); return; } - if (size & (size - 1)) { - size = 1 << qemu_fls(size); - } + size = pow2ceil(size); vmsd = qdev_get_vmsd(DEVICE(pdev)); @@ -2385,17 +2383,14 @@ static void pci_device_class_init(ObjectClass *klass, void *data) AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) { PCIBus *bus = PCI_BUS(dev->bus); + PCIBus *iommu_bus = bus; - if (bus->iommu_fn) { - return bus->iommu_fn(bus, bus->iommu_opaque, dev->devfn); + while(iommu_bus && !iommu_bus->iommu_fn && iommu_bus->parent_dev) { + iommu_bus = PCI_BUS(iommu_bus->parent_dev->bus); } - - if (bus->parent_dev) { - /** We are ignoring the bus master DMA bit of the bridge - * as it would complicate things such as VFIO for no good reason */ - return pci_device_iommu_address_space(bus->parent_dev); + if (iommu_bus && iommu_bus->iommu_fn) { + return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, dev->devfn); } - return &address_space_memory; } diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index bfb4d31b62..d34fdf3ecb 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -1,5 +1,4 @@ #include "qemu-common.h" -#include <strings.h> #include <stdint.h> #include "qemu/range.h" #include "qemu/error-report.h" diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index a8f79d8003..a2feb4c985 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1460,10 +1460,8 @@ static void spapr_pci_pre_save(void *opaque) gpointer key, value; int i; - if (sphb->msi_devs) { - g_free(sphb->msi_devs); - sphb->msi_devs = NULL; - } + g_free(sphb->msi_devs); + sphb->msi_devs = NULL; sphb->msi_devs_num = g_hash_table_size(sphb->msi); if (!sphb->msi_devs_num) { return; @@ -1490,10 +1488,8 @@ static int spapr_pci_post_load(void *opaque, int version_id) sizeof(sphb->msi_devs[i].value)); g_hash_table_insert(sphb->msi, key, value); } - if (sphb->msi_devs) { - g_free(sphb->msi_devs); - sphb->msi_devs = NULL; - } + g_free(sphb->msi_devs); + sphb->msi_devs = NULL; sphb->msi_devs_num = 0; return 0; diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 27cd75a932..527d75400a 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -9,3 +9,5 @@ obj-y += css.o obj-y += s390-virtio-ccw.o obj-y += virtio-ccw.o obj-y += s390-pci-bus.o s390-pci-inst.o +obj-y += s390-skeys.o +obj-$(CONFIG_KVM) += s390-skeys-kvm.o diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 5df450e00b..c033612889 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -261,6 +261,9 @@ static CCW1 copy_ccw_from_guest(hwaddr addr, bool fmt1) ret.flags = tmp0.flags; ret.count = be16_to_cpu(tmp0.count); ret.cda = be16_to_cpu(tmp0.cda1) | (tmp0.cda0 << 16); + if ((ret.cmd_code & 0x0f) == CCW_CMD_TIC) { + ret.cmd_code &= 0x0f; + } } return ret; } @@ -287,6 +290,10 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr) ((ccw.cmd_code & 0xf0) != 0)) { return -EINVAL; } + if (!sch->ccw_fmt_1 && (ccw.count == 0) && + (ccw.cmd_code != CCW_CMD_TIC)) { + return -EINVAL; + } if (ccw.flags & CCW_FLAG_SUSPEND) { return -EINPROGRESS; @@ -392,6 +399,8 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb) path = 0x80; if (!(s->ctrl & SCSW_ACTL_SUSP)) { + s->cstat = 0; + s->dstat = 0; /* Look at the orb and try to execute the channel program. */ assert(orb != NULL); /* resume does not pass an orb */ p->intparm = orb->intparm; diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 0c700effb1..ef2a05160a 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -27,12 +27,12 @@ typedef struct SCLPEventsBus { struct SCLPEventFacility { SysBusDevice parent_obj; SCLPEventsBus sbus; + SCLPEvent quiesce_event; + SCLPEvent cpu_hotplug_event; /* guest' receive mask */ unsigned int receive_mask; }; -static SCLPEvent cpu_hotplug; - /* return true if any child has event pending set */ static bool event_pending(SCLPEventFacility *ef) { @@ -240,12 +240,13 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) sclp_active_selection_mask = sclp_cp_receive_mask; break; case SCLP_SELECTIVE_READ: - if (!(sclp_cp_receive_mask & be32_to_cpu(red->mask))) { + sclp_active_selection_mask = be32_to_cpu(red->mask); + if (!sclp_cp_receive_mask || + (sclp_active_selection_mask & ~sclp_cp_receive_mask)) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK); goto out; } - sclp_active_selection_mask = be32_to_cpu(red->mask); break; default: sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); @@ -286,8 +287,26 @@ out: #define TYPE_SCLP_EVENTS_BUS "s390-sclp-events-bus" +static void sclp_events_bus_realize(BusState *bus, Error **errp) +{ + BusChild *kid; + + /* TODO: recursive realization has to be done in common code */ + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + + object_property_set_bool(OBJECT(dev), true, "realized", errp); + if (*errp) { + return; + } + } +} + static void sclp_events_bus_class_init(ObjectClass *klass, void *data) { + BusClass *bc = BUS_CLASS(klass); + + bc->realize = sclp_events_bus_realize; } static const TypeInfo sclp_events_bus_info = { @@ -324,26 +343,24 @@ static const VMStateDescription vmstate_event_facility = { } }; -static int init_event_facility(SCLPEventFacility *event_facility) +static void init_event_facility(Object *obj) { - DeviceState *sdev = DEVICE(event_facility); - DeviceState *quiesce; + SCLPEventFacility *event_facility = EVENT_FACILITY(obj); + DeviceState *sdev = DEVICE(obj); /* Spawn a new bus for SCLP events */ qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus), TYPE_SCLP_EVENTS_BUS, sdev, NULL); - quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce"); - if (!quiesce) { - return -1; - } - qdev_init_nofail(quiesce); - - object_initialize(&cpu_hotplug, sizeof(cpu_hotplug), TYPE_SCLP_CPU_HOTPLUG); - qdev_set_parent_bus(DEVICE(&cpu_hotplug), BUS(&event_facility->sbus)); - object_property_set_bool(OBJECT(&cpu_hotplug), true, "realized", NULL); - - return 0; + object_initialize(&event_facility->quiesce_event, sizeof(SCLPEvent), + TYPE_SCLP_QUIESCE); + qdev_set_parent_bus(DEVICE(&event_facility->quiesce_event), + &event_facility->sbus.qbus); + object_initialize(&event_facility->cpu_hotplug_event, sizeof(SCLPEvent), + TYPE_SCLP_CPU_HOTPLUG); + qdev_set_parent_bus(DEVICE(&event_facility->cpu_hotplug_event), + &event_facility->sbus.qbus); + /* the facility will automatically realize the devices via the bus */ } static void reset_event_facility(DeviceState *dev) @@ -362,7 +379,6 @@ static void init_event_facility_class(ObjectClass *klass, void *data) dc->reset = reset_event_facility; dc->vmsd = &vmstate_event_facility; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - k->init = init_event_facility; k->command_handler = command_handler; k->event_pending = event_pending; } @@ -370,6 +386,7 @@ static void init_event_facility_class(ObjectClass *klass, void *data) static const TypeInfo sclp_event_facility_info = { .name = TYPE_SCLP_EVENT_FACILITY, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = init_event_facility, .instance_size = sizeof(SCLPEventFacility), .class_init = init_event_facility_class, .class_size = sizeof(SCLPEventFacilityClass), diff --git a/hw/s390x/s390-skeys-kvm.c b/hw/s390x/s390-skeys-kvm.c new file mode 100644 index 0000000000..682949afb2 --- /dev/null +++ b/hw/s390x/s390-skeys-kvm.c @@ -0,0 +1,75 @@ +/* + * s390 storage key device + * + * Copyright 2015 IBM Corp. + * Author(s): Jason J. Herne <jjherne@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "hw/s390x/storage-keys.h" +#include "sysemu/kvm.h" +#include "qemu/error-report.h" + +static int kvm_s390_skeys_enabled(S390SKeysState *ss) +{ + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + uint8_t single_key; + int r; + + r = skeyclass->get_skeys(ss, 0, 1, &single_key); + if (r != 0 && r != KVM_S390_GET_SKEYS_NONE) { + error_report("S390_GET_KEYS error %d\n", r); + } + return (r == 0); +} + +static int kvm_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + struct kvm_s390_skeys args = { + .start_gfn = start_gfn, + .count = count, + .skeydata_addr = (__u64)keys + }; + + return kvm_vm_ioctl(kvm_state, KVM_S390_GET_SKEYS, &args); +} + +static int kvm_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + struct kvm_s390_skeys args = { + .start_gfn = start_gfn, + .count = count, + .skeydata_addr = (__u64)keys + }; + + return kvm_vm_ioctl(kvm_state, KVM_S390_SET_SKEYS, &args); +} + +static void kvm_s390_skeys_class_init(ObjectClass *oc, void *data) +{ + S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); + + skeyclass->skeys_enabled = kvm_s390_skeys_enabled; + skeyclass->get_skeys = kvm_s390_skeys_get; + skeyclass->set_skeys = kvm_s390_skeys_set; +} + +static const TypeInfo kvm_s390_skeys_info = { + .name = TYPE_KVM_S390_SKEYS, + .parent = TYPE_S390_SKEYS, + .instance_size = sizeof(S390SKeysState), + .class_init = kvm_s390_skeys_class_init, + .class_size = sizeof(S390SKeysClass), +}; + +static void kvm_s390_skeys_register_types(void) +{ + type_register_static(&kvm_s390_skeys_info); +} + +type_init(kvm_s390_skeys_register_types) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c new file mode 100644 index 0000000000..539ef6d3a4 --- /dev/null +++ b/hw/s390x/s390-skeys.c @@ -0,0 +1,415 @@ +/* + * s390 storage key device + * + * Copyright 2015 IBM Corp. + * Author(s): Jason J. Herne <jjherne@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "hw/boards.h" +#include "qmp-commands.h" +#include "migration/qemu-file.h" +#include "hw/s390x/storage-keys.h" +#include "qemu/error-report.h" + +#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */ +#define S390_SKEYS_SAVE_FLAG_EOS 0x01 +#define S390_SKEYS_SAVE_FLAG_SKEYS 0x02 +#define S390_SKEYS_SAVE_FLAG_ERROR 0x04 + +S390SKeysState *s390_get_skeys_device(void) +{ + S390SKeysState *ss; + + ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL)); + assert(ss); + return ss; +} + +void s390_skeys_init(void) +{ + Object *obj; + + if (kvm_enabled()) { + obj = object_new(TYPE_KVM_S390_SKEYS); + } else { + obj = object_new(TYPE_QEMU_S390_SKEYS); + } + object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS, + obj, NULL); + object_unref(obj); + + qdev_init_nofail(DEVICE(obj)); +} + +static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn, + uint64_t count, Error **errp) +{ + uint64_t curpage = startgfn; + uint64_t maxpage = curpage + count - 1; + const char *fmt = "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d," + " ch=%d, reserved=%d\n"; + char buf[128]; + int len; + + for (; curpage <= maxpage; curpage++) { + uint8_t acc = (*keys & 0xF0) >> 4; + int fp = (*keys & 0x08); + int ref = (*keys & 0x04); + int ch = (*keys & 0x02); + int res = (*keys & 0x01); + + len = snprintf(buf, sizeof(buf), fmt, curpage, + *keys, acc, fp, ref, ch, res); + assert(len < sizeof(buf)); + qemu_put_buffer(f, (uint8_t *)buf, len); + keys++; + } +} + +void hmp_info_skeys(Monitor *mon, const QDict *qdict) +{ + S390SKeysState *ss = s390_get_skeys_device(); + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + uint64_t addr = qdict_get_int(qdict, "addr"); + uint8_t key; + int r; + + /* Quick check to see if guest is using storage keys*/ + if (!skeyclass->skeys_enabled(ss)) { + monitor_printf(mon, "Error: This guest is not using storage keys\n"); + return; + } + + r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + if (r < 0) { + monitor_printf(mon, "Error: %s\n", strerror(-r)); + return; + } + + monitor_printf(mon, " key: 0x%X\n", key); +} + +void hmp_dump_skeys(Monitor *mon, const QDict *qdict) +{ + const char *filename = qdict_get_str(qdict, "filename"); + Error *err = NULL; + + qmp_dump_skeys(filename, &err); + if (err) { + monitor_printf(mon, "%s\n", error_get_pretty(err)); + error_free(err); + } +} + +void qmp_dump_skeys(const char *filename, Error **errp) +{ + S390SKeysState *ss = s390_get_skeys_device(); + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + const uint64_t total_count = ram_size / TARGET_PAGE_SIZE; + uint64_t handled_count = 0, cur_count; + Error *lerr = NULL; + vaddr cur_gfn = 0; + uint8_t *buf; + int ret; + QEMUFile *f; + + /* Quick check to see if guest is using storage keys*/ + if (!skeyclass->skeys_enabled(ss)) { + error_setg(errp, "This guest is not using storage keys - " + "nothing to dump"); + return; + } + + f = qemu_fopen(filename, "wb"); + if (!f) { + error_setg_file_open(errp, errno, filename); + return; + } + + buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE); + if (!buf) { + error_setg(errp, "Could not allocate memory"); + goto out; + } + + /* we'll only dump initial memory for now */ + while (handled_count < total_count) { + /* Calculate how many keys to ask for & handle overflow case */ + cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE); + + ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf); + if (ret < 0) { + error_setg(errp, "get_keys error %d", ret); + goto out_free; + } + + /* write keys to stream */ + write_keys(f, buf, cur_gfn, cur_count, &lerr); + if (lerr) { + goto out_free; + } + + cur_gfn += cur_count; + handled_count += cur_count; + } + +out_free: + error_propagate(errp, lerr); + g_free(buf); +out: + qemu_fclose(f); +} + +static void qemu_s390_skeys_init(Object *obj) +{ + QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj); + MachineState *machine = MACHINE(qdev_get_machine()); + + skeys->key_count = machine->maxram_size / TARGET_PAGE_SIZE; + skeys->keydata = g_malloc0(skeys->key_count); +} + +static int qemu_s390_skeys_enabled(S390SKeysState *ss) +{ + return 1; +} + +/* + * TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get + * will have to make sure that the given gfn belongs to a memory region and not + * a memory hole. + */ +static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss); + int i; + + /* Check for uint64 overflow and access beyond end of key data */ + if (start_gfn + count > skeydev->key_count || start_gfn + count < count) { + error_report("Error: Setting storage keys for page beyond the end " + "of memory: gfn=%" PRIx64 " count=%" PRId64 "\n", start_gfn, + count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + skeydev->keydata[start_gfn + i] = keys[i]; + } + return 0; +} + +static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss); + int i; + + /* Check for uint64 overflow and access beyond end of key data */ + if (start_gfn + count > skeydev->key_count || start_gfn + count < count) { + error_report("Error: Getting storage keys for page beyond the end " + "of memory: gfn=%" PRIx64 " count=%" PRId64 "\n", start_gfn, + count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + keys[i] = skeydev->keydata[start_gfn + i]; + } + return 0; +} + +static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data) +{ + S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); + + skeyclass->skeys_enabled = qemu_s390_skeys_enabled; + skeyclass->get_skeys = qemu_s390_skeys_get; + skeyclass->set_skeys = qemu_s390_skeys_set; +} + +static const TypeInfo qemu_s390_skeys_info = { + .name = TYPE_QEMU_S390_SKEYS, + .parent = TYPE_S390_SKEYS, + .instance_init = qemu_s390_skeys_init, + .instance_size = sizeof(QEMUS390SKeysState), + .class_init = qemu_s390_skeys_class_init, + .instance_size = sizeof(S390SKeysClass), +}; + +static void s390_storage_keys_save(QEMUFile *f, void *opaque) +{ + S390SKeysState *ss = S390_SKEYS(opaque); + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + uint64_t pages_left = ram_size / TARGET_PAGE_SIZE; + uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS; + vaddr cur_gfn = 0; + int error = 0; + uint8_t *buf; + + if (!skeyclass->skeys_enabled(ss)) { + goto end_stream; + } + + buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE); + if (!buf) { + error_report("storage key save could not allocate memory\n"); + goto end_stream; + } + + /* We only support initial memory. Standby memory is not handled yet. */ + qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS); + qemu_put_be64(f, pages_left); + + while (pages_left) { + read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE); + + if (!error) { + error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf); + if (error) { + /* + * If error: we want to fill the stream with valid data instead + * of stopping early so we pad the stream with 0x00 values and + * use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the + * reading side. + */ + error_report("S390_GET_KEYS error %d\n", error); + memset(buf, 0, S390_SKEYS_BUFFER_SIZE); + eos = S390_SKEYS_SAVE_FLAG_ERROR; + } + } + + qemu_put_buffer(f, buf, read_count); + cur_gfn += read_count; + pages_left -= read_count; + } + + g_free(buf); +end_stream: + qemu_put_be64(f, eos); +} + +static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id) +{ + S390SKeysState *ss = S390_SKEYS(opaque); + S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); + int ret = 0; + + while (!ret) { + ram_addr_t addr; + int flags; + + addr = qemu_get_be64(f); + flags = addr & ~TARGET_PAGE_MASK; + addr &= TARGET_PAGE_MASK; + + switch (flags) { + case S390_SKEYS_SAVE_FLAG_SKEYS: { + const uint64_t total_count = qemu_get_be64(f); + uint64_t handled_count = 0, cur_count; + uint64_t cur_gfn = addr / TARGET_PAGE_SIZE; + uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE); + + if (!buf) { + error_report("storage key load could not allocate memory\n"); + ret = -ENOMEM; + break; + } + + while (handled_count < total_count) { + cur_count = MIN(total_count - handled_count, + S390_SKEYS_BUFFER_SIZE); + qemu_get_buffer(f, buf, cur_count); + + ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf); + if (ret < 0) { + error_report("S390_SET_KEYS error %d\n", ret); + break; + } + handled_count += cur_count; + cur_gfn += cur_count; + } + g_free(buf); + break; + } + case S390_SKEYS_SAVE_FLAG_ERROR: { + error_report("Storage key data is incomplete"); + ret = -EINVAL; + break; + } + case S390_SKEYS_SAVE_FLAG_EOS: + /* normal exit */ + return 0; + default: + error_report("Unexpected storage key flag data: %#x", flags); + ret = -EINVAL; + } + } + + return ret; +} + +static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp) +{ + S390SKeysState *ss = S390_SKEYS(obj); + + return ss->migration_enabled; +} + +static inline void s390_skeys_set_migration_enabled(Object *obj, bool value, + Error **errp) +{ + S390SKeysState *ss = S390_SKEYS(obj); + + /* Prevent double registration of savevm handler */ + if (ss->migration_enabled == value) { + return; + } + + ss->migration_enabled = value; + + if (ss->migration_enabled) { + register_savevm(NULL, TYPE_S390_SKEYS, 0, 1, s390_storage_keys_save, + s390_storage_keys_load, ss); + } else { + unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss); + } +} + +static void s390_skeys_instance_init(Object *obj) +{ + object_property_add_bool(obj, "migration-enabled", + s390_skeys_get_migration_enabled, + s390_skeys_set_migration_enabled, NULL); + object_property_set_bool(obj, true, "migration-enabled", NULL); +} + +static void s390_skeys_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo s390_skeys_info = { + .name = TYPE_S390_SKEYS, + .parent = TYPE_DEVICE, + .instance_init = s390_skeys_instance_init, + .instance_size = sizeof(S390SKeysState), + .class_init = s390_skeys_class_init, + .class_size = sizeof(S390SKeysClass), + .abstract = true, +}; + +static void qemu_s390_skeys_register_types(void) +{ + type_register_static(&s390_skeys_info); + type_register_static(&qemu_s390_skeys_info); +} + +type_init(qemu_s390_skeys_register_types) diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c index 77aec8a5bd..98cb1293c2 100644 --- a/hw/s390x/s390-virtio-bus.c +++ b/hw/s390x/s390-virtio-bus.c @@ -577,17 +577,12 @@ static const TypeInfo s390_virtio_blk = { .class_init = s390_virtio_blk_class_init, }; -static Property s390_virtio_serial_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void s390_virtio_serial_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); k->realize = s390_virtio_serial_realize; - dc->props = s390_virtio_serial_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 4c51d1a5bd..27a8360207 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -19,6 +19,7 @@ #include "virtio-ccw.h" #include "qemu/config-file.h" #include "s390-pci-bus.h" +#include "hw/s390x/storage-keys.h" #define TYPE_S390_CCW_MACHINE "s390-ccw-machine" @@ -99,58 +100,31 @@ static void virtio_ccw_register_hcalls(void) virtio_ccw_hcall_early_printk); } -static void ccw_init(MachineState *machine) +void s390_memory_init(ram_addr_t mem_size) { - ram_addr_t my_ram_size = machine->ram_size; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); - sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev(); - uint8_t *storage_keys; + + /* allocate RAM for core */ + memory_region_init_ram(ram, NULL, "s390.ram", mem_size, &error_abort); + vmstate_register_ram_global(ram); + memory_region_add_subregion(sysmem, 0, ram); + + /* Initialize storage key device */ + s390_skeys_init(); +} + +static void ccw_init(MachineState *machine) +{ int ret; VirtualCssBus *css_bus; DeviceState *dev; - QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL); - ram_addr_t pad_size = 0; - ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size); - ram_addr_t standby_mem_size = maxmem - my_ram_size; - uint64_t kvm_limit; - - /* The storage increment size is a multiple of 1M and is a power of 2. - * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. - * The variable 'mhd->increment_size' is an exponent of 2 that can be - * used to calculate the size (in bytes) of an increment. */ - mhd->increment_size = 20; - while ((my_ram_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) { - mhd->increment_size++; - } - while ((standby_mem_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) { - mhd->increment_size++; - } - /* The core and standby memory areas need to be aligned with - * the increment size. In effect, this can cause the - * user-specified memory size to be rounded down to align - * with the nearest increment boundary. */ - standby_mem_size = standby_mem_size >> mhd->increment_size - << mhd->increment_size; - my_ram_size = my_ram_size >> mhd->increment_size - << mhd->increment_size; - - /* let's propagate the changed ram size into the global variable. */ - ram_size = my_ram_size; - machine->maxram_size = my_ram_size + standby_mem_size; - - ret = s390_set_memory_limit(machine->maxram_size, &kvm_limit); - if (ret == -E2BIG) { - hw_error("qemu: host supports a maximum of %" PRIu64 " GB", - kvm_limit >> 30); - } else if (ret) { - hw_error("qemu: setting the guest size failed"); - } + s390_sclp_init(); + s390_memory_init(machine->ram_size); /* get a BUS */ css_bus = virtual_css_bus_init(); - s390_sclp_init(); s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, machine->initrd_filename, "s390-ccw.img", true); s390_flic_init(); @@ -163,27 +137,8 @@ static void ccw_init(MachineState *machine) /* register hypercalls */ virtio_ccw_register_hcalls(); - /* allocate RAM for core */ - memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort); - vmstate_register_ram_global(ram); - memory_region_add_subregion(sysmem, 0, ram); - - /* If the size of ram is not on a MEM_SECTION_SIZE boundary, - calculate the pad size necessary to force this boundary. */ - if (standby_mem_size) { - if (my_ram_size % MEM_SECTION_SIZE) { - pad_size = MEM_SECTION_SIZE - my_ram_size % MEM_SECTION_SIZE; - } - my_ram_size += standby_mem_size + pad_size; - mhd->pad_size = pad_size; - mhd->standby_mem_size = standby_mem_size; - } - - /* allocate storage keys */ - storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); - /* init CPUs */ - s390_init_cpus(machine->cpu_model, storage_keys); + s390_init_cpus(machine->cpu_model); if (kvm_enabled()) { kvm_s390_enable_css_support(s390_cpu_addr2state(0)); @@ -282,14 +237,24 @@ static const TypeInfo ccw_machine_info = { }, }; +#define CCW_COMPAT_2_4 \ + {\ + .driver = TYPE_S390_SKEYS,\ + .property = "migration-enabled",\ + .value = "off",\ + }, + static void ccw_machine_2_4_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + static GlobalProperty compat_props[] = { + CCW_COMPAT_2_4 + { /* end of list */ } + }; mc->name = "s390-ccw-virtio-2.4"; - mc->alias = "s390-ccw-virtio"; mc->desc = "VirtIO-ccw based S390 machine v2.4"; - mc->is_default = 1; + mc->compat_props = compat_props; } static const TypeInfo ccw_machine_2_4_info = { @@ -298,10 +263,27 @@ static const TypeInfo ccw_machine_2_4_info = { .class_init = ccw_machine_2_4_class_init, }; +static void ccw_machine_2_5_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = "s390-ccw-virtio-2.5"; + mc->alias = "s390-ccw-virtio"; + mc->desc = "VirtIO-ccw based S390 machine v2.5"; + mc->is_default = 1; +} + +static const TypeInfo ccw_machine_2_5_info = { + .name = TYPE_S390_CCW_MACHINE "2.5", + .parent = TYPE_S390_CCW_MACHINE, + .class_init = ccw_machine_2_5_class_init, +}; + static void ccw_machine_register_types(void) { type_register_static(&ccw_machine_info); type_register_static(&ccw_machine_2_4_info); + type_register_static(&ccw_machine_2_5_info); } type_init(ccw_machine_register_types) diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c index 1284e77b22..e4000c92e1 100644 --- a/hw/s390x/s390-virtio.c +++ b/hw/s390x/s390-virtio.c @@ -23,6 +23,7 @@ #include "hw/hw.h" #include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" @@ -38,6 +39,7 @@ #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/s390-virtio.h" +#include "hw/s390x/storage-keys.h" #include "cpu.h" //#define DEBUG_S390 @@ -164,7 +166,7 @@ void s390_init_ipl_dev(const char *kernel_filename, qdev_init_nofail(dev); } -void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys) +void s390_init_cpus(const char *cpu_model) { int i; @@ -184,7 +186,6 @@ void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys) ipi_states[i] = cpu; cs->halted = 1; cs->exception_index = EXCP_HLT; - cpu->env.storage_keys = storage_keys; } } @@ -260,31 +261,20 @@ int gtod_load(QEMUFile *f, void *opaque, int version_id) /* PC hardware initialisation */ static void s390_init(MachineState *machine) { - ram_addr_t my_ram_size = machine->ram_size; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - int increment_size = 20; - uint8_t *storage_keys; + ram_addr_t my_ram_size; void *virtio_region; hwaddr virtio_region_len; hwaddr virtio_region_start; - /* - * The storage increment size is a multiple of 1M and is a power of 2. - * The number of storage increments must be MAX_STORAGE_INCREMENTS or - * fewer. - */ - while ((my_ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) { - increment_size++; + if (machine->ram_slots) { + error_report("Memory hotplug not supported by the selected machine."); + exit(EXIT_FAILURE); } - my_ram_size = my_ram_size >> increment_size << increment_size; - - /* let's propagate the changed ram size into the global variable. */ - ram_size = my_ram_size; + s390_sclp_init(); + my_ram_size = machine->ram_size; /* get a BUS */ s390_bus = s390_virtio_bus_init(&my_ram_size); - s390_sclp_init(); s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, machine->initrd_filename, ZIPL_FILENAME, false); s390_flic_init(); @@ -293,9 +283,7 @@ static void s390_init(MachineState *machine) s390_virtio_register_hcalls(); /* allocate RAM */ - memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort); - vmstate_register_ram_global(ram); - memory_region_add_subregion(sysmem, 0, ram); + s390_memory_init(my_ram_size); /* clear virtio region */ virtio_region_len = my_ram_size - ram_size; @@ -306,11 +294,8 @@ static void s390_init(MachineState *machine) cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1, virtio_region_len); - /* allocate storage keys */ - storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); - /* init CPUs */ - s390_init_cpus(machine->cpu_model, storage_keys); + s390_init_cpus(machine->cpu_model); /* Create VirtIO network adapters */ s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390"); diff --git a/hw/s390x/s390-virtio.h b/hw/s390x/s390-virtio.h index c847853957..f389aa1a67 100644 --- a/hw/s390x/s390-virtio.h +++ b/hw/s390x/s390-virtio.h @@ -19,7 +19,7 @@ typedef int (*s390_virtio_fn)(const uint64_t *args); void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn); -void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys); +void s390_init_cpus(const char *cpu_model); void s390_init_ipl_dev(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, @@ -27,4 +27,5 @@ void s390_init_ipl_dev(const char *kernel_filename, bool enforce_bios); void s390_create_virtio_net(BusState *bus, const char *name); void s390_nmi(NMIState *n, int cpu_index, Error **errp); +void s390_memory_init(ram_addr_t mem_size); #endif diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index b3a6c5e5a4..fd277e1bf0 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -17,37 +17,27 @@ #include "exec/memory.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" -#include "qemu/config-file.h" +#include "hw/boards.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" #include "hw/s390x/s390-pci-bus.h" -static inline SCLPEventFacility *get_event_facility(void) +static inline SCLPDevice *get_sclp_device(void) { - ObjectProperty *op = object_property_find(qdev_get_machine(), - TYPE_SCLP_EVENT_FACILITY, - NULL); - assert(op); - return op->opaque; + return SCLP(object_resolve_path_type("", TYPE_SCLP, NULL)); } /* Provide information about the configuration, CPUs and storage */ -static void read_SCP_info(SCCB *sccb) +static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) { ReadInfo *read_info = (ReadInfo *) sccb; + MachineState *machine = MACHINE(qdev_get_machine()); sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); CPUState *cpu; int cpu_count = 0; int i = 0; - int increment_size = 20; int rnsize, rnmax; - QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL); - int slots = qemu_opt_get_number(opts, "slots", 0); - int max_avail_slots = s390_get_memslot_count(kvm_state); - - if (slots > max_avail_slots) { - slots = max_avail_slots; - } + int slots = MIN(machine->ram_slots, s390_get_memslot_count(kvm_state)); CPU_FOREACH(cpu) { cpu_count++; @@ -66,23 +56,8 @@ static void read_SCP_info(SCCB *sccb) read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO | SCLP_HAS_PCI_RECONFIG); - /* - * The storage increment size is a multiple of 1M and is a power of 2. - * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. - */ - while ((ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) { - increment_size++; - } - rnmax = ram_size >> increment_size; - /* Memory Hotplug is only supported for the ccw machine type */ if (mhd) { - while ((mhd->standby_mem_size >> increment_size) > - MAX_STORAGE_INCREMENTS) { - increment_size++; - } - assert(increment_size == mhd->increment_size); - mhd->standby_subregion_size = MEM_SECTION_SIZE; /* Deduct the memory slot already used for core */ if (slots > 0) { @@ -108,13 +83,11 @@ static void read_SCP_info(SCCB *sccb) } mhd->padded_ram_size = ram_size + mhd->pad_size; mhd->rzm = 1 << mhd->increment_size; - rnmax = ((ram_size + mhd->standby_mem_size + mhd->pad_size) - >> mhd->increment_size); read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR); } - rnsize = 1 << (increment_size - 20); + rnsize = 1 << (sclp->increment_size - 20); if (rnsize <= 128) { read_info->rnsize = rnsize; } else { @@ -122,6 +95,7 @@ static void read_SCP_info(SCCB *sccb) read_info->rnsize2 = cpu_to_be32(rnsize); } + rnmax = machine->maxram_size >> sclp->increment_size; if (rnmax < 0x10000) { read_info->rnmax = cpu_to_be16(rnmax); } else { @@ -132,14 +106,17 @@ static void read_SCP_info(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } -static void read_storage_element0_info(SCCB *sccb) +static void read_storage_element0_info(SCLPDevice *sclp, SCCB *sccb) { int i, assigned; int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID; ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } if ((ram_size >> mhd->increment_size) >= 0x10000) { sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); @@ -158,12 +135,15 @@ static void read_storage_element0_info(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } -static void read_storage_element1_info(SCCB *sccb) +static void read_storage_element1_info(SCLPDevice *sclp, SCCB *sccb) { ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) { sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); @@ -179,13 +159,17 @@ static void read_storage_element1_info(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION); } -static void attach_storage_element(SCCB *sccb, uint16_t element) +static void attach_storage_element(SCLPDevice *sclp, SCCB *sccb, + uint16_t element) { int i, assigned, subincrement_id; AttachStorageElement *attach_info = (AttachStorageElement *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } if (element != 1) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); @@ -203,20 +187,26 @@ static void attach_storage_element(SCCB *sccb, uint16_t element) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); } -static void assign_storage(SCCB *sccb) +static void assign_storage(SCLPDevice *sclp, SCCB *sccb) { MemoryRegion *mr = NULL; uint64_t this_subregion_size; AssignStorage *assign_info = (AssignStorage *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); - ram_addr_t assign_addr = (assign_info->rn - 1) * mhd->rzm; + ram_addr_t assign_addr; MemoryRegion *sysmem = get_system_memory(); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } + assign_addr = (assign_info->rn - 1) * mhd->rzm; + if ((assign_addr % MEM_SECTION_SIZE == 0) && (assign_addr >= mhd->padded_ram_size)) { /* Re-use existing memory region if found */ mr = memory_region_find(sysmem, assign_addr, 1).mr; + memory_region_unref(mr); if (!mr) { MemoryRegion *standby_ram = g_new(MemoryRegion, 1); @@ -242,6 +232,11 @@ static void assign_storage(SCCB *sccb) } memory_region_init_ram(standby_ram, NULL, id, this_subregion_size, &error_abort); + /* This is a hack to make memory hotunplug work again. Once we have + * subdevices, we have to unparent them when unassigning memory, + * instead of doing it via the ref count of the MemoryRegion. */ + object_ref(OBJECT(standby_ram)); + object_unparent(OBJECT(standby_ram)); vmstate_register_ram_global(standby_ram); memory_region_add_subregion(sysmem, offset, standby_ram); } @@ -252,15 +247,20 @@ static void assign_storage(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); } -static void unassign_storage(SCCB *sccb) +static void unassign_storage(SCLPDevice *sclp, SCCB *sccb) { MemoryRegion *mr = NULL; AssignStorage *assign_info = (AssignStorage *) sccb; sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - assert(mhd); - ram_addr_t unassign_addr = (assign_info->rn - 1) * mhd->rzm; + ram_addr_t unassign_addr; MemoryRegion *sysmem = get_system_memory(); + if (!mhd) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } + unassign_addr = (assign_info->rn - 1) * mhd->rzm; + /* if the addr is a multiple of 256 MB */ if ((unassign_addr % MEM_SECTION_SIZE == 0) && (unassign_addr >= mhd->padded_ram_size)) { @@ -269,6 +269,7 @@ static void unassign_storage(SCCB *sccb) /* find the specified memory region and destroy it */ mr = memory_region_find(sysmem, unassign_addr, 1).mr; + memory_region_unref(mr); if (mr) { int i; int is_removable = 1; @@ -287,8 +288,7 @@ static void unassign_storage(SCCB *sccb) } if (is_removable) { memory_region_del_subregion(sysmem, mr); - object_unparent(OBJECT(mr)); - g_free(mr); + object_unref(OBJECT(mr)); } } } @@ -296,7 +296,7 @@ static void unassign_storage(SCCB *sccb) } /* Provide information about the CPU */ -static void sclp_read_cpu_info(SCCB *sccb) +static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb) { ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb; CPUState *cpu; @@ -323,34 +323,35 @@ static void sclp_read_cpu_info(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } -static void sclp_execute(SCCB *sccb, uint32_t code) +static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) { - SCLPEventFacility *ef = get_event_facility(); + SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); + SCLPEventFacility *ef = sclp->event_facility; SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef); switch (code & SCLP_CMD_CODE_MASK) { case SCLP_CMDW_READ_SCP_INFO: case SCLP_CMDW_READ_SCP_INFO_FORCED: - read_SCP_info(sccb); + sclp_c->read_SCP_info(sclp, sccb); break; case SCLP_CMDW_READ_CPU_INFO: - sclp_read_cpu_info(sccb); + sclp_c->read_cpu_info(sclp, sccb); break; case SCLP_READ_STORAGE_ELEMENT_INFO: if (code & 0xff00) { - read_storage_element1_info(sccb); + sclp_c->read_storage_element1_info(sclp, sccb); } else { - read_storage_element0_info(sccb); + sclp_c->read_storage_element0_info(sclp, sccb); } break; case SCLP_ATTACH_STORAGE_ELEMENT: - attach_storage_element(sccb, (code & 0xff00) >> 8); + sclp_c->attach_storage_element(sclp, sccb, (code & 0xff00) >> 8); break; case SCLP_ASSIGN_STORAGE: - assign_storage(sccb); + sclp_c->assign_storage(sclp, sccb); break; case SCLP_UNASSIGN_STORAGE: - unassign_storage(sccb); + sclp_c->unassign_storage(sclp, sccb); break; case SCLP_CMDW_CONFIGURE_PCI: s390_pci_sclp_configure(1, sccb); @@ -366,6 +367,8 @@ static void sclp_execute(SCCB *sccb, uint32_t code) int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) { + SCLPDevice *sclp = get_sclp_device(); + SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); int r = 0; SCCB work_sccb; @@ -400,20 +403,20 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) goto out; } - sclp_execute((SCCB *)&work_sccb, code); + sclp_c->execute(sclp, (SCCB *)&work_sccb, code); cpu_physical_memory_write(sccb, &work_sccb, be16_to_cpu(work_sccb.h.length)); - sclp_service_interrupt(sccb); + sclp_c->service_interrupt(sclp, sccb); out: return r; } -void sclp_service_interrupt(uint32_t sccb) +static void service_interrupt(SCLPDevice *sclp, uint32_t sccb) { - SCLPEventFacility *ef = get_event_facility(); + SCLPEventFacility *ef = sclp->event_facility; SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef); uint32_t param = sccb & ~3; @@ -428,17 +431,148 @@ void sclp_service_interrupt(uint32_t sccb) s390_sclp_extint(param); } +void sclp_service_interrupt(uint32_t sccb) +{ + SCLPDevice *sclp = get_sclp_device(); + SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); + + sclp_c->service_interrupt(sclp, sccb); +} + /* qemu object creation and initialization functions */ void s390_sclp_init(void) { - DeviceState *dev = qdev_create(NULL, TYPE_SCLP_EVENT_FACILITY); + Object *new = object_new(TYPE_SCLP); - object_property_add_child(qdev_get_machine(), TYPE_SCLP_EVENT_FACILITY, - OBJECT(dev), NULL); - qdev_init_nofail(dev); + object_property_add_child(qdev_get_machine(), TYPE_SCLP, new, + NULL); + object_unref(OBJECT(new)); + qdev_init_nofail(DEVICE(new)); +} + +static void sclp_realize(DeviceState *dev, Error **errp) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + SCLPDevice *sclp = SCLP(dev); + Error *l_err = NULL; + uint64_t hw_limit; + int ret; + + object_property_set_bool(OBJECT(sclp->event_facility), true, "realized", + &l_err); + if (l_err) { + goto error; + } + + ret = s390_set_memory_limit(machine->maxram_size, &hw_limit); + if (ret == -E2BIG) { + error_setg(&l_err, "qemu: host supports a maximum of %" PRIu64 " GB", + hw_limit >> 30); + goto error; + } else if (ret) { + error_setg(&l_err, "qemu: setting the guest size failed"); + goto error; + } + return; +error: + assert(l_err); + error_propagate(errp, l_err); +} + +static void sclp_memory_init(SCLPDevice *sclp) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + ram_addr_t initial_mem = machine->ram_size; + ram_addr_t max_mem = machine->maxram_size; + ram_addr_t standby_mem = max_mem - initial_mem; + ram_addr_t pad_mem = 0; + int increment_size = 20; + + /* The storage increment size is a multiple of 1M and is a power of 2. + * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. + * The variable 'increment_size' is an exponent of 2 that can be + * used to calculate the size (in bytes) of an increment. */ + while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { + increment_size++; + } + if (machine->ram_slots) { + while ((standby_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { + increment_size++; + } + } + sclp->increment_size = increment_size; + + /* The core and standby memory areas need to be aligned with + * the increment size. In effect, this can cause the + * user-specified memory size to be rounded down to align + * with the nearest increment boundary. */ + initial_mem = initial_mem >> increment_size << increment_size; + standby_mem = standby_mem >> increment_size << increment_size; + + /* If the size of ram is not on a MEM_SECTION_SIZE boundary, + calculate the pad size necessary to force this boundary. */ + if (machine->ram_slots && standby_mem) { + sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev(); + + if (initial_mem % MEM_SECTION_SIZE) { + pad_mem = MEM_SECTION_SIZE - initial_mem % MEM_SECTION_SIZE; + } + mhd->increment_size = increment_size; + mhd->pad_size = pad_mem; + mhd->standby_mem_size = standby_mem; + } + machine->ram_size = initial_mem; + machine->maxram_size = initial_mem + pad_mem + standby_mem; + /* let's propagate the changed ram size into the global variable. */ + ram_size = initial_mem; +} + +static void sclp_init(Object *obj) +{ + SCLPDevice *sclp = SCLP(obj); + Object *new; + + new = object_new(TYPE_SCLP_EVENT_FACILITY); + object_property_add_child(obj, TYPE_SCLP_EVENT_FACILITY, new, NULL); + /* qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS */ + qdev_set_parent_bus(DEVICE(new), sysbus_get_default()); + object_unref(new); + sclp->event_facility = EVENT_FACILITY(new); + + sclp_memory_init(sclp); } +static void sclp_class_init(ObjectClass *oc, void *data) +{ + SCLPDeviceClass *sc = SCLP_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->desc = "SCLP (Service-Call Logical Processor)"; + dc->realize = sclp_realize; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + sc->read_SCP_info = read_SCP_info; + sc->read_storage_element0_info = read_storage_element0_info; + sc->read_storage_element1_info = read_storage_element1_info; + sc->attach_storage_element = attach_storage_element; + sc->assign_storage = assign_storage; + sc->unassign_storage = unassign_storage; + sc->read_cpu_info = sclp_read_cpu_info; + sc->execute = sclp_execute; + sc->service_interrupt = service_interrupt; +} + +static TypeInfo sclp_info = { + .name = TYPE_SCLP, + .parent = TYPE_DEVICE, + .instance_init = sclp_init, + .instance_size = sizeof(SCLPDevice), + .class_init = sclp_class_init, + .class_size = sizeof(SCLPDeviceClass), +}; + sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void) { DeviceState *dev; @@ -475,5 +609,6 @@ static TypeInfo sclp_memory_hotplug_dev_info = { static void register_types(void) { type_register_static(&sclp_memory_hotplug_dev_info); + type_register_static(&sclp_info); } type_init(register_types); diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c index 2fe8b5aa40..322eb31d95 100644 --- a/hw/s390x/sclpcpu.c +++ b/hw/s390x/sclpcpu.c @@ -25,13 +25,16 @@ typedef struct ConfigMgtData { uint8_t event_qualifier; } QEMU_PACKED ConfigMgtData; -static qemu_irq *irq_cpu_hotplug; /* Only used in this file */ - #define EVENT_QUAL_CPU_CHANGE 1 void raise_irq_cpu_hotplug(void) { - qemu_irq_raise(*irq_cpu_hotplug); + Object *obj = object_resolve_path_type("", TYPE_SCLP_CPU_HOTPLUG, NULL); + + SCLP_EVENT(obj)->event_pending = true; + + /* Trigger SCLP read operation */ + sclp_service_interrupt(0); } static unsigned int send_mask(void) @@ -70,36 +73,19 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, return 1; } -static void trigger_signal(void *opaque, int n, int level) -{ - SCLPEvent *event = opaque; - event->event_pending = true; - - /* Trigger SCLP read operation */ - sclp_service_interrupt(0); -} - -static int irq_cpu_hotplug_init(SCLPEvent *event) -{ - irq_cpu_hotplug = qemu_allocate_irqs(trigger_signal, event, 1); - return 0; -} - static void cpu_class_init(ObjectClass *oc, void *data) { SCLPEventClass *k = SCLP_EVENT_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - k->init = irq_cpu_hotplug_init; k->get_send_mask = send_mask; k->get_receive_mask = receive_mask; k->read_event_data = read_event_data; - k->write_event_data = NULL; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo sclp_cpu_info = { - .name = "sclp-cpu-hotplug", + .name = TYPE_SCLP_CPU_HOTPLUG, .parent = TYPE_SCLP_EVENT, .instance_size = sizeof(SCLPEvent), .class_init = cpu_class_init, diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c index ffa5553135..15b06e108b 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -66,7 +66,7 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, } static const VMStateDescription vmstate_sclpquiesce = { - .name = "sclpquiesce", + .name = TYPE_SCLP_QUIESCE, .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { @@ -127,7 +127,7 @@ static void quiesce_class_init(ObjectClass *klass, void *data) } static const TypeInfo sclp_quiesce_info = { - .name = "sclpquiesce", + .name = TYPE_SCLP_QUIESCE, .parent = TYPE_SCLP_EVENT, .instance_size = sizeof(SCLPEvent), .class_init = quiesce_class_init, diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index a8bb1c66f9..1c33f146a7 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -145,7 +145,7 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req, * * TODO: always disable this workaround for virtio 1.0 devices. */ - if (!virtio_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) { if (req->elem.out_num) { req_size = req->elem.out_sg[0].iov_len; } @@ -759,7 +759,7 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIODevice *vdev = VIRTIO_DEVICE(s); - if (virtio_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) && + if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) && dev->type != TYPE_ROM) { virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE, sense.asc | (sense.ascq << 8)); @@ -783,7 +783,7 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, aio_context_release(s->ctx); } - if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_push_event(s, sd, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN); @@ -797,7 +797,7 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, VirtIOSCSI *s = VIRTIO_SCSI(vdev); SCSIDevice *sd = SCSI_DEVICE(dev); - if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_push_event(s, sd, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_REMOVED); diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index d072deca11..35d8033402 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -578,8 +578,7 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base, BlockBackend *blk, qemu_irq irq, qemu_irq dma[], omap_clk clk) { - struct omap_mmc_s *s = (struct omap_mmc_s *) - g_malloc0(sizeof(struct omap_mmc_s)); + struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1); s->irq = irq; s->dma = dma; @@ -605,8 +604,7 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta, BlockBackend *blk, qemu_irq irq, qemu_irq dma[], omap_clk fclk, omap_clk iclk) { - struct omap_mmc_s *s = (struct omap_mmc_s *) - g_malloc0(sizeof(struct omap_mmc_s)); + struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1); s->irq = irq; s->dma = dma; diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 11fcd479df..5242176a33 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -46,7 +46,7 @@ typedef struct PL181State { int32_t fifo_pos; int32_t fifo_len; /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives - while it is reading the FIFO. We hack around this be defering + while it is reading the FIFO. We hack around this by deferring subsequent transfers until after the driver polls the status word. http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1 */ diff --git a/hw/sd/sd.c b/hw/sd/sd.c index a1ff465a67..3e2a451154 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -412,8 +412,7 @@ static void sd_reset(SDState *sd) sd_set_cardstatus(sd); sd_set_sdstatus(sd); - if (sd->wp_groups) - g_free(sd->wp_groups); + g_free(sd->wp_groups); sd->wp_switch = sd->blk ? blk_is_read_only(sd->blk) : false; sd->wpgrps_size = sect; sd->wp_groups = bitmap_new(sd->wpgrps_size); diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index e63367ba56..65304cff5e 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1169,10 +1169,8 @@ static void sdhci_uninitfn(SDHCIState *s) qemu_free_irq(s->eject_cb); qemu_free_irq(s->ro_cb); - if (s->fifo_buffer) { - g_free(s->fifo_buffer); - s->fifo_buffer = NULL; - } + g_free(s->fifo_buffer); + s->fifo_buffer = NULL; } const VMStateDescription sdhci_vmstate = { diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 5e22ed79b2..3b0b2ecfa4 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -338,9 +338,9 @@ static void r2d_init(MachineState *machine) } /* initialization which should be done by firmware */ - boot_params.loader_type = 1; - boot_params.initrd_start = INITRD_LOAD_OFFSET; - boot_params.initrd_size = initrd_size; + boot_params.loader_type = tswap32(1); + boot_params.initrd_start = tswap32(INITRD_LOAD_OFFSET); + boot_params.initrd_size = tswap32(initrd_size); } if (kernel_cmdline) { diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index efdbb5de6f..b81a1d349d 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -55,7 +55,9 @@ static uint8_t *smbios_tables; static size_t smbios_tables_len; static unsigned smbios_table_max; static unsigned smbios_table_cnt; -static struct smbios_entry_point ep; +static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_21; + +static SmbiosEntryPoint ep; static int smbios_type4_count = 0; static bool smbios_immutable; @@ -771,11 +773,12 @@ void smbios_set_cpuid(uint32_t version, uint32_t features) void smbios_set_defaults(const char *manufacturer, const char *product, const char *version, bool legacy_mode, - bool uuid_encoded) + bool uuid_encoded, SmbiosEntryPointType ep_type) { smbios_have_defaults = true; smbios_legacy = legacy_mode; smbios_uuid_encoded = uuid_encoded; + smbios_ep_type = ep_type; /* drop unwanted version of command-line file blob(s) */ if (smbios_legacy) { @@ -808,26 +811,53 @@ void smbios_set_defaults(const char *manufacturer, const char *product, static void smbios_entry_point_setup(void) { - memcpy(ep.anchor_string, "_SM_", 4); - memcpy(ep.intermediate_anchor_string, "_DMI_", 5); - ep.length = sizeof(struct smbios_entry_point); - ep.entry_point_revision = 0; /* formatted_area reserved, per spec v2.1+ */ - memset(ep.formatted_area, 0, 5); - - /* compliant with smbios spec v2.8 */ - ep.smbios_major_version = 2; - ep.smbios_minor_version = 8; - ep.smbios_bcd_revision = 0x28; - - /* set during table construction, but BIOS may override: */ - ep.structure_table_length = cpu_to_le16(smbios_tables_len); - ep.max_structure_size = cpu_to_le16(smbios_table_max); - ep.number_of_structures = cpu_to_le16(smbios_table_cnt); - - /* BIOS must recalculate: */ - ep.checksum = 0; - ep.intermediate_checksum = 0; - ep.structure_table_address = cpu_to_le32(0); + switch (smbios_ep_type) { + case SMBIOS_ENTRY_POINT_21: + memcpy(ep.ep21.anchor_string, "_SM_", 4); + memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5); + ep.ep21.length = sizeof(struct smbios_21_entry_point); + ep.ep21.entry_point_revision = 0; /* formatted_area reserved */ + memset(ep.ep21.formatted_area, 0, 5); + + /* compliant with smbios spec v2.8 */ + ep.ep21.smbios_major_version = 2; + ep.ep21.smbios_minor_version = 8; + ep.ep21.smbios_bcd_revision = 0x28; + + /* set during table construction, but BIOS may override: */ + ep.ep21.structure_table_length = cpu_to_le16(smbios_tables_len); + ep.ep21.max_structure_size = cpu_to_le16(smbios_table_max); + ep.ep21.number_of_structures = cpu_to_le16(smbios_table_cnt); + + /* BIOS must recalculate */ + ep.ep21.checksum = 0; + ep.ep21.intermediate_checksum = 0; + ep.ep21.structure_table_address = cpu_to_le32(0); + + break; + case SMBIOS_ENTRY_POINT_30: + memcpy(ep.ep30.anchor_string, "_SM3_", 5); + ep.ep30.length = sizeof(struct smbios_30_entry_point); + ep.ep30.entry_point_revision = 1; + ep.ep30.reserved = 0; + + /* compliant with smbios spec 3.0 */ + ep.ep30.smbios_major_version = 3; + ep.ep30.smbios_minor_version = 0; + ep.ep30.smbios_doc_rev = 0; + + /* set during table construct, but BIOS might override */ + ep.ep30.structure_table_max_size = cpu_to_le32(smbios_tables_len); + + /* BIOS must recalculate */ + ep.ep30.checksum = 0; + ep.ep30.structure_table_address = cpu_to_le64(0); + + break; + default: + abort(); + break; + } } void smbios_get_tables(const struct smbios_phys_mem_area *mem_array, @@ -885,7 +915,15 @@ void smbios_get_tables(const struct smbios_phys_mem_area *mem_array, *tables = smbios_tables; *tables_len = smbios_tables_len; *anchor = (uint8_t *)&ep; - *anchor_len = sizeof(struct smbios_entry_point); + + /* calculate length based on anchor string */ + if (!strncmp((char *)&ep, "_SM_", 4)) { + *anchor_len = sizeof(struct smbios_21_entry_point); + } else if (!strncmp((char *)&ep, "_SM3_", 5)) { + *anchor_len = sizeof(struct smbios_30_entry_point); + } else { + abort(); + } } static void save_opt(const char **dest, QemuOpts *opts, const char *name) diff --git a/hw/ssi/omap_spi.c b/hw/ssi/omap_spi.c index 119e325a64..27263299be 100644 --- a/hw/ssi/omap_spi.c +++ b/hw/ssi/omap_spi.c @@ -342,8 +342,7 @@ static const MemoryRegionOps omap_mcspi_ops = { struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) { - struct omap_mcspi_s *s = (struct omap_mcspi_s *) - g_malloc0(sizeof(struct omap_mcspi_s)); + struct omap_mcspi_s *s = g_new0(struct omap_mcspi_s, 1); struct omap_mcspi_ch_s *ch = s->ch; s->irq = irq; diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c index 10c5d2b91e..9649851526 100644 --- a/hw/timer/imx_epit.c +++ b/hw/timer/imx_epit.c @@ -12,7 +12,6 @@ * */ -#include "hw/arm/imx.h" #include "hw/timer/imx_epit.h" #include "hw/misc/imx_ccm.h" #include "qemu/main-loop.h" @@ -287,16 +286,6 @@ static void imx_epit_cmp(void *opaque) imx_epit_update_int(s); } -void imx_timerp_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm) -{ - IMXEPITState *pp; - DeviceState *dev; - - dev = sysbus_create_simple(TYPE_IMX_EPIT, addr, irq); - pp = IMX_EPIT(dev); - pp->ccm = ccm; -} - static const MemoryRegionOps imx_epit_ops = { .read = imx_epit_read, .write = imx_epit_write, diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 01f802e8f1..4bac67d333 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -12,7 +12,6 @@ * */ -#include "hw/arm/imx.h" #include "hw/timer/imx_gpt.h" #include "hw/misc/imx_ccm.h" #include "qemu/main-loop.h" @@ -449,16 +448,6 @@ static void imx_gpt_realize(DeviceState *dev, Error **errp) s->timer = ptimer_init(bh); } -void imx_timerg_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm) -{ - IMXGPTState *pp; - DeviceState *dev; - - dev = sysbus_create_simple(TYPE_IMX_GPT, addr, irq); - pp = IMX_GPT(dev); - pp->ccm = ccm; -} - static void imx_gpt_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/omap_gptimer.c b/hw/timer/omap_gptimer.c index b8c8c0137d..dcf706c46e 100644 --- a/hw/timer/omap_gptimer.c +++ b/hw/timer/omap_gptimer.c @@ -468,8 +468,7 @@ static const MemoryRegionOps omap_gp_timer_ops = { struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, qemu_irq irq, omap_clk fclk, omap_clk iclk) { - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) - g_malloc0(sizeof(struct omap_gp_timer_s)); + struct omap_gp_timer_s *s = g_new0(struct omap_gp_timer_s, 1); s->ta = ta; s->irq = irq; diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index 79a8f98af0..be160c19b3 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -22,8 +22,6 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/> */ -#include <dirent.h> - #include "qemu-common.h" #include "qapi/error.h" #include "qemu/error-report.h" diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 7afa5f9d67..16fb845d07 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -95,10 +95,8 @@ static void usb_ehci_pci_exit(PCIDevice *dev) usb_ehci_unrealize(s, DEVICE(dev), NULL); - if (s->irq) { - g_free(s->irq); - s->irq = NULL; - } + g_free(s->irq); + s->irq = NULL; } static void usb_ehci_pci_reset(DeviceState *dev) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index c673bed4c4..1c57e20e70 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1453,9 +1453,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) t->running_retry = 0; killed = 1; } - if (t->trbs) { - g_free(t->trbs); - } + g_free(t->trbs); t->trbs = NULL; t->trb_count = t->trb_alloced = 0; diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 11429f5e73..5e492fda0f 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1429,7 +1429,7 @@ static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps, * still present in the first place. Attemping to contine where we * left off is impossible. * - * What we are going to to to here is emulate a surprise removal of + * What we are going to do here is emulate a surprise removal of * the usb device passed through, then kick host scan so the device * will get re-attached (and re-initialized by the guest) in case it * is still present. diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 25df25fd0f..38086cd0f2 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -33,9 +33,7 @@ #include "qemu/iov.h" #include "sysemu/char.h" -#include <dirent.h> #include <sys/ioctl.h> -#include <signal.h> #include <usbredirparser.h> #include <usbredirfilter.h> diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 85ee9b005e..6d21311575 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -226,7 +226,7 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova, /* * Try the mapping, if it fails with EBUSY, unmap the region and try * again. This shouldn't be necessary, but we sometimes see it in - * the the VGA ROM space. + * the VGA ROM space. */ if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0 || (errno == EBUSY && vfio_dma_unmap(container, iova, size) == 0 && diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4023d8e823..73d34b9b2e 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -18,7 +18,6 @@ * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) */ -#include <dirent.h> #include <linux/vfio.h> #include <sys/ioctl.h> #include <sys/mman.h> @@ -3378,7 +3377,7 @@ static bool vfio_radeon_smc_is_running(VFIOPCIDevice *vdev) uint32_t clk, pc_c; /* - * Registers 200h and 204h are index and data registers for acessing + * Registers 200h and 204h are index and data registers for accessing * indirect configuration registers within the device. */ vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4); diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 60365d1279..de4ec52a07 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -234,7 +234,7 @@ static void vfio_intp_interrupt(VFIOINTp *intp) ret = event_notifier_test_and_clear(&intp->interrupt); if (!ret) { - error_report("Error when clearing fd=%d (ret = %d)\n", + error_report("Error when clearing fd=%d (ret = %d)", event_notifier_get_fd(&intp->interrupt), ret); } diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index 07fd69c69e..fece83a829 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -105,7 +105,7 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n) /* Disable guest->host notifies */ void vring_disable_notification(VirtIODevice *vdev, Vring *vring) { - if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_set_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); } } @@ -116,7 +116,7 @@ void vring_disable_notification(VirtIODevice *vdev, Vring *vring) */ bool vring_enable_notification(VirtIODevice *vdev, Vring *vring) { - if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(&vring->vr) = vring->vr.avail->idx; } else { vring_clear_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); @@ -135,12 +135,12 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring) * interrupts. */ smp_mb(); - if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && unlikely(!vring_more_avail(vdev, vring))) { return true; } - if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { return !(vring_get_avail_flags(vdev, vring) & VRING_AVAIL_F_NO_INTERRUPT); } @@ -402,7 +402,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, /* On success, increment avail index. */ vring->last_avail_idx++; - if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(&vring->vr) = virtio_tswap16(vdev, vring->last_avail_idx); } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 2712c6fc0a..a08c36bb45 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -742,7 +742,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, return -errno; } - if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) && + if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) && virtio_legacy_is_cross_endian(vdev)) { r = vhost_virtqueue_set_vring_endian_legacy(dev, virtio_is_big_endian(vdev), @@ -839,7 +839,7 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, /* In the cross-endian case, we need to reset the vring endianness to * native as legacy devices expect so by default. */ - if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) && + if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) && virtio_legacy_is_cross_endian(vdev)) { r = vhost_virtqueue_set_vring_endian_legacy(dev, !virtio_is_big_endian(vdev), diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 3577b7af91..c419b17143 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -70,7 +70,7 @@ static inline void reset_stats(VirtIOBalloon *dev) static bool balloon_stats_supported(const VirtIOBalloon *s) { VirtIODevice *vdev = VIRTIO_DEVICE(s); - return virtio_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ); + return virtio_vdev_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ); } static bool balloon_stats_enabled(const VirtIOBalloon *s) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index c024161f59..eda8205d58 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1505,9 +1505,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) if (legacy) { size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + virtio_bus_get_vdev_config_len(bus); - if (size & (size - 1)) { - size = 1 << qemu_fls(size); - } + size = pow2ceil(size); memory_region_init_io(&proxy->bar, OBJECT(proxy), &virtio_pci_config_ops, @@ -2009,10 +2007,6 @@ static const TypeInfo virtio_net_pci_info = { /* virtio-rng-pci */ -static Property virtio_rng_pci_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev); @@ -2039,7 +2033,6 @@ static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) k->realize = virtio_rng_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = virtio_rng_pci_properties; pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 788b556a74..0832db9935 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -220,7 +220,7 @@ static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val) void virtio_queue_set_notification(VirtQueue *vq, int enable) { vq->notification = enable; - if (virtio_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_set_avail_event(vq, vring_avail_idx(vq)); } else if (enable) { vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY); @@ -471,7 +471,7 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) max = vq->vring.num; i = head = virtqueue_get_head(vq, vq->last_avail_idx++); - if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_set_avail_event(vq, vq->last_avail_idx); } @@ -560,7 +560,7 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); trace_virtio_set_status(vdev, val); - if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) && val & VIRTIO_CONFIG_S_FEATURES_OK) { int ret = virtio_validate_features(vdev); @@ -898,7 +898,7 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); /* virtio-1 compliant devices cannot change the alignment */ - if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { error_report("tried to modify queue alignment for virtio-1 device"); return; } @@ -993,12 +993,12 @@ static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq) /* We need to expose used array entries before checking used event. */ smp_mb(); /* Always notify when queue is empty (when feature acknowledge) */ - if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx) { return true; } - if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT); } @@ -1035,7 +1035,7 @@ static bool virtio_device_endian_needed(void *opaque) VirtIODevice *vdev = opaque; assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); - if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { return vdev->device_endian != virtio_default_endian(); } /* Devices conforming to VIRTIO 1.0 or later are always LE. */ diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index cfa2b1be13..3e07d44878 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -136,7 +136,7 @@ static void i6300esb_restart_timer(I6300State *d, int stage) * multiply here can exceed 64-bits, before we divide by 33MHz, so * we use a higher-precision intermediate result. */ - timeout = muldiv64(get_ticks_per_sec(), timeout, 33000000); + timeout = muldiv64(timeout, get_ticks_per_sec(), 33000000); i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout); diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs index a0ca0aa3df..a9ad7e70f7 100644 --- a/hw/xen/Makefile.objs +++ b/hw/xen/Makefile.objs @@ -3,3 +3,4 @@ common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o +obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o xen_pt_graphics.o diff --git a/hw/xen/xen-host-pci-device.c b/hw/xen/xen-host-pci-device.c index 743b37b991..7d8a0237cf 100644 --- a/hw/xen/xen-host-pci-device.c +++ b/hw/xen/xen-host-pci-device.c @@ -40,7 +40,7 @@ static int xen_host_pci_sysfs_path(const XenHostPCIDevice *d, d->domain, d->bus, d->dev, d->func, name); if (rc >= size || rc < 0) { - /* The ouput is truncated or an other error is encountered */ + /* The output is truncated, or some other error was encountered */ return -ENODEV; } return 0; @@ -376,6 +376,11 @@ int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, goto error; } d->irq = v; + rc = xen_host_pci_get_hex_value(d, "class", &v); + if (rc) { + goto error; + } + d->class_code = v; d->is_virtfn = xen_host_pci_dev_is_virtfn(d); return 0; @@ -387,6 +392,11 @@ error: return rc; } +bool xen_host_pci_device_closed(XenHostPCIDevice *d) +{ + return d->config_fd == -1; +} + void xen_host_pci_device_put(XenHostPCIDevice *d) { if (d->config_fd >= 0) { diff --git a/hw/xen/xen-host-pci-device.h b/hw/xen/xen-host-pci-device.h index c2486f0c19..3d44e044ff 100644 --- a/hw/xen/xen-host-pci-device.h +++ b/hw/xen/xen-host-pci-device.h @@ -25,6 +25,7 @@ typedef struct XenHostPCIDevice { uint16_t vendor_id; uint16_t device_id; + uint32_t class_code; int irq; XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1]; @@ -38,6 +39,7 @@ typedef struct XenHostPCIDevice { int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, uint8_t bus, uint8_t dev, uint8_t func); void xen_host_pci_device_put(XenHostPCIDevice *pci_dev); +bool xen_host_pci_device_closed(XenHostPCIDevice *d); int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p); int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p); diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index ed5fcaec0d..2b54f52707 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -56,6 +56,7 @@ #include "hw/pci/pci.h" #include "hw/xen/xen.h" +#include "hw/i386/pc.h" #include "hw/xen/xen_backend.h" #include "xen_pt.h" #include "qemu/range.h" @@ -378,7 +379,7 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, } } - /* need to shift back before passing them to xen_host_pci_device */ + /* need to shift back before passing them to xen_host_pci_set_block. */ val >>= (addr & 3) << 3; memory_region_transaction_commit(); @@ -406,7 +407,7 @@ out: (uint8_t *)&val + index, len); if (rc < 0) { - XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc); + XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc); } } } @@ -502,6 +503,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd) d->rom.size, d->rom.base_addr); } + xen_pt_register_vga_regions(d); return 0; } @@ -683,13 +685,86 @@ static const MemoryListener xen_pt_io_listener = { .priority = 10, }; +static void +xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s, + XenHostPCIDevice *dev) +{ + uint16_t gpu_dev_id; + PCIDevice *d = &s->dev; + + gpu_dev_id = dev->device_id; + igd_passthrough_isa_bridge_create(d->bus, gpu_dev_id); +} + +/* destroy. */ +static void xen_pt_destroy(PCIDevice *d) { + + XenPCIPassthroughState *s = XEN_PT_DEVICE(d); + XenHostPCIDevice *host_dev = &s->real_device; + uint8_t machine_irq = s->machine_irq; + uint8_t intx; + int rc; + + if (machine_irq && !xen_host_pci_device_closed(&s->real_device)) { + intx = xen_pt_pci_intx(s); + rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, + PT_IRQ_TYPE_PCI, + pci_bus_num(d->bus), + PCI_SLOT(s->dev.devfn), + intx, + 0 /* isa_irq */); + if (rc < 0) { + XEN_PT_ERR(d, "unbinding of interrupt INT%c failed." + " (machine irq: %i, err: %d)" + " But bravely continuing on..\n", + 'a' + intx, machine_irq, errno); + } + } + + /* N.B. xen_pt_config_delete takes care of freeing them. */ + if (s->msi) { + xen_pt_msi_disable(s); + } + if (s->msix) { + xen_pt_msix_disable(s); + } + + if (machine_irq) { + xen_pt_mapped_machine_irq[machine_irq]--; + + if (xen_pt_mapped_machine_irq[machine_irq] == 0) { + rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); + + if (rc < 0) { + XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)" + " But bravely continuing on..\n", + machine_irq, errno); + } + } + s->machine_irq = 0; + } + + /* delete all emulated config registers */ + xen_pt_config_delete(s); + + xen_pt_unregister_vga_regions(host_dev); + + if (s->listener_set) { + memory_listener_unregister(&s->memory_listener); + memory_listener_unregister(&s->io_listener); + s->listener_set = false; + } + if (!xen_host_pci_device_closed(&s->real_device)) { + xen_host_pci_device_put(&s->real_device); + } +} /* init */ static int xen_pt_initfn(PCIDevice *d) { XenPCIPassthroughState *s = XEN_PT_DEVICE(d); int rc = 0; - uint8_t machine_irq = 0; + uint8_t machine_irq = 0, scratch; uint16_t cmd = 0; int pirq = XEN_PT_UNASSIGNED_PIRQ; @@ -715,27 +790,48 @@ static int xen_pt_initfn(PCIDevice *d) } /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ - if (xen_host_pci_get_block(&s->real_device, 0, d->config, - PCI_CONFIG_SPACE_SIZE) == -1) { - xen_host_pci_device_put(&s->real_device); - return -1; - } + memset(d->config, 0, PCI_CONFIG_SPACE_SIZE); s->memory_listener = xen_pt_memory_listener; s->io_listener = xen_pt_io_listener; + /* Setup VGA bios for passthrough GFX */ + if ((s->real_device.domain == 0) && (s->real_device.bus == 0) && + (s->real_device.dev == 2) && (s->real_device.func == 0)) { + if (!is_igd_vga_passthrough(&s->real_device)) { + XEN_PT_ERR(d, "Need to enable igd-passthru if you're trying" + " to passthrough IGD GFX.\n"); + xen_host_pci_device_put(&s->real_device); + return -1; + } + + if (xen_pt_setup_vga(s, &s->real_device) < 0) { + XEN_PT_ERR(d, "Setup VGA BIOS of passthrough GFX failed!\n"); + xen_host_pci_device_put(&s->real_device); + return -1; + } + + /* Register ISA bridge for passthrough GFX. */ + xen_igd_passthrough_isa_bridge_create(s, &s->real_device); + } + /* Handle real device's MMIO/PIO BARs */ xen_pt_register_regions(s, &cmd); /* reinitialize each config register to be emulated */ - if (xen_pt_config_init(s)) { + rc = xen_pt_config_init(s); + if (rc) { XEN_PT_ERR(d, "PCI Config space initialisation failed.\n"); - xen_host_pci_device_put(&s->real_device); - return -1; + goto err_out; } /* Bind interrupt */ - if (!s->dev.config[PCI_INTERRUPT_PIN]) { + rc = xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &scratch); + if (rc) { + XEN_PT_ERR(d, "Failed to read PCI_INTERRUPT_PIN! (rc:%d)\n", rc); + goto err_out; + } + if (!scratch) { XEN_PT_LOG(d, "no pin interrupt\n"); goto out; } @@ -785,69 +881,41 @@ static int xen_pt_initfn(PCIDevice *d) out: if (cmd) { - xen_host_pci_set_word(&s->real_device, PCI_COMMAND, - pci_get_word(d->config + PCI_COMMAND) | cmd); + uint16_t val; + + rc = xen_host_pci_get_word(&s->real_device, PCI_COMMAND, &val); + if (rc) { + XEN_PT_ERR(d, "Failed to read PCI_COMMAND! (rc: %d)\n", rc); + goto err_out; + } else { + val |= cmd; + rc = xen_host_pci_set_word(&s->real_device, PCI_COMMAND, val); + if (rc) { + XEN_PT_ERR(d, "Failed to write PCI_COMMAND val=0x%x!(rc: %d)\n", + val, rc); + goto err_out; + } + } } memory_listener_register(&s->memory_listener, &s->dev.bus_master_as); memory_listener_register(&s->io_listener, &address_space_io); + s->listener_set = true; XEN_PT_LOG(d, "Real physical device %02x:%02x.%d registered successfully!\n", s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function); return 0; + +err_out: + xen_pt_destroy(d); + assert(rc); + return rc; } static void xen_pt_unregister_device(PCIDevice *d) { - XenPCIPassthroughState *s = XEN_PT_DEVICE(d); - uint8_t machine_irq = s->machine_irq; - uint8_t intx = xen_pt_pci_intx(s); - int rc; - - if (machine_irq) { - rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, - PT_IRQ_TYPE_PCI, - pci_bus_num(d->bus), - PCI_SLOT(s->dev.devfn), - intx, - 0 /* isa_irq */); - if (rc < 0) { - XEN_PT_ERR(d, "unbinding of interrupt INT%c failed." - " (machine irq: %i, err: %d)" - " But bravely continuing on..\n", - 'a' + intx, machine_irq, errno); - } - } - - if (s->msi) { - xen_pt_msi_disable(s); - } - if (s->msix) { - xen_pt_msix_disable(s); - } - - if (machine_irq) { - xen_pt_mapped_machine_irq[machine_irq]--; - - if (xen_pt_mapped_machine_irq[machine_irq] == 0) { - rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); - - if (rc < 0) { - XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)" - " But bravely continuing on..\n", - machine_irq, errno); - } - } - } - - /* delete all emulated config registers */ - xen_pt_config_delete(s); - - memory_listener_unregister(&s->memory_listener); - memory_listener_unregister(&s->io_listener); - - xen_host_pci_device_put(&s->real_device); + xen_pt_destroy(d); } static Property xen_pci_passthrough_properties[] = { diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 393f36ccbf..3bc22eb1d1 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -40,6 +40,9 @@ typedef struct XenPCIPassthroughState XenPCIPassthroughState; #define XEN_PT_DEVICE(obj) \ OBJECT_CHECK(XenPCIPassthroughState, (obj), TYPE_XEN_PT_DEVICE) +uint32_t igd_read_opregion(XenPCIPassthroughState *s); +void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val); + /* function type for config reg */ typedef int (*xen_pt_conf_reg_init) (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset, @@ -66,8 +69,9 @@ typedef int (*xen_pt_conf_byte_read) #define XEN_PT_BAR_ALLF 0xFFFFFFFF #define XEN_PT_BAR_UNMAPPED (-1) -#define PCI_CAP_MAX 48 +#define XEN_PCI_CAP_MAX 48 +#define XEN_PCI_INTEL_OPREGION 0xfc typedef enum { XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */ @@ -134,7 +138,11 @@ struct XenPTRegInfo { struct XenPTReg { QLIST_ENTRY(XenPTReg) entries; XenPTRegInfo *reg; - uint32_t data; /* emulated value */ + union { + uint8_t *byte; + uint16_t *half_word; + uint32_t *word; + } ptr; /* pointer to dev.config. */ }; typedef const struct XenPTRegGroupInfo XenPTRegGroupInfo; @@ -217,6 +225,7 @@ struct XenPCIPassthroughState { MemoryListener memory_listener; MemoryListener io_listener; + bool listener_set; }; int xen_pt_config_init(XenPCIPassthroughState *s); @@ -282,6 +291,7 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s) " value=%i, acceptable range is 1 - 4\n", r_val); r_val = 0; } else { + /* Note that if s.real_device.config_fd is closed we make 0xff. */ r_val -= 1; } @@ -289,7 +299,6 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s) } /* MSI/MSI-X */ -int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool en); int xen_pt_msi_setup(XenPCIPassthroughState *s); int xen_pt_msi_update(XenPCIPassthroughState *d); void xen_pt_msi_disable(XenPCIPassthroughState *s); @@ -305,5 +314,18 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) return s->msix && s->msix->bar_index == bar; } - +extern void *pci_assign_dev_load_option_rom(PCIDevice *dev, + struct Object *owner, int *size, + unsigned int domain, + unsigned int bus, unsigned int slot, + unsigned int function); +extern bool has_igd_gfx_passthru; +static inline bool is_igd_vga_passthrough(XenHostPCIDevice *dev) +{ + return (has_igd_gfx_passthru + && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA)); +} +int xen_pt_register_vga_regions(XenHostPCIDevice *dev); +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev); +int xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev); #endif /* !XEN_PT_H */ diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index dd37be38a4..4a5bc11f30 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -128,10 +128,11 @@ static int xen_pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint8_t valid_emu_mask = 0; + uint8_t *data = cfg_entry->ptr.byte; /* emulate byte register */ valid_emu_mask = reg->emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask); return 0; } @@ -140,10 +141,11 @@ static int xen_pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint16_t valid_emu_mask = 0; + uint16_t *data = cfg_entry->ptr.half_word; /* emulate word register */ valid_emu_mask = reg->emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask); return 0; } @@ -152,10 +154,11 @@ static int xen_pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; uint32_t valid_emu_mask = 0; + uint32_t *data = cfg_entry->ptr.word; /* emulate long register */ valid_emu_mask = reg->emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask); return 0; } @@ -169,10 +172,11 @@ static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, XenPTRegInfo *reg = cfg_entry->reg; uint8_t writable_mask = 0; uint8_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint8_t *data = cfg_entry->ptr.byte; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); @@ -186,10 +190,11 @@ static int xen_pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint16_t *data = cfg_entry->ptr.half_word; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); @@ -203,10 +208,11 @@ static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint32_t *data = cfg_entry->ptr.word; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); @@ -255,7 +261,7 @@ static int xen_pt_status_reg_init(XenPCIPassthroughState *s, reg_entry = xen_pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST); if (reg_entry) { /* check Capabilities Pointer register */ - if (reg_entry->data) { + if (*reg_entry->ptr.half_word) { reg_field |= PCI_STATUS_CAP_LIST; } else { reg_field &= ~PCI_STATUS_CAP_LIST; @@ -301,10 +307,11 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint16_t *data = cfg_entry->ptr.half_word; /* modify emulate register */ writable_mask = ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ if (*val & PCI_COMMAND_INTX_DISABLE) { @@ -447,7 +454,7 @@ static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, /* emulate BAR */ valid_emu_mask = bar_emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + *value = XEN_PT_MERGE_VALUE(*value, *cfg_entry->ptr.word, ~valid_emu_mask); return 0; } @@ -464,6 +471,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint32_t bar_ro_mask = 0; uint32_t r_size = 0; int index = 0; + uint32_t *data = cfg_entry->ptr.word; index = xen_pt_bar_offset_to_index(reg->offset); if (index < 0 || index >= PCI_NUM_REGIONS) { @@ -500,7 +508,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, /* modify emulate register */ writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* check whether we need to update the virtual region address or not */ switch (s->bases[index].bar_flag) { @@ -533,6 +541,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask); pcibus_t r_size = 0; uint32_t bar_ro_mask = 0; + uint32_t *data = cfg_entry->ptr.word; r_size = d->io_regions[PCI_ROM_SLOT].size; base = &s->bases[PCI_ROM_SLOT]; @@ -544,7 +553,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, /* modify emulate register */ writable_mask = ~bar_ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); @@ -552,6 +561,22 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, return 0; } +static int xen_pt_intel_opregion_read(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, + uint32_t *value, uint32_t valid_mask) +{ + *value = igd_read_opregion(s); + return 0; +} + +static int xen_pt_intel_opregion_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint32_t *value, + uint32_t dev_value, uint32_t valid_mask) +{ + igd_write_opregion(s, *value); + return 0; +} + /* Header Type0 reg static information table */ static XenPTRegInfo xen_pt_emu_reg_header0[] = { /* Vendor ID reg */ @@ -800,15 +825,21 @@ static XenPTRegInfo xen_pt_emu_reg_vendor[] = { static inline uint8_t get_capability_version(XenPCIPassthroughState *s, uint32_t offset) { - uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS); - return flags & PCI_EXP_FLAGS_VERS; + uint8_t flag; + if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) { + return 0; + } + return flag & PCI_EXP_FLAGS_VERS; } static inline uint8_t get_device_type(XenPCIPassthroughState *s, uint32_t offset) { - uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS); - return (flags & PCI_EXP_FLAGS_TYPE) >> 4; + uint8_t flag; + if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) { + return 0; + } + return (flag & PCI_EXP_FLAGS_TYPE) >> 4; } /* initialize Link Control register */ @@ -857,8 +888,14 @@ static int xen_pt_linkctrl2_reg_init(XenPCIPassthroughState *s, reg_field = XEN_PT_INVALID_REG; } else { /* set Supported Link Speed */ - uint8_t lnkcap = pci_get_byte(s->dev.config + real_offset - reg->offset - + PCI_EXP_LNKCAP); + uint8_t lnkcap; + int rc; + rc = xen_host_pci_get_byte(&s->real_device, + real_offset - reg->offset + PCI_EXP_LNKCAP, + &lnkcap); + if (rc) { + return rc; + } reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap; } @@ -971,10 +1008,11 @@ static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s, XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint16_t *data = cfg_entry->ptr.half_word; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS, @@ -1039,13 +1077,15 @@ static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { - PCIDevice *d = &s->dev; XenPTMSI *msi = s->msi; - uint16_t reg_field = 0; + uint16_t reg_field; + int rc; /* use I/O device register's value as initial value */ - reg_field = pci_get_word(d->config + real_offset); - + rc = xen_host_pci_get_word(&s->real_device, real_offset, ®_field); + if (rc) { + return rc; + } if (reg_field & PCI_MSI_FLAGS_ENABLE) { XEN_PT_LOG(&s->dev, "MSI already enabled, disabling it first\n"); xen_host_pci_set_word(&s->real_device, real_offset, @@ -1067,6 +1107,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, XenPTMSI *msi = s->msi; uint16_t writable_mask = 0; uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + uint16_t *data = cfg_entry->ptr.half_word; /* Currently no support for multi-vector */ if (*val & PCI_MSI_FLAGS_QSIZE) { @@ -1075,8 +1116,8 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); - msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE; + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); + msi->flags |= *data & ~PCI_MSI_FLAGS_ENABLE; /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); @@ -1086,7 +1127,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, /* setup MSI pirq for the first time */ if (!msi->initialized) { /* Init physical one */ - XEN_PT_LOG(&s->dev, "setup MSI\n"); + XEN_PT_LOG(&s->dev, "setup MSI (register: %x).\n", *val); if (xen_pt_msi_setup(s)) { /* We do not broadcast the error to the framework code, so * that MSI errors are contained in MSI emulation code and @@ -1094,12 +1135,12 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, * Guest MSI would be actually not working. */ *val &= ~PCI_MSI_FLAGS_ENABLE; - XEN_PT_WARN(&s->dev, "Can not map MSI.\n"); + XEN_PT_WARN(&s->dev, "Can not map MSI (register: %x)!\n", *val); return 0; } if (xen_pt_msi_update(s)) { *val &= ~PCI_MSI_FLAGS_ENABLE; - XEN_PT_WARN(&s->dev, "Can not bind MSI\n"); + XEN_PT_WARN(&s->dev, "Can not bind MSI (register: %x)!\n", *val); return 0; } msi->initialized = true; @@ -1190,18 +1231,19 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s, { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; - uint32_t old_addr = cfg_entry->data; + uint32_t old_addr = *cfg_entry->ptr.word; + uint32_t *data = cfg_entry->ptr.word; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); - s->msi->addr_lo = cfg_entry->data; + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); + s->msi->addr_lo = *data; /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); /* update MSI */ - if (cfg_entry->data != old_addr) { + if (*data != old_addr) { if (s->msi->mapped) { xen_pt_msi_update(s); } @@ -1216,7 +1258,8 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s, { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; - uint32_t old_addr = cfg_entry->data; + uint32_t old_addr = *cfg_entry->ptr.word; + uint32_t *data = cfg_entry->ptr.word; /* check whether the type is 64 bit or not */ if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) { @@ -1227,15 +1270,15 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s, /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* update the msi_info too */ - s->msi->addr_hi = cfg_entry->data; + s->msi->addr_hi = *data; /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); /* update MSI */ - if (cfg_entry->data != old_addr) { + if (*data != old_addr) { if (s->msi->mapped) { xen_pt_msi_update(s); } @@ -1254,8 +1297,9 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s, XenPTRegInfo *reg = cfg_entry->reg; XenPTMSI *msi = s->msi; uint16_t writable_mask = 0; - uint16_t old_data = cfg_entry->data; + uint16_t old_data = *cfg_entry->ptr.half_word; uint32_t offset = reg->offset; + uint16_t *data = cfg_entry->ptr.half_word; /* check the offset whether matches the type or not */ if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) { @@ -1266,15 +1310,15 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s, /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* update the msi_info too */ - msi->data = cfg_entry->data; + msi->data = *data; /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); /* update MSI */ - if (cfg_entry->data != old_data) { + if (*data != old_data) { if (msi->mapped) { xen_pt_msi_update(s); } @@ -1411,14 +1455,16 @@ static int xen_pt_msixctrl_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { - PCIDevice *d = &s->dev; - uint16_t reg_field = 0; + uint16_t reg_field; + int rc; /* use I/O device register's value as initial value */ - reg_field = pci_get_word(d->config + real_offset); - + rc = xen_host_pci_get_word(&s->real_device, real_offset, ®_field); + if (rc) { + return rc; + } if (reg_field & PCI_MSIX_FLAGS_ENABLE) { - XEN_PT_LOG(d, "MSIX already enabled, disabling it first\n"); + XEN_PT_LOG(&s->dev, "MSIX already enabled, disabling it first\n"); xen_host_pci_set_word(&s->real_device, real_offset, reg_field & ~PCI_MSIX_FLAGS_ENABLE); } @@ -1436,10 +1482,11 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s, uint16_t writable_mask = 0; uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); int debug_msix_enabled_old; + uint16_t *data = cfg_entry->ptr.half_word; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); /* create value for writing to I/O device register */ *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); @@ -1492,6 +1539,19 @@ static XenPTRegInfo xen_pt_emu_reg_msix[] = { }, }; +static XenPTRegInfo xen_pt_emu_reg_igd_opregion[] = { + /* Intel IGFX OpRegion reg */ + { + .offset = 0x0, + .size = 4, + .init_val = 0, + .u.dw.read = xen_pt_intel_opregion_read, + .u.dw.write = xen_pt_intel_opregion_write, + }, + { + .size = 0, + }, +}; /**************************** * Capabilities @@ -1511,8 +1571,7 @@ static int xen_pt_vendor_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { - *size = pci_get_byte(s->dev.config + base_offset + 0x02); - return 0; + return xen_host_pci_get_byte(&s->real_device, base_offset + 0x02, size); } /* get PCI Express Capability Structure register group size */ static int xen_pt_pcie_size_init(XenPCIPassthroughState *s, @@ -1591,12 +1650,15 @@ static int xen_pt_msi_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { - PCIDevice *d = &s->dev; uint16_t msg_ctrl = 0; uint8_t msi_size = 0xa; + int rc; - msg_ctrl = pci_get_word(d->config + (base_offset + PCI_MSI_FLAGS)); - + rc = xen_host_pci_get_word(&s->real_device, base_offset + PCI_MSI_FLAGS, + &msg_ctrl); + if (rc) { + return rc; + } /* check if 64-bit address is capable of per-vector masking */ if (msg_ctrl & PCI_MSI_FLAGS_64BIT) { msi_size += 4; @@ -1729,6 +1791,14 @@ static const XenPTRegGroupInfo xen_pt_emu_reg_grps[] = { .size_init = xen_pt_msix_size_init, .emu_regs = xen_pt_emu_reg_msix, }, + /* Intel IGD Opregion group */ + { + .grp_id = XEN_PCI_INTEL_OPREGION, + .grp_type = XEN_PT_GRP_TYPE_EMU, + .grp_size = 0x4, + .size_init = xen_pt_reg_grp_size_init, + .emu_regs = xen_pt_emu_reg_igd_opregion, + }, { .grp_size = 0, }, @@ -1739,11 +1809,14 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { - int i; - uint8_t *config = s->dev.config; - uint32_t reg_field = pci_get_byte(config + real_offset); + int i, rc; + uint8_t reg_field; uint8_t cap_id = 0; + rc = xen_host_pci_get_byte(&s->real_device, real_offset, ®_field); + if (rc) { + return rc; + } /* find capability offset */ while (reg_field) { for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) { @@ -1752,7 +1825,13 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, continue; } - cap_id = pci_get_byte(config + reg_field + PCI_CAP_LIST_ID); + rc = xen_host_pci_get_byte(&s->real_device, + reg_field + PCI_CAP_LIST_ID, &cap_id); + if (rc) { + XEN_PT_ERR(&s->dev, "Failed to read capability @0x%x (rc:%d)\n", + reg_field + PCI_CAP_LIST_ID, rc); + return rc; + } if (xen_pt_emu_reg_grps[i].grp_id == cap_id) { if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) { goto out; @@ -1763,7 +1842,11 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, } /* next capability */ - reg_field = pci_get_byte(config + reg_field + PCI_CAP_LIST_NEXT); + rc = xen_host_pci_get_byte(&s->real_device, + reg_field + PCI_CAP_LIST_NEXT, ®_field); + if (rc) { + return rc; + } } out: @@ -1779,7 +1862,7 @@ out: static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap) { uint8_t id; - unsigned max_cap = PCI_CAP_MAX; + unsigned max_cap = XEN_PCI_CAP_MAX; uint8_t pos = PCI_CAPABILITY_LIST; uint8_t status = 0; @@ -1827,6 +1910,10 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s, reg_entry->reg = reg; if (reg->init) { + uint32_t host_mask, size_mask; + unsigned int offset; + uint32_t val; + /* initialize emulate register */ rc = reg->init(s, reg_entry->reg, reg_grp->base_offset + reg->offset, &data); @@ -1839,8 +1926,67 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s, g_free(reg_entry); return 0; } - /* set register value */ - reg_entry->data = data; + /* Sync up the data to dev.config */ + offset = reg_grp->base_offset + reg->offset; + size_mask = 0xFFFFFFFF >> ((4 - reg->size) << 3); + + switch (reg->size) { + case 1: rc = xen_host_pci_get_byte(&s->real_device, offset, (uint8_t *)&val); + break; + case 2: rc = xen_host_pci_get_word(&s->real_device, offset, (uint16_t *)&val); + break; + case 4: rc = xen_host_pci_get_long(&s->real_device, offset, &val); + break; + default: assert(1); + } + if (rc) { + /* Serious issues when we cannot read the host values! */ + g_free(reg_entry); + return rc; + } + /* Set bits in emu_mask are the ones we emulate. The dev.config shall + * contain the emulated view of the guest - therefore we flip the mask + * to mask out the host values (which dev.config initially has) . */ + host_mask = size_mask & ~reg->emu_mask; + + if ((data & host_mask) != (val & host_mask)) { + uint32_t new_val; + + /* Mask out host (including past size). */ + new_val = val & host_mask; + /* Merge emulated ones (excluding the non-emulated ones). */ + new_val |= data & host_mask; + /* Leave intact host and emulated values past the size - even though + * we do not care as we write per reg->size granularity, but for the + * logging below lets have the proper value. */ + new_val |= ((val | data)) & ~size_mask; + XEN_PT_LOG(&s->dev,"Offset 0x%04x mismatch! Emulated=0x%04x, host=0x%04x, syncing to 0x%04x.\n", + offset, data, val, new_val); + val = new_val; + } else + val = data; + + if (val & ~size_mask) { + XEN_PT_ERR(&s->dev,"Offset 0x%04x:0x%04x expands past register size(%d)!\n", + offset, val, reg->size); + g_free(reg_entry); + return -ENXIO; + } + /* This could be just pci_set_long as we don't modify the bits + * past reg->size, but in case this routine is run in parallel or the + * init value is larger, we do not want to over-write registers. */ + switch (reg->size) { + case 1: pci_set_byte(s->dev.config + offset, (uint8_t)val); + break; + case 2: pci_set_word(s->dev.config + offset, (uint16_t)val); + break; + case 4: pci_set_long(s->dev.config + offset, val); + break; + default: assert(1); + } + /* set register value pointer to the data. */ + reg_entry->ptr.byte = s->dev.config + offset; + } /* list add register entry */ QLIST_INSERT_HEAD(®_grp->reg_tbl_list, reg_entry, entries); @@ -1858,7 +2004,8 @@ int xen_pt_config_init(XenPCIPassthroughState *s) uint32_t reg_grp_offset = 0; XenPTRegGroup *reg_grp_entry = NULL; - if (xen_pt_emu_reg_grps[i].grp_id != 0xFF) { + if (xen_pt_emu_reg_grps[i].grp_id != 0xFF + && xen_pt_emu_reg_grps[i].grp_id != XEN_PCI_INTEL_OPREGION) { if (xen_pt_hide_dev_cap(&s->real_device, xen_pt_emu_reg_grps[i].grp_id)) { continue; @@ -1871,6 +2018,15 @@ int xen_pt_config_init(XenPCIPassthroughState *s) } } + /* + * By default we will trap up to 0x40 in the cfg space. + * If an intel device is pass through we need to trap 0xfc, + * therefore the size should be 0xff. + */ + if (xen_pt_emu_reg_grps[i].grp_id == XEN_PCI_INTEL_OPREGION) { + reg_grp_offset = XEN_PCI_INTEL_OPREGION; + } + reg_grp_entry = g_new0(XenPTRegGroup, 1); QLIST_INIT(®_grp_entry->reg_tbl_list); QLIST_INSERT_HEAD(&s->reg_grps, reg_grp_entry, entries); @@ -1883,6 +2039,9 @@ int xen_pt_config_init(XenPCIPassthroughState *s) reg_grp_offset, ®_grp_entry->size); if (rc < 0) { + XEN_PT_LOG(&s->dev, "Failed to initialize %d/%ld, type=0x%x, rc:%d\n", + i, ARRAY_SIZE(xen_pt_emu_reg_grps), + xen_pt_emu_reg_grps[i].grp_type, rc); xen_pt_config_delete(s); return rc; } @@ -1897,6 +2056,10 @@ int xen_pt_config_init(XenPCIPassthroughState *s) /* initialize capability register */ rc = xen_pt_config_reg_init(s, reg_grp_entry, regs); if (rc < 0) { + XEN_PT_LOG(&s->dev, "Failed to initialize %d/%ld reg 0x%x in grp_type=0x%x (%d/%ld), rc=%d\n", + j, ARRAY_SIZE(xen_pt_emu_reg_grps[i].emu_regs), + regs->offset, xen_pt_emu_reg_grps[i].grp_type, + i, ARRAY_SIZE(xen_pt_emu_reg_grps), rc); xen_pt_config_delete(s); return rc; } @@ -1918,9 +2081,7 @@ void xen_pt_config_delete(XenPCIPassthroughState *s) if (s->msix) { xen_pt_msix_delete(s); } - if (s->msi) { - g_free(s->msi); - } + g_free(s->msi); /* free all register group entry */ QLIST_FOREACH_SAFE(reg_group, &s->reg_grps, entries, next_grp) { diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c new file mode 100644 index 0000000000..df6069bf63 --- /dev/null +++ b/hw/xen/xen_pt_graphics.c @@ -0,0 +1,272 @@ +/* + * graphics passthrough + */ +#include "xen_pt.h" +#include "xen-host-pci-device.h" +#include "hw/xen/xen_backend.h" + +static unsigned long igd_guest_opregion; +static unsigned long igd_host_opregion; + +#define XEN_PCI_INTEL_OPREGION_MASK 0xfff + +typedef struct VGARegion { + int type; /* Memory or port I/O */ + uint64_t guest_base_addr; + uint64_t machine_base_addr; + uint64_t size; /* size of the region */ + int rc; +} VGARegion; + +#define IORESOURCE_IO 0x00000100 +#define IORESOURCE_MEM 0x00000200 + +static struct VGARegion vga_args[] = { + { + .type = IORESOURCE_IO, + .guest_base_addr = 0x3B0, + .machine_base_addr = 0x3B0, + .size = 0xC, + .rc = -1, + }, + { + .type = IORESOURCE_IO, + .guest_base_addr = 0x3C0, + .machine_base_addr = 0x3C0, + .size = 0x20, + .rc = -1, + }, + { + .type = IORESOURCE_MEM, + .guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT, + .machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT, + .size = 0x20, + .rc = -1, + }, +}; + +/* + * register VGA resources for the domain with assigned gfx + */ +int xen_pt_register_vga_regions(XenHostPCIDevice *dev) +{ + int i = 0; + + if (!is_igd_vga_passthrough(dev)) { + return 0; + } + + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { + if (vga_args[i].type == IORESOURCE_IO) { + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_ADD_MAPPING); + } else { + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_ADD_MAPPING); + } + + if (vga_args[i].rc) { + XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n", + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", + vga_args[i].rc); + return vga_args[i].rc; + } + } + + return 0; +} + +/* + * unregister VGA resources for the domain with assigned gfx + */ +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) +{ + int i = 0; + int ret = 0; + + if (!is_igd_vga_passthrough(dev)) { + return 0; + } + + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { + if (vga_args[i].type == IORESOURCE_IO) { + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_REMOVE_MAPPING); + } else { + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_REMOVE_MAPPING); + } + + if (vga_args[i].rc) { + XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n", + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", + vga_args[i].rc); + return vga_args[i].rc; + } + } + + if (igd_guest_opregion) { + ret = xc_domain_memory_mapping(xen_xc, xen_domid, + (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT), + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + 3, + DPCI_REMOVE_MAPPING); + if (ret) { + return ret; + } + } + + return 0; +} + +static void *get_vgabios(XenPCIPassthroughState *s, int *size, + XenHostPCIDevice *dev) +{ + return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size, + dev->domain, dev->bus, + dev->dev, dev->func); +} + +/* Refer to Seabios. */ +struct rom_header { + uint16_t signature; + uint8_t size; + uint8_t initVector[4]; + uint8_t reserved[17]; + uint16_t pcioffset; + uint16_t pnpoffset; +} __attribute__((packed)); + +struct pci_data { + uint32_t signature; + uint16_t vendor; + uint16_t device; + uint16_t vitaldata; + uint16_t dlen; + uint8_t drevision; + uint8_t class_lo; + uint16_t class_hi; + uint16_t ilen; + uint16_t irevision; + uint8_t type; + uint8_t indicator; + uint16_t reserved; +} __attribute__((packed)); + +int xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev) +{ + unsigned char *bios = NULL; + struct rom_header *rom; + int bios_size; + char *c = NULL; + char checksum = 0; + uint32_t len = 0; + struct pci_data *pd = NULL; + + if (!is_igd_vga_passthrough(dev)) { + return -1; + } + + bios = get_vgabios(s, &bios_size, dev); + if (!bios) { + XEN_PT_ERR(&s->dev, "VGA: Can't getting VBIOS!\n"); + return -1; + } + + /* Currently we fixed this address as a primary. */ + rom = (struct rom_header *)bios; + pd = (void *)(bios + (unsigned char)rom->pcioffset); + + /* We may need to fixup Device Identification. */ + if (pd->device != s->real_device.device_id) { + pd->device = s->real_device.device_id; + + len = rom->size * 512; + /* Then adjust the bios checksum */ + for (c = (char *)bios; c < ((char *)bios + len); c++) { + checksum += *c; + } + if (checksum) { + bios[len - 1] -= checksum; + XEN_PT_LOG(&s->dev, "vga bios checksum is adjusted %x!\n", + checksum); + } + } + + /* Currently we fixed this address as a primary for legacy BIOS. */ + cpu_physical_memory_rw(0xc0000, bios, bios_size, 1); + return 0; +} + +uint32_t igd_read_opregion(XenPCIPassthroughState *s) +{ + uint32_t val = 0; + + if (!igd_guest_opregion) { + return val; + } + + val = igd_guest_opregion; + + XEN_PT_LOG(&s->dev, "Read opregion val=%x\n", val); + return val; +} + +#define XEN_PCI_INTEL_OPREGION_PAGES 0x3 +#define XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED 0x1 +void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val) +{ + int ret; + + if (igd_guest_opregion) { + XEN_PT_LOG(&s->dev, "opregion register already been set, ignoring %x\n", + val); + return; + } + + /* We just work with LE. */ + xen_host_pci_get_block(&s->real_device, XEN_PCI_INTEL_OPREGION, + (uint8_t *)&igd_host_opregion, 4); + igd_guest_opregion = (unsigned long)(val & ~XEN_PCI_INTEL_OPREGION_MASK) + | (igd_host_opregion & XEN_PCI_INTEL_OPREGION_MASK); + + ret = xc_domain_iomem_permission(xen_xc, xen_domid, + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + XEN_PCI_INTEL_OPREGION_PAGES, + XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED); + + if (ret) { + XEN_PT_ERR(&s->dev, "[%d]:Can't enable to access IGD host opregion:" + " 0x%lx.\n", ret, + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT)), + igd_guest_opregion = 0; + return; + } + + ret = xc_domain_memory_mapping(xen_xc, xen_domid, + (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT), + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + XEN_PCI_INTEL_OPREGION_PAGES, + DPCI_ADD_MAPPING); + + if (ret) { + XEN_PT_ERR(&s->dev, "[%d]:Can't map IGD host opregion:0x%lx to" + " guest opregion:0x%lx.\n", ret, + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT)); + igd_guest_opregion = 0; + return; + } + + XEN_PT_LOG(&s->dev, "Map OpRegion: 0x%lx -> 0x%lx\n", + (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), + (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT)); +} diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index 263e0514a2..e3d71945cd 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -75,19 +75,29 @@ static int msi_msix_enable(XenPCIPassthroughState *s, bool enable) { uint16_t val = 0; + int rc; if (!address) { return -1; } - xen_host_pci_get_word(&s->real_device, address, &val); + rc = xen_host_pci_get_word(&s->real_device, address, &val); + if (rc) { + XEN_PT_ERR(&s->dev, "Failed to read MSI/MSI-X register (0x%x), rc:%d\n", + address, rc); + return rc; + } if (enable) { val |= flag; } else { val &= ~flag; } - xen_host_pci_set_word(&s->real_device, address, val); - return 0; + rc = xen_host_pci_set_word(&s->real_device, address, val); + if (rc) { + XEN_PT_ERR(&s->dev, "Failed to write MSI/MSI-X register (0x%x), rc:%d\n", + address, rc); + } + return rc; } static int msi_msix_setup(XenPCIPassthroughState *s, @@ -220,7 +230,7 @@ static int msi_msix_disable(XenPCIPassthroughState *s, * MSI virtualization functions */ -int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable) +static int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable) { XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling"); @@ -276,7 +286,7 @@ void xen_pt_msi_disable(XenPCIPassthroughState *s) return; } - xen_pt_msi_set_enable(s, false); + (void)xen_pt_msi_set_enable(s, false); msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false, msi->initialized); |