diff options
95 files changed, 4188 insertions, 1075 deletions
diff --git a/block/crypto.c b/block/crypto.c index 32ba17ce00..758e14e032 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -196,7 +196,6 @@ block_crypto_open_opts_init(QCryptoBlockFormat format, OptsVisitor *ov; QCryptoBlockOpenOptions *ret = NULL; Error *local_err = NULL; - Error *end_err = NULL; ret = g_new0(QCryptoBlockOpenOptions, 1); ret->format = format; @@ -219,9 +218,11 @@ block_crypto_open_opts_init(QCryptoBlockFormat format, error_setg(&local_err, "Unsupported block format %d", format); break; } + if (!local_err) { + visit_check_struct(opts_get_visitor(ov), &local_err); + } - visit_end_struct(opts_get_visitor(ov), &end_err); - error_propagate(&local_err, end_err); + visit_end_struct(opts_get_visitor(ov)); out: if (local_err) { @@ -242,7 +243,6 @@ block_crypto_create_opts_init(QCryptoBlockFormat format, OptsVisitor *ov; QCryptoBlockCreateOptions *ret = NULL; Error *local_err = NULL; - Error *end_err = NULL; ret = g_new0(QCryptoBlockCreateOptions, 1); ret->format = format; @@ -265,9 +265,11 @@ block_crypto_create_opts_init(QCryptoBlockFormat format, error_setg(&local_err, "Unsupported block format %d", format); break; } + if (!local_err) { + visit_check_struct(opts_get_visitor(ov), &local_err); + } - visit_end_struct(opts_get_visitor(ov), &end_err); - error_propagate(&local_err, end_err); + visit_end_struct(opts_get_visitor(ov)); out: if (local_err) { diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index c63cdd073d..c5bcba754a 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -100,6 +100,7 @@ CONFIG_ALLWINNER_A10_PIT=y CONFIG_ALLWINNER_A10_PIC=y CONFIG_ALLWINNER_A10=y +CONFIG_FSL_IMX6=y CONFIG_FSL_IMX31=y CONFIG_FSL_IMX25=y diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 0e4bafff08..d7d6987821 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -899,10 +899,16 @@ Example: goto out_obj; } visit_type_UserDefOne_members(v, *obj, &err); - error_propagate(errp, err); - err = NULL; + if (err) { + goto out_obj; + } + visit_check_struct(v, &err); out_obj: - visit_end_struct(v, &err); + visit_end_struct(v); + if (err && visit_is_input(v)) { + qapi_free_UserDefOne(*obj); + *obj = NULL; + } out: error_propagate(errp, err); } @@ -910,21 +916,27 @@ Example: void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp) { Error *err = NULL; - GenericList *i, **prev; + UserDefOneList *tail; + size_t size = sizeof(**obj); - visit_start_list(v, name, &err); + visit_start_list(v, name, (GenericList **)obj, size, &err); if (err) { goto out; } - for (prev = (GenericList **)obj; - !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL; - prev = &i) { - UserDefOneList *native_i = (UserDefOneList *)i; - visit_type_UserDefOne(v, NULL, &native_i->value, &err); + for (tail = *obj; tail; + tail = (UserDefOneList *)visit_next_list(v, (GenericList *)tail, size)) { + visit_type_UserDefOne(v, NULL, &tail->value, &err); + if (err) { + break; + } } visit_end_list(v); + if (err && visit_is_input(v)) { + qapi_free_UserDefOneList(*obj); + *obj = NULL; + } out: error_propagate(errp, err); } @@ -996,13 +1008,21 @@ Example: { Error *err = NULL; UserDefOne *retval; - QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args)); + QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true); QapiDeallocVisitor *qdv; Visitor *v; UserDefOneList *arg1 = NULL; v = qmp_input_get_visitor(qiv); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } visit_type_UserDefOneList(v, "arg1", &arg1, &err); + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v); if (err) { goto out; } @@ -1019,7 +1039,9 @@ Example: qmp_input_visitor_cleanup(qiv); qdv = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(qdv); + visit_start_struct(v, NULL, NULL, 0, NULL); visit_type_UserDefOneList(v, "arg1", &arg1, NULL); + visit_end_struct(v); qapi_dealloc_visitor_cleanup(qdv); } diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index ab89ca6380..cedb74e7cf 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1563,3 +1563,14 @@ build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets, build_header(linker, table_data, (void *)rsdt, "RSDT", rsdt_len, 1, oem_id, oem_table_id); } + +void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, + uint64_t len, int node, MemoryAffinityFlags flags) +{ + numamem->type = ACPI_SRAT_MEMORY; + numamem->length = sizeof(*numamem); + numamem->proximity = cpu_to_le32(node); + numamem->flags = cpu_to_le32(flags); + numamem->base_addr = cpu_to_le64(base); + numamem->range_length = cpu_to_le64(len); +} diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 954c9fe15e..12764ef2b7 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -16,4 +16,5 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o +obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index bb2a22d967..49d30782c8 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -132,14 +132,14 @@ typedef struct { uint32_t base; } BitBandState; -static int bitband_init(SysBusDevice *dev) +static void bitband_init(Object *obj) { - BitBandState *s = BITBAND(dev); + BitBandState *s = BITBAND(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &bitband_ops, &s->base, + memory_region_init_io(&s->iomem, obj, &bitband_ops, &s->base, "bitband", 0x02000000); sysbus_init_mmio(dev, &s->iomem); - return 0; } static void armv7m_bitband_init(void) @@ -244,9 +244,7 @@ static Property bitband_properties[] = { static void bitband_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = bitband_init; dc->props = bitband_properties; } @@ -254,6 +252,7 @@ static const TypeInfo bitband_info = { .name = TYPE_BITBAND, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(BitBandState), + .instance_init = bitband_init, .class_init = bitband_class_init, }; diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 5876945575..1b913a43ca 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -14,6 +14,7 @@ #include "hw/arm/linux-boot-if.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" +#include "sysemu/numa.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" @@ -405,6 +406,9 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, void *fdt = NULL; int size, rc; uint32_t acells, scells; + char *nodename; + unsigned int i; + hwaddr mem_base, mem_len; if (binfo->dtb_filename) { char *filename; @@ -456,12 +460,39 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, goto fail; } - rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", - acells, binfo->loader_start, - scells, binfo->ram_size); - if (rc < 0) { - fprintf(stderr, "couldn't set /memory/reg\n"); - goto fail; + if (nb_numa_nodes > 0) { + /* + * Turn the /memory node created before into a NOP node, then create + * /memory@addr nodes for all numa nodes respectively. + */ + qemu_fdt_nop_node(fdt, "/memory"); + mem_base = binfo->loader_start; + for (i = 0; i < nb_numa_nodes; i++) { + mem_len = numa_info[i].node_mem; + nodename = g_strdup_printf("/memory@%" PRIx64, mem_base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + rc = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + acells, mem_base, + scells, mem_len); + if (rc < 0) { + fprintf(stderr, "couldn't set %s/reg for node %d\n", nodename, + i); + goto fail; + } + + qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", i); + mem_base += mem_len; + g_free(nodename); + } + } else { + rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", + acells, binfo->loader_start, + scells, binfo->ram_size); + if (rc < 0) { + fprintf(stderr, "couldn't set /memory/reg\n"); + goto fail; + } } if (binfo->kernel_cmdline && *binfo->kernel_cmdline) { diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c new file mode 100644 index 0000000000..a5331bfd33 --- /dev/null +++ b/hw/arm/fsl-imx6.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * i.MX6 SOC emulation. + * + * Based on hw/arm/fsl-imx31.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/arm/fsl-imx6.h" +#include "sysemu/sysemu.h" +#include "sysemu/char.h" +#include "qemu/error-report.h" + +#define NAME_SIZE 20 + +static void fsl_imx6_init(Object *obj) +{ + FslIMX6State *s = FSL_IMX6(obj); + char name[NAME_SIZE]; + int i; + + if (smp_cpus > FSL_IMX6_NUM_CPUS) { + error_report("%s: Only %d CPUs are supported (%d requested)", + TYPE_FSL_IMX6, FSL_IMX6_NUM_CPUS, smp_cpus); + exit(1); + } + + for (i = 0; i < smp_cpus; i++) { + object_initialize(&s->cpu[i], sizeof(s->cpu[i]), + "cortex-a9-" TYPE_ARM_CPU); + snprintf(name, NAME_SIZE, "cpu%d", i); + object_property_add_child(obj, name, OBJECT(&s->cpu[i]), NULL); + } + + object_initialize(&s->a9mpcore, sizeof(s->a9mpcore), TYPE_A9MPCORE_PRIV); + qdev_set_parent_bus(DEVICE(&s->a9mpcore), sysbus_get_default()); + object_property_add_child(obj, "a9mpcore", OBJECT(&s->a9mpcore), NULL); + + object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX6_CCM); + qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + object_property_add_child(obj, "ccm", OBJECT(&s->ccm), NULL); + + object_initialize(&s->src, sizeof(s->src), TYPE_IMX6_SRC); + qdev_set_parent_bus(DEVICE(&s->src), sysbus_get_default()); + object_property_add_child(obj, "src", OBJECT(&s->src), NULL); + + for (i = 0; i < FSL_IMX6_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()); + snprintf(name, NAME_SIZE, "uart%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->uart[i]), NULL); + } + + object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX_GPT); + qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); + object_property_add_child(obj, "gpt", OBJECT(&s->gpt), NULL); + + for (i = 0; i < FSL_IMX6_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()); + snprintf(name, NAME_SIZE, "epit%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->epit[i]), NULL); + } + + for (i = 0; i < FSL_IMX6_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()); + snprintf(name, NAME_SIZE, "i2c%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL); + } + + for (i = 0; i < FSL_IMX6_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()); + snprintf(name, NAME_SIZE, "gpio%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->gpio[i]), NULL); + } + + for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) { + object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_SYSBUS_SDHCI); + qdev_set_parent_bus(DEVICE(&s->esdhc[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "sdhc%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->esdhc[i]), NULL); + } + + for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { + object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI); + qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "spi%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL); + } +} + +static void fsl_imx6_realize(DeviceState *dev, Error **errp) +{ + FslIMX6State *s = FSL_IMX6(dev); + uint16_t i; + Error *err = NULL; + + for (i = 0; i < smp_cpus; i++) { + + /* On uniprocessor, the CBAR is set to 0 */ + if (smp_cpus > 1) { + object_property_set_int(OBJECT(&s->cpu[i]), FSL_IMX6_A9MPCORE_ADDR, + "reset-cbar", &error_abort); + } + + /* All CPU but CPU 0 start in power off mode */ + if (i) { + object_property_set_bool(OBJECT(&s->cpu[i]), true, + "start-powered-off", &error_abort); + } + + object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + object_property_set_int(OBJECT(&s->a9mpcore), smp_cpus, "num-cpu", + &error_abort); + + object_property_set_int(OBJECT(&s->a9mpcore), + FSL_IMX6_MAX_IRQ + GIC_INTERNAL, "num-irq", + &error_abort); + + object_property_set_bool(OBJECT(&s->a9mpcore), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, FSL_IMX6_A9MPCORE_ADDR); + + for (i = 0; i < smp_cpus; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + smp_cpus, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), 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_IMX6_CCM_ADDR); + + object_property_set_bool(OBJECT(&s->src), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->src), 0, FSL_IMX6_SRC_ADDR); + + /* Initialize all UARTs */ + for (i = 0; i < FSL_IMX6_NUM_UARTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX6_NUM_UARTS] = { + { FSL_IMX6_UART1_ADDR, FSL_IMX6_UART1_IRQ }, + { FSL_IMX6_UART2_ADDR, FSL_IMX6_UART2_IRQ }, + { FSL_IMX6_UART3_ADDR, FSL_IMX6_UART3_IRQ }, + { FSL_IMX6_UART4_ADDR, FSL_IMX6_UART4_IRQ }, + { FSL_IMX6_UART5_ADDR, FSL_IMX6_UART5_IRQ }, + }; + + if (i < MAX_SERIAL_PORTS) { + CharDriverState *chr; + + chr = serial_hds[i]; + + if (!chr) { + char *label = g_strdup_printf("imx6.uart%d", i + 1); + chr = qemu_chr_new(label, "null", NULL); + g_free(label); + serial_hds[i] = chr; + } + + 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->a9mpcore), + serial_table[i].irq)); + } + + s->gpt.ccm = IMX_CCM(&s->ccm); + + object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt), 0, FSL_IMX6_GPT_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + FSL_IMX6_GPT_IRQ)); + + /* Initialize all EPIT timers */ + for (i = 0; i < FSL_IMX6_NUM_EPITS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } epit_table[FSL_IMX6_NUM_EPITS] = { + { FSL_IMX6_EPIT1_ADDR, FSL_IMX6_EPIT1_IRQ }, + { FSL_IMX6_EPIT2_ADDR, FSL_IMX6_EPIT2_IRQ }, + }; + + s->epit[i].ccm = IMX_CCM(&s->ccm); + + object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + epit_table[i].irq)); + } + + /* Initialize all I2C */ + for (i = 0; i < FSL_IMX6_NUM_I2CS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX6_NUM_I2CS] = { + { FSL_IMX6_I2C1_ADDR, FSL_IMX6_I2C1_IRQ }, + { FSL_IMX6_I2C2_ADDR, FSL_IMX6_I2C2_IRQ }, + { FSL_IMX6_I2C3_ADDR, FSL_IMX6_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->a9mpcore), + i2c_table[i].irq)); + } + + /* Initialize all GPIOs */ + for (i = 0; i < FSL_IMX6_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq_low; + unsigned int irq_high; + } gpio_table[FSL_IMX6_NUM_GPIOS] = { + { + FSL_IMX6_GPIO1_ADDR, + FSL_IMX6_GPIO1_LOW_IRQ, + FSL_IMX6_GPIO1_HIGH_IRQ + }, + { + FSL_IMX6_GPIO2_ADDR, + FSL_IMX6_GPIO2_LOW_IRQ, + FSL_IMX6_GPIO2_HIGH_IRQ + }, + { + FSL_IMX6_GPIO3_ADDR, + FSL_IMX6_GPIO3_LOW_IRQ, + FSL_IMX6_GPIO3_HIGH_IRQ + }, + { + FSL_IMX6_GPIO4_ADDR, + FSL_IMX6_GPIO4_LOW_IRQ, + FSL_IMX6_GPIO4_HIGH_IRQ + }, + { + FSL_IMX6_GPIO5_ADDR, + FSL_IMX6_GPIO5_LOW_IRQ, + FSL_IMX6_GPIO5_HIGH_IRQ + }, + { + FSL_IMX6_GPIO6_ADDR, + FSL_IMX6_GPIO6_LOW_IRQ, + FSL_IMX6_GPIO6_HIGH_IRQ + }, + { + FSL_IMX6_GPIO7_ADDR, + FSL_IMX6_GPIO7_LOW_IRQ, + FSL_IMX6_GPIO7_HIGH_IRQ + }, + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), true, "has-edge-sel", + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), true, "has-upper-pin-irq", + &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); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + gpio_table[i].irq_low)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + gpio_table[i].irq_high)); + } + + /* Initialize all SDHC */ + for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } esdhc_table[FSL_IMX6_NUM_ESDHCS] = { + { FSL_IMX6_uSDHC1_ADDR, FSL_IMX6_uSDHC1_IRQ }, + { FSL_IMX6_uSDHC2_ADDR, FSL_IMX6_uSDHC2_IRQ }, + { FSL_IMX6_uSDHC3_ADDR, FSL_IMX6_uSDHC3_IRQ }, + { FSL_IMX6_uSDHC4_ADDR, FSL_IMX6_uSDHC4_IRQ }, + }; + + object_property_set_bool(OBJECT(&s->esdhc[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->esdhc[i]), 0, esdhc_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->esdhc[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + esdhc_table[i].irq)); + } + + /* Initialize all ECSPI */ + for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } spi_table[FSL_IMX6_NUM_ECSPIS] = { + { FSL_IMX6_eCSPI1_ADDR, FSL_IMX6_ECSPI1_IRQ }, + { FSL_IMX6_eCSPI2_ADDR, FSL_IMX6_ECSPI2_IRQ }, + { FSL_IMX6_eCSPI3_ADDR, FSL_IMX6_ECSPI3_IRQ }, + { FSL_IMX6_eCSPI4_ADDR, FSL_IMX6_ECSPI4_IRQ }, + { FSL_IMX6_eCSPI5_ADDR, FSL_IMX6_ECSPI5_IRQ }, + }; + + /* Initialize the SPI */ + object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + spi_table[i].irq)); + } + + /* ROM memory */ + memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx6.rom", + FSL_IMX6_ROM_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX6_ROM_ADDR, + &s->rom); + + /* CAAM memory */ + memory_region_init_rom_device(&s->caam, NULL, NULL, NULL, "imx6.caam", + FSL_IMX6_CAAM_MEM_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX6_CAAM_MEM_ADDR, + &s->caam); + + /* OCRAM memory */ + memory_region_init_ram(&s->ocram, NULL, "imx6.ocram", FSL_IMX6_OCRAM_SIZE, + &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ADDR, + &s->ocram); + vmstate_register_ram_global(&s->ocram); + + /* internal OCRAM (256 KB) is aliased over 1 MB */ + memory_region_init_alias(&s->ocram_alias, NULL, "imx6.ocram_alias", + &s->ocram, 0, FSL_IMX6_OCRAM_ALIAS_SIZE); + memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ALIAS_ADDR, + &s->ocram_alias); +} + +static void fsl_imx6_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx6_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; + dc->desc = "i.MX6 SOC"; +} + +static const TypeInfo fsl_imx6_type_info = { + .name = TYPE_FSL_IMX6, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslIMX6State), + .instance_init = fsl_imx6_init, + .class_init = fsl_imx6_class_init, +}; + +static void fsl_imx6_register_types(void) +{ + type_register_static(&fsl_imx6_type_info); +} + +type_init(fsl_imx6_register_types) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index d9930c0d34..41029a651d 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -168,23 +168,20 @@ static void highbank_regs_reset(DeviceState *dev) s->regs[0x43] = 0x05F40121; } -static int highbank_regs_init(SysBusDevice *dev) +static void highbank_regs_init(Object *obj) { - HighbankRegsState *s = HIGHBANK_REGISTERS(dev); + HighbankRegsState *s = HIGHBANK_REGISTERS(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &hb_mem_ops, s->regs, + memory_region_init_io(&s->iomem, obj, &hb_mem_ops, s->regs, "highbank_regs", 0x1000); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void highbank_regs_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = highbank_regs_init; dc->desc = "Calxeda Highbank registers"; dc->vmsd = &vmstate_highbank_regs; dc->reset = highbank_regs_reset; @@ -194,6 +191,7 @@ static const TypeInfo highbank_regs_info = { .name = TYPE_HIGHBANK_REGISTERS, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(HighbankRegsState), + .instance_init = highbank_regs_init, .class_init = highbank_regs_class_init, }; diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index e31bca6e72..24f16874f9 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -242,9 +242,10 @@ static const MemoryRegionOps integratorcm_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int integratorcm_init(SysBusDevice *dev) +static void integratorcm_init(Object *obj) { - IntegratorCMState *s = INTEGRATOR_CM(dev); + IntegratorCMState *s = INTEGRATOR_CM(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); s->cm_osc = 0x01000048; /* ??? What should the high bits of this value be? */ @@ -269,17 +270,16 @@ static int integratorcm_init(SysBusDevice *dev) s->cm_init = 0x00000112; s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24, 1000); - memory_region_init_ram(&s->flash, OBJECT(s), "integrator.flash", 0x100000, + memory_region_init_ram(&s->flash, obj, "integrator.flash", 0x100000, &error_fatal); vmstate_register_ram_global(&s->flash); - memory_region_init_io(&s->iomem, OBJECT(s), &integratorcm_ops, s, + memory_region_init_io(&s->iomem, obj, &integratorcm_ops, s, "integratorcm", 0x00800000); sysbus_init_mmio(dev, &s->iomem); integratorcm_do_remap(s); /* ??? Save/restore. */ - return 0; } /* Integrator/CP hardware emulation. */ @@ -394,18 +394,18 @@ static const MemoryRegionOps icp_pic_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int icp_pic_init(SysBusDevice *sbd) +static void icp_pic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - icp_pic_state *s = INTEGRATOR_PIC(dev); + DeviceState *dev = DEVICE(obj); + icp_pic_state *s = INTEGRATOR_PIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, icp_pic_set_irq, 32); sysbus_init_irq(sbd, &s->parent_irq); sysbus_init_irq(sbd, &s->parent_fiq); - memory_region_init_io(&s->iomem, OBJECT(s), &icp_pic_ops, s, + memory_region_init_io(&s->iomem, obj, &icp_pic_ops, s, "icp-pic", 0x00800000); sysbus_init_mmio(sbd, &s->iomem); - return 0; } /* CP control registers. */ @@ -630,9 +630,7 @@ static Property core_properties[] = { static void core_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = integratorcm_init; dc->props = core_properties; } @@ -640,21 +638,15 @@ static const TypeInfo core_info = { .name = TYPE_INTEGRATOR_CM, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IntegratorCMState), + .instance_init = integratorcm_init, .class_init = core_class_init, }; -static void icp_pic_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = icp_pic_init; -} - static const TypeInfo icp_pic_info = { .name = TYPE_INTEGRATOR_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(icp_pic_state), - .class_init = icp_pic_class_init, + .instance_init = icp_pic_init, }; static const TypeInfo icp_ctrl_regs_info = { diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 5382505559..c7068c0d38 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -1364,7 +1364,7 @@ static void n8x0_init(MachineState *machine, if (option_rom[0].name && (machine->boot_order[0] == 'n' || !machine->kernel_filename)) { - uint8_t nolo_tags[0x10000]; + uint8_t *nolo_tags = g_new(uint8_t, 0x10000); /* No, wait, better start at the ROM. */ s->mpu->cpu->env.regs[15] = OMAP2_Q2_BASE + 0x400000; @@ -1383,6 +1383,7 @@ static void n8x0_init(MachineState *machine, n800_setup_nolo_tags(nolo_tags); cpu_physical_memory_write(OMAP2_SRAM_BASE, nolo_tags, 0x10000); + g_free(nolo_tags); } } diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 1a8c36033a..e41a7c92ab 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1107,9 +1107,10 @@ static const MemoryRegionOps pxa2xx_rtc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int pxa2xx_rtc_init(SysBusDevice *dev) +static void pxa2xx_rtc_init(Object *obj) { - PXA2xxRTCState *s = PXA2XX_RTC(dev); + PXA2xxRTCState *s = PXA2XX_RTC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); struct tm tm; int wom; @@ -1138,11 +1139,9 @@ static int pxa2xx_rtc_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->rtc_irq); - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_rtc_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_rtc_ops, s, "pxa2xx-rtc", 0x10000); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void pxa2xx_rtc_pre_save(void *opaque) @@ -1195,9 +1194,7 @@ static const VMStateDescription vmstate_pxa2xx_rtc_regs = { static void pxa2xx_rtc_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pxa2xx_rtc_init; dc->desc = "PXA2xx RTC Controller"; dc->vmsd = &vmstate_pxa2xx_rtc_regs; } @@ -1206,6 +1203,7 @@ static const TypeInfo pxa2xx_rtc_sysbus_info = { .name = TYPE_PXA2XX_RTC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxRTCState), + .instance_init = pxa2xx_rtc_init, .class_init = pxa2xx_rtc_sysbus_class_init, }; @@ -1501,19 +1499,18 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, return s; } -static int pxa2xx_i2c_initfn(SysBusDevice *sbd) +static void pxa2xx_i2c_initfn(Object *obj) { - DeviceState *dev = DEVICE(sbd); - PXA2xxI2CState *s = PXA2XX_I2C(dev); + DeviceState *dev = DEVICE(obj); + PXA2xxI2CState *s = PXA2XX_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->bus = i2c_init_bus(dev, "i2c"); - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_i2c_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_i2c_ops, s, "pxa2xx-i2c", s->region_size); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); - - return 0; } I2CBus *pxa2xx_i2c_bus(PXA2xxI2CState *s) @@ -1530,9 +1527,7 @@ static Property pxa2xx_i2c_properties[] = { static void pxa2xx_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pxa2xx_i2c_initfn; dc->desc = "PXA2xx I2C Bus Controller"; dc->vmsd = &vmstate_pxa2xx_i2c; dc->props = pxa2xx_i2c_properties; @@ -1542,6 +1537,7 @@ static const TypeInfo pxa2xx_i2c_info = { .name = TYPE_PXA2XX_I2C, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxI2CState), + .instance_init = pxa2xx_i2c_initfn, .class_init = pxa2xx_i2c_class_init, }; diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index 7e51532cde..b516ced8c0 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -310,17 +310,10 @@ static VMStateDescription vmstate_pxa2xx_pic_regs = { }, }; -static int pxa2xx_pic_initfn(SysBusDevice *dev) -{ - return 0; -} - static void pxa2xx_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pxa2xx_pic_initfn; dc->desc = "PXA2xx PIC"; dc->vmsd = &vmstate_pxa2xx_pic_regs; } diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c new file mode 100644 index 0000000000..776c51e398 --- /dev/null +++ b/hw/arm/sabrelite.c @@ -0,0 +1,121 @@ +/* + * SABRELITE Board System emulation. + * + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This code is licensed under the GPL, version 2 or later. + * See the file `COPYING' in the top level directory. + * + * It (partially) emulates a sabrelite board, with a Freescale + * i.MX6 SoC + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/arm/fsl-imx6.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "qemu/error-report.h" +#include "sysemu/qtest.h" + +typedef struct IMX6Sabrelite { + FslIMX6State soc; + MemoryRegion ram; +} IMX6Sabrelite; + +static struct arm_boot_info sabrelite_binfo = { + /* DDR memory start */ + .loader_start = FSL_IMX6_MMDC_ADDR, + /* No board ID, we boot from DT tree */ + .board_id = -1, +}; + +/* No need to do any particular setup for secondary boot */ +static void sabrelite_write_secondary(ARMCPU *cpu, + const struct arm_boot_info *info) +{ +} + +/* Secondary cores are reset through SRC device */ +static void sabrelite_reset_secondary(ARMCPU *cpu, + const struct arm_boot_info *info) +{ +} + +static void sabrelite_init(MachineState *machine) +{ + IMX6Sabrelite *s = g_new0(IMX6Sabrelite, 1); + Error *err = NULL; + + /* Check the amount of memory is compatible with the SOC */ + if (machine->ram_size > FSL_IMX6_MMDC_SIZE) { + error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)", + machine->ram_size, FSL_IMX6_MMDC_SIZE); + exit(1); + } + + object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX6); + 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); + } + + memory_region_allocate_system_memory(&s->ram, NULL, "sabrelite.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), FSL_IMX6_MMDC_ADDR, + &s->ram); + + { + /* + * TODO: Ideally we would expose the chip select and spi bus on the + * SoC object using alias properties; then we would not need to + * directly access the underlying spi device object. + */ + /* Add the sst25vf016b NOR FLASH memory to first SPI */ + Object *spi_dev; + + spi_dev = object_resolve_path_component(OBJECT(&s->soc), "spi1"); + if (spi_dev) { + SSIBus *spi_bus; + + spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev), "spi"); + if (spi_bus) { + DeviceState *flash_dev; + + flash_dev = ssi_create_slave(spi_bus, "sst25vf016b"); + if (flash_dev) { + qemu_irq cs_line = qdev_get_gpio_in_named(flash_dev, + SSI_GPIO_CS, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line); + } + } + } + } + + sabrelite_binfo.ram_size = machine->ram_size; + sabrelite_binfo.kernel_filename = machine->kernel_filename; + sabrelite_binfo.kernel_cmdline = machine->kernel_cmdline; + sabrelite_binfo.initrd_filename = machine->initrd_filename; + sabrelite_binfo.nb_cpus = smp_cpus; + sabrelite_binfo.secure_boot = true; + sabrelite_binfo.write_secondary_boot = sabrelite_write_secondary; + sabrelite_binfo.secondary_cpu_reset_hook = sabrelite_reset_secondary; + + if (!qtest_enabled()) { + arm_load_kernel(&s->soc.cpu[0], &sabrelite_binfo); + } +} + +static void sabrelite_machine_init(MachineClass *mc) +{ + mc->desc = "Freescale i.MX6 Quad SABRE Lite Board (Cortex A9)"; + mc->init = sabrelite_init; + mc->max_cpus = FSL_IMX6_NUM_CPUS; +} + +DEFINE_MACHINE("sabrelite", sabrelite_machine_init) diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index bf61d63b58..ba40f8302b 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -164,9 +164,10 @@ static void sl_flash_register(PXA2xxState *cpu, int size) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, FLASH_BASE); } -static int sl_nand_init(SysBusDevice *dev) +static void sl_nand_init(Object *obj) { - SLNANDState *s = SL_NAND(dev); + SLNANDState *s = SL_NAND(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); DriveInfo *nand; s->ctl = 0; @@ -175,10 +176,8 @@ static int sl_nand_init(SysBusDevice *dev) s->nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, s->manf_id, s->chip_id); - memory_region_init_io(&s->iomem, OBJECT(s), &sl_ops, s, "sl", 0x40); + memory_region_init_io(&s->iomem, obj, &sl_ops, s, "sl", 0x40); sysbus_init_mmio(dev, &s->iomem); - - return 0; } /* Spitz Keyboard */ @@ -501,10 +500,10 @@ static void spitz_keyboard_register(PXA2xxState *cpu) qemu_add_kbd_event_handler(spitz_keyboard_handler, s); } -static int spitz_keyboard_init(SysBusDevice *sbd) +static void spitz_keyboard_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - SpitzKeyboardState *s = SPITZ_KEYBOARD(dev); + DeviceState *dev = DEVICE(obj); + SpitzKeyboardState *s = SPITZ_KEYBOARD(obj); int i, j; for (i = 0; i < 0x80; i ++) @@ -519,8 +518,6 @@ static int spitz_keyboard_init(SysBusDevice *sbd) s->kbdtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, spitz_keyboard_tick, s); qdev_init_gpio_in(dev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM); qdev_init_gpio_out(dev, s->sense, SPITZ_KEY_SENSE_NUM); - - return 0; } /* LCD backlight controller */ @@ -1065,9 +1062,7 @@ static Property sl_nand_properties[] = { static void sl_nand_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = sl_nand_init; dc->vmsd = &vmstate_sl_nand_info; dc->props = sl_nand_properties; /* Reason: init() method uses drive_get() */ @@ -1078,6 +1073,7 @@ static const TypeInfo sl_nand_info = { .name = TYPE_SL_NAND, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLNANDState), + .instance_init = sl_nand_init, .class_init = sl_nand_class_init, }; @@ -1097,9 +1093,7 @@ static VMStateDescription vmstate_spitz_kbd = { static void spitz_keyboard_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = spitz_keyboard_init; dc->vmsd = &vmstate_spitz_kbd; } @@ -1107,6 +1101,7 @@ static const TypeInfo spitz_keyboard_info = { .name = TYPE_SPITZ_KEYBOARD, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SpitzKeyboardState), + .instance_init = spitz_keyboard_init, .class_init = spitz_keyboard_class_init, }; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index c1766f856a..f90b9fd190 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -316,23 +316,22 @@ static const VMStateDescription vmstate_stellaris_gptm = { } }; -static int stellaris_gptm_init(SysBusDevice *sbd) +static void stellaris_gptm_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - gptm_state *s = STELLARIS_GPTM(dev); + DeviceState *dev = DEVICE(obj); + gptm_state *s = STELLARIS_GPTM(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_out(dev, &s->trigger, 1); - memory_region_init_io(&s->iomem, OBJECT(s), &gptm_ops, s, + memory_region_init_io(&s->iomem, obj, &gptm_ops, s, "gptm", 0x1000); sysbus_init_mmio(sbd, &s->iomem); s->opaque[0] = s->opaque[1] = s; s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]); s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]); - vmstate_register(dev, -1, &vmstate_stellaris_gptm, s); - return 0; } @@ -873,23 +872,22 @@ static const VMStateDescription vmstate_stellaris_i2c = { } }; -static int stellaris_i2c_init(SysBusDevice *sbd) +static void stellaris_i2c_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - stellaris_i2c_state *s = STELLARIS_I2C(dev); + DeviceState *dev = DEVICE(obj); + stellaris_i2c_state *s = STELLARIS_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); I2CBus *bus; sysbus_init_irq(sbd, &s->irq); bus = i2c_init_bus(dev, "i2c"); s->bus = bus; - memory_region_init_io(&s->iomem, OBJECT(s), &stellaris_i2c_ops, s, + memory_region_init_io(&s->iomem, obj, &stellaris_i2c_ops, s, "i2c", 0x1000); sysbus_init_mmio(sbd, &s->iomem); /* ??? For now we only implement the master interface. */ stellaris_i2c_reset(s); - vmstate_register(dev, -1, &vmstate_stellaris_i2c, s); - return 0; } /* Analogue to Digital Converter. This is only partially implemented, @@ -1160,23 +1158,22 @@ static const VMStateDescription vmstate_stellaris_adc = { } }; -static int stellaris_adc_init(SysBusDevice *sbd) +static void stellaris_adc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - stellaris_adc_state *s = STELLARIS_ADC(dev); + DeviceState *dev = DEVICE(obj); + stellaris_adc_state *s = STELLARIS_ADC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); int n; for (n = 0; n < 4; n++) { sysbus_init_irq(sbd, &s->irq[n]); } - memory_region_init_io(&s->iomem, OBJECT(s), &stellaris_adc_ops, s, + memory_region_init_io(&s->iomem, obj, &stellaris_adc_ops, s, "adc", 0x1000); sysbus_init_mmio(sbd, &s->iomem); stellaris_adc_reset(s); qdev_init_gpio_in(dev, stellaris_adc_trigger, 1); - vmstate_register(dev, -1, &vmstate_stellaris_adc, s); - return 0; } static @@ -1425,43 +1422,46 @@ type_init(stellaris_machine_init) static void stellaris_i2c_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = stellaris_i2c_init; + dc->vmsd = &vmstate_stellaris_i2c; } static const TypeInfo stellaris_i2c_info = { .name = TYPE_STELLARIS_I2C, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(stellaris_i2c_state), + .instance_init = stellaris_i2c_init, .class_init = stellaris_i2c_class_init, }; static void stellaris_gptm_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = stellaris_gptm_init; + dc->vmsd = &vmstate_stellaris_gptm; } static const TypeInfo stellaris_gptm_info = { .name = TYPE_STELLARIS_GPTM, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(gptm_state), + .instance_init = stellaris_gptm_init, .class_init = stellaris_gptm_class_init, }; static void stellaris_adc_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = stellaris_adc_init; + dc->vmsd = &vmstate_stellaris_adc; } static const TypeInfo stellaris_adc_info = { .name = TYPE_STELLARIS_ADC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(stellaris_adc_state), + .instance_init = stellaris_adc_init, .class_init = stellaris_adc_class_init, }; diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 1eeb1ab391..3bc8a98150 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -179,19 +179,18 @@ static const MemoryRegionOps strongarm_pic_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int strongarm_pic_initfn(SysBusDevice *sbd) +static void strongarm_pic_initfn(Object *obj) { - DeviceState *dev = DEVICE(sbd); - StrongARMPICState *s = STRONGARM_PIC(dev); + DeviceState *dev = DEVICE(obj); + StrongARMPICState *s = STRONGARM_PIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, strongarm_pic_set_irq, SA_PIC_SRCS); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_pic_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_pic_ops, s, "pic", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->fiq); - - return 0; } static int strongarm_pic_post_load(void *opaque, int version_id) @@ -217,9 +216,7 @@ static VMStateDescription vmstate_strongarm_pic_regs = { static void strongarm_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_pic_initfn; dc->desc = "StrongARM PIC"; dc->vmsd = &vmstate_strongarm_pic_regs; } @@ -228,6 +225,7 @@ static const TypeInfo strongarm_pic_info = { .name = TYPE_STRONGARM_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMPICState), + .instance_init = strongarm_pic_initfn, .class_init = strongarm_pic_class_init, }; @@ -381,9 +379,10 @@ static const MemoryRegionOps strongarm_rtc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int strongarm_rtc_init(SysBusDevice *dev) +static void strongarm_rtc_init(Object *obj) { - StrongARMRTCState *s = STRONGARM_RTC(dev); + StrongARMRTCState *s = STRONGARM_RTC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); struct tm tm; s->rttr = 0x0; @@ -400,11 +399,9 @@ static int strongarm_rtc_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->rtc_irq); sysbus_init_irq(dev, &s->rtc_hz_irq); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_rtc_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_rtc_ops, s, "rtc", 0x10000); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void strongarm_rtc_pre_save(void *opaque) @@ -443,9 +440,7 @@ static const VMStateDescription vmstate_strongarm_rtc_regs = { static void strongarm_rtc_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_rtc_init; dc->desc = "StrongARM RTC Controller"; dc->vmsd = &vmstate_strongarm_rtc_regs; } @@ -454,6 +449,7 @@ static const TypeInfo strongarm_rtc_sysbus_info = { .name = TYPE_STRONGARM_RTC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMRTCState), + .instance_init = strongarm_rtc_init, .class_init = strongarm_rtc_sysbus_class_init, }; @@ -646,16 +642,17 @@ static DeviceState *strongarm_gpio_init(hwaddr base, return dev; } -static int strongarm_gpio_initfn(SysBusDevice *sbd) +static void strongarm_gpio_initfn(Object *obj) { - DeviceState *dev = DEVICE(sbd); - StrongARMGPIOInfo *s = STRONGARM_GPIO(dev); + DeviceState *dev = DEVICE(obj); + StrongARMGPIOInfo *s = STRONGARM_GPIO(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); int i; qdev_init_gpio_in(dev, strongarm_gpio_set, 28); qdev_init_gpio_out(dev, s->handler, 28); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_gpio_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_gpio_ops, s, "gpio", 0x1000); sysbus_init_mmio(sbd, &s->iomem); @@ -663,8 +660,6 @@ static int strongarm_gpio_initfn(SysBusDevice *sbd) sysbus_init_irq(sbd, &s->irqs[i]); } sysbus_init_irq(sbd, &s->irqX); - - return 0; } static const VMStateDescription vmstate_strongarm_gpio_regs = { @@ -687,9 +682,7 @@ static const VMStateDescription vmstate_strongarm_gpio_regs = { static void strongarm_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_gpio_initfn; dc->desc = "StrongARM GPIO controller"; dc->vmsd = &vmstate_strongarm_gpio_regs; } @@ -698,6 +691,7 @@ static const TypeInfo strongarm_gpio_info = { .name = TYPE_STRONGARM_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMGPIOInfo), + .instance_init = strongarm_gpio_initfn, .class_init = strongarm_gpio_class_init, }; @@ -824,20 +818,19 @@ static const MemoryRegionOps strongarm_ppc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int strongarm_ppc_init(SysBusDevice *sbd) +static void strongarm_ppc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - StrongARMPPCInfo *s = STRONGARM_PPC(dev); + DeviceState *dev = DEVICE(obj); + StrongARMPPCInfo *s = STRONGARM_PPC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, strongarm_ppc_set, 22); qdev_init_gpio_out(dev, s->handler, 22); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_ppc_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_ppc_ops, s, "ppc", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - - return 0; } static const VMStateDescription vmstate_strongarm_ppc_regs = { @@ -859,9 +852,7 @@ static const VMStateDescription vmstate_strongarm_ppc_regs = { static void strongarm_ppc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_ppc_init; dc->desc = "StrongARM PPC controller"; dc->vmsd = &vmstate_strongarm_ppc_regs; } @@ -870,6 +861,7 @@ static const TypeInfo strongarm_ppc_info = { .name = TYPE_STRONGARM_PPC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMPPCInfo), + .instance_init = strongarm_ppc_init, .class_init = strongarm_ppc_class_init, }; @@ -1231,11 +1223,12 @@ static const MemoryRegionOps strongarm_uart_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int strongarm_uart_init(SysBusDevice *dev) +static void strongarm_uart_init(Object *obj) { - StrongARMUARTState *s = STRONGARM_UART(dev); + StrongARMUARTState *s = STRONGARM_UART(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_uart_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_uart_ops, s, "uart", 0x10000); sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); @@ -1250,8 +1243,6 @@ static int strongarm_uart_init(SysBusDevice *dev) strongarm_uart_event, s); } - - return 0; } static void strongarm_uart_reset(DeviceState *dev) @@ -1321,9 +1312,7 @@ static Property strongarm_uart_properties[] = { static void strongarm_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_uart_init; dc->desc = "StrongARM UART controller"; dc->reset = strongarm_uart_reset; dc->vmsd = &vmstate_strongarm_uart_regs; @@ -1334,6 +1323,7 @@ static const TypeInfo strongarm_uart_info = { .name = TYPE_STRONGARM_UART, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMUARTState), + .instance_init = strongarm_uart_init, .class_init = strongarm_uart_class_init, }; diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index e5a80c2d2c..d079bc9e82 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -153,10 +153,11 @@ static const MemoryRegionOps vpb_sic_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int vpb_sic_init(SysBusDevice *sbd) +static void vpb_sic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - vpb_sic_state *s = VERSATILE_PB_SIC(dev); + DeviceState *dev = DEVICE(obj); + vpb_sic_state *s = VERSATILE_PB_SIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); int i; qdev_init_gpio_in(dev, vpb_sic_set_irq, 32); @@ -164,10 +165,9 @@ static int vpb_sic_init(SysBusDevice *sbd) sysbus_init_irq(sbd, &s->parent[i]); } s->irq = 31; - memory_region_init_io(&s->iomem, OBJECT(s), &vpb_sic_ops, s, + memory_region_init_io(&s->iomem, obj, &vpb_sic_ops, s, "vpb-sic", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - return 0; } /* Board init. */ @@ -427,9 +427,7 @@ type_init(versatile_machine_init) static void vpb_sic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = vpb_sic_init; dc->vmsd = &vmstate_vpb_sic; } @@ -437,6 +435,7 @@ static const TypeInfo vpb_sic_info = { .name = TYPE_VERSATILE_PB_SIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(vpb_sic_state), + .instance_init = vpb_sic_init, .class_init = vpb_sic_class_init, }; diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index f51fe396ce..26a7bac48f 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -43,6 +43,7 @@ #include "hw/acpi/aml-build.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci.h" +#include "sysemu/numa.h" #define ARM_SPI_BASE 32 #define ACPI_POWER_BUTTON_DEVICE "PWRB" @@ -414,6 +415,52 @@ build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) } static void +build_srat(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) +{ + AcpiSystemResourceAffinityTable *srat; + AcpiSratProcessorGiccAffinity *core; + AcpiSratMemoryAffinity *numamem; + int i, j, srat_start; + uint64_t mem_base; + uint32_t *cpu_node = g_malloc0(guest_info->smp_cpus * sizeof(uint32_t)); + + for (i = 0; i < guest_info->smp_cpus; i++) { + for (j = 0; j < nb_numa_nodes; j++) { + if (test_bit(i, numa_info[j].node_cpu)) { + cpu_node[i] = j; + break; + } + } + } + + srat_start = table_data->len; + srat = acpi_data_push(table_data, sizeof(*srat)); + srat->reserved1 = cpu_to_le32(1); + + for (i = 0; i < guest_info->smp_cpus; ++i) { + core = acpi_data_push(table_data, sizeof(*core)); + core->type = ACPI_SRAT_PROCESSOR_GICC; + core->length = sizeof(*core); + core->proximity = cpu_to_le32(cpu_node[i]); + core->acpi_processor_uid = cpu_to_le32(i); + core->flags = cpu_to_le32(1); + } + g_free(cpu_node); + + mem_base = guest_info->memmap[VIRT_MEM].base; + for (i = 0; i < nb_numa_nodes; ++i) { + numamem = acpi_data_push(table_data, sizeof(*numamem)); + build_srat_memory(numamem, mem_base, numa_info[i].node_mem, i, + MEM_AFFINITY_ENABLED); + mem_base += numa_info[i].node_mem; + } + + build_header(linker, table_data, + (void *)(table_data->data + srat_start), "SRAT", + table_data->len - srat_start, 3, NULL, NULL); +} + +static void build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) { AcpiTableMcfg *mcfg; @@ -638,6 +685,11 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_spcr(tables_blob, tables->linker, guest_info); + if (nb_numa_nodes > 0) { + acpi_add_table(table_offsets, tables_blob); + build_srat(tables_blob, tables->linker, guest_info); + } + /* RSDT is pointed to by RSDP */ rsdt = tables_blob->len; build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 56d35c7716..fe6b11d24e 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -38,6 +38,7 @@ #include "net/net.h" #include "sysemu/block-backend.h" #include "sysemu/device_tree.h" +#include "sysemu/numa.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "hw/boards.h" @@ -329,6 +330,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) { int cpu; int addr_cells = 1; + unsigned int i; /* * From Documentation/devicetree/bindings/arm/cpus.txt @@ -378,6 +380,12 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) armcpu->mp_affinity); } + for (i = 0; i < nb_numa_nodes; i++) { + if (test_bit(cpu, numa_info[i].node_cpu)) { + qemu_fdt_setprop_cell(vbi->fdt, nodename, "numa-node-id", i); + } + } + g_free(nodename); } } diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c index c231960d96..cbf07d14d9 100644 --- a/hw/display/blizzard.c +++ b/hw/display/blizzard.c @@ -925,16 +925,83 @@ static void blizzard_update_display(void *opaque) s->my[1] = 0; } -#define DEPTH 8 -#include "blizzard_template.h" -#define DEPTH 15 -#include "blizzard_template.h" -#define DEPTH 16 -#include "blizzard_template.h" -#define DEPTH 24 -#include "blizzard_template.h" -#define DEPTH 32 -#include "blizzard_template.h" +static void blizzard_draw_line16_32(uint32_t *dest, + const uint16_t *src, unsigned int width) +{ + uint16_t data; + unsigned int r, g, b; + const uint16_t *end = (const void *) src + width; + while (src < end) { + data = *src ++; + b = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x3f) << 2; + data >>= 6; + r = (data & 0x1f) << 3; + data >>= 5; + *dest++ = rgb_to_pixel32(r, g, b); + } +} + +static void blizzard_draw_line24mode1_32(uint32_t *dest, + const uint8_t *src, unsigned int width) +{ + /* TODO: check if SDL 24-bit planes are not in the same format and + * if so, use memcpy */ + unsigned int r[2], g[2], b[2]; + const uint8_t *end = src + width; + while (src < end) { + g[0] = *src ++; + r[0] = *src ++; + r[1] = *src ++; + b[0] = *src ++; + *dest++ = rgb_to_pixel32(r[0], g[0], b[0]); + b[1] = *src ++; + g[1] = *src ++; + *dest++ = rgb_to_pixel32(r[1], g[1], b[1]); + } +} + +static void blizzard_draw_line24mode2_32(uint32_t *dest, + const uint8_t *src, unsigned int width) +{ + unsigned int r, g, b; + const uint8_t *end = src + width; + while (src < end) { + r = *src ++; + src ++; + b = *src ++; + g = *src ++; + *dest++ = rgb_to_pixel32(r, g, b); + } +} + +/* No rotation */ +static blizzard_fn_t blizzard_draw_fn_32[0x10] = { + NULL, + /* RGB 5:6:5*/ + (blizzard_fn_t) blizzard_draw_line16_32, + /* RGB 6:6:6 mode 1 */ + (blizzard_fn_t) blizzard_draw_line24mode1_32, + /* RGB 8:8:8 mode 1 */ + (blizzard_fn_t) blizzard_draw_line24mode1_32, + NULL, NULL, + /* RGB 6:6:6 mode 2 */ + (blizzard_fn_t) blizzard_draw_line24mode2_32, + /* RGB 8:8:8 mode 2 */ + (blizzard_fn_t) blizzard_draw_line24mode2_32, + /* YUV 4:2:2 */ + NULL, + /* YUV 4:2:0 */ + NULL, + NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* 90deg, 180deg and 270deg rotation */ +static blizzard_fn_t blizzard_draw_fn_r_32[0x10] = { + /* TODO */ + [0 ... 0xf] = NULL, +}; static const GraphicHwOps blizzard_ops = { .invalidate = blizzard_invalidate_display, @@ -951,35 +1018,10 @@ void *s1d13745_init(qemu_irq gpio_int) s->con = graphic_console_init(NULL, 0, &blizzard_ops, s); surface = qemu_console_surface(s->con); - switch (surface_bits_per_pixel(surface)) { - case 0: - s->line_fn_tab[0] = s->line_fn_tab[1] = - g_malloc0(sizeof(blizzard_fn_t) * 0x10); - break; - case 8: - s->line_fn_tab[0] = blizzard_draw_fn_8; - s->line_fn_tab[1] = blizzard_draw_fn_r_8; - break; - case 15: - s->line_fn_tab[0] = blizzard_draw_fn_15; - s->line_fn_tab[1] = blizzard_draw_fn_r_15; - break; - case 16: - s->line_fn_tab[0] = blizzard_draw_fn_16; - s->line_fn_tab[1] = blizzard_draw_fn_r_16; - break; - case 24: - s->line_fn_tab[0] = blizzard_draw_fn_24; - s->line_fn_tab[1] = blizzard_draw_fn_r_24; - break; - case 32: - s->line_fn_tab[0] = blizzard_draw_fn_32; - s->line_fn_tab[1] = blizzard_draw_fn_r_32; - break; - default: - fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); - exit(1); - } + assert(surface_bits_per_pixel(surface) == 32); + + s->line_fn_tab[0] = blizzard_draw_fn_32; + s->line_fn_tab[1] = blizzard_draw_fn_r_32; blizzard_reset(s); diff --git a/hw/display/blizzard_template.h b/hw/display/blizzard_template.h deleted file mode 100644 index b7ef27c808..0000000000 --- a/hw/display/blizzard_template.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * QEMU Epson S1D13744/S1D13745 templates - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski <andrew@openedhand.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 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/>. - */ - -#define SKIP_PIXEL(to) (to += deststep) -#if DEPTH == 8 -# define PIXEL_TYPE uint8_t -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -# define COPY_PIXEL1(to, from) (*to++ = from) -#elif DEPTH == 15 || DEPTH == 16 -# define PIXEL_TYPE uint16_t -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -# define COPY_PIXEL1(to, from) (*to++ = from) -#elif DEPTH == 24 -# define PIXEL_TYPE uint8_t -# define COPY_PIXEL(to, from) \ - do { \ - to[0] = from; \ - to[1] = (from) >> 8; \ - to[2] = (from) >> 16; \ - SKIP_PIXEL(to); \ - } while (0) - -# define COPY_PIXEL1(to, from) \ - do { \ - *to++ = from; \ - *to++ = (from) >> 8; \ - *to++ = (from) >> 16; \ - } while (0) -#elif DEPTH == 32 -# define PIXEL_TYPE uint32_t -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -# define COPY_PIXEL1(to, from) (*to++ = from) -#else -# error unknown bit depth -#endif - -#ifdef HOST_WORDS_BIGENDIAN -# define SWAP_WORDS 1 -#endif - -static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest, - const uint16_t *src, unsigned int width) -{ -#if !defined(SWAP_WORDS) && DEPTH == 16 - memcpy(dest, src, width); -#else - uint16_t data; - unsigned int r, g, b; - const uint16_t *end = (const void *) src + width; - while (src < end) { - data = *src ++; - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x1f) << 3; - data >>= 5; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b)); - } -#endif -} - -static void glue(blizzard_draw_line24mode1_, DEPTH)(PIXEL_TYPE *dest, - const uint8_t *src, unsigned int width) -{ - /* TODO: check if SDL 24-bit planes are not in the same format and - * if so, use memcpy */ - unsigned int r[2], g[2], b[2]; - const uint8_t *end = src + width; - while (src < end) { - g[0] = *src ++; - r[0] = *src ++; - r[1] = *src ++; - b[0] = *src ++; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[0], g[0], b[0])); - b[1] = *src ++; - g[1] = *src ++; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[1], g[1], b[1])); - } -} - -static void glue(blizzard_draw_line24mode2_, DEPTH)(PIXEL_TYPE *dest, - const uint8_t *src, unsigned int width) -{ - unsigned int r, g, b; - const uint8_t *end = src + width; - while (src < end) { - r = *src ++; - src ++; - b = *src ++; - g = *src ++; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b)); - } -} - -/* No rotation */ -static blizzard_fn_t glue(blizzard_draw_fn_, DEPTH)[0x10] = { - NULL, - /* RGB 5:6:5*/ - (blizzard_fn_t) glue(blizzard_draw_line16_, DEPTH), - /* RGB 6:6:6 mode 1 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH), - /* RGB 8:8:8 mode 1 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH), - NULL, NULL, - /* RGB 6:6:6 mode 2 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH), - /* RGB 8:8:8 mode 2 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH), - /* YUV 4:2:2 */ - NULL, - /* YUV 4:2:0 */ - NULL, - NULL, NULL, NULL, NULL, NULL, NULL, -}; - -/* 90deg, 180deg and 270deg rotation */ -static blizzard_fn_t glue(blizzard_draw_fn_r_, DEPTH)[0x10] = { - /* TODO */ - [0 ... 0xf] = NULL, -}; - -#undef DEPTH -#undef SKIP_PIXEL -#undef COPY_PIXEL -#undef COPY_PIXEL1 -#undef PIXEL_TYPE - -#undef SWAP_WORDS diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 728eb214a4..e5be713406 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1909,9 +1909,10 @@ static const GraphicHwOps exynos4210_fimd_ops = { .gfx_update = exynos4210_fimd_update, }; -static int exynos4210_fimd_init(SysBusDevice *dev) +static void exynos4210_fimd_init(Object *obj) { - Exynos4210fimdState *s = EXYNOS4210_FIMD(dev); + Exynos4210fimdState *s = EXYNOS4210_FIMD(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); s->ifb = NULL; @@ -1919,28 +1920,32 @@ static int exynos4210_fimd_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->irq[1]); sysbus_init_irq(dev, &s->irq[2]); - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_fimd_mmio_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_fimd_mmio_ops, s, "exynos4210.fimd", FIMD_REGS_SIZE); sysbus_init_mmio(dev, &s->iomem); - s->console = graphic_console_init(DEVICE(dev), 0, &exynos4210_fimd_ops, s); +} - return 0; +static void exynos4210_fimd_realize(DeviceState *dev, Error **errp) +{ + Exynos4210fimdState *s = EXYNOS4210_FIMD(dev); + + s->console = graphic_console_init(dev, 0, &exynos4210_fimd_ops, s); } static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); dc->vmsd = &exynos4210_fimd_vmstate; dc->reset = exynos4210_fimd_reset; - k->init = exynos4210_fimd_init; + dc->realize = exynos4210_fimd_realize; } static const TypeInfo exynos4210_fimd_info = { .name = TYPE_EXYNOS4210_FIMD, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210fimdState), + .instance_init = exynos4210_fimd_init, .class_init = exynos4210_fimd_class_init, }; diff --git a/hw/display/omap_lcd_template.h b/hw/display/omap_lcd_template.h index f0ce71fd66..1025ff3825 100644 --- a/hw/display/omap_lcd_template.h +++ b/hw/display/omap_lcd_template.h @@ -27,13 +27,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if DEPTH == 8 -# define BPP 1 -# define PIXEL_TYPE uint8_t -#elif DEPTH == 15 || DEPTH == 16 -# define BPP 2 -# define PIXEL_TYPE uint16_t -#elif DEPTH == 32 +#if DEPTH == 32 # define BPP 4 # define PIXEL_TYPE uint32_t #else @@ -152,7 +146,7 @@ static void glue(draw_line12_, DEPTH)(void *opaque, static void glue(draw_line16_, DEPTH)(void *opaque, uint8_t *d, const uint8_t *s, int width, int deststep) { -#if DEPTH == 16 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) +#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) memcpy(d, s, width * 2); #else uint16_t v; diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c index ce1058bf85..07a5effe04 100644 --- a/hw/display/omap_lcdc.c +++ b/hw/display/omap_lcdc.c @@ -71,47 +71,9 @@ static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) #define draw_line_func drawfn -#define DEPTH 8 -#include "omap_lcd_template.h" -#define DEPTH 15 -#include "omap_lcd_template.h" -#define DEPTH 16 -#include "omap_lcd_template.h" #define DEPTH 32 #include "omap_lcd_template.h" -static draw_line_func draw_line_table2[33] = { - [0 ... 32] = NULL, - [8] = draw_line2_8, - [15] = draw_line2_15, - [16] = draw_line2_16, - [32] = draw_line2_32, -}, draw_line_table4[33] = { - [0 ... 32] = NULL, - [8] = draw_line4_8, - [15] = draw_line4_15, - [16] = draw_line4_16, - [32] = draw_line4_32, -}, draw_line_table8[33] = { - [0 ... 32] = NULL, - [8] = draw_line8_8, - [15] = draw_line8_15, - [16] = draw_line8_16, - [32] = draw_line8_32, -}, draw_line_table12[33] = { - [0 ... 32] = NULL, - [8] = draw_line12_8, - [15] = draw_line12_15, - [16] = draw_line12_16, - [32] = draw_line12_32, -}, draw_line_table16[33] = { - [0 ... 32] = NULL, - [8] = draw_line16_8, - [15] = draw_line16_15, - [16] = draw_line16_16, - [32] = draw_line16_32, -}; - static void omap_update_display(void *opaque) { struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; @@ -143,25 +105,25 @@ static void omap_update_display(void *opaque) /* Colour depth */ switch ((omap_lcd->palette[0] >> 12) & 7) { case 1: - draw_line = draw_line_table2[surface_bits_per_pixel(surface)]; + draw_line = draw_line2_32; bpp = 2; break; case 2: - draw_line = draw_line_table4[surface_bits_per_pixel(surface)]; + draw_line = draw_line4_32; bpp = 4; break; case 3: - draw_line = draw_line_table8[surface_bits_per_pixel(surface)]; + draw_line = draw_line8_32; bpp = 8; break; case 4 ... 7: if (!omap_lcd->tft) - draw_line = draw_line_table12[surface_bits_per_pixel(surface)]; + draw_line = draw_line12_32; else - draw_line = draw_line_table16[surface_bits_per_pixel(surface)]; + draw_line = draw_line16_32; bpp = 16; break; diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 64770034ff..279f0d7d05 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2427,26 +2427,6 @@ build_tpm2(GArray *table_data, GArray *linker) (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); } -typedef enum { - MEM_AFFINITY_NOFLAGS = 0, - MEM_AFFINITY_ENABLED = (1 << 0), - MEM_AFFINITY_HOTPLUGGABLE = (1 << 1), - MEM_AFFINITY_NON_VOLATILE = (1 << 2), -} MemoryAffinityFlags; - -static void -acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, - uint64_t len, int node, MemoryAffinityFlags flags) -{ - numamem->type = ACPI_SRAT_MEMORY; - numamem->length = sizeof(*numamem); - memset(numamem->proximity, 0, 4); - numamem->proximity[0] = node; - numamem->flags = cpu_to_le32(flags); - numamem->base_addr = cpu_to_le64(base); - numamem->range_length = cpu_to_le64(len); -} - static void build_srat(GArray *table_data, GArray *linker, MachineState *machine) { @@ -2474,7 +2454,7 @@ build_srat(GArray *table_data, GArray *linker, MachineState *machine) int apic_id = apic_ids->cpus[i].arch_id; core = acpi_data_push(table_data, sizeof *core); - core->type = ACPI_SRAT_PROCESSOR; + core->type = ACPI_SRAT_PROCESSOR_APIC; core->length = sizeof(*core); core->local_apic_id = apic_id; curnode = pcms->node_cpu[apic_id]; @@ -2492,7 +2472,7 @@ build_srat(GArray *table_data, GArray *linker, MachineState *machine) numa_start = table_data->len; numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, 0, 640*1024, 0, MEM_AFFINITY_ENABLED); + build_srat_memory(numamem, 0, 640 * 1024, 0, MEM_AFFINITY_ENABLED); next_base = 1024 * 1024; for (i = 1; i < pcms->numa_nodes + 1; ++i) { mem_base = next_base; @@ -2508,21 +2488,21 @@ build_srat(GArray *table_data, GArray *linker, MachineState *machine) mem_len -= next_base - pcms->below_4g_mem_size; if (mem_len > 0) { numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, - MEM_AFFINITY_ENABLED); + build_srat_memory(numamem, mem_base, mem_len, i - 1, + MEM_AFFINITY_ENABLED); } mem_base = 1ULL << 32; mem_len = next_base - pcms->below_4g_mem_size; next_base += (1ULL << 32) - pcms->below_4g_mem_size; } numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, - MEM_AFFINITY_ENABLED); + build_srat_memory(numamem, mem_base, mem_len, i - 1, + MEM_AFFINITY_ENABLED); } slots = (table_data->len - numa_start) / sizeof *numamem; for (; slots < pcms->numa_nodes + 2; slots++) { numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); + build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); } /* @@ -2532,10 +2512,9 @@ build_srat(GArray *table_data, GArray *linker, MachineState *machine) */ if (hotplugabble_address_space_size) { numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, pcms->hotplug_memory.base, - hotplugabble_address_space_size, 0, - MEM_AFFINITY_HOTPLUGGABLE | - MEM_AFFINITY_ENABLED); + build_srat_memory(numamem, pcms->hotplug_memory.base, + hotplugabble_address_space_size, 0, + MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } build_header(linker, table_data, diff --git a/hw/intc/etraxfs_pic.c b/hw/intc/etraxfs_pic.c index 48f9477065..64a6f4b4ba 100644 --- a/hw/intc/etraxfs_pic.c +++ b/hw/intc/etraxfs_pic.c @@ -146,19 +146,19 @@ static void irq_handler(void *opaque, int irq, int level) pic_update(fs); } -static int etraxfs_pic_init(SysBusDevice *sbd) +static void etraxfs_pic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - struct etrax_pic *s = ETRAX_FS_PIC(dev); + DeviceState *dev = DEVICE(obj); + struct etrax_pic *s = ETRAX_FS_PIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, irq_handler, 32); sysbus_init_irq(sbd, &s->parent_irq); sysbus_init_irq(sbd, &s->parent_nmi); - memory_region_init_io(&s->mmio, OBJECT(s), &pic_ops, s, + memory_region_init_io(&s->mmio, obj, &pic_ops, s, "etraxfs-pic", R_MAX * 4); sysbus_init_mmio(sbd, &s->mmio); - return 0; } static Property etraxfs_pic_properties[] = { @@ -169,9 +169,7 @@ static Property etraxfs_pic_properties[] = { static void etraxfs_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = etraxfs_pic_init; dc->props = etraxfs_pic_properties; /* * Note: pointer property "interrupt_vector" may remain null, thus @@ -183,6 +181,7 @@ static const TypeInfo etraxfs_pic_info = { .name = TYPE_ETRAX_FS_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct etrax_pic), + .instance_init = etraxfs_pic_init, .class_init = etraxfs_pic_class_init, }; diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index dc0c903266..f19a7062be 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -406,10 +406,11 @@ static const MemoryRegionOps exynos4210_combiner_ops = { /* * Internal Combiner initialization. */ -static int exynos4210_combiner_init(SysBusDevice *sbd) +static void exynos4210_combiner_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - Exynos4210CombinerState *s = EXYNOS4210_COMBINER(dev); + DeviceState *dev = DEVICE(obj); + Exynos4210CombinerState *s = EXYNOS4210_COMBINER(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); unsigned int i; /* Allocate general purpose input signals and connect a handler to each of @@ -421,11 +422,9 @@ static int exynos4210_combiner_init(SysBusDevice *sbd) sysbus_init_irq(sbd, &s->output_irq[i]); } - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_combiner_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_combiner_ops, s, "exynos4210-combiner", IIC_REGION_SIZE); sysbus_init_mmio(sbd, &s->iomem); - - return 0; } static Property exynos4210_combiner_properties[] = { @@ -436,9 +435,7 @@ static Property exynos4210_combiner_properties[] = { static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_combiner_init; dc->reset = exynos4210_combiner_reset; dc->props = exynos4210_combiner_properties; dc->vmsd = &vmstate_exynos4210_combiner; @@ -448,6 +445,7 @@ static const TypeInfo exynos4210_combiner_info = { .name = TYPE_EXYNOS4210_COMBINER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210CombinerState), + .instance_init = exynos4210_combiner_init, .class_init = exynos4210_combiner_class_init, }; diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index 4f7e89f7b8..fd7a8f3058 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -281,10 +281,11 @@ static void exynos4210_gic_set_irq(void *opaque, int irq, int level) qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); } -static int exynos4210_gic_init(SysBusDevice *sbd) +static void exynos4210_gic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - Exynos4210GicState *s = EXYNOS4210_GIC(dev); + DeviceState *dev = DEVICE(obj); + Exynos4210GicState *s = EXYNOS4210_GIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); uint32_t i; const char cpu_prefix[] = "exynos4210-gic-alias_cpu"; const char dist_prefix[] = "exynos4210-gic-alias_dist"; @@ -305,15 +306,15 @@ static int exynos4210_gic_init(SysBusDevice *sbd) qdev_init_gpio_in(dev, exynos4210_gic_set_irq, EXYNOS4210_GIC_NIRQ - 32); - memory_region_init(&s->cpu_container, OBJECT(s), "exynos4210-cpu-container", + memory_region_init(&s->cpu_container, obj, "exynos4210-cpu-container", EXYNOS4210_EXT_GIC_CPU_REGION_SIZE); - memory_region_init(&s->dist_container, OBJECT(s), "exynos4210-dist-container", + memory_region_init(&s->dist_container, obj, "exynos4210-dist-container", EXYNOS4210_EXT_GIC_DIST_REGION_SIZE); for (i = 0; i < s->num_cpu; i++) { /* Map CPU interface per SMP Core */ sprintf(cpu_alias_name, "%s%x", cpu_prefix, i); - memory_region_init_alias(&s->cpu_alias[i], OBJECT(s), + memory_region_init_alias(&s->cpu_alias[i], obj, cpu_alias_name, sysbus_mmio_get_region(busdev, 1), 0, @@ -323,7 +324,7 @@ static int exynos4210_gic_init(SysBusDevice *sbd) /* Map Distributor per SMP Core */ sprintf(dist_alias_name, "%s%x", dist_prefix, i); - memory_region_init_alias(&s->dist_alias[i], OBJECT(s), + memory_region_init_alias(&s->dist_alias[i], obj, dist_alias_name, sysbus_mmio_get_region(busdev, 0), 0, @@ -334,8 +335,6 @@ static int exynos4210_gic_init(SysBusDevice *sbd) sysbus_init_mmio(sbd, &s->cpu_container); sysbus_init_mmio(sbd, &s->dist_container); - - return 0; } static Property exynos4210_gic_properties[] = { @@ -346,9 +345,7 @@ static Property exynos4210_gic_properties[] = { static void exynos4210_gic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_gic_init; dc->props = exynos4210_gic_properties; } @@ -356,6 +353,7 @@ static const TypeInfo exynos4210_gic_info = { .name = TYPE_EXYNOS4210_GIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210GicState), + .instance_init = exynos4210_gic_init, .class_init = exynos4210_gic_class_init, }; @@ -430,9 +428,16 @@ static void exynos4210_irq_gate_reset(DeviceState *d) /* * IRQ Gate initialization. */ -static int exynos4210_irq_gate_init(SysBusDevice *sbd) +static void exynos4210_irq_gate_init(Object *obj) +{ + Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + sysbus_init_irq(sbd, &s->out); +} + +static void exynos4210_irq_gate_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(dev); /* Allocate general purpose input signals and connect a handler to each of @@ -440,27 +445,23 @@ static int exynos4210_irq_gate_init(SysBusDevice *sbd) qdev_init_gpio_in(dev, exynos4210_irq_gate_handler, s->n_in); s->level = g_malloc0(s->n_in * sizeof(*s->level)); - - sysbus_init_irq(sbd, &s->out); - - return 0; } static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_irq_gate_init; dc->reset = exynos4210_irq_gate_reset; dc->vmsd = &vmstate_exynos4210_irq_gate; dc->props = exynos4210_irq_gate_properties; + dc->realize = exynos4210_irq_gate_realize; } static const TypeInfo exynos4210_irq_gate_info = { .name = TYPE_EXYNOS4210_IRQ_GATE, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210IRQGateState), + .instance_init = exynos4210_irq_gate_init, .class_init = exynos4210_irq_gate_class_init, }; diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index f5ca8f752b..ac7e63f38b 100644 --- a/hw/intc/grlib_irqmp.c +++ b/hw/intc/grlib_irqmp.c @@ -31,6 +31,7 @@ #include "hw/sparc/grlib.h" #include "trace.h" +#include "qapi/error.h" #define IRQMP_MAX_CPU 16 #define IRQMP_REG_SIZE 256 /* Size of memory mapped registers */ @@ -323,23 +324,27 @@ static void grlib_irqmp_reset(DeviceState *d) irqmp->state->parent = irqmp; } -static int grlib_irqmp_init(SysBusDevice *dev) +static void grlib_irqmp_init(Object *obj) { - IRQMP *irqmp = GRLIB_IRQMP(dev); - - /* Check parameters */ - if (irqmp->set_pil_in == NULL) { - return -1; - } + IRQMP *irqmp = GRLIB_IRQMP(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&irqmp->iomem, OBJECT(dev), &grlib_irqmp_ops, irqmp, + memory_region_init_io(&irqmp->iomem, obj, &grlib_irqmp_ops, irqmp, "irqmp", IRQMP_REG_SIZE); irqmp->state = g_malloc0(sizeof *irqmp->state); sysbus_init_mmio(dev, &irqmp->iomem); +} - return 0; +static void grlib_irqmp_realize(DeviceState *dev, Error **errp) +{ + IRQMP *irqmp = GRLIB_IRQMP(dev); + + /* Check parameters */ + if (irqmp->set_pil_in == NULL) { + error_setg(errp, "set_pil_in cannot be NULL."); + } } static Property grlib_irqmp_properties[] = { @@ -351,19 +356,19 @@ static Property grlib_irqmp_properties[] = { static void grlib_irqmp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = grlib_irqmp_init; dc->reset = grlib_irqmp_reset; dc->props = grlib_irqmp_properties; /* Reason: pointer properties "set_pil_in", "set_pil_in_opaque" */ dc->cannot_instantiate_with_device_add_yet = true; + dc->realize = grlib_irqmp_realize; } static const TypeInfo grlib_irqmp_info = { .name = TYPE_GRLIB_IRQMP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IRQMP), + .instance_init = grlib_irqmp_init, .class_init = grlib_irqmp_class_init, }; diff --git a/hw/intc/imx_avic.c b/hw/intc/imx_avic.c index 7027655774..d21cb97451 100644 --- a/hw/intc/imx_avic.c +++ b/hw/intc/imx_avic.c @@ -321,28 +321,26 @@ static void imx_avic_reset(DeviceState *dev) memset(s->prio, 0, sizeof s->prio); } -static int imx_avic_init(SysBusDevice *sbd) +static void imx_avic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - IMXAVICState *s = IMX_AVIC(dev); + DeviceState *dev = DEVICE(obj); + IMXAVICState *s = IMX_AVIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &imx_avic_ops, s, + memory_region_init_io(&s->iomem, obj, &imx_avic_ops, s, TYPE_IMX_AVIC, 0x1000); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_in(dev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS); sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->fiq); - - return 0; } static void imx_avic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = imx_avic_init; + dc->vmsd = &vmstate_imx_avic; dc->reset = imx_avic_reset; dc->desc = "i.MX Advanced Vector Interrupt Controller"; @@ -352,6 +350,7 @@ static const TypeInfo imx_avic_info = { .name = TYPE_IMX_AVIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IMXAVICState), + .instance_init = imx_avic_init, .class_init = imx_avic_class_init, }; diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c index 336882510b..877be67971 100644 --- a/hw/intc/omap_intc.c +++ b/hw/intc/omap_intc.c @@ -22,6 +22,7 @@ #include "hw/arm/omap.h" #include "hw/sysbus.h" #include "qemu/error-report.h" +#include "qapi/error.h" /* Interrupt Handlers */ struct omap_intr_handler_bank_s { @@ -363,23 +364,28 @@ static void omap_inth_reset(DeviceState *dev) qemu_set_irq(s->parent_intr[1], 0); } -static int omap_intc_init(SysBusDevice *sbd) +static void omap_intc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - struct omap_intr_handler_s *s = OMAP_INTC(dev); + DeviceState *dev = DEVICE(obj); + struct omap_intr_handler_s *s = OMAP_INTC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - if (!s->iclk) { - error_report("omap-intc: clk not connected"); - return -1; - } s->nbanks = 1; sysbus_init_irq(sbd, &s->parent_intr[0]); sysbus_init_irq(sbd, &s->parent_intr[1]); qdev_init_gpio_in(dev, omap_set_intr, s->nbanks * 32); - memory_region_init_io(&s->mmio, OBJECT(s), &omap_inth_mem_ops, s, + memory_region_init_io(&s->mmio, obj, &omap_inth_mem_ops, s, "omap-intc", s->size); sysbus_init_mmio(sbd, &s->mmio); - return 0; +} + +static void omap_intc_realize(DeviceState *dev, Error **errp) +{ + struct omap_intr_handler_s *s = OMAP_INTC(dev); + + if (!s->iclk) { + error_setg(errp, "omap-intc: clk not connected"); + } } static Property omap_intc_properties[] = { @@ -391,18 +397,18 @@ static Property omap_intc_properties[] = { static void omap_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = omap_intc_init; dc->reset = omap_inth_reset; dc->props = omap_intc_properties; /* Reason: pointer property "clk" */ dc->cannot_instantiate_with_device_add_yet = true; + dc->realize = omap_intc_realize; } static const TypeInfo omap_intc_info = { .name = "omap-intc", .parent = TYPE_OMAP_INTC, + .instance_init = omap_intc_init, .class_init = omap_intc_class_init, }; @@ -605,28 +611,34 @@ static const MemoryRegionOps omap2_inth_mem_ops = { }, }; -static int omap2_intc_init(SysBusDevice *sbd) +static void omap2_intc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - struct omap_intr_handler_s *s = OMAP_INTC(dev); + DeviceState *dev = DEVICE(obj); + struct omap_intr_handler_s *s = OMAP_INTC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - if (!s->iclk) { - error_report("omap2-intc: iclk not connected"); - return -1; - } - if (!s->fclk) { - error_report("omap2-intc: fclk not connected"); - return -1; - } s->level_only = 1; s->nbanks = 3; sysbus_init_irq(sbd, &s->parent_intr[0]); sysbus_init_irq(sbd, &s->parent_intr[1]); qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32); - memory_region_init_io(&s->mmio, OBJECT(s), &omap2_inth_mem_ops, s, + memory_region_init_io(&s->mmio, obj, &omap2_inth_mem_ops, s, "omap2-intc", 0x1000); sysbus_init_mmio(sbd, &s->mmio); - return 0; +} + +static void omap2_intc_realize(DeviceState *dev, Error **errp) +{ + struct omap_intr_handler_s *s = OMAP_INTC(dev); + + if (!s->iclk) { + error_setg(errp, "omap2-intc: iclk not connected"); + return; + } + if (!s->fclk) { + error_setg(errp, "omap2-intc: fclk not connected"); + return; + } } static Property omap2_intc_properties[] = { @@ -640,18 +652,18 @@ static Property omap2_intc_properties[] = { static void omap2_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = omap2_intc_init; dc->reset = omap_inth_reset; dc->props = omap2_intc_properties; /* Reason: pointer property "iclk", "fclk" */ dc->cannot_instantiate_with_device_add_yet = true; + dc->realize = omap2_intc_realize; } static const TypeInfo omap2_intc_info = { .name = "omap2-intc", .parent = TYPE_OMAP_INTC, + .instance_init = omap2_intc_init, .class_init = omap2_intc_class_init, }; diff --git a/hw/intc/pl190.c b/hw/intc/pl190.c index 5ecbc4a485..1e50baf237 100644 --- a/hw/intc/pl190.c +++ b/hw/intc/pl190.c @@ -236,17 +236,17 @@ static void pl190_reset(DeviceState *d) pl190_update_vectors(s); } -static int pl190_init(SysBusDevice *sbd) +static void pl190_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - PL190State *s = PL190(dev); + DeviceState *dev = DEVICE(obj); + PL190State *s = PL190(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &pl190_ops, s, "pl190", 0x1000); + memory_region_init_io(&s->iomem, obj, &pl190_ops, s, "pl190", 0x1000); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_in(dev, pl190_set_irq, 32); sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->fiq); - return 0; } static const VMStateDescription vmstate_pl190 = { @@ -271,9 +271,7 @@ static const VMStateDescription vmstate_pl190 = { static void pl190_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pl190_init; dc->reset = pl190_reset; dc->vmsd = &vmstate_pl190; } @@ -282,6 +280,7 @@ static const TypeInfo pl190_info = { .name = TYPE_PL190, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PL190State), + .instance_init = pl190_init, .class_init = pl190_class_init, }; diff --git a/hw/intc/slavio_intctl.c b/hw/intc/slavio_intctl.c index c9486ed999..e82e893628 100644 --- a/hw/intc/slavio_intctl.c +++ b/hw/intc/slavio_intctl.c @@ -418,15 +418,16 @@ static void slavio_intctl_reset(DeviceState *d) slavio_check_interrupts(s, 0); } -static int slavio_intctl_init1(SysBusDevice *sbd) +static void slavio_intctl_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - SLAVIO_INTCTLState *s = SLAVIO_INTCTL(dev); + DeviceState *dev = DEVICE(obj); + SLAVIO_INTCTLState *s = SLAVIO_INTCTL(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); unsigned int i, j; char slave_name[45]; qdev_init_gpio_in(dev, slavio_set_irq_all, 32 + MAX_CPUS); - memory_region_init_io(&s->iomem, OBJECT(s), &slavio_intctlm_mem_ops, s, + memory_region_init_io(&s->iomem, obj, &slavio_intctlm_mem_ops, s, "master-interrupt-controller", INTCTLM_SIZE); sysbus_init_mmio(sbd, &s->iomem); @@ -443,16 +444,12 @@ static int slavio_intctl_init1(SysBusDevice *sbd) s->slaves[i].cpu = i; s->slaves[i].master = s; } - - return 0; } static void slavio_intctl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = slavio_intctl_init1; dc->reset = slavio_intctl_reset; dc->vmsd = &vmstate_intctl; } @@ -461,6 +458,7 @@ static const TypeInfo slavio_intctl_info = { .name = TYPE_SLAVIO_INTCTL, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLAVIO_INTCTLState), + .instance_init = slavio_intctl_init, .class_init = slavio_intctl_class_init, }; diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 93f952880a..bc0dd2cc75 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -29,6 +29,7 @@ obj-$(CONFIG_IMX) += imx_ccm.o obj-$(CONFIG_IMX) += imx31_ccm.o obj-$(CONFIG_IMX) += imx25_ccm.o obj-$(CONFIG_IMX) += imx6_ccm.o +obj-$(CONFIG_IMX) += imx6_src.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 530411f841..34473469d4 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -21,6 +21,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) int n; uint32_t offset, length, color; uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; + uint32_t tmp_xres, tmp_yres, tmp_xoffset, tmp_yoffset; + uint32_t tmp_bpp, tmp_pixo, tmp_alpha; uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; @@ -139,7 +141,11 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00040001: /* Allocate buffer */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); - stl_le_phys(&s->dma_as, value + 16, s->fbdev->size); + tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; + tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; + tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 16, + tmp_xres * tmp_yres * tmp_bpp / 8); resplen = 8; break; case 0x00048001: /* Release buffer */ @@ -150,8 +156,10 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; case 0x00040003: /* Get display width/height */ case 0x00040004: - stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres); - stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres); + tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; + tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; + stl_le_phys(&s->dma_as, value + 12, tmp_xres); + stl_le_phys(&s->dma_as, value + 16, tmp_yres); resplen = 8; break; case 0x00044003: /* Test display width/height */ @@ -167,7 +175,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; case 0x00040005: /* Get depth */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp); + tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 12, tmp_bpp); resplen = 4; break; case 0x00044005: /* Test depth */ @@ -179,7 +188,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00040006: /* Get pixel order */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo); + tmp_pixo = newpixo != NULL ? *newpixo : s->fbdev->pixo; + stl_le_phys(&s->dma_as, value + 12, tmp_pixo); resplen = 4; break; case 0x00044006: /* Test pixel order */ @@ -191,7 +201,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00040007: /* Get alpha */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha); + tmp_alpha = newalpha != NULL ? *newalpha : s->fbdev->alpha; + stl_le_phys(&s->dma_as, value + 12, tmp_alpha); resplen = 4; break; case 0x00044007: /* Test pixel alpha */ @@ -203,12 +214,16 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00040008: /* Get pitch */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch); + tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; + tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 12, tmp_xres * tmp_bpp / 8); resplen = 4; break; case 0x00040009: /* Get virtual offset */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset); - stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset); + tmp_xoffset = newxoffset != NULL ? *newxoffset : s->fbdev->xoffset; + tmp_yoffset = newyoffset != NULL ? *newyoffset : s->fbdev->yoffset; + stl_le_phys(&s->dma_as, value + 12, tmp_xoffset); + stl_le_phys(&s->dma_as, value + 16, tmp_yoffset); resplen = 8; break; case 0x00044009: /* Test virtual offset */ diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c new file mode 100644 index 0000000000..6b026b459f --- /dev/null +++ b/hw/misc/imx6_src.c @@ -0,0 +1,264 @@ +/* + * IMX6 System Reset Controller + * + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "hw/misc/imx6_src.h" +#include "sysemu/sysemu.h" +#include "qemu/bitops.h" +#include "arm-powerctl.h" + +#ifndef DEBUG_IMX6_SRC +#define DEBUG_IMX6_SRC 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX6_SRC) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_SRC, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx6_src_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case SRC_SCR: + return "SRC_SCR"; + case SRC_SBMR1: + return "SRC_SBMR1"; + case SRC_SRSR: + return "SRC_SRSR"; + case SRC_SISR: + return "SRC_SISR"; + case SRC_SIMR: + return "SRC_SIMR"; + case SRC_SBMR2: + return "SRC_SBMR2"; + case SRC_GPR1: + return "SRC_GPR1"; + case SRC_GPR2: + return "SRC_GPR2"; + case SRC_GPR3: + return "SRC_GPR3"; + case SRC_GPR4: + return "SRC_GPR4"; + case SRC_GPR5: + return "SRC_GPR5"; + case SRC_GPR6: + return "SRC_GPR6"; + case SRC_GPR7: + return "SRC_GPR7"; + case SRC_GPR8: + return "SRC_GPR8"; + case SRC_GPR9: + return "SRC_GPR9"; + case SRC_GPR10: + return "SRC_GPR10"; + default: + sprintf(unknown, "%d ?", reg); + return unknown; + } +} + +static const VMStateDescription vmstate_imx6_src = { + .name = TYPE_IMX6_SRC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, IMX6SRCState, SRC_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx6_src_reset(DeviceState *dev) +{ + IMX6SRCState *s = IMX6_SRC(dev); + + DPRINTF("\n"); + + memset(s->regs, 0, sizeof(s->regs)); + + /* Set reset values */ + s->regs[SRC_SCR] = 0x521; + s->regs[SRC_SRSR] = 0x1; + s->regs[SRC_SIMR] = 0x1F; +} + +static uint64_t imx6_src_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + IMX6SRCState *s = (IMX6SRCState *)opaque; + uint32_t index = offset >> 2; + + if (index < SRC_MAX) { + value = s->regs[index]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX6_SRC, __func__, offset); + + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_src_reg_name(index), value); + + return value; +} + +static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMX6SRCState *s = (IMX6SRCState *)opaque; + uint32_t index = offset >> 2; + unsigned long change_mask; + unsigned long current_value = value; + + if (index >= SRC_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX6_SRC, __func__, offset); + return; + } + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_src_reg_name(index), + (uint32_t)current_value); + + change_mask = s->regs[index] ^ (uint32_t)current_value; + + switch (index) { + case SRC_SCR: + /* + * On real hardware when the system reset controller starts a + * secondary CPU it runs through some boot ROM code which reads + * the SRC_GPRX registers controlling the start address and branches + * to it. + * Here we are taking a short cut and branching directly to the + * requested address (we don't want to run the boot ROM code inside + * QEMU) + */ + if (EXTRACT(change_mask, CORE3_ENABLE)) { + if (EXTRACT(current_value, CORE3_ENABLE)) { + /* CORE 3 is brought up */ + arm_set_cpu_on(3, s->regs[SRC_GPR7], s->regs[SRC_GPR8], + 3, false); + } else { + /* CORE 3 is shut down */ + arm_set_cpu_off(3); + } + /* We clear the reset bits as the processor changed state */ + clear_bit(CORE3_RST_SHIFT, ¤t_value); + clear_bit(CORE3_RST_SHIFT, &change_mask); + } + if (EXTRACT(change_mask, CORE2_ENABLE)) { + if (EXTRACT(current_value, CORE2_ENABLE)) { + /* CORE 2 is brought up */ + arm_set_cpu_on(2, s->regs[SRC_GPR5], s->regs[SRC_GPR6], + 3, false); + } else { + /* CORE 3 is shut down */ + arm_set_cpu_off(2); + } + /* We clear the reset bits as the processor changed state */ + clear_bit(CORE2_RST_SHIFT, ¤t_value); + clear_bit(CORE2_RST_SHIFT, &change_mask); + } + if (EXTRACT(change_mask, CORE1_ENABLE)) { + if (EXTRACT(current_value, CORE1_ENABLE)) { + /* CORE 1 is brought up */ + arm_set_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4], + 3, false); + } else { + /* CORE 3 is shut down */ + arm_set_cpu_off(1); + } + /* We clear the reset bits as the processor changed state */ + clear_bit(CORE1_RST_SHIFT, ¤t_value); + clear_bit(CORE1_RST_SHIFT, &change_mask); + } + if (EXTRACT(change_mask, CORE0_RST)) { + arm_reset_cpu(0); + clear_bit(CORE0_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, CORE1_RST)) { + arm_reset_cpu(1); + clear_bit(CORE1_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, CORE2_RST)) { + arm_reset_cpu(2); + clear_bit(CORE2_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, CORE3_RST)) { + arm_reset_cpu(3); + clear_bit(CORE3_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, SW_IPU2_RST)) { + /* We pretend the IPU2 is reset */ + clear_bit(SW_IPU2_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, SW_IPU1_RST)) { + /* We pretend the IPU1 is reset */ + clear_bit(SW_IPU1_RST_SHIFT, ¤t_value); + } + s->regs[index] = current_value; + break; + default: + s->regs[index] = current_value; + break; + } +} + +static const struct MemoryRegionOps imx6_src_ops = { + .read = imx6_src_read, + .write = imx6_src_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx6_src_realize(DeviceState *dev, Error **errp) +{ + IMX6SRCState *s = IMX6_SRC(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx6_src_ops, s, + TYPE_IMX6_SRC, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); +} + +static void imx6_src_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx6_src_realize; + dc->reset = imx6_src_reset; + dc->vmsd = &vmstate_imx6_src; + dc->desc = "i.MX6 System Reset Controller"; +} + +static const TypeInfo imx6_src_info = { + .name = TYPE_IMX6_SRC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX6SRCState), + .class_init = imx6_src_class_init, +}; + +static void imx6_src_register_types(void) +{ + type_register_static(&imx6_src_info); +} + +type_init(imx6_src_register_types) diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 1f5f1d790a..94c875d752 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -269,11 +269,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, void *fdt; if (!drc->fdt) { - visit_start_struct(v, name, NULL, 0, &err); - if (!err) { - visit_end_struct(v, &err); - } - error_propagate(errp, err); + visit_type_null(v, NULL, errp); return; } @@ -301,7 +297,8 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, case FDT_END_NODE: /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */ g_assert(fdt_depth > 0); - visit_end_struct(v, &err); + visit_check_struct(v, &err); + visit_end_struct(v); if (err) { error_propagate(errp, err); return; @@ -312,7 +309,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, int i; prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len); name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); - visit_start_list(v, name, &err); + visit_start_list(v, name, NULL, 0, &err); if (err) { error_propagate(errp, err); return; diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs index 9555825aca..fcbb79ef01 100644 --- a/hw/ssi/Makefile.objs +++ b/hw/ssi/Makefile.objs @@ -4,3 +4,4 @@ common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o obj-$(CONFIG_OMAP) += omap_spi.o +obj-$(CONFIG_IMX) += imx_spi.o diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c new file mode 100644 index 0000000000..d5dd42aca6 --- /dev/null +++ b/hw/ssi/imx_spi.c @@ -0,0 +1,454 @@ +/* + * IMX SPI Controller + * + * Copyright (c) 2016 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "hw/ssi/imx_spi.h" +#include "sysemu/sysemu.h" + +#ifndef DEBUG_IMX_SPI +#define DEBUG_IMX_SPI 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_SPI) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_SPI, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx_spi_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case ECSPI_RXDATA: + return "ECSPI_RXDATA"; + case ECSPI_TXDATA: + return "ECSPI_TXDATA"; + case ECSPI_CONREG: + return "ECSPI_CONREG"; + case ECSPI_CONFIGREG: + return "ECSPI_CONFIGREG"; + case ECSPI_INTREG: + return "ECSPI_INTREG"; + case ECSPI_DMAREG: + return "ECSPI_DMAREG"; + case ECSPI_STATREG: + return "ECSPI_STATREG"; + case ECSPI_PERIODREG: + return "ECSPI_PERIODREG"; + case ECSPI_TESTREG: + return "ECSPI_TESTREG"; + case ECSPI_MSGDATA: + return "ECSPI_MSGDATA"; + default: + sprintf(unknown, "%d ?", reg); + return unknown; + } +} + +static const VMStateDescription vmstate_imx_spi = { + .name = TYPE_IMX_SPI, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_FIFO32(tx_fifo, IMXSPIState), + VMSTATE_FIFO32(rx_fifo, IMXSPIState), + VMSTATE_INT16(burst_length, IMXSPIState), + VMSTATE_UINT32_ARRAY(regs, IMXSPIState, ECSPI_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx_spi_txfifo_reset(IMXSPIState *s) +{ + fifo32_reset(&s->tx_fifo); + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TE; + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TF; +} + +static void imx_spi_rxfifo_reset(IMXSPIState *s) +{ + fifo32_reset(&s->rx_fifo); + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RR; + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RF; + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RO; +} + +static void imx_spi_update_irq(IMXSPIState *s) +{ + int level; + + if (fifo32_is_empty(&s->rx_fifo)) { + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RR; + } else { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RR; + } + + if (fifo32_is_full(&s->rx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RF; + } else { + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RF; + } + + if (fifo32_is_empty(&s->tx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TE; + } else { + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TE; + } + + if (fifo32_is_full(&s->tx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TF; + } else { + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TF; + } + + level = s->regs[ECSPI_STATREG] & s->regs[ECSPI_INTREG] ? 1 : 0; + + qemu_set_irq(s->irq, level); + + DPRINTF("IRQ level is %d\n", level); +} + +static uint8_t imx_spi_selected_channel(IMXSPIState *s) +{ + return EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_CHANNEL_SELECT); +} + +static uint32_t imx_spi_burst_length(IMXSPIState *s) +{ + return EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_BURST_LENGTH) + 1; +} + +static bool imx_spi_is_enabled(IMXSPIState *s) +{ + return s->regs[ECSPI_CONREG] & ECSPI_CONREG_EN; +} + +static bool imx_spi_channel_is_master(IMXSPIState *s) +{ + uint8_t mode = EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_CHANNEL_MODE); + + return (mode & (1 << imx_spi_selected_channel(s))) ? true : false; +} + +static bool imx_spi_is_multiple_master_burst(IMXSPIState *s) +{ + uint8_t wave = EXTRACT(s->regs[ECSPI_CONFIGREG], ECSPI_CONFIGREG_SS_CTL); + + return imx_spi_channel_is_master(s) && + !(s->regs[ECSPI_CONREG] & ECSPI_CONREG_SMC) && + ((wave & (1 << imx_spi_selected_channel(s))) ? true : false); +} + +static void imx_spi_flush_txfifo(IMXSPIState *s) +{ + uint32_t tx; + uint32_t rx; + + DPRINTF("Begin: TX Fifo Size = %d, RX Fifo Size = %d\n", + fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo)); + + while (!fifo32_is_empty(&s->tx_fifo)) { + int tx_burst = 0; + int index = 0; + + if (s->burst_length <= 0) { + s->burst_length = imx_spi_burst_length(s); + + DPRINTF("Burst length = %d\n", s->burst_length); + + if (imx_spi_is_multiple_master_burst(s)) { + s->regs[ECSPI_CONREG] |= ECSPI_CONREG_XCH; + } + } + + tx = fifo32_pop(&s->tx_fifo); + + DPRINTF("data tx:0x%08x\n", tx); + + tx_burst = MIN(s->burst_length, 32); + + rx = 0; + + while (tx_burst) { + uint8_t byte = tx & 0xff; + + DPRINTF("writing 0x%02x\n", (uint32_t)byte); + + /* We need to write one byte at a time */ + byte = ssi_transfer(s->bus, byte); + + DPRINTF("0x%02x read\n", (uint32_t)byte); + + tx = tx >> 8; + rx |= (byte << (index * 8)); + + /* Remove 8 bits from the actual burst */ + tx_burst -= 8; + s->burst_length -= 8; + index++; + } + + DPRINTF("data rx:0x%08x\n", rx); + + if (fifo32_is_full(&s->rx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RO; + } else { + fifo32_push(&s->rx_fifo, (uint8_t)rx); + } + + if (s->burst_length <= 0) { + s->regs[ECSPI_CONREG] &= ~ECSPI_CONREG_XCH; + + if (!imx_spi_is_multiple_master_burst(s)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; + break; + } + } + } + + if (fifo32_is_empty(&s->tx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; + } + + /* TODO: We should also use TDR and RDR bits */ + + DPRINTF("End: TX Fifo Size = %d, RX Fifo Size = %d\n", + fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo)); +} + +static void imx_spi_reset(DeviceState *dev) +{ + IMXSPIState *s = IMX_SPI(dev); + + DPRINTF("\n"); + + memset(s->regs, 0, sizeof(s->regs)); + + s->regs[ECSPI_STATREG] = 0x00000003; + + imx_spi_rxfifo_reset(s); + imx_spi_txfifo_reset(s); + + imx_spi_update_irq(s); + + s->burst_length = 0; +} + +static uint64_t imx_spi_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + IMXSPIState *s = opaque; + uint32_t index = offset >> 2; + + if (index >= ECSPI_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SPI, __func__, offset); + return 0; + } + + switch (index) { + case ECSPI_RXDATA: + if (!imx_spi_is_enabled(s)) { + value = 0; + } else if (fifo32_is_empty(&s->rx_fifo)) { + /* value is undefined */ + value = 0xdeadbeef; + } else { + /* read from the RX FIFO */ + value = fifo32_pop(&s->rx_fifo); + } + + break; + case ECSPI_TXDATA: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from TX FIFO\n", + TYPE_IMX_SPI, __func__); + + /* Reading from TXDATA gives 0 */ + + break; + case ECSPI_MSGDATA: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from MSG FIFO\n", + TYPE_IMX_SPI, __func__); + + /* Reading from MSGDATA gives 0 */ + + break; + default: + value = s->regs[index]; + break; + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_spi_reg_name(index), value); + + imx_spi_update_irq(s); + + return (uint64_t)value; +} + +static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMXSPIState *s = opaque; + uint32_t index = offset >> 2; + uint32_t change_mask; + + if (index >= ECSPI_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SPI, __func__, offset); + return; + } + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_spi_reg_name(index), + (uint32_t)value); + + change_mask = s->regs[index] ^ value; + + switch (index) { + case ECSPI_RXDATA: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to write to RX FIFO\n", + TYPE_IMX_SPI, __func__); + break; + case ECSPI_TXDATA: + case ECSPI_MSGDATA: + /* Is there any difference between TXDATA and MSGDATA ? */ + /* I'll have to look in the linux driver */ + if (!imx_spi_is_enabled(s)) { + /* Ignore writes if device is disabled */ + break; + } else if (fifo32_is_full(&s->tx_fifo)) { + /* Ignore writes if queue is full */ + break; + } + + fifo32_push(&s->tx_fifo, (uint32_t)value); + + if (imx_spi_channel_is_master(s) && + (s->regs[ECSPI_CONREG] & ECSPI_CONREG_SMC)) { + /* + * Start emitting if current channel is master and SMC bit is + * set. + */ + imx_spi_flush_txfifo(s); + } + + break; + case ECSPI_STATREG: + /* the RO and TC bits are write-one-to-clear */ + value &= ECSPI_STATREG_RO | ECSPI_STATREG_TC; + s->regs[ECSPI_STATREG] &= ~value; + + break; + case ECSPI_CONREG: + s->regs[ECSPI_CONREG] = value; + + if (!imx_spi_is_enabled(s)) { + /* device is disabled, so this is a reset */ + imx_spi_reset(DEVICE(s)); + return; + } + + if (imx_spi_channel_is_master(s)) { + int i; + + /* We are in master mode */ + + for (i = 0; i < 4; i++) { + qemu_set_irq(s->cs_lines[i], + i == imx_spi_selected_channel(s) ? 0 : 1); + } + + if ((value & change_mask & ECSPI_CONREG_SMC) && + !fifo32_is_empty(&s->tx_fifo)) { + /* SMC bit is set and TX FIFO has some slots filled in */ + imx_spi_flush_txfifo(s); + } else if ((value & change_mask & ECSPI_CONREG_XCH) && + !(value & ECSPI_CONREG_SMC)) { + /* This is a request to start emitting */ + imx_spi_flush_txfifo(s); + } + } + + break; + default: + s->regs[index] = value; + + break; + } + + imx_spi_update_irq(s); +} + +static const struct MemoryRegionOps imx_spi_ops = { + .read = imx_spi_read, + .write = imx_spi_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx_spi_realize(DeviceState *dev, Error **errp) +{ + IMXSPIState *s = IMX_SPI(dev); + int i; + + s->bus = ssi_create_bus(dev, "spi"); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx_spi_ops, s, + TYPE_IMX_SPI, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + + ssi_auto_connect_slaves(dev, s->cs_lines, s->bus); + + for (i = 0; i < 4; ++i) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]); + } + + s->burst_length = 0; + + fifo32_create(&s->tx_fifo, ECSPI_FIFO_SIZE); + fifo32_create(&s->rx_fifo, ECSPI_FIFO_SIZE); +} + +static void imx_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx_spi_realize; + dc->vmsd = &vmstate_imx_spi; + dc->reset = imx_spi_reset; + dc->desc = "i.MX SPI Controller"; +} + +static const TypeInfo imx_spi_info = { + .name = TYPE_IMX_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXSPIState), + .class_init = imx_spi_class_init, +}; + +static void imx_spi_register_types(void) +{ + type_register_static(&imx_spi_info); +} + +type_init(imx_spi_register_types) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index bda84a64bd..1be85ae75a 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -788,8 +788,8 @@ static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c) trace_usb_mtp_op_get_device_info(s->dev.addr); usb_mtp_add_u16(d, 100); - usb_mtp_add_u32(d, 0xffffffff); - usb_mtp_add_u16(d, 0x0101); + usb_mtp_add_u32(d, 0x00000006); + usb_mtp_add_u16(d, 0x0064); usb_mtp_add_wstr(d, L""); usb_mtp_add_u16(d, 0x0000); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index bcde8a2f48..43ba61599a 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1531,7 +1531,10 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, usb_packet_cleanup(&epctx->transfers[i].packet); } - xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); + /* only touch guest RAM if we're not resetting the HC */ + if (xhci->dcbaap_low || xhci->dcbaap_high) { + xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); + } timer_free(epctx->kick_timer); g_free(epctx); diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 6458a94485..8b774f4939 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -34,7 +34,9 @@ */ #include "qemu/osdep.h" +#ifndef CONFIG_WIN32 #include <poll.h> +#endif #include <libusb.h> #include "qapi/error.h" @@ -204,6 +206,8 @@ static const char *err_names[] = { static libusb_context *ctx; static uint32_t loglevel; +#ifndef CONFIG_WIN32 + static void usb_host_handle_fd(void *opaque) { struct timeval tv = { 0, 0 }; @@ -223,9 +227,13 @@ static void usb_host_del_fd(int fd, void *user_data) qemu_set_fd_handler(fd, NULL, NULL, NULL); } +#endif /* !CONFIG_WIN32 */ + static int usb_host_init(void) { +#ifndef CONFIG_WIN32 const struct libusb_pollfd **poll; +#endif int i, rc; if (ctx) { @@ -236,7 +244,9 @@ static int usb_host_init(void) return -1; } libusb_set_debug(ctx, loglevel); - +#ifdef CONFIG_WIN32 + /* FIXME: add support for Windows. */ +#else libusb_set_pollfd_notifiers(ctx, usb_host_add_fd, usb_host_del_fd, ctx); @@ -247,6 +257,7 @@ static int usb_host_init(void) } } free(poll); +#endif return 0; } diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 9dbe681790..8c15e09470 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -138,17 +138,18 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name, for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { visit_type_uint64(v, balloon_stat_names[i], &s->stats[i], &err); if (err) { - break; + goto out_nested; } } - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); + visit_check_struct(v, &err); +out_nested: + visit_end_struct(v); + if (!err) { + visit_check_struct(v, &err); + } out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); + visit_end_struct(v); out: error_propagate(errp, err); } diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h index 05d89d358f..a011324b92 100644 --- a/include/exec/gen-icount.h +++ b/include/exec/gen-icount.h @@ -5,14 +5,13 @@ /* Helpers for instruction counting code generation. */ -static TCGArg *icount_arg; +static int icount_start_insn_idx; static TCGLabel *icount_label; static TCGLabel *exitreq_label; static inline void gen_tb_start(TranslationBlock *tb) { TCGv_i32 count, flag, imm; - int i; exitreq_label = gen_new_label(); flag = tcg_temp_new_i32(); @@ -31,13 +30,12 @@ static inline void gen_tb_start(TranslationBlock *tb) -ENV_OFFSET + offsetof(CPUState, icount_decr.u32)); imm = tcg_temp_new_i32(); + /* We emit a movi with a dummy immediate argument. Keep the insn index + * of the movi so that we later (when we know the actual insn count) + * can update the immediate argument with the actual insn count. */ + icount_start_insn_idx = tcg_op_buf_count(); tcg_gen_movi_i32(imm, 0xdeadbeef); - /* This is a horrid hack to allow fixing up the value later. */ - i = tcg_ctx.gen_last_op_idx; - i = tcg_ctx.gen_op_buf[i].args; - icount_arg = &tcg_ctx.gen_opparam_buf[i + 1]; - tcg_gen_sub_i32(count, count, imm); tcg_temp_free_i32(imm); @@ -53,7 +51,9 @@ static void gen_tb_end(TranslationBlock *tb, int num_insns) tcg_gen_exit_tb((uintptr_t)tb + TB_EXIT_REQUESTED); if (tb->cflags & CF_USE_ICOUNT) { - *icount_arg = num_insns; + /* Update the num_insn immediate parameter now that we know + * the actual insn count. */ + tcg_set_insn_param(icount_start_insn_idx, 1, num_insns); gen_set_label(icount_label); tcg_gen_exit_tb((uintptr_t)tb + TB_EXIT_ICOUNT_EXPIRED); } diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index c7a03d43b9..850a9626b7 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -455,8 +455,10 @@ struct AcpiSystemResourceAffinityTable } QEMU_PACKED; typedef struct AcpiSystemResourceAffinityTable AcpiSystemResourceAffinityTable; -#define ACPI_SRAT_PROCESSOR 0 +#define ACPI_SRAT_PROCESSOR_APIC 0 #define ACPI_SRAT_MEMORY 1 +#define ACPI_SRAT_PROCESSOR_x2APIC 2 +#define ACPI_SRAT_PROCESSOR_GICC 3 struct AcpiSratProcessorAffinity { @@ -473,7 +475,7 @@ typedef struct AcpiSratProcessorAffinity AcpiSratProcessorAffinity; struct AcpiSratMemoryAffinity { ACPI_SUB_HEADER_DEF - uint8_t proximity[4]; + uint32_t proximity; uint16_t reserved1; uint64_t base_addr; uint64_t range_length; @@ -483,6 +485,17 @@ struct AcpiSratMemoryAffinity } QEMU_PACKED; typedef struct AcpiSratMemoryAffinity AcpiSratMemoryAffinity; +struct AcpiSratProcessorGiccAffinity +{ + ACPI_SUB_HEADER_DEF + uint32_t proximity; + uint32_t acpi_processor_uid; + uint32_t flags; + uint32_t clock_domain; +} QEMU_PACKED; + +typedef struct AcpiSratProcessorGiccAffinity AcpiSratProcessorGiccAffinity; + /* PCI fw r3.0 MCFG table. */ /* Subtable */ struct AcpiMcfgAllocation { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 2c994b351a..7eb51c7885 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -198,6 +198,13 @@ typedef enum { AML_PULL_NONE = 3, } AmlPinConfig; +typedef enum { + MEM_AFFINITY_NOFLAGS = 0, + MEM_AFFINITY_ENABLED = (1 << 0), + MEM_AFFINITY_HOTPLUGGABLE = (1 << 1), + MEM_AFFINITY_NON_VOLATILE = (1 << 2), +} MemoryAffinityFlags; + typedef struct AcpiBuildTables { GArray *table_data; @@ -372,4 +379,7 @@ int build_append_named_dword(GArray *array, const char *name_format, ...) GCC_FMT_ATTR(2, 3); +void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, + uint64_t len, int node, MemoryAffinityFlags flags); + #endif diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h new file mode 100644 index 0000000000..d24aaee1c1 --- /dev/null +++ b/include/hw/arm/fsl-imx6.h @@ -0,0 +1,450 @@ +/* + * Freescale i.MX31 SoC 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 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef FSL_IMX6_H +#define FSL_IMX6_H + +#include "hw/arm/arm.h" +#include "hw/cpu/a9mpcore.h" +#include "hw/misc/imx6_ccm.h" +#include "hw/misc/imx6_src.h" +#include "hw/char/imx_serial.h" +#include "hw/timer/imx_gpt.h" +#include "hw/timer/imx_epit.h" +#include "hw/i2c/imx_i2c.h" +#include "hw/gpio/imx_gpio.h" +#include "hw/sd/sdhci.h" +#include "hw/ssi/imx_spi.h" +#include "exec/memory.h" + +#define TYPE_FSL_IMX6 "fsl,imx6" +#define FSL_IMX6(obj) OBJECT_CHECK(FslIMX6State, (obj), TYPE_FSL_IMX6) + +#define FSL_IMX6_NUM_CPUS 4 +#define FSL_IMX6_NUM_UARTS 5 +#define FSL_IMX6_NUM_EPITS 2 +#define FSL_IMX6_NUM_I2CS 3 +#define FSL_IMX6_NUM_GPIOS 7 +#define FSL_IMX6_NUM_ESDHCS 4 +#define FSL_IMX6_NUM_ECSPIS 5 + +typedef struct FslIMX6State { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + ARMCPU cpu[FSL_IMX6_NUM_CPUS]; + A9MPPrivState a9mpcore; + IMX6CCMState ccm; + IMX6SRCState src; + IMXSerialState uart[FSL_IMX6_NUM_UARTS]; + IMXGPTState gpt; + IMXEPITState epit[FSL_IMX6_NUM_EPITS]; + IMXI2CState i2c[FSL_IMX6_NUM_I2CS]; + IMXGPIOState gpio[FSL_IMX6_NUM_GPIOS]; + SDHCIState esdhc[FSL_IMX6_NUM_ESDHCS]; + IMXSPIState spi[FSL_IMX6_NUM_ECSPIS]; + MemoryRegion rom; + MemoryRegion caam; + MemoryRegion ocram; + MemoryRegion ocram_alias; +} FslIMX6State; + + +#define FSL_IMX6_MMDC_ADDR 0x10000000 +#define FSL_IMX6_MMDC_SIZE 0xF0000000 +#define FSL_IMX6_EIM_MEM_ADDR 0x08000000 +#define FSL_IMX6_EIM_MEM_SIZE 0x8000000 +#define FSL_IMX6_IPU_2_ADDR 0x02800000 +#define FSL_IMX6_IPU_2_SIZE 0x400000 +#define FSL_IMX6_IPU_1_ADDR 0x02400000 +#define FSL_IMX6_IPU_1_SIZE 0x400000 +#define FSL_IMX6_MIPI_HSI_ADDR 0x02208000 +#define FSL_IMX6_MIPI_HSI_SIZE 0x4000 +#define FSL_IMX6_OPENVG_ADDR 0x02204000 +#define FSL_IMX6_OPENVG_SIZE 0x4000 +#define FSL_IMX6_SATA_ADDR 0x02200000 +#define FSL_IMX6_SATA_SIZE 0x4000 +#define FSL_IMX6_AIPS_2_ADDR 0x02100000 +#define FSL_IMX6_AIPS_2_SIZE 0x100000 +/* AIPS2 */ +#define FSL_IMX6_UART5_ADDR 0x021F4000 +#define FSL_IMX6_UART5_SIZE 0x4000 +#define FSL_IMX6_UART4_ADDR 0x021F0000 +#define FSL_IMX6_UART4_SIZE 0x4000 +#define FSL_IMX6_UART3_ADDR 0x021EC000 +#define FSL_IMX6_UART3_SIZE 0x4000 +#define FSL_IMX6_UART2_ADDR 0x021E8000 +#define FSL_IMX6_UART2_SIZE 0x4000 +#define FSL_IMX6_VDOA_ADDR 0x021E4000 +#define FSL_IMX6_VDOA_SIZE 0x4000 +#define FSL_IMX6_MIPI_DSI_ADDR 0x021E0000 +#define FSL_IMX6_MIPI_DSI_SIZE 0x4000 +#define FSL_IMX6_MIPI_CSI_ADDR 0x021DC000 +#define FSL_IMX6_MIPI_CSI_SIZE 0x4000 +#define FSL_IMX6_AUDMUX_ADDR 0x021D8000 +#define FSL_IMX6_AUDMUX_SIZE 0x4000 +#define FSL_IMX6_TZASC2_ADDR 0x021D4000 +#define FSL_IMX6_TZASC2_SIZE 0x4000 +#define FSL_IMX6_TZASC1_ADDR 0x021D0000 +#define FSL_IMX6_TZASC1_SIZE 0x4000 +#define FSL_IMX6_CSU_ADDR 0x021C0000 +#define FSL_IMX6_CSU_SIZE 0x4000 +#define FSL_IMX6_OCOTPCTRL_ADDR 0x021BC000 +#define FSL_IMX6_OCOTPCTRL_SIZE 0x4000 +#define FSL_IMX6_EIM_ADDR 0x021B8000 +#define FSL_IMX6_EIM_SIZE 0x4000 +#define FSL_IMX6_MMDC1_ADDR 0x021B4000 +#define FSL_IMX6_MMDC1_SIZE 0x4000 +#define FSL_IMX6_MMDC0_ADDR 0x021B0000 +#define FSL_IMX6_MMDC0_SIZE 0x4000 +#define FSL_IMX6_ROMCP_ADDR 0x021AC000 +#define FSL_IMX6_ROMCP_SIZE 0x4000 +#define FSL_IMX6_I2C3_ADDR 0x021A8000 +#define FSL_IMX6_I2C3_SIZE 0x4000 +#define FSL_IMX6_I2C2_ADDR 0x021A4000 +#define FSL_IMX6_I2C2_SIZE 0x4000 +#define FSL_IMX6_I2C1_ADDR 0x021A0000 +#define FSL_IMX6_I2C1_SIZE 0x4000 +#define FSL_IMX6_uSDHC4_ADDR 0x0219C000 +#define FSL_IMX6_uSDHC4_SIZE 0x4000 +#define FSL_IMX6_uSDHC3_ADDR 0x02198000 +#define FSL_IMX6_uSDHC3_SIZE 0x4000 +#define FSL_IMX6_uSDHC2_ADDR 0x02194000 +#define FSL_IMX6_uSDHC2_SIZE 0x4000 +#define FSL_IMX6_uSDHC1_ADDR 0x02190000 +#define FSL_IMX6_uSDHC1_SIZE 0x4000 +#define FSL_IMX6_MLB150_ADDR 0x0218C000 +#define FSL_IMX6_MLB150_SIZE 0x4000 +#define FSL_IMX6_ENET_ADDR 0x02188000 +#define FSL_IMX6_ENET_SIZE 0x4000 +#define FSL_IMX6_USBOH3_USB_ADDR 0x02184000 +#define FSL_IMX6_USBOH3_USB_SIZE 0x4000 +#define FSL_IMX6_AIPS2_CFG_ADDR 0x0217C000 +#define FSL_IMX6_AIPS2_CFG_SIZE 0x4000 +/* DAP */ +#define FSL_IMX6_PTF_CTRL_ADDR 0x02160000 +#define FSL_IMX6_PTF_CTRL_SIZE 0x1000 +#define FSL_IMX6_PTM3_ADDR 0x0215F000 +#define FSL_IMX6_PTM3_SIZE 0x1000 +#define FSL_IMX6_PTM2_ADDR 0x0215E000 +#define FSL_IMX6_PTM2_SIZE 0x1000 +#define FSL_IMX6_PTM1_ADDR 0x0215D000 +#define FSL_IMX6_PTM1_SIZE 0x1000 +#define FSL_IMX6_PTM0_ADDR 0x0215C000 +#define FSL_IMX6_PTM0_SIZE 0x1000 +#define FSL_IMX6_CTI3_ADDR 0x0215B000 +#define FSL_IMX6_CTI3_SIZE 0x1000 +#define FSL_IMX6_CTI2_ADDR 0x0215A000 +#define FSL_IMX6_CTI2_SIZE 0x1000 +#define FSL_IMX6_CTI1_ADDR 0x02159000 +#define FSL_IMX6_CTI1_SIZE 0x1000 +#define FSL_IMX6_CTI0_ADDR 0x02158000 +#define FSL_IMX6_CTI0_SIZE 0x1000 +#define FSL_IMX6_CPU3_PMU_ADDR 0x02157000 +#define FSL_IMX6_CPU3_PMU_SIZE 0x1000 +#define FSL_IMX6_CPU3_DEBUG_IF_ADDR 0x02156000 +#define FSL_IMX6_CPU3_DEBUG_IF_SIZE 0x1000 +#define FSL_IMX6_CPU2_PMU_ADDR 0x02155000 +#define FSL_IMX6_CPU2_PMU_SIZE 0x1000 +#define FSL_IMX6_CPU2_DEBUG_IF_ADDR 0x02154000 +#define FSL_IMX6_CPU2_DEBUG_IF_SIZE 0x1000 +#define FSL_IMX6_CPU1_PMU_ADDR 0x02153000 +#define FSL_IMX6_CPU1_PMU_SIZE 0x1000 +#define FSL_IMX6_CPU1_DEBUG_IF_ADDR 0x02152000 +#define FSL_IMX6_CPU1_DEBUG_IF_SIZE 0x1000 +#define FSL_IMX6_CPU0_PMU_ADDR 0x02151000 +#define FSL_IMX6_CPU0_PMU_SIZE 0x1000 +#define FSL_IMX6_CPU0_DEBUG_IF_ADDR 0x02150000 +#define FSL_IMX6_CPU0_DEBUG_IF_SIZE 0x1000 +#define FSL_IMX6_CA9_INTEG_ADDR 0x0214F000 +#define FSL_IMX6_CA9_INTEG_SIZE 0x1000 +#define FSL_IMX6_FUNNEL_ADDR 0x02144000 +#define FSL_IMX6_FUNNEL_SIZE 0x1000 +#define FSL_IMX6_TPIU_ADDR 0x02143000 +#define FSL_IMX6_TPIU_SIZE 0x1000 +#define FSL_IMX6_EXT_CTI_ADDR 0x02142000 +#define FSL_IMX6_EXT_CTI_SIZE 0x1000 +#define FSL_IMX6_ETB_ADDR 0x02141000 +#define FSL_IMX6_ETB_SIZE 0x1000 +#define FSL_IMX6_DAP_ROM_TABLE_ADDR 0x02140000 +#define FSL_IMX6_DAP_ROM_TABLE_SIZE 0x1000 +/* DAP end */ +#define FSL_IMX6_CAAM_ADDR 0x02100000 +#define FSL_IMX6_CAAM_SIZE 0x10000 +/* AIPS2 end */ +#define FSL_IMX6_AIPS_1_ADDR 0x02000000 +#define FSL_IMX6_AIPS_1_SIZE 0x100000 +/* AIPS1 */ +#define FSL_IMX6_SDMA_ADDR 0x020EC000 +#define FSL_IMX6_SDMA_SIZE 0x4000 +#define FSL_IMX6_DCIC2_ADDR 0x020E8000 +#define FSL_IMX6_DCIC2_SIZE 0x4000 +#define FSL_IMX6_DCIC1_ADDR 0x020E4000 +#define FSL_IMX6_DCIC1_SIZE 0x4000 +#define FSL_IMX6_IOMUXC_ADDR 0x020E0000 +#define FSL_IMX6_IOMUXC_SIZE 0x4000 +#define FSL_IMX6_PGCARM_ADDR 0x020DCA00 +#define FSL_IMX6_PGCARM_SIZE 0x20 +#define FSL_IMX6_PGCPU_ADDR 0x020DC260 +#define FSL_IMX6_PGCPU_SIZE 0x20 +#define FSL_IMX6_GPC_ADDR 0x020DC000 +#define FSL_IMX6_GPC_SIZE 0x4000 +#define FSL_IMX6_SRC_ADDR 0x020D8000 +#define FSL_IMX6_SRC_SIZE 0x4000 +#define FSL_IMX6_EPIT2_ADDR 0x020D4000 +#define FSL_IMX6_EPIT2_SIZE 0x4000 +#define FSL_IMX6_EPIT1_ADDR 0x020D0000 +#define FSL_IMX6_EPIT1_SIZE 0x4000 +#define FSL_IMX6_SNVSHP_ADDR 0x020CC000 +#define FSL_IMX6_SNVSHP_SIZE 0x4000 +#define FSL_IMX6_USBPHY2_ADDR 0x020CA000 +#define FSL_IMX6_USBPHY2_SIZE 0x1000 +#define FSL_IMX6_USBPHY1_ADDR 0x020C9000 +#define FSL_IMX6_USBPHY1_SIZE 0x1000 +#define FSL_IMX6_ANALOG_ADDR 0x020C8000 +#define FSL_IMX6_ANALOG_SIZE 0x1000 +#define FSL_IMX6_CCM_ADDR 0x020C4000 +#define FSL_IMX6_CCM_SIZE 0x4000 +#define FSL_IMX6_WDOG2_ADDR 0x020C0000 +#define FSL_IMX6_WDOG2_SIZE 0x4000 +#define FSL_IMX6_WDOG1_ADDR 0x020BC000 +#define FSL_IMX6_WDOG1_SIZE 0x4000 +#define FSL_IMX6_KPP_ADDR 0x020B8000 +#define FSL_IMX6_KPP_SIZE 0x4000 +#define FSL_IMX6_GPIO7_ADDR 0x020B4000 +#define FSL_IMX6_GPIO7_SIZE 0x4000 +#define FSL_IMX6_GPIO6_ADDR 0x020B0000 +#define FSL_IMX6_GPIO6_SIZE 0x4000 +#define FSL_IMX6_GPIO5_ADDR 0x020AC000 +#define FSL_IMX6_GPIO5_SIZE 0x4000 +#define FSL_IMX6_GPIO4_ADDR 0x020A8000 +#define FSL_IMX6_GPIO4_SIZE 0x4000 +#define FSL_IMX6_GPIO3_ADDR 0x020A4000 +#define FSL_IMX6_GPIO3_SIZE 0x4000 +#define FSL_IMX6_GPIO2_ADDR 0x020A0000 +#define FSL_IMX6_GPIO2_SIZE 0x4000 +#define FSL_IMX6_GPIO1_ADDR 0x0209C000 +#define FSL_IMX6_GPIO1_SIZE 0x4000 +#define FSL_IMX6_GPT_ADDR 0x02098000 +#define FSL_IMX6_GPT_SIZE 0x4000 +#define FSL_IMX6_CAN2_ADDR 0x02094000 +#define FSL_IMX6_CAN2_SIZE 0x4000 +#define FSL_IMX6_CAN1_ADDR 0x02090000 +#define FSL_IMX6_CAN1_SIZE 0x4000 +#define FSL_IMX6_PWM4_ADDR 0x0208C000 +#define FSL_IMX6_PWM4_SIZE 0x4000 +#define FSL_IMX6_PWM3_ADDR 0x02088000 +#define FSL_IMX6_PWM3_SIZE 0x4000 +#define FSL_IMX6_PWM2_ADDR 0x02084000 +#define FSL_IMX6_PWM2_SIZE 0x4000 +#define FSL_IMX6_PWM1_ADDR 0x02080000 +#define FSL_IMX6_PWM1_SIZE 0x4000 +#define FSL_IMX6_AIPS1_CFG_ADDR 0x0207C000 +#define FSL_IMX6_AIPS1_CFG_SIZE 0x4000 +#define FSL_IMX6_VPU_ADDR 0x02040000 +#define FSL_IMX6_VPU_SIZE 0x3C000 +#define FSL_IMX6_AIPS1_SPBA_ADDR 0x0203C000 +#define FSL_IMX6_AIPS1_SPBA_SIZE 0x4000 +#define FSL_IMX6_ASRC_ADDR 0x02034000 +#define FSL_IMX6_ASRC_SIZE 0x4000 +#define FSL_IMX6_SSI3_ADDR 0x02030000 +#define FSL_IMX6_SSI3_SIZE 0x4000 +#define FSL_IMX6_SSI2_ADDR 0x0202C000 +#define FSL_IMX6_SSI2_SIZE 0x4000 +#define FSL_IMX6_SSI1_ADDR 0x02028000 +#define FSL_IMX6_SSI1_SIZE 0x4000 +#define FSL_IMX6_ESAI_ADDR 0x02024000 +#define FSL_IMX6_ESAI_SIZE 0x4000 +#define FSL_IMX6_UART1_ADDR 0x02020000 +#define FSL_IMX6_UART1_SIZE 0x4000 +#define FSL_IMX6_eCSPI5_ADDR 0x02018000 +#define FSL_IMX6_eCSPI5_SIZE 0x4000 +#define FSL_IMX6_eCSPI4_ADDR 0x02014000 +#define FSL_IMX6_eCSPI4_SIZE 0x4000 +#define FSL_IMX6_eCSPI3_ADDR 0x02010000 +#define FSL_IMX6_eCSPI3_SIZE 0x4000 +#define FSL_IMX6_eCSPI2_ADDR 0x0200C000 +#define FSL_IMX6_eCSPI2_SIZE 0x4000 +#define FSL_IMX6_eCSPI1_ADDR 0x02008000 +#define FSL_IMX6_eCSPI1_SIZE 0x4000 +#define FSL_IMX6_SPDIF_ADDR 0x02004000 +#define FSL_IMX6_SPDIF_SIZE 0x4000 +/* AIPS1 end */ +#define FSL_IMX6_PCIe_REG_ADDR 0x01FFC000 +#define FSL_IMX6_PCIe_REG_SIZE 0x4000 +#define FSL_IMX6_PCIe_ADDR 0x01000000 +#define FSL_IMX6_PCIe_SIZE 0xFFC000 +#define FSL_IMX6_GPV_1_PL301_CFG_ADDR 0x00C00000 +#define FSL_IMX6_GPV_1_PL301_CFG_SIZE 0x100000 +#define FSL_IMX6_GPV_0_PL301_CFG_ADDR 0x00B00000 +#define FSL_IMX6_GPV_0_PL301_CFG_SIZE 0x100000 +#define FSL_IMX6_PL310_ADDR 0x00A02000 +#define FSL_IMX6_PL310_SIZE 0x1000 +#define FSL_IMX6_A9MPCORE_ADDR 0x00A00000 +#define FSL_IMX6_A9MPCORE_SIZE 0x2000 +#define FSL_IMX6_OCRAM_ALIAS_ADDR 0x00940000 +#define FSL_IMX6_OCRAM_ALIAS_SIZE 0xC0000 +#define FSL_IMX6_OCRAM_ADDR 0x00900000 +#define FSL_IMX6_OCRAM_SIZE 0x40000 +#define FSL_IMX6_GPV_4_PL301_CFG_ADDR 0x00800000 +#define FSL_IMX6_GPV_4_PL301_CFG_SIZE 0x100000 +#define FSL_IMX6_GPV_3_PL301_CFG_ADDR 0x00300000 +#define FSL_IMX6_GPV_3_PL301_CFG_SIZE 0x100000 +#define FSL_IMX6_GPV_2_PL301_CFG_ADDR 0x00200000 +#define FSL_IMX6_GPV_2_PL301_CFG_SIZE 0x100000 +#define FSL_IMX6_DTCP_ADDR 0x00138000 +#define FSL_IMX6_DTCP_SIZE 0x4000 +#define FSL_IMX6_GPU_2D_ADDR 0x00134000 +#define FSL_IMX6_GPU_2D_SIZE 0x4000 +#define FSL_IMX6_GPU_3D_ADDR 0x00130000 +#define FSL_IMX6_GPU_3D_SIZE 0x4000 +#define FSL_IMX6_HDMI_ADDR 0x00120000 +#define FSL_IMX6_HDMI_SIZE 0x9000 +#define FSL_IMX6_BCH_ADDR 0x00114000 +#define FSL_IMX6_BCH_SIZE 0x4000 +#define FSL_IMX6_GPMI_ADDR 0x00112000 +#define FSL_IMX6_GPMI_SIZE 0x2000 +#define FSL_IMX6_APBH_BRIDGE_DMA_ADDR 0x00110000 +#define FSL_IMX6_APBH_BRIDGE_DMA_SIZE 0x2000 +#define FSL_IMX6_CAAM_MEM_ADDR 0x00100000 +#define FSL_IMX6_CAAM_MEM_SIZE 0x4000 +#define FSL_IMX6_ROM_ADDR 0x00000000 +#define FSL_IMX6_ROM_SIZE 0x18000 + +#define FSL_IMX6_IOMUXC_IRQ 0 +#define FSL_IMX6_DAP_IRQ 1 +#define FSL_IMX6_SDMA_IRQ 2 +#define FSL_IMX6_VPU_JPEG_IRQ 3 +#define FSL_IMX6_SNVS_PMIC_IRQ 4 +#define FSL_IMX6_IPU1_ERROR_IRQ 5 +#define FSL_IMX6_IPU1_SYNC_IRQ 6 +#define FSL_IMX6_IPU2_ERROR_IRQ 7 +#define FSL_IMX6_IPU2_SYNC_IRQ 8 +#define FSL_IMX6_GPU3D_IRQ 9 +#define FSL_IMX6_R2D_IRQ 10 +#define FSL_IMX6_V2D_IRQ 11 +#define FSL_IMX6_VPU_IRQ 12 +#define FSL_IMX6_APBH_BRIDGE_DMA_IRQ 13 +#define FSL_IMX6_EIM_IRQ 14 +#define FSL_IMX6_BCH_IRQ 15 +#define FSL_IMX6_GPMI_IRQ 16 +#define FSL_IMX6_DTCP_IRQ 17 +#define FSL_IMX6_VDOA_IRQ 18 +#define FSL_IMX6_SNVS_CONS_IRQ 19 +#define FSL_IMX6_SNVS_SEC_IRQ 20 +#define FSL_IMX6_CSU_IRQ 21 +#define FSL_IMX6_uSDHC1_IRQ 22 +#define FSL_IMX6_uSDHC2_IRQ 23 +#define FSL_IMX6_uSDHC3_IRQ 24 +#define FSL_IMX6_uSDHC4_IRQ 25 +#define FSL_IMX6_UART1_IRQ 26 +#define FSL_IMX6_UART2_IRQ 27 +#define FSL_IMX6_UART3_IRQ 28 +#define FSL_IMX6_UART4_IRQ 29 +#define FSL_IMX6_UART5_IRQ 30 +#define FSL_IMX6_ECSPI1_IRQ 31 +#define FSL_IMX6_ECSPI2_IRQ 32 +#define FSL_IMX6_ECSPI3_IRQ 33 +#define FSL_IMX6_ECSPI4_IRQ 34 +#define FSL_IMX6_ECSPI5_IRQ 35 +#define FSL_IMX6_I2C1_IRQ 36 +#define FSL_IMX6_I2C2_IRQ 37 +#define FSL_IMX6_I2C3_IRQ 38 +#define FSL_IMX6_SATA_IRQ 39 +#define FSL_IMX6_USB_HOST1_IRQ 40 +#define FSL_IMX6_USB_HOST2_IRQ 41 +#define FSL_IMX6_USB_HOST3_IRQ 42 +#define FSL_IMX6_USB_OTG_IRQ 43 +#define FSL_IMX6_USB_PHY_UTMI0_IRQ 44 +#define FSL_IMX6_USB_PHY_UTMI1_IRQ 45 +#define FSL_IMX6_SSI1_IRQ 46 +#define FSL_IMX6_SSI2_IRQ 47 +#define FSL_IMX6_SSI3_IRQ 48 +#define FSL_IMX6_TEMP_IRQ 49 +#define FSL_IMX6_ASRC_IRQ 50 +#define FSL_IMX6_ESAI_IRQ 51 +#define FSL_IMX6_SPDIF_IRQ 52 +#define FSL_IMX6_MLB150_IRQ 53 +#define FSL_IMX6_PMU1_IRQ 54 +#define FSL_IMX6_GPT_IRQ 55 +#define FSL_IMX6_EPIT1_IRQ 56 +#define FSL_IMX6_EPIT2_IRQ 57 +#define FSL_IMX6_GPIO1_INT7_IRQ 58 +#define FSL_IMX6_GPIO1_INT6_IRQ 59 +#define FSL_IMX6_GPIO1_INT5_IRQ 60 +#define FSL_IMX6_GPIO1_INT4_IRQ 61 +#define FSL_IMX6_GPIO1_INT3_IRQ 62 +#define FSL_IMX6_GPIO1_INT2_IRQ 63 +#define FSL_IMX6_GPIO1_INT1_IRQ 64 +#define FSL_IMX6_GPIO1_INT0_IRQ 65 +#define FSL_IMX6_GPIO1_LOW_IRQ 66 +#define FSL_IMX6_GPIO1_HIGH_IRQ 67 +#define FSL_IMX6_GPIO2_LOW_IRQ 68 +#define FSL_IMX6_GPIO2_HIGH_IRQ 69 +#define FSL_IMX6_GPIO3_LOW_IRQ 70 +#define FSL_IMX6_GPIO3_HIGH_IRQ 71 +#define FSL_IMX6_GPIO4_LOW_IRQ 72 +#define FSL_IMX6_GPIO4_HIGH_IRQ 73 +#define FSL_IMX6_GPIO5_LOW_IRQ 74 +#define FSL_IMX6_GPIO5_HIGH_IRQ 75 +#define FSL_IMX6_GPIO6_LOW_IRQ 76 +#define FSL_IMX6_GPIO6_HIGH_IRQ 77 +#define FSL_IMX6_GPIO7_LOW_IRQ 78 +#define FSL_IMX6_GPIO7_HIGH_IRQ 79 +#define FSL_IMX6_WDOG1_IRQ 80 +#define FSL_IMX6_WDOG2_IRQ 81 +#define FSL_IMX6_KPP_IRQ 82 +#define FSL_IMX6_PWM1_IRQ 83 +#define FSL_IMX6_PWM2_IRQ 84 +#define FSL_IMX6_PWM3_IRQ 85 +#define FSL_IMX6_PWM4_IRQ 86 +#define FSL_IMX6_CCM1_IRQ 87 +#define FSL_IMX6_CCM2_IRQ 88 +#define FSL_IMX6_GPC_IRQ 89 +#define FSL_IMX6_SRC_IRQ 91 +#define FSL_IMX6_CPU_L2_IRQ 92 +#define FSL_IMX6_CPU_PARITY_IRQ 93 +#define FSL_IMX6_CPU_PERF_IRQ 94 +#define FSL_IMX6_CPU_CTI_IRQ 95 +#define FSL_IMX6_SRC_COMB_IRQ 96 +#define FSL_IMX6_MIPI_CSI1_IRQ 100 +#define FSL_IMX6_MIPI_CSI2_IRQ 101 +#define FSL_IMX6_MIPI_DSI_IRQ 102 +#define FSL_IMX6_MIPI_HSI_IRQ 103 +#define FSL_IMX6_SJC_IRQ 104 +#define FSL_IMX6_CAAM0_IRQ 105 +#define FSL_IMX6_CAAM1_IRQ 106 +#define FSL_IMX6_ASC1_IRQ 108 +#define FSL_IMX6_ASC2_IRQ 109 +#define FSL_IMX6_FLEXCAN1_IRQ 110 +#define FSL_IMX6_FLEXCAN2_IRQ 111 +#define FSL_IMX6_HDMI_MASTER_IRQ 115 +#define FSL_IMX6_HDMI_CEC_IRQ 116 +#define FSL_IMX6_MLB150_LOW_IRQ 117 +#define FSL_IMX6_ENET_MAC_IRQ 118 +#define FSL_IMX6_ENET_MAC_1588_IRQ 119 +#define FSL_IMX6_PCIE1_IRQ 120 +#define FSL_IMX6_PCIE2_IRQ 121 +#define FSL_IMX6_PCIE3_IRQ 122 +#define FSL_IMX6_PCIE4_IRQ 123 +#define FSL_IMX6_DCIC1_IRQ 124 +#define FSL_IMX6_DCIC2_IRQ 125 +#define FSL_IMX6_MLB150_HIGH_IRQ 126 +#define FSL_IMX6_PMU2_IRQ 127 +#define FSL_IMX6_MAX_IRQ 128 + +#endif /* FSL_IMX6_H */ diff --git a/include/hw/misc/imx6_src.h b/include/hw/misc/imx6_src.h new file mode 100644 index 0000000000..eb3640732e --- /dev/null +++ b/include/hw/misc/imx6_src.h @@ -0,0 +1,73 @@ +/* + * IMX6 System Reset Controller + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX6_SRC_H +#define IMX6_SRC_H + +#include "hw/sysbus.h" +#include "qemu/bitops.h" + +#define SRC_SCR 0 +#define SRC_SBMR1 1 +#define SRC_SRSR 2 +#define SRC_SISR 5 +#define SRC_SIMR 6 +#define SRC_SBMR2 7 +#define SRC_GPR1 8 +#define SRC_GPR2 9 +#define SRC_GPR3 10 +#define SRC_GPR4 11 +#define SRC_GPR5 12 +#define SRC_GPR6 13 +#define SRC_GPR7 14 +#define SRC_GPR8 15 +#define SRC_GPR9 16 +#define SRC_GPR10 17 +#define SRC_MAX 18 + +/* SRC_SCR */ +#define CORE3_ENABLE_SHIFT 24 +#define CORE3_ENABLE_LENGTH 1 +#define CORE2_ENABLE_SHIFT 23 +#define CORE2_ENABLE_LENGTH 1 +#define CORE1_ENABLE_SHIFT 22 +#define CORE1_ENABLE_LENGTH 1 +#define CORE3_RST_SHIFT 16 +#define CORE3_RST_LENGTH 1 +#define CORE2_RST_SHIFT 15 +#define CORE2_RST_LENGTH 1 +#define CORE1_RST_SHIFT 14 +#define CORE1_RST_LENGTH 1 +#define CORE0_RST_SHIFT 13 +#define CORE0_RST_LENGTH 1 +#define SW_IPU1_RST_SHIFT 3 +#define SW_IPU1_RST_LENGTH 1 +#define SW_IPU2_RST_SHIFT 12 +#define SW_IPU2_RST_LENGTH 1 +#define WARM_RST_ENABLE_SHIFT 0 +#define WARM_RST_ENABLE_LENGTH 1 + +#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH) + +#define TYPE_IMX6_SRC "imx6.src" +#define IMX6_SRC(obj) OBJECT_CHECK(IMX6SRCState, (obj), TYPE_IMX6_SRC) + +typedef struct IMX6SRCState { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + + uint32_t regs[SRC_MAX]; + +} IMX6SRCState; + +#endif /* IMX6_SRC_H */ diff --git a/include/hw/ssi/imx_spi.h b/include/hw/ssi/imx_spi.h new file mode 100644 index 0000000000..7103953581 --- /dev/null +++ b/include/hw/ssi/imx_spi.h @@ -0,0 +1,103 @@ +/* + * IMX SPI Controller + * + * Copyright 2016 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX_SPI_H +#define IMX_SPI_H + +#include "hw/sysbus.h" +#include "hw/ssi/ssi.h" +#include "qemu/bitops.h" +#include "qemu/fifo32.h" + +#define ECSPI_FIFO_SIZE 64 + +#define ECSPI_RXDATA 0 +#define ECSPI_TXDATA 1 +#define ECSPI_CONREG 2 +#define ECSPI_CONFIGREG 3 +#define ECSPI_INTREG 4 +#define ECSPI_DMAREG 5 +#define ECSPI_STATREG 6 +#define ECSPI_PERIODREG 7 +#define ECSPI_TESTREG 8 +#define ECSPI_MSGDATA 16 +#define ECSPI_MAX 17 + +/* ECSPI_CONREG */ +#define ECSPI_CONREG_EN (1 << 0) +#define ECSPI_CONREG_HT (1 << 1) +#define ECSPI_CONREG_XCH (1 << 2) +#define ECSPI_CONREG_SMC (1 << 3) +#define ECSPI_CONREG_CHANNEL_MODE_SHIFT 4 +#define ECSPI_CONREG_CHANNEL_MODE_LENGTH 4 +#define ECSPI_CONREG_DRCTL_SHIFT 16 +#define ECSPI_CONREG_DRCTL_LENGTH 2 +#define ECSPI_CONREG_CHANNEL_SELECT_SHIFT 18 +#define ECSPI_CONREG_CHANNEL_SELECT_LENGTH 2 +#define ECSPI_CONREG_BURST_LENGTH_SHIFT 20 +#define ECSPI_CONREG_BURST_LENGTH_LENGTH 12 + +/* ECSPI_CONFIGREG */ +#define ECSPI_CONFIGREG_SS_CTL_SHIFT 8 +#define ECSPI_CONFIGREG_SS_CTL_LENGTH 4 + +/* ECSPI_INTREG */ +#define ECSPI_INTREG_TEEN (1 << 0) +#define ECSPI_INTREG_TDREN (1 << 1) +#define ECSPI_INTREG_TFEN (1 << 2) +#define ECSPI_INTREG_RREN (1 << 3) +#define ECSPI_INTREG_RDREN (1 << 4) +#define ECSPI_INTREG_RFEN (1 << 5) +#define ECSPI_INTREG_ROEN (1 << 6) +#define ECSPI_INTREG_TCEN (1 << 7) + +/* ECSPI_DMAREG */ +#define ECSPI_DMAREG_RXTDEN (1 << 31) +#define ECSPI_DMAREG_RXDEN (1 << 23) +#define ECSPI_DMAREG_TEDEN (1 << 7) +#define ECSPI_DMAREG_RX_THRESHOLD_SHIFT 16 +#define ECSPI_DMAREG_RX_THRESHOLD_LENGTH 6 + +/* ECSPI_STATREG */ +#define ECSPI_STATREG_TE (1 << 0) +#define ECSPI_STATREG_TDR (1 << 1) +#define ECSPI_STATREG_TF (1 << 2) +#define ECSPI_STATREG_RR (1 << 3) +#define ECSPI_STATREG_RDR (1 << 4) +#define ECSPI_STATREG_RF (1 << 5) +#define ECSPI_STATREG_RO (1 << 6) +#define ECSPI_STATREG_TC (1 << 7) + +#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH) + +#define TYPE_IMX_SPI "imx.spi" +#define IMX_SPI(obj) OBJECT_CHECK(IMXSPIState, (obj), TYPE_IMX_SPI) + +typedef struct IMXSPIState { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + + qemu_irq irq; + + qemu_irq cs_lines[4]; + + SSIBus *bus; + + uint32_t regs[ECSPI_MAX]; + + Fifo32 rx_fifo; + Fifo32 tx_fifo; + + int16_t burst_length; +} IMXSPIState; + +#endif /* IMX_SPI_H */ diff --git a/include/qapi/dealloc-visitor.h b/include/qapi/dealloc-visitor.h index cf4c36d2d3..45b06b248c 100644 --- a/include/qapi/dealloc-visitor.h +++ b/include/qapi/dealloc-visitor.h @@ -18,6 +18,11 @@ typedef struct QapiDeallocVisitor QapiDeallocVisitor; +/* + * The dealloc visitor is primarly used only by generated + * qapi_free_FOO() functions, and is the only visitor designed to work + * correctly in the face of a partially-constructed QAPI tree. + */ QapiDeallocVisitor *qapi_dealloc_visitor_new(void); void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *d); diff --git a/include/qapi/opts-visitor.h b/include/qapi/opts-visitor.h index fd48c14ec8..ae1bf7cf51 100644 --- a/include/qapi/opts-visitor.h +++ b/include/qapi/opts-visitor.h @@ -29,6 +29,11 @@ typedef struct OptsVisitor OptsVisitor; * - string representations of negative numbers yield negative values, * - values below INT64_MIN or LLONG_MIN are rejected, * - values above INT64_MAX or LLONG_MAX are rejected. + * + * The Opts input visitor does not implement support for visiting QAPI + * alternates, numbers (other than integers), null, or arbitrary + * QTypes. It also requires a non-null list argument to + * visit_start_list(). */ OptsVisitor *opts_visitor_new(const QemuOpts *opts); void opts_visitor_cleanup(OptsVisitor *nv); diff --git a/include/qapi/qmp-input-visitor.h b/include/qapi/qmp-input-visitor.h index 3ed499cc42..b0624d8683 100644 --- a/include/qapi/qmp-input-visitor.h +++ b/include/qapi/qmp-input-visitor.h @@ -19,8 +19,13 @@ typedef struct QmpInputVisitor QmpInputVisitor; -QmpInputVisitor *qmp_input_visitor_new(QObject *obj); -QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj); +/* + * Return a new input visitor that converts QMP to QAPI. + * + * Set @strict to reject a parse that doesn't consume all keys of a + * dictionary; otherwise excess input is ignored. + */ +QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict); void qmp_input_visitor_cleanup(QmpInputVisitor *v); diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 495520994c..5609946a16 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -19,11 +19,6 @@ typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); -typedef enum QmpCommandType -{ - QCT_NORMAL, -} QmpCommandType; - typedef enum QmpCommandOptions { QCO_NO_OPTIONS = 0x0, @@ -33,7 +28,6 @@ typedef enum QmpCommandOptions typedef struct QmpCommand { const char *name; - QmpCommandType type; QmpCommandFunc *fn; QmpCommandOptions options; QTAILQ_ENTRY(QmpCommand) node; diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h index 089243c09e..7b76c2b9e3 100644 --- a/include/qapi/string-input-visitor.h +++ b/include/qapi/string-input-visitor.h @@ -17,6 +17,11 @@ typedef struct StringInputVisitor StringInputVisitor; +/* + * The string input visitor does not implement support for visiting + * QAPI structs, alternates, null, or arbitrary QTypes. It also + * requires a non-null list argument to visit_start_list(). + */ StringInputVisitor *string_input_visitor_new(const char *str); void string_input_visitor_cleanup(StringInputVisitor *v); diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h index d99717f650..e10522a35b 100644 --- a/include/qapi/string-output-visitor.h +++ b/include/qapi/string-output-visitor.h @@ -17,6 +17,11 @@ typedef struct StringOutputVisitor StringOutputVisitor; +/* + * The string output visitor does not implement support for visiting + * QAPI structs, alternates, null, or arbitrary QTypes. It also + * requires a non-null list argument to visit_start_list(). + */ StringOutputVisitor *string_output_visitor_new(bool human); void string_output_visitor_cleanup(StringOutputVisitor *v); diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index 2bd8f292b2..145afd03e7 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -14,55 +14,96 @@ #include "qapi/visitor.h" +/* + * This file describes the callback interface for implementing a QAPI + * visitor. For the client interface, see visitor.h. When + * implementing the callbacks, it is easiest to declare a struct with + * 'Visitor visitor;' as the first member. A callback's contract + * matches the corresponding public functions' contract unless stated + * otherwise. In the comments below, some callbacks are marked "must + * be set for $TYPE visits to work"; if a visitor implementation omits + * that callback, it should also document that it is only useful for a + * subset of QAPI. + */ + +/* + * There are three classes of visitors; setting the class determines + * how QAPI enums are visited, as well as what additional restrictions + * can be asserted. + */ +typedef enum VisitorType { + VISITOR_INPUT, + VISITOR_OUTPUT, + VISITOR_DEALLOC, +} VisitorType; + struct Visitor { - /* Must be set */ + /* Must be set to visit structs */ void (*start_struct)(Visitor *v, const char *name, void **obj, size_t size, Error **errp); - void (*end_struct)(Visitor *v, Error **errp); - void (*start_list)(Visitor *v, const char *name, Error **errp); + /* Optional; intended for input visitors */ + void (*check_struct)(Visitor *v, Error **errp); + + /* Must be set to visit structs */ + void (*end_struct)(Visitor *v); + + /* Must be set; implementations may require @list to be non-null, + * but must document it. */ + void (*start_list)(Visitor *v, const char *name, GenericList **list, + size_t size, Error **errp); + /* Must be set */ - GenericList *(*next_list)(Visitor *v, GenericList **list, size_t size); + GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size); + /* Must be set */ void (*end_list)(Visitor *v); - /* Optional, needed for input and dealloc visitors. */ + /* Must be set by input and dealloc visitors to visit alternates; + * optional for output visitors. */ void (*start_alternate)(Visitor *v, const char *name, GenericAlternate **obj, size_t size, bool promote_int, Error **errp); - /* Optional, needed for dealloc visitor. */ + /* Optional, needed for dealloc visitor */ void (*end_alternate)(Visitor *v); - /* Must be set. */ - void (*type_enum)(Visitor *v, const char *name, int *obj, - const char *const strings[], Error **errp); - - /* Must be set. */ + /* Must be set */ void (*type_int64)(Visitor *v, const char *name, int64_t *obj, Error **errp); - /* Must be set. */ + + /* Must be set */ void (*type_uint64)(Visitor *v, const char *name, uint64_t *obj, Error **errp); - /* Optional; fallback is type_uint64(). */ + + /* Optional; fallback is type_uint64() */ void (*type_size)(Visitor *v, const char *name, uint64_t *obj, Error **errp); - /* Must be set. */ + + /* Must be set */ void (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp); + + /* Must be set */ void (*type_str)(Visitor *v, const char *name, char **obj, Error **errp); + + /* Must be set to visit numbers */ void (*type_number)(Visitor *v, const char *name, double *obj, Error **errp); + + /* Must be set to visit arbitrary QTypes */ void (*type_any)(Visitor *v, const char *name, QObject **obj, Error **errp); - /* May be NULL; most useful for input visitors. */ + /* Must be set to visit explicit null values. */ + void (*type_null)(Visitor *v, const char *name, Error **errp); + + /* Must be set for input visitors, optional otherwise. The core + * takes care of the return type in the public interface. */ void (*optional)(Visitor *v, const char *name, bool *present); -}; -void input_type_enum(Visitor *v, const char *name, int *obj, - const char *const strings[], Error **errp); -void output_type_enum(Visitor *v, const char *name, int *obj, - const char *const strings[], Error **errp); + /* Must be set */ + VisitorType type; +}; #endif diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index 9a8d0105fb..4d12167bdc 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -16,8 +16,199 @@ #include "qapi/qmp/qobject.h" +/* + * The QAPI schema defines both a set of C data types, and a QMP wire + * format. QAPI objects can contain references to other QAPI objects, + * resulting in a directed acyclic graph. QAPI also generates visitor + * functions to walk these graphs. This file represents the interface + * for doing work at each node of a QAPI graph; it can also be used + * for a virtual walk, where there is no actual QAPI C struct. + * + * There are three kinds of visitor classes: input visitors (QMP, + * string, and QemuOpts) parse an external representation and build + * the corresponding QAPI graph, output visitors (QMP and string) take + * a completed QAPI graph and generate an external representation, and + * the dealloc visitor can take a QAPI graph (possibly partially + * constructed) and recursively free its resources. While the dealloc + * and QMP input/output visitors are general, the string and QemuOpts + * visitors have some implementation limitations; see the + * documentation for each visitor for more details on what it + * supports. Also, see visitor-impl.h for the callback contracts + * implemented by each visitor, and docs/qapi-code-gen.txt for more + * about the QAPI code generator. + * + * All QAPI types have a corresponding function with a signature + * roughly compatible with this: + * + * void visit_type_FOO(Visitor *v, const char *name, T obj, Error **errp); + * + * where T is FOO for scalar types, and FOO * otherwise. The scalar + * visitors are declared here; the remaining visitors are generated in + * qapi-visit.h. + * + * The @name parameter of visit_type_FOO() describes the relation + * between this QAPI value and its parent container. When visiting + * the root of a tree, @name is ignored; when visiting a member of an + * object, @name is the key associated with the value; and when + * visiting a member of a list, @name is NULL. + * + * FIXME: Clients must pass NULL for @name when visiting a member of a + * list, but this leads to poor error messages; it might be nicer to + * require a non-NULL name such as "key.0" for '{ "key": [ "value" ] + * }' if an error is encountered on "value" (or to have the visitor + * core auto-generate the nicer name). + * + * The visit_type_FOO() functions expect a non-null @obj argument; + * they allocate *@obj during input visits, leave it unchanged on + * output visits, and recursively free any resources during a dealloc + * visit. Each function also takes the customary @errp argument (see + * qapi/error.h for details), for reporting any errors (such as if a + * member @name is not present, or is present but not the specified + * type). + * + * If an error is detected during visit_type_FOO() with an input + * visitor, then *@obj will be NULL for pointer types, and left + * unchanged for scalar types. Using an output visitor with an + * incomplete object has undefined behavior (other than a special case + * for visit_type_str() treating NULL like ""), while the dealloc + * visitor safely handles incomplete objects. Since input visitors + * never produce an incomplete object, such an object is possible only + * by manual construction. + * + * For the QAPI object types (structs, unions, and alternates), there + * is an additional generated function in qapi-visit.h compatible + * with: + * + * void visit_type_FOO_members(Visitor *v, FOO *obj, Error **errp); + * + * for visiting the members of a type without also allocating the QAPI + * struct. + * + * Additionally, in qapi-types.h, all QAPI pointer types (structs, + * unions, alternates, and lists) have a generated function compatible + * with: + * + * void qapi_free_FOO(FOO *obj); + * + * which behaves like free() in that @obj may be NULL. Because of + * these functions, the dealloc visitor is seldom used directly + * outside of generated code. QAPI types can also inherit from a base + * class; when this happens, a function is generated for easily going + * from the derived type to the base type: + * + * BASE *qapi_CHILD_base(CHILD *obj); + * + * For a real QAPI struct, typical input usage involves: + * + * <example> + * Foo *f; + * Error *err = NULL; + * Visitor *v; + * + * v = ...obtain input visitor... + * visit_type_Foo(v, NULL, &f, &err); + * if (err) { + * ...handle error... + * } else { + * ...use f... + * } + * ...clean up v... + * qapi_free_Foo(f); + * </example> + * + * For a list, it is: + * <example> + * FooList *l; + * Error *err = NULL; + * Visitor *v; + * + * v = ...obtain input visitor... + * visit_type_FooList(v, NULL, &l, &err); + * if (err) { + * ...handle error... + * } else { + * for ( ; l; l = l->next) { + * ...use l->value... + * } + * } + * ...clean up v... + * qapi_free_FooList(l); + * </example> + * + * Similarly, typical output usage is: + * + * <example> + * Foo *f = ...obtain populated object... + * Error *err = NULL; + * Visitor *v; + * + * v = ...obtain output visitor... + * visit_type_Foo(v, NULL, &f, &err); + * if (err) { + * ...handle error... + * } + * ...clean up v... + * </example> + * + * When visiting a real QAPI struct, this file provides several + * helpers that rely on in-tree information to control the walk: + * visit_optional() for the 'has_member' field associated with + * optional 'member' in the C struct; and visit_next_list() for + * advancing through a FooList linked list. Similarly, the + * visit_is_input() helper makes it possible to write code that is + * visitor-agnostic everywhere except for cleanup. Only the generated + * visit_type functions need to use these helpers. + * + * It is also possible to use the visitors to do a virtual walk, where + * no actual QAPI struct is present. In this situation, decisions + * about what needs to be walked are made by the calling code, and + * structured visits are split between pairs of start and end methods + * (where the end method must be called if the start function + * succeeded, even if an intermediate visit encounters an error). + * Thus, a virtual walk corresponding to '{ "list": [1, 2] }' looks + * like: + * + * <example> + * Visitor *v; + * Error *err = NULL; + * int value; + * + * v = ...obtain visitor... + * visit_start_struct(v, NULL, NULL, 0, &err); + * if (err) { + * goto out; + * } + * visit_start_list(v, "list", NULL, 0, &err); + * if (err) { + * goto outobj; + * } + * value = 1; + * visit_type_int(v, NULL, &value, &err); + * if (err) { + * goto outlist; + * } + * value = 2; + * visit_type_int(v, NULL, &value, &err); + * if (err) { + * goto outlist; + * } + * outlist: + * visit_end_list(v); + * if (!err) { + * visit_check_struct(v, &err); + * } + * outobj: + * visit_end_struct(v); + * out: + * error_propagate(errp, err); + * ...clean up v... + * </example> + */ + +/*** Useful types ***/ + /* This struct is layout-compatible with all other *List structs - * created by the qapi generator. It is used as a typical + * created by the QAPI generator. It is used as a typical * singly-linked list. */ typedef struct GenericList { struct GenericList *next; @@ -25,35 +216,139 @@ typedef struct GenericList { } GenericList; /* This struct is layout-compatible with all Alternate types - * created by the qapi generator. */ + * created by the QAPI generator. */ typedef struct GenericAlternate { QType type; char padding[]; } GenericAlternate; +/*** Visiting structures ***/ + +/* + * Start visiting an object @obj (struct or union). + * + * @name expresses the relationship of this object to its parent + * container; see the general description of @name above. + * + * @obj must be non-NULL for a real walk, in which case @size + * determines how much memory an input visitor will allocate into + * *@obj. @obj may also be NULL for a virtual walk, in which case + * @size is ignored. + * + * @errp obeys typical error usage, and reports failures such as a + * member @name is not present, or present but not an object. On + * error, input visitors set *@obj to NULL. + * + * After visit_start_struct() succeeds, the caller may visit its + * members one after the other, passing the member's name and address + * within the struct. Finally, visit_end_struct() needs to be called + * to clean up, even if intermediate visits fail. See the examples + * above. + * + * FIXME Should this be named visit_start_object, since it is also + * used for QAPI unions, and maps to JSON objects? + */ void visit_start_struct(Visitor *v, const char *name, void **obj, size_t size, Error **errp); -void visit_end_struct(Visitor *v, Error **errp); -void visit_start_list(Visitor *v, const char *name, Error **errp); -GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size); +/* + * Prepare for completing an object visit. + * + * @errp obeys typical error usage, and reports failures such as + * unparsed keys remaining in the input stream. + * + * Should be called prior to visit_end_struct() if all other + * intermediate visit steps were successful, to allow the visitor one + * last chance to report errors. May be skipped on a cleanup path, + * where there is no need to check for further errors. + */ +void visit_check_struct(Visitor *v, Error **errp); + +/* + * Complete an object visit started earlier. + * + * Must be called after any successful use of visit_start_struct(), + * even if intermediate processing was skipped due to errors, to allow + * the backend to release any resources. Destroying the visitor early + * behaves as if this was implicitly called. + */ +void visit_end_struct(Visitor *v); + + +/*** Visiting lists ***/ + +/* + * Start visiting a list. + * + * @name expresses the relationship of this list to its parent + * container; see the general description of @name above. + * + * @list must be non-NULL for a real walk, in which case @size + * determines how much memory an input visitor will allocate into + * *@list (at least sizeof(GenericList)). Some visitors also allow + * @list to be NULL for a virtual walk, in which case @size is + * ignored. + * + * @errp obeys typical error usage, and reports failures such as a + * member @name is not present, or present but not a list. On error, + * input visitors set *@list to NULL. + * + * After visit_start_list() succeeds, the caller may visit its members + * one after the other. A real visit (where @obj is non-NULL) uses + * visit_next_list() for traversing the linked list, while a virtual + * visit (where @obj is NULL) uses other means. For each list + * element, call the appropriate visit_type_FOO() with name set to + * NULL and obj set to the address of the value member of the list + * element. Finally, visit_end_list() needs to be called to clean up, + * even if intermediate visits fail. See the examples above. + */ +void visit_start_list(Visitor *v, const char *name, GenericList **list, + size_t size, Error **errp); + +/* + * Iterate over a GenericList during a non-virtual list visit. + * + * @size represents the size of a linked list node (at least + * sizeof(GenericList)). + * + * @tail must not be NULL; on the first call, @tail is the value of + * *list after visit_start_list(), and on subsequent calls @tail must + * be the previously returned value. Should be called in a loop until + * a NULL return or error occurs; for each non-NULL return, the caller + * then calls the appropriate visit_type_*() for the element type of + * the list, with that function's name parameter set to NULL and obj + * set to the address of @tail->value. + */ +GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size); + +/* + * Complete a list visit started earlier. + * + * Must be called after any successful use of visit_start_list(), even + * if intermediate processing was skipped due to errors, to allow the + * backend to release any resources. Destroying the visitor early + * behaves as if this was implicitly called. + */ void visit_end_list(Visitor *v); + +/*** Visiting alternates ***/ + /* - * Start the visit of an alternate @obj with the given @size. + * Start the visit of an alternate @obj. * - * @name specifies the relationship to the containing struct (ignored - * for a top level visit, the name of the key if this alternate is - * part of an object, or NULL if this alternate is part of a list). + * @name expresses the relationship of this alternate to its parent + * container; see the general description of @name above. * - * @obj must not be NULL. Input visitors will allocate @obj and - * determine the qtype of the next thing to be visited, stored in - * (*@obj)->type. Other visitors will leave @obj unchanged. + * @obj must not be NULL. Input visitors use @size to determine how + * much memory to allocate into *@obj, then determine the qtype of the + * next thing to be visited, stored in (*@obj)->type. Other visitors + * will leave @obj unchanged. * * If @promote_int, treat integers as QTYPE_FLOAT. * - * If successful, this must be paired with visit_end_alternate(), even - * if visiting the contents of the alternate fails. + * If successful, this must be paired with visit_end_alternate() to + * clean up, even if visiting the contents of the alternate fails. */ void visit_start_alternate(Visitor *v, const char *name, GenericAlternate **obj, size_t size, @@ -62,46 +357,202 @@ void visit_start_alternate(Visitor *v, const char *name, /* * Finish visiting an alternate type. * - * Must be called after a successful visit_start_alternate(), even if - * an error occurred in the meantime. + * Must be called after any successful use of visit_start_alternate(), + * even if intermediate processing was skipped due to errors, to allow + * the backend to release any resources. Destroying the visitor early + * behaves as if this was implicitly called. * * TODO: Should all the visit_end_* interfaces take obj parameter, so * that dealloc visitor need not track what was passed in visit_start? */ void visit_end_alternate(Visitor *v); -/** - * Check if an optional member @name of an object needs visiting. - * For input visitors, set *@present according to whether the - * corresponding visit_type_*() needs calling; for other visitors, - * leave *@present unchanged. Return *@present for convenience. + +/*** Other helpers ***/ + +/* + * Does optional struct member @name need visiting? + * + * @name must not be NULL. This function is only useful between + * visit_start_struct() and visit_end_struct(), since only objects + * have optional keys. + * + * @present points to the address of the optional member's has_ flag. + * + * Input visitors set *@present according to input; other visitors + * leave it unchanged. In either case, return *@present for + * convenience. */ bool visit_optional(Visitor *v, const char *name, bool *present); +/* + * Visit an enum value. + * + * @name expresses the relationship of this enum to its parent + * container; see the general description of @name above. + * + * @obj must be non-NULL. Input visitors parse input and set *@obj to + * the enumeration value, leaving @obj unchanged on error; other + * visitors use *@obj but leave it unchanged. + * + * Currently, all input visitors parse text input, and all output + * visitors produce text output. The mapping between enumeration + * values and strings is done by the visitor core, using @strings; it + * should be the ENUM_lookup array from visit-types.h. + * + * May call visit_type_str() under the hood, and the enum visit may + * fail even if the corresponding string visit succeeded; this implies + * that visit_type_str() must have no unwelcome side effects. + */ void visit_type_enum(Visitor *v, const char *name, int *obj, const char *const strings[], Error **errp); + +/* + * Check if visitor is an input visitor. + */ +bool visit_is_input(Visitor *v); + +/*** Visiting built-in types ***/ + +/* + * Visit an integer value. + * + * @name expresses the relationship of this integer to its parent + * container; see the general description of @name above. + * + * @obj must be non-NULL. Input visitors set *@obj to the value; + * other visitors will leave *@obj unchanged. + */ void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp); + +/* + * Visit a uint8_t value. + * Like visit_type_int(), except clamps the value to uint8_t range. + */ void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, Error **errp); + +/* + * Visit a uint16_t value. + * Like visit_type_int(), except clamps the value to uint16_t range. + */ void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, Error **errp); + +/* + * Visit a uint32_t value. + * Like visit_type_int(), except clamps the value to uint32_t range. + */ void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, Error **errp); + +/* + * Visit a uint64_t value. + * Like visit_type_int(), except clamps the value to uint64_t range, + * that is, ensures it is unsigned. + */ void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp); + +/* + * Visit an int8_t value. + * Like visit_type_int(), except clamps the value to int8_t range. + */ void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp); + +/* + * Visit an int16_t value. + * Like visit_type_int(), except clamps the value to int16_t range. + */ void visit_type_int16(Visitor *v, const char *name, int16_t *obj, Error **errp); + +/* + * Visit an int32_t value. + * Like visit_type_int(), except clamps the value to int32_t range. + */ void visit_type_int32(Visitor *v, const char *name, int32_t *obj, Error **errp); + +/* + * Visit an int64_t value. + * Identical to visit_type_int(). + */ void visit_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp); + +/* + * Visit a uint64_t value. + * Like visit_type_uint64(), except that some visitors may choose to + * recognize additional syntax, such as suffixes for easily scaling + * values. + */ void visit_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp); + +/* + * Visit a boolean value. + * + * @name expresses the relationship of this boolean to its parent + * container; see the general description of @name above. + * + * @obj must be non-NULL. Input visitors set *@obj to the value; + * other visitors will leave *@obj unchanged. + */ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp); + +/* + * Visit a string value. + * + * @name expresses the relationship of this string to its parent + * container; see the general description of @name above. + * + * @obj must be non-NULL. Input visitors set *@obj to the value + * (never NULL). Other visitors leave *@obj unchanged, and commonly + * treat NULL like "". + * + * It is safe to cast away const when preparing a (const char *) value + * into @obj for use by an output visitor. + * + * FIXME: Callers that try to output NULL *obj should not be allowed. + */ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp); + +/* + * Visit a number (i.e. double) value. + * + * @name expresses the relationship of this number to its parent + * container; see the general description of @name above. + * + * @obj must be non-NULL. Input visitors set *@obj to the value; + * other visitors will leave *@obj unchanged. Visitors should + * document if infinity or NaN are not permitted. + */ void visit_type_number(Visitor *v, const char *name, double *obj, Error **errp); + +/* + * Visit an arbitrary value. + * + * @name expresses the relationship of this value to its parent + * container; see the general description of @name above. + * + * @obj must be non-NULL. Input visitors set *@obj to the value; + * other visitors will leave *@obj unchanged. *@obj must be non-NULL + * for output visitors. + */ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp); +/* + * Visit a JSON null value. + * + * @name expresses the relationship of the null value to its parent + * container; see the general description of @name above. + * + * Unlike all other visit_type_* functions, no obj parameter is + * needed; rather, this is a witness that an explicit null value is + * expected rather than any other type. + */ +void visit_type_null(Visitor *v, const char *name, Error **errp); + #endif diff --git a/include/qemu/fifo32.h b/include/qemu/fifo32.h new file mode 100644 index 0000000000..2e5a0ccddf --- /dev/null +++ b/include/qemu/fifo32.h @@ -0,0 +1,191 @@ +/* + * Generic FIFO32 component, based on FIFO8. + * + * Copyright (c) 2016 Jean-Christophe Dubois + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef FIFO32_H +#define FIFO32_H + +#include "qemu/osdep.h" +#include "qemu/fifo8.h" + +typedef struct { + Fifo8 fifo; +} Fifo32; + +/** + * fifo32_create: + * @fifo: struct Fifo32 to initialise with new FIFO + * @capacity: capacity of the newly created FIFO expressed in 32 bit words + * + * Create a FIFO of the specified size. Clients should call fifo32_destroy() + * when finished using the fifo. The FIFO is initially empty. + */ + +static inline void fifo32_create(Fifo32 *fifo, uint32_t capacity) +{ + fifo8_create(&fifo->fifo, capacity * sizeof(uint32_t)); +} + +/** + * fifo32_destroy: + * @fifo: FIFO to cleanup + * + * Cleanup a FIFO created with fifo32_create(). Frees memory created for FIFO + * storage. The FIFO is no longer usable after this has been called. + */ + +static inline void fifo32_destroy(Fifo32 *fifo) +{ + fifo8_destroy(&fifo->fifo); +} + +/** + * fifo32_num_free: + * @fifo: FIFO to check + * + * Return the number of free uint32_t slots in the FIFO. + * + * Returns: Number of free 32 bit words. + */ + +static inline uint32_t fifo32_num_free(Fifo32 *fifo) +{ + return DIV_ROUND_UP(fifo8_num_free(&fifo->fifo), sizeof(uint32_t)); +} + +/** + * fifo32_num_used: + * @fifo: FIFO to check + * + * Return the number of used uint32_t slots in the FIFO. + * + * Returns: Number of used 32 bit words. + */ + +static inline uint32_t fifo32_num_used(Fifo32 *fifo) +{ + return DIV_ROUND_UP(fifo8_num_used(&fifo->fifo), sizeof(uint32_t)); +} + +/** + * fifo32_push: + * @fifo: FIFO to push to + * @data: 32 bits data word to push + * + * Push a 32 bits data word to the FIFO. Behaviour is undefined if the FIFO + * is full. Clients are responsible for checking for fullness using + * fifo32_is_full(). + */ + +static inline void fifo32_push(Fifo32 *fifo, uint32_t data) +{ + int i; + + for (i = 0; i < sizeof(data); i++) { + fifo8_push(&fifo->fifo, data & 0xff); + data >>= 8; + } +} + +/** + * fifo32_push_all: + * @fifo: FIFO to push to + * @data: data to push + * @size: number of 32 bit words to push + * + * Push a 32 bit word array to the FIFO. Behaviour is undefined if the FIFO + * is full. Clients are responsible for checking the space left in the FIFO + * using fifo32_num_free(). + */ + +static inline void fifo32_push_all(Fifo32 *fifo, const uint32_t *data, + uint32_t num) +{ + int i; + + for (i = 0; i < num; i++) { + fifo32_push(fifo, data[i]); + } +} + +/** + * fifo32_pop: + * @fifo: fifo to pop from + * + * Pop a 32 bits data word from the FIFO. Behaviour is undefined if the FIFO + * is empty. Clients are responsible for checking for emptiness using + * fifo32_is_empty(). + * + * Returns: The popped 32 bits data word. + */ + +static inline uint32_t fifo32_pop(Fifo32 *fifo) +{ + uint32_t ret = 0; + int i; + + for (i = 0; i < sizeof(uint32_t); i++) { + ret |= (fifo8_pop(&fifo->fifo) << (i * 8)); + } + + return ret; +} + +/** + * There is no fifo32_pop_buf() because the data is not stored in the buffer + * as a set of native-order words. + */ + +/** + * fifo32_reset: + * @fifo: FIFO to reset + * + * Reset a FIFO. All data is discarded and the FIFO is emptied. + */ + +static inline void fifo32_reset(Fifo32 *fifo) +{ + fifo8_reset(&fifo->fifo); +} + +/** + * fifo32_is_empty: + * @fifo: FIFO to check + * + * Check if a FIFO is empty. + * + * Returns: True if the fifo is empty, false otherwise. + */ + +static inline bool fifo32_is_empty(Fifo32 *fifo) +{ + return fifo8_is_empty(&fifo->fifo); +} + +/** + * fifo32_is_full: + * @fifo: FIFO to check + * + * Check if a FIFO is full. + * + * Returns: True if the fifo is full, false otherwise. + */ + +static inline bool fifo32_is_full(Fifo32 *fifo) +{ + return fifo8_num_free(&fifo->fifo) < sizeof(uint32_t); +} + +#define VMSTATE_FIFO32(_field, _state) VMSTATE_FIFO8(_field.fifo, _state) + +#endif /* FIFO32_H */ diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 602f2609cc..4cf1cf885b 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -23,9 +23,8 @@ enum ListMode { LM_NONE, /* not traversing a list of repeated options */ - LM_STARTED, /* opts_start_list() succeeded */ - LM_IN_PROGRESS, /* opts_next_list() has been called. + LM_IN_PROGRESS, /* opts_next_list() ready to be called. * * Generating the next list link will consume the most * recently parsed QemuOpt instance of the repeated @@ -133,7 +132,7 @@ opts_start_struct(Visitor *v, const char *name, void **obj, const QemuOpt *opt; if (obj) { - *obj = g_malloc0(size > 0 ? size : 1); + *obj = g_malloc0(size); } if (ov->depth++ > 0) { return; @@ -159,13 +158,13 @@ opts_start_struct(Visitor *v, const char *name, void **obj, static void -opts_end_struct(Visitor *v, Error **errp) +opts_check_struct(Visitor *v, Error **errp) { OptsVisitor *ov = to_ov(v); GHashTableIter iter; GQueue *any; - if (--ov->depth > 0) { + if (ov->depth > 0) { return; } @@ -177,6 +176,18 @@ opts_end_struct(Visitor *v, Error **errp) first = g_queue_peek_head(any); error_setg(errp, QERR_INVALID_PARAMETER, first->name); } +} + + +static void +opts_end_struct(Visitor *v) +{ + OptsVisitor *ov = to_ov(v); + + if (--ov->depth > 0) { + return; + } + g_hash_table_destroy(ov->unprocessed_opts); ov->unprocessed_opts = NULL; if (ov->fake_id_opt) { @@ -202,35 +213,33 @@ lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp) static void -opts_start_list(Visitor *v, const char *name, Error **errp) +opts_start_list(Visitor *v, const char *name, GenericList **list, size_t size, + Error **errp) { OptsVisitor *ov = to_ov(v); /* we can't traverse a list in a list */ assert(ov->list_mode == LM_NONE); + /* we don't support visits without a list */ + assert(list); ov->repeated_opts = lookup_distinct(ov, name, errp); - if (ov->repeated_opts != NULL) { - ov->list_mode = LM_STARTED; + if (ov->repeated_opts) { + ov->list_mode = LM_IN_PROGRESS; + *list = g_malloc0(size); + } else { + *list = NULL; } } static GenericList * -opts_next_list(Visitor *v, GenericList **list, size_t size) +opts_next_list(Visitor *v, GenericList *tail, size_t size) { OptsVisitor *ov = to_ov(v); - GenericList **link; switch (ov->list_mode) { - case LM_STARTED: - ov->list_mode = LM_IN_PROGRESS; - link = list; - break; - case LM_SIGNED_INTERVAL: case LM_UNSIGNED_INTERVAL: - link = &(*list)->next; - if (ov->list_mode == LM_SIGNED_INTERVAL) { if (ov->range_next.s < ov->range_limit.s) { ++ov->range_next.s; @@ -251,7 +260,6 @@ opts_next_list(Visitor *v, GenericList **list, size_t size) g_hash_table_remove(ov->unprocessed_opts, opt->name); return NULL; } - link = &(*list)->next; break; } @@ -259,8 +267,8 @@ opts_next_list(Visitor *v, GenericList **list, size_t size) abort(); } - *link = g_malloc0(size); - return *link; + tail->next = g_malloc0(size); + return tail->next; } @@ -269,8 +277,7 @@ opts_end_list(Visitor *v) { OptsVisitor *ov = to_ov(v); - assert(ov->list_mode == LM_STARTED || - ov->list_mode == LM_IN_PROGRESS || + assert(ov->list_mode == LM_IN_PROGRESS || ov->list_mode == LM_SIGNED_INTERVAL || ov->list_mode == LM_UNSIGNED_INTERVAL); ov->repeated_opts = NULL; @@ -314,9 +321,15 @@ opts_type_str(Visitor *v, const char *name, char **obj, Error **errp) opt = lookup_scalar(ov, name, errp); if (!opt) { + *obj = NULL; return; } *obj = g_strdup(opt->str ? opt->str : ""); + /* Note that we consume a string even if this is called as part of + * an enum visit that later fails because the string is not a + * valid enum value; this is harmless because tracking what gets + * consumed only matters to visit_end_struct() as the final error + * check if there were no other failures during the visit. */ processed(ov, name); } @@ -507,23 +520,16 @@ opts_visitor_new(const QemuOpts *opts) ov = g_malloc0(sizeof *ov); + ov->visitor.type = VISITOR_INPUT; + ov->visitor.start_struct = &opts_start_struct; + ov->visitor.check_struct = &opts_check_struct; ov->visitor.end_struct = &opts_end_struct; ov->visitor.start_list = &opts_start_list; ov->visitor.next_list = &opts_next_list; ov->visitor.end_list = &opts_end_list; - /* input_type_enum() covers both "normal" enums and union discriminators. - * The union discriminator field is always generated as "type"; it should - * match the "type" QemuOpt child of any QemuOpts. - * - * input_type_enum() will remove the looked-up key from the - * "unprocessed_opts" hash even if the lookup fails, because the removal is - * done earlier in opts_type_str(). This should be harmless. - */ - ov->visitor.type_enum = &input_type_enum; - ov->visitor.type_int64 = &opts_type_int64; ov->visitor.type_uint64 = &opts_type_uint64; ov->visitor.type_size = &opts_type_size; diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index 69221794ec..cd68b55a1a 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -22,7 +22,6 @@ typedef struct StackEntry { void *value; - bool is_list_head; QTAILQ_ENTRY(StackEntry) node; } StackEntry; @@ -43,10 +42,6 @@ static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value) e->value = value; - /* see if we're just pushing a list head tracker */ - if (value == NULL) { - e->is_list_head = true; - } QTAILQ_INSERT_HEAD(&qov->stack, e, node); } @@ -67,7 +62,7 @@ static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj, qapi_dealloc_push(qov, obj); } -static void qapi_dealloc_end_struct(Visitor *v, Error **errp) +static void qapi_dealloc_end_struct(Visitor *v) { QapiDeallocVisitor *qov = to_qov(v); void **obj = qapi_dealloc_pop(qov); @@ -93,38 +88,22 @@ static void qapi_dealloc_end_alternate(Visitor *v) } } -static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp) +static void qapi_dealloc_start_list(Visitor *v, const char *name, + GenericList **list, size_t size, + Error **errp) { - QapiDeallocVisitor *qov = to_qov(v); - qapi_dealloc_push(qov, NULL); } -static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp, +static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *tail, size_t size) { - GenericList *list = *listp; - QapiDeallocVisitor *qov = to_qov(v); - StackEntry *e = QTAILQ_FIRST(&qov->stack); - - if (e && e->is_list_head) { - e->is_list_head = false; - return list; - } - - if (list) { - list = list->next; - g_free(*listp); - return list; - } - - return NULL; + GenericList *next = tail->next; + g_free(tail); + return next; } static void qapi_dealloc_end_list(Visitor *v) { - QapiDeallocVisitor *qov = to_qov(v); - void *obj = qapi_dealloc_pop(qov); - assert(obj == NULL); /* should've been list head tracker with no payload */ } static void qapi_dealloc_type_str(Visitor *v, const char *name, char **obj, @@ -163,8 +142,7 @@ static void qapi_dealloc_type_anything(Visitor *v, const char *name, } } -static void qapi_dealloc_type_enum(Visitor *v, const char *name, int *obj, - const char * const strings[], Error **errp) +static void qapi_dealloc_type_null(Visitor *v, const char *name, Error **errp) { } @@ -184,6 +162,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void) v = g_malloc0(sizeof(*v)); + v->visitor.type = VISITOR_DEALLOC; v->visitor.start_struct = qapi_dealloc_start_struct; v->visitor.end_struct = qapi_dealloc_end_struct; v->visitor.start_alternate = qapi_dealloc_start_alternate; @@ -191,13 +170,13 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void) v->visitor.start_list = qapi_dealloc_start_list; v->visitor.next_list = qapi_dealloc_next_list; v->visitor.end_list = qapi_dealloc_end_list; - v->visitor.type_enum = qapi_dealloc_type_enum; v->visitor.type_int64 = qapi_dealloc_type_int64; v->visitor.type_uint64 = qapi_dealloc_type_uint64; v->visitor.type_bool = qapi_dealloc_type_bool; v->visitor.type_str = qapi_dealloc_type_str; v->visitor.type_number = qapi_dealloc_type_number; v->visitor.type_any = qapi_dealloc_type_anything; + v->visitor.type_null = qapi_dealloc_type_null; QTAILQ_INIT(&v->stack); diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index fa680c9991..eada4676a2 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -23,23 +23,48 @@ void visit_start_struct(Visitor *v, const char *name, void **obj, size_t size, Error **errp) { - v->start_struct(v, name, obj, size, errp); + Error *err = NULL; + + if (obj) { + assert(size); + assert(v->type != VISITOR_OUTPUT || *obj); + } + v->start_struct(v, name, obj, size, &err); + if (obj && v->type == VISITOR_INPUT) { + assert(!err != !*obj); + } + error_propagate(errp, err); } -void visit_end_struct(Visitor *v, Error **errp) +void visit_check_struct(Visitor *v, Error **errp) { - v->end_struct(v, errp); + if (v->check_struct) { + v->check_struct(v, errp); + } } -void visit_start_list(Visitor *v, const char *name, Error **errp) +void visit_end_struct(Visitor *v) { - v->start_list(v, name, errp); + v->end_struct(v); } -GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size) +void visit_start_list(Visitor *v, const char *name, GenericList **list, + size_t size, Error **errp) { - assert(list && size >= sizeof(GenericList)); - return v->next_list(v, list, size); + Error *err = NULL; + + assert(!list || size >= sizeof(GenericList)); + v->start_list(v, name, list, size, &err); + if (list && v->type == VISITOR_INPUT) { + assert(!(err && *list)); + } + error_propagate(errp, err); +} + +GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size) +{ + assert(tail && size >= sizeof(GenericList)); + return v->next_list(v, tail, size); } void visit_end_list(Visitor *v) @@ -51,10 +76,17 @@ void visit_start_alternate(Visitor *v, const char *name, GenericAlternate **obj, size_t size, bool promote_int, Error **errp) { + Error *err = NULL; + assert(obj && size >= sizeof(GenericAlternate)); + assert(v->type != VISITOR_OUTPUT || *obj); if (v->start_alternate) { - v->start_alternate(v, name, obj, size, promote_int, errp); + v->start_alternate(v, name, obj, size, promote_int, &err); } + if (v->type == VISITOR_INPUT) { + assert(v->start_alternate && !err != !*obj); + } + error_propagate(errp, err); } void visit_end_alternate(Visitor *v) @@ -72,14 +104,14 @@ bool visit_optional(Visitor *v, const char *name, bool *present) return *present; } -void visit_type_enum(Visitor *v, const char *name, int *obj, - const char *const strings[], Error **errp) +bool visit_is_input(Visitor *v) { - v->type_enum(v, name, obj, strings, errp); + return v->type == VISITOR_INPUT; } void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp) { + assert(obj); v->type_int64(v, name, obj, errp); } @@ -127,6 +159,7 @@ void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) { + assert(obj); v->type_uint64(v, name, obj, errp); } @@ -174,12 +207,14 @@ void visit_type_int32(Visitor *v, const char *name, int32_t *obj, void visit_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp) { + assert(obj); v->type_int64(v, name, obj, errp); } void visit_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp) { + assert(obj); if (v->type_size) { v->type_size(v, name, obj, errp); } else { @@ -189,33 +224,58 @@ void visit_type_size(Visitor *v, const char *name, uint64_t *obj, void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { + assert(obj); v->type_bool(v, name, obj, errp); } void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) { - v->type_str(v, name, obj, errp); + Error *err = NULL; + + assert(obj); + /* TODO: Fix callers to not pass NULL when they mean "", so that we + * can enable: + assert(v->type != VISITOR_OUTPUT || *obj); + */ + v->type_str(v, name, obj, &err); + if (v->type == VISITOR_INPUT) { + assert(!err != !*obj); + } + error_propagate(errp, err); } void visit_type_number(Visitor *v, const char *name, double *obj, Error **errp) { + assert(obj); v->type_number(v, name, obj, errp); } void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) { - v->type_any(v, name, obj, errp); + Error *err = NULL; + + assert(obj); + assert(v->type != VISITOR_OUTPUT || *obj); + v->type_any(v, name, obj, &err); + if (v->type == VISITOR_INPUT) { + assert(!err != !*obj); + } + error_propagate(errp, err); +} + +void visit_type_null(Visitor *v, const char *name, Error **errp) +{ + v->type_null(v, name, errp); } -void output_type_enum(Visitor *v, const char *name, int *obj, - const char *const strings[], Error **errp) +static void output_type_enum(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp) { int i = 0; int value = *obj; char *enum_str; - assert(strings); while (strings[i++] != NULL); if (value < 0 || value >= i - 1) { error_setg(errp, QERR_INVALID_PARAMETER, name ? name : "null"); @@ -226,15 +286,13 @@ void output_type_enum(Visitor *v, const char *name, int *obj, visit_type_str(v, name, &enum_str, errp); } -void input_type_enum(Visitor *v, const char *name, int *obj, - const char *const strings[], Error **errp) +static void input_type_enum(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp) { Error *local_err = NULL; int64_t value = 0; char *enum_str; - assert(strings); - visit_type_str(v, name, &enum_str, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -257,3 +315,14 @@ void input_type_enum(Visitor *v, const char *name, int *obj, g_free(enum_str); *obj = value; } + +void visit_type_enum(Visitor *v, const char *name, int *obj, + const char *const strings[], Error **errp) +{ + assert(obj && strings); + if (v->type == VISITOR_INPUT) { + input_type_enum(v, name, obj, strings, errp); + } else if (v->type == VISITOR_OUTPUT) { + output_type_enum(v, name, obj, strings, errp); + } +} diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 510a1aead8..08faf853ac 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -94,17 +94,13 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp) QINCREF(args); } - switch (cmd->type) { - case QCT_NORMAL: - cmd->fn(args, &ret, &local_err); - if (local_err) { - error_propagate(errp, local_err); - } else if (cmd->options & QCO_NO_SUCCESS_RESP) { - g_assert(!ret); - } else if (!ret) { - ret = QOBJECT(qdict_new()); - } - break; + cmd->fn(args, &ret, &local_err); + if (local_err) { + error_propagate(errp, local_err); + } else if (cmd->options & QCO_NO_SUCCESS_RESP) { + g_assert(!ret); + } else if (!ret) { + ret = QOBJECT(qdict_new()); } QDECREF(args); diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c index 7cd1b777a0..aea90a1378 100644 --- a/qapi/qmp-input-visitor.c +++ b/qapi/qmp-input-visitor.c @@ -25,16 +25,25 @@ typedef struct StackObject { - QObject *obj; - const QListEntry *entry; - GHashTable *h; + QObject *obj; /* Object being visited */ + + GHashTable *h; /* If obj is dict: unvisited keys */ + const QListEntry *entry; /* If obj is list: unvisited tail */ } StackObject; struct QmpInputVisitor { Visitor visitor; + + /* Root of visit at visitor creation. */ + QObject *root; + + /* Stack of objects being visited (all entries will be either + * QDict or QList). */ StackObject stack[QIV_STACK_SIZE]; int nb_stack; + + /* True to reject parse in visit_end_struct() if unvisited keys remain. */ bool strict; }; @@ -47,20 +56,37 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv, const char *name, bool consume) { - QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj; + StackObject *tos; + QObject *qobj; + QObject *ret; - if (qobj) { - if (name && qobject_type(qobj) == QTYPE_QDICT) { - if (qiv->stack[qiv->nb_stack - 1].h && consume) { - g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name); - } - return qdict_get(qobject_to_qdict(qobj), name); - } else if (qiv->stack[qiv->nb_stack - 1].entry) { - return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); + if (!qiv->nb_stack) { + /* Starting at root, name is ignored. */ + return qiv->root; + } + + /* We are in a container; find the next element. */ + tos = &qiv->stack[qiv->nb_stack - 1]; + qobj = tos->obj; + assert(qobj); + + if (qobject_type(qobj) == QTYPE_QDICT) { + assert(name); + ret = qdict_get(qobject_to_qdict(qobj), name); + if (tos->h && consume && ret) { + bool removed = g_hash_table_remove(tos->h, name); + assert(removed); + } + } else { + assert(qobject_type(qobj) == QTYPE_QLIST); + assert(!name); + ret = qlist_entry_obj(tos->entry); + if (consume) { + tos->entry = qlist_next(tos->entry); } } - return qobj; + return ret; } static void qdict_add_key(const char *key, QObject *obj, void *opaque) @@ -69,35 +95,44 @@ static void qdict_add_key(const char *key, QObject *obj, void *opaque) g_hash_table_insert(h, (gpointer) key, NULL); } -static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp) +static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj, + Error **errp) { GHashTable *h; + StackObject *tos = &qiv->stack[qiv->nb_stack]; + assert(obj); if (qiv->nb_stack >= QIV_STACK_SIZE) { error_setg(errp, "An internal buffer overran"); - return; + return NULL; } - qiv->stack[qiv->nb_stack].obj = obj; - qiv->stack[qiv->nb_stack].entry = NULL; - qiv->stack[qiv->nb_stack].h = NULL; + tos->obj = obj; + assert(!tos->h); + assert(!tos->entry); if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) { h = g_hash_table_new(g_str_hash, g_str_equal); qdict_iter(qobject_to_qdict(obj), qdict_add_key, h); - qiv->stack[qiv->nb_stack].h = h; + tos->h = h; + } else if (qobject_type(obj) == QTYPE_QLIST) { + tos->entry = qlist_first(qobject_to_qlist(obj)); } qiv->nb_stack++; + return tos->entry; } -static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) +static void qmp_input_check_struct(Visitor *v, Error **errp) { + QmpInputVisitor *qiv = to_qiv(v); + StackObject *tos = &qiv->stack[qiv->nb_stack - 1]; + assert(qiv->nb_stack > 0); if (qiv->strict) { - GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h; + GHashTable *const top_ht = tos->h; if (top_ht) { GHashTableIter iter; const char *key; @@ -106,8 +141,23 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { error_setg(errp, QERR_QMP_EXTRA_MEMBER, key); } + } + } +} + +static void qmp_input_pop(Visitor *v) +{ + QmpInputVisitor *qiv = to_qiv(v); + StackObject *tos = &qiv->stack[qiv->nb_stack - 1]; + + assert(qiv->nb_stack > 0); + + if (qiv->strict) { + GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h; + if (top_ht) { g_hash_table_unref(top_ht); } + tos->h = NULL; } qiv->nb_stack--; @@ -120,6 +170,9 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj, QObject *qobj = qmp_input_get_object(qiv, name, true); Error *err = NULL; + if (obj) { + *obj = NULL; + } if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "QDict"); @@ -137,63 +190,46 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj, } } -static void qmp_input_end_struct(Visitor *v, Error **errp) -{ - QmpInputVisitor *qiv = to_qiv(v); - - qmp_input_pop(qiv, errp); -} -static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) +static void qmp_input_start_list(Visitor *v, const char *name, + GenericList **list, size_t size, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); QObject *qobj = qmp_input_get_object(qiv, name, true); + const QListEntry *entry; if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { + if (list) { + *list = NULL; + } error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "list"); return; } - qmp_input_push(qiv, qobj, errp); + entry = qmp_input_push(qiv, qobj, errp); + if (list) { + if (entry) { + *list = g_malloc0(size); + } else { + *list = NULL; + } + } } -static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, +static GenericList *qmp_input_next_list(Visitor *v, GenericList *tail, size_t size) { QmpInputVisitor *qiv = to_qiv(v); - GenericList *entry; StackObject *so = &qiv->stack[qiv->nb_stack - 1]; - bool first; - if (so->entry == NULL) { - so->entry = qlist_first(qobject_to_qlist(so->obj)); - first = true; - } else { - so->entry = qlist_next(so->entry); - first = false; - } - - if (so->entry == NULL) { + if (!so->entry) { return NULL; } - - entry = g_malloc0(size); - if (first) { - *list = entry; - } else { - (*list)->next = entry; - } - - return entry; + tail->next = g_malloc0(size); + return tail->next; } -static void qmp_input_end_list(Visitor *v) -{ - QmpInputVisitor *qiv = to_qiv(v); - - qmp_input_pop(qiv, &error_abort); -} static void qmp_input_start_alternate(Visitor *v, const char *name, GenericAlternate **obj, size_t size, @@ -267,6 +303,7 @@ static void qmp_input_type_str(Visitor *v, const char *name, char **obj, QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true)); if (!qstr) { + *obj = NULL; error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "string"); return; @@ -309,11 +346,22 @@ static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj, *obj = qobj; } -static void qmp_input_optional(Visitor *v, const char *name, bool *present) +static void qmp_input_type_null(Visitor *v, const char *name, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); QObject *qobj = qmp_input_get_object(qiv, name, true); + if (qobject_type(qobj) != QTYPE_QNULL) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "null"); + } +} + +static void qmp_input_optional(Visitor *v, const char *name, bool *present) +{ + QmpInputVisitor *qiv = to_qiv(v); + QObject *qobj = qmp_input_get_object(qiv, name, false); + if (!qobj) { *present = false; return; @@ -329,43 +377,36 @@ Visitor *qmp_input_get_visitor(QmpInputVisitor *v) void qmp_input_visitor_cleanup(QmpInputVisitor *v) { - qobject_decref(v->stack[0].obj); + qobject_decref(v->root); g_free(v); } -QmpInputVisitor *qmp_input_visitor_new(QObject *obj) +QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict) { QmpInputVisitor *v; v = g_malloc0(sizeof(*v)); + v->visitor.type = VISITOR_INPUT; v->visitor.start_struct = qmp_input_start_struct; - v->visitor.end_struct = qmp_input_end_struct; + v->visitor.check_struct = qmp_input_check_struct; + v->visitor.end_struct = qmp_input_pop; v->visitor.start_list = qmp_input_start_list; v->visitor.next_list = qmp_input_next_list; - v->visitor.end_list = qmp_input_end_list; + v->visitor.end_list = qmp_input_pop; v->visitor.start_alternate = qmp_input_start_alternate; - v->visitor.type_enum = input_type_enum; v->visitor.type_int64 = qmp_input_type_int64; v->visitor.type_uint64 = qmp_input_type_uint64; v->visitor.type_bool = qmp_input_type_bool; v->visitor.type_str = qmp_input_type_str; v->visitor.type_number = qmp_input_type_number; v->visitor.type_any = qmp_input_type_any; + v->visitor.type_null = qmp_input_type_null; v->visitor.optional = qmp_input_optional; + v->strict = strict; - qmp_input_push(v, obj, NULL); + v->root = obj; qobject_incref(obj); return v; } - -QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj) -{ - QmpInputVisitor *v; - - v = qmp_input_visitor_new(obj); - v->strict = true; - - return v; -} diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c index d44c676317..4d3cf78333 100644 --- a/qapi/qmp-output-visitor.c +++ b/qapi/qmp-output-visitor.c @@ -22,7 +22,6 @@ typedef struct QStackEntry { QObject *value; - bool is_list_head; QTAILQ_ENTRY(QStackEntry) node; } QStackEntry; @@ -52,9 +51,6 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value) assert(qov->root); assert(value); e->value = value; - if (qobject_type(e->value) == QTYPE_QLIST) { - e->is_list_head = true; - } QTAILQ_INSERT_HEAD(&qov->stack, e, node); } @@ -82,9 +78,8 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name, QObject *cur = e ? e->value : NULL; if (!cur) { - /* FIXME we should require the user to reset the visitor, rather - * than throwing away the previous root */ - qobject_decref(qov->root); + /* Don't allow reuse of visitor on more than one root */ + assert(!qov->root); qov->root = value; } else { switch (qobject_type(cur)) { @@ -93,6 +88,7 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name, qdict_put_obj(qobject_to_qdict(cur), name, value); break; case QTYPE_QLIST: + assert(!name); qlist_append_obj(qobject_to_qlist(cur), value); break; default: @@ -111,13 +107,16 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj, qmp_output_push(qov, dict); } -static void qmp_output_end_struct(Visitor *v, Error **errp) +static void qmp_output_end_struct(Visitor *v) { QmpOutputVisitor *qov = to_qov(v); - qmp_output_pop(qov); + QObject *value = qmp_output_pop(qov); + assert(qobject_type(value) == QTYPE_QDICT); } -static void qmp_output_start_list(Visitor *v, const char *name, Error **errp) +static void qmp_output_start_list(Visitor *v, const char *name, + GenericList **listp, size_t size, + Error **errp) { QmpOutputVisitor *qov = to_qov(v); QList *list = qlist_new(); @@ -126,26 +125,17 @@ static void qmp_output_start_list(Visitor *v, const char *name, Error **errp) qmp_output_push(qov, list); } -static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp, +static GenericList *qmp_output_next_list(Visitor *v, GenericList *tail, size_t size) { - GenericList *list = *listp; - QmpOutputVisitor *qov = to_qov(v); - QStackEntry *e = QTAILQ_FIRST(&qov->stack); - - assert(e); - if (e->is_list_head) { - e->is_list_head = false; - return list; - } - - return list ? list->next : NULL; + return tail->next; } static void qmp_output_end_list(Visitor *v) { QmpOutputVisitor *qov = to_qov(v); - qmp_output_pop(qov); + QObject *value = qmp_output_pop(qov); + assert(qobject_type(value) == QTYPE_QLIST); } static void qmp_output_type_int64(Visitor *v, const char *name, int64_t *obj, @@ -196,18 +186,22 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj, qmp_output_add_obj(qov, name, *obj); } -/* Finish building, and return the root object. Will not be NULL. */ +static void qmp_output_type_null(Visitor *v, const char *name, Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_add_obj(qov, name, qnull()); +} + +/* Finish building, and return the root object. + * The root object is never null. The caller becomes the object's + * owner, and should use qobject_decref() when done with it. */ QObject *qmp_output_get_qobject(QmpOutputVisitor *qov) { - /* FIXME: we should require that a visit occurred, and that it is - * complete (no starts without a matching end) */ - QObject *obj = qov->root; - if (obj) { - qobject_incref(obj); - } else { - obj = qnull(); - } - return obj; + /* A visit must have occurred, with each start paired with end. */ + assert(qov->root && QTAILQ_EMPTY(&qov->stack)); + + qobject_incref(qov->root); + return qov->root; } Visitor *qmp_output_get_visitor(QmpOutputVisitor *v) @@ -234,18 +228,19 @@ QmpOutputVisitor *qmp_output_visitor_new(void) v = g_malloc0(sizeof(*v)); + v->visitor.type = VISITOR_OUTPUT; v->visitor.start_struct = qmp_output_start_struct; v->visitor.end_struct = qmp_output_end_struct; v->visitor.start_list = qmp_output_start_list; v->visitor.next_list = qmp_output_next_list; v->visitor.end_list = qmp_output_end_list; - v->visitor.type_enum = output_type_enum; v->visitor.type_int64 = qmp_output_type_int64; v->visitor.type_uint64 = qmp_output_type_uint64; v->visitor.type_bool = qmp_output_type_bool; v->visitor.type_str = qmp_output_type_str; v->visitor.type_number = qmp_output_type_number; v->visitor.type_any = qmp_output_type_any; + v->visitor.type_null = qmp_output_type_null; QTAILQ_INIT(&v->stack); diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index 4ebfbccd46..4332a6818d 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -25,7 +25,6 @@ void qmp_register_command(const char *name, QmpCommandFunc *fn, QmpCommand *cmd = g_malloc0(sizeof(*cmd)); cmd->name = name; - cmd->type = QCT_NORMAL; cmd->fn = fn; cmd->enabled = true; cmd->options = options; diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index 5ea2d77b5a..30b58791c9 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -25,8 +25,6 @@ struct StringInputVisitor { Visitor visitor; - bool head; - GList *ranges; GList *cur_range; int64_t cur; @@ -44,7 +42,7 @@ static void free_range(void *range, void *dummy) g_free(range); } -static void parse_str(StringInputVisitor *siv, Error **errp) +static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) { char *str = (char *) siv->string; long long start, end; @@ -52,7 +50,7 @@ static void parse_str(StringInputVisitor *siv, Error **errp) char *endptr; if (siv->ranges) { - return; + return 0; } do { @@ -117,19 +115,29 @@ static void parse_str(StringInputVisitor *siv, Error **errp) } } while (str); - return; + return 0; error: g_list_foreach(siv->ranges, free_range, NULL); g_list_free(siv->ranges); siv->ranges = NULL; + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "an int64 value or range"); + return -1; } static void -start_list(Visitor *v, const char *name, Error **errp) +start_list(Visitor *v, const char *name, GenericList **list, size_t size, + Error **errp) { StringInputVisitor *siv = to_siv(v); - parse_str(siv, errp); + /* We don't support visits without a list */ + assert(list); + + if (parse_str(siv, name, errp) < 0) { + *list = NULL; + return; + } siv->cur_range = g_list_first(siv->ranges); if (siv->cur_range) { @@ -137,13 +145,15 @@ start_list(Visitor *v, const char *name, Error **errp) if (r) { siv->cur = r->begin; } + *list = g_malloc0(size); + } else { + *list = NULL; } } -static GenericList *next_list(Visitor *v, GenericList **list, size_t size) +static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) { StringInputVisitor *siv = to_siv(v); - GenericList **link; Range *r; if (!siv->ranges || !siv->cur_range) { @@ -167,21 +177,12 @@ static GenericList *next_list(Visitor *v, GenericList **list, size_t size) siv->cur = r->begin; } - if (siv->head) { - link = list; - siv->head = false; - } else { - link = &(*list)->next; - } - - *link = g_malloc0(size); - return *link; + tail->next = g_malloc0(size); + return tail->next; } static void end_list(Visitor *v) { - StringInputVisitor *siv = to_siv(v); - siv->head = true; } static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, @@ -195,7 +196,9 @@ static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, return; } - parse_str(siv, errp); + if (parse_str(siv, name, errp) < 0) { + return; + } if (!siv->ranges) { goto error; @@ -293,6 +296,7 @@ static void parse_type_str(Visitor *v, const char *name, char **obj, if (siv->string) { *obj = g_strdup(siv->string); } else { + *obj = NULL; error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "string"); } @@ -348,7 +352,7 @@ StringInputVisitor *string_input_visitor_new(const char *str) v = g_malloc0(sizeof(*v)); - v->visitor.type_enum = input_type_enum; + v->visitor.type = VISITOR_INPUT; v->visitor.type_int64 = parse_type_int64; v->visitor.type_uint64 = parse_type_uint64; v->visitor.type_size = parse_type_size; @@ -361,6 +365,5 @@ StringInputVisitor *string_input_visitor_new(const char *str) v->visitor.optional = parse_optional; v->string = str; - v->head = true; return v; } diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c index c2e5c5b92b..d01319628b 100644 --- a/qapi/string-output-visitor.c +++ b/qapi/string-output-visitor.c @@ -20,7 +20,7 @@ enum ListMode { LM_NONE, /* not traversing a list of repeated options */ - LM_STARTED, /* start_list() succeeded */ + LM_STARTED, /* next_list() ready to be called */ LM_IN_PROGRESS, /* next_list() has been called. * @@ -48,7 +48,7 @@ enum ListMode { LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */ - LM_END + LM_END, /* next_list() called, about to see last element. */ }; typedef enum ListMode ListMode; @@ -58,7 +58,6 @@ struct StringOutputVisitor Visitor visitor; bool human; GString *string; - bool head; ListMode list_mode; union { int64_t s; @@ -266,39 +265,29 @@ static void print_type_number(Visitor *v, const char *name, double *obj, } static void -start_list(Visitor *v, const char *name, Error **errp) +start_list(Visitor *v, const char *name, GenericList **list, size_t size, + Error **errp) { StringOutputVisitor *sov = to_sov(v); /* we can't traverse a list in a list */ assert(sov->list_mode == LM_NONE); - sov->list_mode = LM_STARTED; - sov->head = true; + /* We don't support visits without a list */ + assert(list); + /* List handling is only needed if there are at least two elements */ + if (*list && (*list)->next) { + sov->list_mode = LM_STARTED; + } } -static GenericList *next_list(Visitor *v, GenericList **list, size_t size) +static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) { StringOutputVisitor *sov = to_sov(v); - GenericList *ret = NULL; - if (*list) { - if (sov->head) { - ret = *list; - } else { - ret = (*list)->next; - } + GenericList *ret = tail->next; - if (sov->head) { - if (ret && ret->next == NULL) { - sov->list_mode = LM_NONE; - } - sov->head = false; - } else { - if (ret && ret->next == NULL) { - sov->list_mode = LM_END; - } - } + if (ret && !ret->next) { + sov->list_mode = LM_END; } - return ret; } @@ -311,8 +300,6 @@ static void end_list(Visitor *v) sov->list_mode == LM_NONE || sov->list_mode == LM_IN_PROGRESS); sov->list_mode = LM_NONE; - sov->head = true; - } char *string_output_get_string(StringOutputVisitor *sov) @@ -351,7 +338,7 @@ StringOutputVisitor *string_output_visitor_new(bool human) v->string = g_string_new(NULL); v->human = human; - v->visitor.type_enum = output_type_enum; + v->visitor.type = VISITOR_OUTPUT; v->visitor.type_int64 = print_type_int64; v->visitor.type_uint64 = print_type_uint64; v->visitor.type_size = print_type_size; @@ -663,7 +663,7 @@ void qmp_object_add(const char *type, const char *id, } } - qiv = qmp_input_visitor_new(props); + qiv = qmp_input_visitor_new(props, true); obj = user_creatable_add_type(type, id, pdict, qmp_input_get_visitor(qiv), errp); qmp_input_visitor_cleanup(qiv); diff --git a/qom/object.c b/qom/object.c index 8e6e68dffc..3bc8a009bb 100644 --- a/qom/object.c +++ b/qom/object.c @@ -2036,10 +2036,9 @@ static void property_get_tm(Object *obj, Visitor *v, const char *name, if (err) { goto out_end; } + visit_check_struct(v, &err); out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, errp); + visit_end_struct(v); out: error_propagate(errp, err); diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 393189024f..51e62e29d6 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -42,7 +42,7 @@ Object *user_creatable_add(const QDict *qdict, char *type = NULL; char *id = NULL; Object *obj = NULL; - Error *local_err = NULL, *end_err = NULL; + Error *local_err = NULL; QDict *pdict; pdict = qdict_clone_shallow(qdict); @@ -63,21 +63,15 @@ Object *user_creatable_add(const QDict *qdict, if (local_err) { goto out_visit; } - - obj = user_creatable_add_type(type, id, pdict, v, &local_err); + visit_check_struct(v, &local_err); if (local_err) { goto out_visit; } - out_visit: - visit_end_struct(v, &end_err); - if (end_err) { - error_propagate(&local_err, end_err); - if (obj) { - user_creatable_del(id, NULL); - } - goto out; - } + obj = user_creatable_add_type(type, id, pdict, v, &local_err); + +out_visit: + visit_end_struct(v); out: QDECREF(pdict); @@ -118,15 +112,25 @@ Object *user_creatable_add_type(const char *type, const char *id, return NULL; } + assert(qdict); obj = object_new(type); - if (qdict) { - for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { - object_property_set(obj, v, e->key, &local_err); - if (local_err) { - goto out; - } + visit_start_struct(v, NULL, NULL, 0, &local_err); + if (local_err) { + goto out; + } + for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { + object_property_set(obj, v, e->key, &local_err); + if (local_err) { + break; } } + if (!local_err) { + visit_check_struct(v, &local_err); + } + visit_end_struct(v); + if (local_err) { + goto out; + } object_property_add_child(object_get_objects_root(), id, obj, &local_err); diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c index e6b17c1f1b..b66088d730 100644 --- a/qom/qom-qobject.c +++ b/qom/qom-qobject.c @@ -22,7 +22,8 @@ void object_property_set_qobject(Object *obj, QObject *value, const char *name, Error **errp) { QmpInputVisitor *qiv; - qiv = qmp_input_visitor_new(value); + /* TODO: Should we reject, rather than ignore, excess input? */ + qiv = qmp_input_visitor_new(value, false); object_property_set(obj, qmp_input_get_visitor(qiv), name, errp); qmp_input_visitor_cleanup(qiv); diff --git a/replay/replay-input.c b/replay/replay-input.c index 06babe0ecc..03e99d5aba 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -37,7 +37,7 @@ static InputEvent *qapi_clone_InputEvent(InputEvent *src) return NULL; } - qiv = qmp_input_visitor_new(obj); + qiv = qmp_input_visitor_new(obj, true); iv = qmp_input_get_visitor(qiv); visit_type_InputEvent(iv, NULL, &dst, &error_abort); qmp_input_visitor_cleanup(qiv); diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index b570069faa..8c6acb3f3f 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -115,13 +115,21 @@ def gen_marshal(name, arg_type, ret_type): if arg_type and arg_type.members: ret += mcgen(''' - QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args)); + QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true); QapiDeallocVisitor *qdv; Visitor *v; %(c_name)s arg = {0}; v = qmp_input_get_visitor(qiv); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } visit_type_%(c_name)s_members(v, &arg, &err); + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v); if (err) { goto out; } @@ -150,7 +158,9 @@ out: qmp_input_visitor_cleanup(qiv); qdv = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(qdv); + visit_start_struct(v, NULL, NULL, 0, NULL); visit_type_%(c_name)s_members(v, &arg, NULL); + visit_end_struct(v); qapi_dealloc_visitor_cleanup(qdv); ''', c_name=arg_type.c_name()) diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index 9b5c5b535d..21fb16744d 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -98,7 +98,10 @@ def gen_event_send(name, arg_type): goto out; } visit_type_%(c_name)s_members(v, ¶m, &err); - visit_end_struct(v, err ? NULL : &err); + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v); if (err) { goto out; } diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 31d2330356..70ea8caef5 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -108,30 +108,32 @@ out: def gen_visit_list(name, element_type): - # FIXME: if *obj is NULL on entry, and the first visit_next_list() - # assigns to *obj, while a later one fails, we should clean up *obj - # rather than leaving it non-NULL. As currently written, the caller must - # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList. return mcgen(''' void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) { Error *err = NULL; - GenericList *i, **prev; + %(c_name)s *tail; + size_t size = sizeof(**obj); - visit_start_list(v, name, &err); + visit_start_list(v, name, (GenericList **)obj, size, &err); if (err) { goto out; } - for (prev = (GenericList **)obj; - !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL; - prev = &i) { - %(c_name)s *native_i = (%(c_name)s *)i; - visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err); + for (tail = *obj; tail; + tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) { + visit_type_%(c_elt_type)s(v, NULL, &tail->value, &err); + if (err) { + break; + } } visit_end_list(v); + if (err && visit_is_input(v)) { + qapi_free_%(c_name)s(*obj); + *obj = NULL; + } out: error_propagate(errp, err); } @@ -186,9 +188,10 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error break; } visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err); - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v); ''', c_type=var.type.c_name(), c_name=c_name(var.name)) @@ -208,20 +211,20 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error "%(name)s"); } visit_end_alternate(v); + if (err && visit_is_input(v)) { + qapi_free_%(c_name)s(*obj); + *obj = NULL; + } out: error_propagate(errp, err); } ''', - name=name) + name=name, c_name=c_name(name)) return ret def gen_visit_object(name, base, members, variants): - # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to - # *obj, but then visit_type_FOO_members() fails, we should clean up *obj - # rather than leaving it non-NULL. As currently written, the caller must - # call qapi_free_FOO() to avoid a memory leak of the partial FOO. return mcgen(''' void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) @@ -236,10 +239,16 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error goto out_obj; } visit_type_%(c_name)s_members(v, *obj, &err); - error_propagate(errp, err); - err = NULL; + if (err) { + goto out_obj; + } + visit_check_struct(v, &err); out_obj: - visit_end_struct(v, &err); + visit_end_struct(v); + if (err && visit_is_input(v)) { + qapi_free_%(c_name)s(*obj); + *obj = NULL; + } out: error_propagate(errp, err); } diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index 82cbe6bbad..f20641163c 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -9,3 +9,4 @@ obj-y += neon_helper.o iwmmxt_helper.o obj-y += gdbstub.o obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o obj-y += crypto_helper.o +obj-y += arm-powerctl.o diff --git a/target-arm/arm-powerctl.c b/target-arm/arm-powerctl.c new file mode 100644 index 0000000000..cb9919b465 --- /dev/null +++ b/target-arm/arm-powerctl.c @@ -0,0 +1,224 @@ +/* + * QEMU support -- ARM Power Control specific functions. + * + * Copyright (c) 2016 Jean-Christophe Dubois + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include <cpu.h> +#include <cpu-qom.h> +#include "internals.h" +#include "arm-powerctl.h" + +#ifndef DEBUG_ARM_POWERCTL +#define DEBUG_ARM_POWERCTL 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_ARM_POWERCTL) { \ + fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \ + } \ + } while (0) + +CPUState *arm_get_cpu_by_id(uint64_t id) +{ + CPUState *cpu; + + DPRINTF("cpu %" PRId64 "\n", id); + + CPU_FOREACH(cpu) { + ARMCPU *armcpu = ARM_CPU(cpu); + + if (armcpu->mp_affinity == id) { + return cpu; + } + } + + qemu_log_mask(LOG_GUEST_ERROR, + "[ARM]%s: Requesting unknown CPU %" PRId64 "\n", + __func__, id); + + return NULL; +} + +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id, + uint32_t target_el, bool target_aa64) +{ + CPUState *target_cpu_state; + ARMCPU *target_cpu; + + DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 + "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry, + context_id); + + /* requested EL level need to be in the 1 to 3 range */ + assert((target_el > 0) && (target_el < 4)); + + if (target_aa64 && (entry & 3)) { + /* + * if we are booting in AArch64 mode then "entry" needs to be 4 bytes + * aligned. + */ + return QEMU_ARM_POWERCTL_INVALID_PARAM; + } + + /* Retrieve the cpu we are powering up */ + target_cpu_state = arm_get_cpu_by_id(cpuid); + if (!target_cpu_state) { + /* The cpu was not found */ + return QEMU_ARM_POWERCTL_INVALID_PARAM; + } + + target_cpu = ARM_CPU(target_cpu_state); + if (!target_cpu->powered_off) { + qemu_log_mask(LOG_GUEST_ERROR, + "[ARM]%s: CPU %" PRId64 " is already on\n", + __func__, cpuid); + return QEMU_ARM_POWERCTL_ALREADY_ON; + } + + /* + * The newly brought CPU is requested to enter the exception level + * "target_el" and be in the requested mode (AArch64 or AArch32). + */ + + if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) || + ((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) { + /* + * The CPU does not support requested level + */ + return QEMU_ARM_POWERCTL_INVALID_PARAM; + } + + if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) { + /* + * For now we don't support booting an AArch64 CPU in AArch32 mode + * TODO: We should add this support later + */ + qemu_log_mask(LOG_UNIMP, + "[ARM]%s: Starting AArch64 CPU %" PRId64 + " in AArch32 mode is not supported yet\n", + __func__, cpuid); + return QEMU_ARM_POWERCTL_INVALID_PARAM; + } + + /* Initialize the cpu we are turning on */ + cpu_reset(target_cpu_state); + target_cpu->powered_off = false; + target_cpu_state->halted = 0; + + if (target_aa64) { + if ((target_el < 3) && arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) { + /* + * As target mode is AArch64, we need to set lower + * exception level (the requested level 2) to AArch64 + */ + target_cpu->env.cp15.scr_el3 |= SCR_RW; + } + + if ((target_el < 2) && arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) { + /* + * As target mode is AArch64, we need to set lower + * exception level (the requested level 1) to AArch64 + */ + target_cpu->env.cp15.hcr_el2 |= HCR_RW; + } + + target_cpu->env.pstate = aarch64_pstate_mode(target_el, true); + } else { + /* We are requested to boot in AArch32 mode */ + static uint32_t mode_for_el[] = { 0, + ARM_CPU_MODE_SVC, + ARM_CPU_MODE_HYP, + ARM_CPU_MODE_SVC }; + + cpsr_write(&target_cpu->env, mode_for_el[target_el], CPSR_M, + CPSRWriteRaw); + } + + if (target_el == 3) { + /* Processor is in secure mode */ + target_cpu->env.cp15.scr_el3 &= ~SCR_NS; + } else { + /* Processor is not in secure mode */ + target_cpu->env.cp15.scr_el3 |= SCR_NS; + } + + /* We check if the started CPU is now at the correct level */ + assert(target_el == arm_current_el(&target_cpu->env)); + + if (target_aa64) { + target_cpu->env.xregs[0] = context_id; + target_cpu->env.thumb = false; + } else { + target_cpu->env.regs[0] = context_id; + target_cpu->env.thumb = entry & 1; + entry &= 0xfffffffe; + } + + /* Start the new CPU at the requested address */ + cpu_set_pc(target_cpu_state, entry); + + /* We are good to go */ + return QEMU_ARM_POWERCTL_RET_SUCCESS; +} + +int arm_set_cpu_off(uint64_t cpuid) +{ + CPUState *target_cpu_state; + ARMCPU *target_cpu; + + DPRINTF("cpu %" PRId64 "\n", cpuid); + + /* change to the cpu we are powering up */ + target_cpu_state = arm_get_cpu_by_id(cpuid); + if (!target_cpu_state) { + return QEMU_ARM_POWERCTL_INVALID_PARAM; + } + target_cpu = ARM_CPU(target_cpu_state); + if (target_cpu->powered_off) { + qemu_log_mask(LOG_GUEST_ERROR, + "[ARM]%s: CPU %" PRId64 " is already off\n", + __func__, cpuid); + return QEMU_ARM_POWERCTL_IS_OFF; + } + + target_cpu->powered_off = true; + target_cpu_state->halted = 1; + target_cpu_state->exception_index = EXCP_HLT; + cpu_loop_exit(target_cpu_state); + /* notreached */ + + return QEMU_ARM_POWERCTL_RET_SUCCESS; +} + +int arm_reset_cpu(uint64_t cpuid) +{ + CPUState *target_cpu_state; + ARMCPU *target_cpu; + + DPRINTF("cpu %" PRId64 "\n", cpuid); + + /* change to the cpu we are resetting */ + target_cpu_state = arm_get_cpu_by_id(cpuid); + if (!target_cpu_state) { + return QEMU_ARM_POWERCTL_INVALID_PARAM; + } + target_cpu = ARM_CPU(target_cpu_state); + if (target_cpu->powered_off) { + qemu_log_mask(LOG_GUEST_ERROR, + "[ARM]%s: CPU %" PRId64 " is off\n", + __func__, cpuid); + return QEMU_ARM_POWERCTL_IS_OFF; + } + + /* Reset the cpu */ + cpu_reset(target_cpu_state); + + return QEMU_ARM_POWERCTL_RET_SUCCESS; +} diff --git a/target-arm/arm-powerctl.h b/target-arm/arm-powerctl.h new file mode 100644 index 0000000000..98ee04989b --- /dev/null +++ b/target-arm/arm-powerctl.h @@ -0,0 +1,75 @@ +/* + * QEMU support -- ARM Power Control specific functions. + * + * Copyright (c) 2016 Jean-Christophe Dubois + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_ARM_POWERCTL_H +#define QEMU_ARM_POWERCTL_H + +#include "kvm-consts.h" + +#define QEMU_ARM_POWERCTL_RET_SUCCESS QEMU_PSCI_RET_SUCCESS +#define QEMU_ARM_POWERCTL_INVALID_PARAM QEMU_PSCI_RET_INVALID_PARAMS +#define QEMU_ARM_POWERCTL_ALREADY_ON QEMU_PSCI_RET_ALREADY_ON +#define QEMU_ARM_POWERCTL_IS_OFF QEMU_PSCI_RET_DENIED + +/* + * arm_get_cpu_by_id: + * @cpuid: the id of the CPU we want to retrieve the state + * + * Retrieve a CPUState object from its CPU ID provided in @cpuid. + * + * Returns: a pointer to the CPUState structure of the requested CPU. + */ +CPUState *arm_get_cpu_by_id(uint64_t cpuid); + +/* + * arm_set_cpu_on: + * @cpuid: the id of the CPU we want to start/wake up. + * @entry: the address the CPU shall start from. + * @context_id: the value to put in r0/x0. + * @target_el: The desired exception level. + * @target_aa64: 1 if the requested mode is AArch64. 0 otherwise. + * + * Start the cpu designated by @cpuid in @target_el exception level. The mode + * shall be AArch64 if @target_aa64 is set to 1. Otherwise the mode is + * AArch32. The CPU shall start at @entry with @context_id in r0/x0. + * + * Returns: QEMU_ARM_POWERCTL_RET_SUCCESS on success. + * QEMU_ARM_POWERCTL_INVALID_PARAM if bad parameters are provided. + * QEMU_ARM_POWERCTL_ALREADY_ON if the CPU was already started. + */ +int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id, + uint32_t target_el, bool target_aa64); + +/* + * arm_set_cpu_off: + * @cpuid: the id of the CPU we want to stop/shut down. + * + * Stop the cpu designated by @cpuid. + * + * Returns: QEMU_ARM_POWERCTL_RET_SUCCESS on success. + * QEMU_ARM_POWERCTL_INVALID_PARAM if bad parameters are provided. + * QEMU_ARM_POWERCTL_IS_OFF if CPU is already off + */ + +int arm_set_cpu_off(uint64_t cpuid); + +/* + * arm_reset_cpu: + * @cpuid: the id of the CPU we want to reset. + * + * Reset the cpu designated by @cpuid. + * + * Returns: QEMU_ARM_POWERCTL_RET_SUCCESS on success. + * QEMU_ARM_POWERCTL_INVALID_PARAM if bad parameters are provided. + * QEMU_ARM_POWERCTL_IS_OFF if CPU is off + */ +int arm_reset_cpu(uint64_t cpuid); + +#endif diff --git a/target-arm/helper.c b/target-arm/helper.c index 09638b2e7d..a2ab701ca5 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -3559,8 +3559,10 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .resetvalue = 0 }, { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2, - .access = PL2_RW, .writefn = vmsa_tcr_el1_write, - .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, + .access = PL2_RW, + /* no .writefn needed as this can't cause an ASID change; + * no .raw_writefn or .resetfn needed as we never use mask/base_mask + */ .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[2]) }, { .name = "VTCR", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, @@ -3753,8 +3755,10 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[3]) }, { .name = "TCR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 0, .opc2 = 2, - .access = PL3_RW, .writefn = vmsa_tcr_el1_write, - .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, + .access = PL3_RW, + /* no .writefn needed as this can't cause an ASID change; + * no .raw_writefn or .resetfn needed as we never use mask/base_mask + */ .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[3]) }, { .name = "ELR_EL3", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, @@ -6708,7 +6712,9 @@ static int get_S2prot(CPUARMState *env, int s2ap, int xn) prot |= PAGE_WRITE; } if (!xn) { - prot |= PAGE_EXEC; + if (arm_el_is_aa64(env, 2) || prot & PAGE_READ) { + prot |= PAGE_EXEC; + } } return prot; } @@ -7248,7 +7254,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, uint32_t tg; uint64_t ttbr; int ttbr_select; - hwaddr descaddr, descmask; + hwaddr descaddr, indexmask, indexmask_grainsize; uint32_t tableattrs; target_ulong page_size; uint32_t attrs; @@ -7437,28 +7443,20 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, level = startlevel; } - /* Clear the vaddr bits which aren't part of the within-region address, - * so that we don't have to special case things when calculating the - * first descriptor address. - */ - if (va_size != inputsize) { - address &= (1ULL << inputsize) - 1; - } - - descmask = (1ULL << (stride + 3)) - 1; + indexmask_grainsize = (1ULL << (stride + 3)) - 1; + indexmask = (1ULL << (inputsize - (stride * (4 - level)))) - 1; /* Now we can extract the actual base address from the TTBR */ descaddr = extract64(ttbr, 0, 48); - descaddr &= ~((1ULL << (inputsize - (stride * (4 - level)))) - 1); + descaddr &= ~indexmask; /* The address field in the descriptor goes up to bit 39 for ARMv7 - * but up to bit 47 for ARMv8. + * but up to bit 47 for ARMv8, but we use the descaddrmask + * up to bit 39 for AArch32, because we don't need other bits in that case + * to construct next descriptor address (anyway they should be all zeroes). */ - if (arm_feature(env, ARM_FEATURE_V8)) { - descaddrmask = 0xfffffffff000ULL; - } else { - descaddrmask = 0xfffffff000ULL; - } + descaddrmask = ((1ull << (va_size == 64 ? 48 : 40)) - 1) & + ~indexmask_grainsize; /* Secure accesses start with the page table in secure memory and * can be downgraded to non-secure at any step. Non-secure accesses @@ -7470,7 +7468,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, uint64_t descriptor; bool nstable; - descaddr |= (address >> (stride * (4 - level))) & descmask; + descaddr |= (address >> (stride * (4 - level))) & indexmask; descaddr &= ~7ULL; nstable = extract32(tableattrs, 4, 1); descriptor = arm_ldq_ptw(cs, descaddr, !nstable, mmu_idx, fsr, fi); @@ -7493,6 +7491,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, */ tableattrs |= extract64(descriptor, 59, 5); level++; + indexmask = indexmask_grainsize; continue; } /* Block entry at level 1 or 2, or page entry at level 3. diff --git a/target-arm/internals.h b/target-arm/internals.h index 2e70272be2..54a0fb1db7 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -263,7 +263,9 @@ enum arm_exception_class { #define ARM_EL_EC_SHIFT 26 #define ARM_EL_IL_SHIFT 25 +#define ARM_EL_ISV_SHIFT 24 #define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) +#define ARM_EL_ISV (1 << ARM_EL_ISV_SHIFT) /* Utility functions for constructing various kinds of syndrome value. * Note that in general we follow the AArch64 syndrome values; in a @@ -383,11 +385,27 @@ static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) | (ea << 9) | (s1ptw << 7) | fsc; } -static inline uint32_t syn_data_abort(int same_el, int ea, int cm, int s1ptw, - int wnr, int fsc) +static inline uint32_t syn_data_abort_no_iss(int same_el, + int ea, int cm, int s1ptw, + int wnr, int fsc) { return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc; + | ARM_EL_IL + | (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc; +} + +static inline uint32_t syn_data_abort_with_iss(int same_el, + int sas, int sse, int srt, + int sf, int ar, + int ea, int cm, int s1ptw, + int wnr, int fsc, + bool is_16bit) +{ + return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) + | (is_16bit ? 0 : ARM_EL_IL) + | ARM_EL_ISV | (sas << 22) | (sse << 21) | (srt << 16) + | (sf << 15) | (ar << 14) + | (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc; } static inline uint32_t syn_swstep(int same_el, int isv, int ex) diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index d626ff1a20..c7fba8526c 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -115,7 +115,8 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, syn = syn_insn_abort(same_el, 0, fi.s1ptw, syn); exc = EXCP_PREFETCH_ABORT; } else { - syn = syn_data_abort(same_el, 0, 0, fi.s1ptw, is_write == 1, syn); + syn = syn_data_abort_no_iss(same_el, + 0, 0, fi.s1ptw, is_write == 1, syn); if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) { fsr |= (1 << 11); } @@ -161,7 +162,8 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write, } raise_exception(env, EXCP_DATA_ABORT, - syn_data_abort(same_el, 0, 0, 0, is_write == 1, 0x21), + syn_data_abort_no_iss(same_el, + 0, 0, 0, is_write == 1, 0x21), target_el); } diff --git a/target-arm/psci.c b/target-arm/psci.c index c55487f872..ce2e0dca39 100644 --- a/target-arm/psci.c +++ b/target-arm/psci.c @@ -22,6 +22,7 @@ #include <kvm-consts.h> #include <sysemu/sysemu.h> #include "internals.h" +#include "arm-powerctl.h" bool arm_is_psci_call(ARMCPU *cpu, int excp_type) { @@ -73,21 +74,6 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type) } } -static CPUState *get_cpu_by_id(uint64_t id) -{ - CPUState *cpu; - - CPU_FOREACH(cpu) { - ARMCPU *armcpu = ARM_CPU(cpu); - - if (armcpu->mp_affinity == id) { - return cpu; - } - } - - return NULL; -} - void arm_handle_psci_call(ARMCPU *cpu) { /* @@ -98,7 +84,6 @@ void arm_handle_psci_call(ARMCPU *cpu) * Additional information about the calling convention used is available in * the document 'SMC Calling Convention' (ARM DEN 0028) */ - CPUState *cs = CPU(cpu); CPUARMState *env = &cpu->env; uint64_t param[4]; uint64_t context_id, mpidr; @@ -123,7 +108,6 @@ void arm_handle_psci_call(ARMCPU *cpu) switch (param[0]) { CPUState *target_cpu_state; ARMCPU *target_cpu; - CPUClass *target_cpu_class; case QEMU_PSCI_0_2_FN_PSCI_VERSION: ret = QEMU_PSCI_0_2_RET_VERSION_0_2; @@ -137,7 +121,7 @@ void arm_handle_psci_call(ARMCPU *cpu) switch (param[2]) { case 0: - target_cpu_state = get_cpu_by_id(mpidr); + target_cpu_state = arm_get_cpu_by_id(mpidr); if (!target_cpu_state) { ret = QEMU_PSCI_RET_INVALID_PARAMS; break; @@ -167,52 +151,13 @@ void arm_handle_psci_call(ARMCPU *cpu) mpidr = param[1]; entry = param[2]; context_id = param[3]; - - /* change to the cpu we are powering up */ - target_cpu_state = get_cpu_by_id(mpidr); - if (!target_cpu_state) { - ret = QEMU_PSCI_RET_INVALID_PARAMS; - break; - } - target_cpu = ARM_CPU(target_cpu_state); - if (!target_cpu->powered_off) { - ret = QEMU_PSCI_RET_ALREADY_ON; - break; - } - target_cpu_class = CPU_GET_CLASS(target_cpu); - - /* Initialize the cpu we are turning on */ - cpu_reset(target_cpu_state); - target_cpu->powered_off = false; - target_cpu_state->halted = 0; - /* * The PSCI spec mandates that newly brought up CPUs enter the * exception level of the caller in the same execution mode as * the caller, with context_id in x0/r0, respectively. - * - * For now, it is sufficient to assert() that CPUs come out of - * reset in the same mode as the calling CPU, since we only - * implement EL1, which means that - * (a) there is no EL2 for the calling CPU to trap into to change - * its state - * (b) the newly brought up CPU enters EL1 immediately after coming - * out of reset in the default state */ - assert(is_a64(env) == is_a64(&target_cpu->env)); - if (is_a64(env)) { - if (entry & 1) { - ret = QEMU_PSCI_RET_INVALID_PARAMS; - break; - } - target_cpu->env.xregs[0] = context_id; - } else { - target_cpu->env.regs[0] = context_id; - target_cpu->env.thumb = entry & 1; - } - target_cpu_class->set_pc(target_cpu_state, entry); - - ret = 0; + ret = arm_set_cpu_on(mpidr, entry, context_id, arm_current_el(env), + is_a64(env)); break; case QEMU_PSCI_0_1_FN_CPU_OFF: case QEMU_PSCI_0_2_FN_CPU_OFF: @@ -250,9 +195,8 @@ err: return; cpu_off: - cpu->powered_off = true; - cs->halted = 1; - cs->exception_index = EXCP_HLT; - cpu_loop_exit(cs); + ret = arm_set_cpu_off(cpu->mp_affinity); /* notreached */ + /* sanity check in case something failed */ + assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS); } diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index b13cff756a..24f5e177dd 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -2086,19 +2086,19 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) * size: 00 -> 8 bit, 01 -> 16 bit, 10 -> 32 bit, 11 -> 64bit * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32 */ -static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn) +static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, + int opc, + int size, + int rt, + bool is_vector) { - int rt = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); int imm9 = sextract32(insn, 12, 9); - int opc = extract32(insn, 22, 2); - int size = extract32(insn, 30, 2); int idx = extract32(insn, 10, 2); bool is_signed = false; bool is_store = false; bool is_extended = false; bool is_unpriv = (idx == 2); - bool is_vector = extract32(insn, 26, 1); bool post_index; bool writeback; @@ -2128,8 +2128,8 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn) return; } is_store = (opc == 0); - is_signed = opc & (1<<1); - is_extended = (size < 3) && (opc & 1); + is_signed = extract32(opc, 1, 1); + is_extended = (size < 3) && extract32(opc, 0, 1); } switch (idx) { @@ -2205,19 +2205,19 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn) * Rn: address register or SP for base * Rm: offset register or ZR for offset */ -static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn) +static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, + int opc, + int size, + int rt, + bool is_vector) { - int rt = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); int shift = extract32(insn, 12, 1); int rm = extract32(insn, 16, 5); - int opc = extract32(insn, 22, 2); int opt = extract32(insn, 13, 3); - int size = extract32(insn, 30, 2); bool is_signed = false; bool is_store = false; bool is_extended = false; - bool is_vector = extract32(insn, 26, 1); TCGv_i64 tcg_rm; TCGv_i64 tcg_addr; @@ -2294,14 +2294,14 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn) * Rn: base address register (inc SP) * Rt: target register */ -static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn) +static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, + int opc, + int size, + int rt, + bool is_vector) { - int rt = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); unsigned int imm12 = extract32(insn, 10, 12); - bool is_vector = extract32(insn, 26, 1); - int size = extract32(insn, 30, 2); - int opc = extract32(insn, 22, 2); unsigned int offset; TCGv_i64 tcg_addr; @@ -2360,20 +2360,25 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn) /* Load/store register (all forms) */ static void disas_ldst_reg(DisasContext *s, uint32_t insn) { + int rt = extract32(insn, 0, 5); + int opc = extract32(insn, 22, 2); + bool is_vector = extract32(insn, 26, 1); + int size = extract32(insn, 30, 2); + switch (extract32(insn, 24, 2)) { case 0: if (extract32(insn, 21, 1) == 1 && extract32(insn, 10, 2) == 2) { - disas_ldst_reg_roffset(s, insn); + disas_ldst_reg_roffset(s, insn, opc, size, rt, is_vector); } else { /* Load/store register (unscaled immediate) * Load/store immediate pre/post-indexed * Load/store register unprivileged */ - disas_ldst_reg_imm9(s, insn); + disas_ldst_reg_imm9(s, insn, opc, size, rt, is_vector); } break; case 1: - disas_ldst_reg_unsigned_imm(s, insn); + disas_ldst_reg_unsigned_imm(s, insn, opc, size, rt, is_vector); break; default: unallocated_encoding(s); @@ -595,6 +595,12 @@ struct TCGContext { extern TCGContext tcg_ctx; +static inline void tcg_set_insn_param(int op_idx, int arg, TCGArg v) +{ + int op_argi = tcg_ctx.gen_op_buf[op_idx].args; + tcg_ctx.gen_opparam_buf[op_argi + arg] = v; +} + /* The number of opcodes emitted so far. */ static inline int tcg_op_buf_count(void) { diff --git a/tests/.gitignore b/tests/.gitignore index 9eed22988b..a06a8ba26f 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -3,6 +3,7 @@ check-qfloat check-qint check-qjson check-qlist +check-qnull check-qstring check-qom-interface check-qom-proplist diff --git a/tests/Makefile b/tests/Makefile index 9194f1850b..9dddde6589 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -16,6 +16,8 @@ check-unit-y += tests/check-qstring$(EXESUF) gcov-files-check-qstring-y = qobject/qstring.c check-unit-y += tests/check-qlist$(EXESUF) gcov-files-check-qlist-y = qobject/qlist.c +check-unit-y += tests/check-qnull$(EXESUF) +gcov-files-check-qnull-y = qobject/qnull.c check-unit-y += tests/check-qjson$(EXESUF) gcov-files-check-qjson-y = qobject/qjson.c check-unit-y += tests/test-qmp-output-visitor$(EXESUF) @@ -382,7 +384,8 @@ GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \ tests/test-qmp-introspect.h test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ - tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \ + tests/check-qlist.o tests/check-qfloat.o tests/check-qnull.o \ + tests/check-qjson.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ @@ -410,6 +413,7 @@ tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) tests/check-qfloat$(EXESUF): tests/check-qfloat.o $(test-util-obj-y) +tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y) tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y) tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y) tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y) diff --git a/tests/check-qnull.c b/tests/check-qnull.c new file mode 100644 index 0000000000..fd9c68f7e1 --- /dev/null +++ b/tests/check-qnull.c @@ -0,0 +1,75 @@ +/* + * QNull unit-tests. + * + * Copyright (C) 2016 Red Hat Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#include "qemu/osdep.h" +#include <glib.h> + +#include "qapi/qmp/qobject.h" +#include "qemu-common.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/error.h" + +/* + * Public Interface test-cases + * + * (with some violations to access 'private' data) + */ + +static void qnull_ref_test(void) +{ + QObject *obj; + + g_assert(qnull_.refcnt == 1); + obj = qnull(); + g_assert(obj); + g_assert(obj == &qnull_); + g_assert(qnull_.refcnt == 2); + g_assert(qobject_type(obj) == QTYPE_QNULL); + qobject_decref(obj); + g_assert(qnull_.refcnt == 1); +} + +static void qnull_visit_test(void) +{ + QObject *obj; + QmpOutputVisitor *qov; + QmpInputVisitor *qiv; + + /* + * Most tests of interactions between QObject and visitors are in + * test-qmp-*-visitor; but these tests live here because they + * depend on layering violations to check qnull_ refcnt. + */ + + g_assert(qnull_.refcnt == 1); + obj = qnull(); + qiv = qmp_input_visitor_new(obj, true); + qobject_decref(obj); + visit_type_null(qmp_input_get_visitor(qiv), NULL, &error_abort); + qmp_input_visitor_cleanup(qiv); + + qov = qmp_output_visitor_new(); + visit_type_null(qmp_output_get_visitor(qov), NULL, &error_abort); + obj = qmp_output_get_qobject(qov); + g_assert(obj == &qnull_); + qobject_decref(obj); + qmp_output_visitor_cleanup(qov); + + g_assert(qnull_.refcnt == 1); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/qnull_ref", qnull_ref_test); + g_test_add_func("/public/qnull_visit", qnull_visit_test); + + return g_test_run(); +} diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index 14a9ebbd5a..5c3edd753a 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -222,20 +222,19 @@ static void test_dealloc_partial(void) ud2_dict = qdict_new(); qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text))); - qiv = qmp_input_visitor_new(QOBJECT(ud2_dict)); + qiv = qmp_input_visitor_new(QOBJECT(ud2_dict), true); visit_type_UserDefTwo(qmp_input_get_visitor(qiv), NULL, &ud2, &err); qmp_input_visitor_cleanup(qiv); QDECREF(ud2_dict); } - /* verify partial success */ - assert(ud2 != NULL); - assert(ud2->string0 != NULL); - assert(strcmp(ud2->string0, text) == 0); - assert(ud2->dict1 == NULL); - - /* confirm & release construction error */ + /* verify that visit_type_XXX() cleans up properly on error */ error_free_or_abort(&err); + assert(!ud2); + + /* Manually create a partial object, leaving ud2->dict1 at NULL */ + ud2 = g_new0(UserDefTwo, 1); + ud2->string0 = g_strdup(text); /* tear down partial object */ qapi_free_UserDefTwo(ud2); diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c index d71727e272..4602529ea0 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qmp-input-strict.c @@ -55,7 +55,7 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data, data->obj = qobject_from_jsonv(json_string, ap); g_assert(data->obj); - data->qiv = qmp_input_visitor_new_strict(data->obj); + data->qiv = qmp_input_visitor_new(data->obj, true); g_assert(data->qiv); v = qmp_input_get_visitor(data->qiv); @@ -182,10 +182,7 @@ static void test_validate_fail_struct(TestInputVisitorData *data, visit_type_TestStruct(v, NULL, &p, &err); error_free_or_abort(&err); - if (p) { - g_free(p->string); - } - g_free(p); + g_assert(!p); } static void test_validate_fail_struct_nested(TestInputVisitorData *data, @@ -199,7 +196,7 @@ static void test_validate_fail_struct_nested(TestInputVisitorData *data, visit_type_UserDefTwo(v, NULL, &udp, &err); error_free_or_abort(&err); - qapi_free_UserDefTwo(udp); + g_assert(!udp); } static void test_validate_fail_list(TestInputVisitorData *data, @@ -213,7 +210,7 @@ static void test_validate_fail_list(TestInputVisitorData *data, visit_type_UserDefOneList(v, NULL, &head, &err); error_free_or_abort(&err); - qapi_free_UserDefOneList(head); + g_assert(!head); } static void test_validate_fail_union_native_list(TestInputVisitorData *data, @@ -228,7 +225,7 @@ static void test_validate_fail_union_native_list(TestInputVisitorData *data, visit_type_UserDefNativeListUnion(v, NULL, &tmp, &err); error_free_or_abort(&err); - qapi_free_UserDefNativeListUnion(tmp); + g_assert(!tmp); } static void test_validate_fail_union_flat(TestInputVisitorData *data, @@ -242,7 +239,7 @@ static void test_validate_fail_union_flat(TestInputVisitorData *data, visit_type_UserDefFlatUnion(v, NULL, &tmp, &err); error_free_or_abort(&err); - qapi_free_UserDefFlatUnion(tmp); + g_assert(!tmp); } static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data, @@ -257,13 +254,13 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data, visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err); error_free_or_abort(&err); - qapi_free_UserDefFlatUnion2(tmp); + g_assert(!tmp); } static void test_validate_fail_alternate(TestInputVisitorData *data, const void *unused) { - UserDefAlternate *tmp = NULL; + UserDefAlternate *tmp; Visitor *v; Error *err = NULL; @@ -271,7 +268,7 @@ static void test_validate_fail_alternate(TestInputVisitorData *data, visit_type_UserDefAlternate(v, NULL, &tmp, &err); error_free_or_abort(&err); - qapi_free_UserDefAlternate(tmp); + g_assert(!tmp); } static void do_test_validate_qmp_introspect(TestInputVisitorData *data, diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index 80527eb850..cee07ce8dd 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -51,7 +51,7 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data, data->obj = qobject_from_jsonv(json_string, ap); g_assert(data->obj); - data->qiv = qmp_input_visitor_new(data->obj); + data->qiv = qmp_input_visitor_new(data->obj, false); g_assert(data->qiv); v = qmp_input_get_visitor(data->qiv); @@ -279,6 +279,34 @@ static void test_visitor_in_any(TestInputVisitorData *data, qobject_decref(res); } +static void test_visitor_in_null(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + Error *err = NULL; + char *tmp; + + /* + * FIXME: Since QAPI doesn't know the 'null' type yet, we can't + * test visit_type_null() by reading into a QAPI struct then + * checking that it was populated correctly. The best we can do + * for now is ensure that we consumed null from the input, proven + * by the fact that we can't re-read the key; and that we detect + * when input is not null. + */ + + v = visitor_input_test_init(data, "{ 'a': null, 'b': '' }"); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_null(v, "a", &error_abort); + visit_type_str(v, "a", &tmp, &err); + g_assert(!tmp); + error_free_or_abort(&err); + visit_type_null(v, "b", &err); + error_free_or_abort(&err); + visit_check_struct(v, &error_abort); + visit_end_struct(v); +} + static void test_visitor_in_union_flat(TestInputVisitorData *data, const void *unused) { @@ -745,18 +773,12 @@ static void test_visitor_in_errors(TestInputVisitorData *data, visit_type_TestStruct(v, NULL, &p, &err); error_free_or_abort(&err); - /* FIXME - a failed parse should not leave a partially-allocated p - * for us to clean up; this could cause callers to leak memory. */ - g_assert(p->string == NULL); - - g_free(p->string); - g_free(p); + g_assert(!p); v = visitor_input_test_init(data, "[ '1', '2', false, '3' ]"); visit_type_strList(v, NULL, &q, &err); error_free_or_abort(&err); - assert(q); - qapi_free_strList(q); + assert(!q); } static void test_visitor_in_wrong_type(TestInputVisitorData *data, @@ -829,6 +851,8 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_list); input_visitor_test_add("/visitor/input/any", &in_visitor_data, test_visitor_in_any); + input_visitor_test_add("/visitor/input/null", + &in_visitor_data, test_visitor_in_null); input_visitor_test_add("/visitor/input/union-flat", &in_visitor_data, test_visitor_in_union_flat); input_visitor_test_add("/visitor/input/alternate", diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index c70926793a..1f80e696ea 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -43,6 +43,12 @@ static void visitor_output_teardown(TestOutputVisitorData *data, data->ov = NULL; } +static void visitor_reset(TestOutputVisitorData *data) +{ + visitor_output_teardown(data, NULL); + visitor_output_setup(data, NULL); +} + static void test_visitor_out_int(TestOutputVisitorData *data, const void *unused) { @@ -139,6 +145,7 @@ static void test_visitor_out_enum(TestOutputVisitorData *data, g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, EnumOne_lookup[i]); qobject_decref(obj); + visitor_reset(data); } } @@ -153,6 +160,7 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data, visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err); g_assert(err); error_free(err); + visitor_reset(data); } } @@ -262,6 +270,7 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data, visit_type_UserDefOne(data->ov, "unused", &pu, &err); g_assert(err); error_free(err); + visitor_reset(data); } } @@ -366,6 +375,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, qobject_decref(obj); qobject_decref(qobj); + visitor_reset(data); qdict = qdict_new(); qdict_put(qdict, "integer", qint_from_int(-42)); qdict_put(qdict, "boolean", qbool_from_bool(true)); @@ -442,6 +452,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, qapi_free_UserDefAlternate(tmp); qobject_decref(arg); + visitor_reset(data); tmp = g_new0(UserDefAlternate, 1); tmp->type = QTYPE_QSTRING; tmp->u.s = g_strdup("hello"); @@ -455,6 +466,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, qapi_free_UserDefAlternate(tmp); qobject_decref(arg); + visitor_reset(data); tmp = g_new0(UserDefAlternate, 1); tmp->type = QTYPE_QDICT; tmp->u.udfu.integer = 1; @@ -477,15 +489,24 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, qobject_decref(arg); } -static void test_visitor_out_empty(TestOutputVisitorData *data, - const void *unused) +static void test_visitor_out_null(TestOutputVisitorData *data, + const void *unused) { QObject *arg; + QDict *qdict; + QObject *nil; + visit_start_struct(data->ov, NULL, NULL, 0, &error_abort); + visit_type_null(data->ov, "a", &error_abort); + visit_check_struct(data->ov, &error_abort); + visit_end_struct(data->ov); arg = qmp_output_get_qobject(data->qov); - g_assert(qobject_type(arg) == QTYPE_QNULL); - /* Check that qnull reference counting is sane */ - g_assert(arg->refcnt == 2); + g_assert(qobject_type(arg) == QTYPE_QDICT); + qdict = qobject_to_qdict(arg); + g_assert_cmpint(qdict_size(qdict), ==, 1); + nil = qdict_get(qdict, "a"); + g_assert(nil); + g_assert(qobject_type(nil) == QTYPE_QNULL); qobject_decref(arg); } @@ -839,8 +860,8 @@ int main(int argc, char **argv) &out_visitor_data, test_visitor_out_union_flat); output_visitor_test_add("/visitor/output/alternate", &out_visitor_data, test_visitor_out_alternate); - output_visitor_test_add("/visitor/output/empty", - &out_visitor_data, test_visitor_out_empty); + output_visitor_test_add("/visitor/output/null", + &out_visitor_data, test_visitor_out_null); output_visitor_test_add("/visitor/output/native_list/int", &out_visitor_data, test_visitor_out_native_list_int); diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 9e6906a567..5a56920222 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -63,6 +63,13 @@ static void test_visitor_in_int(TestInputVisitorData *data, visit_type_int(v, NULL, &res, &err); g_assert(!err); g_assert_cmpint(res, ==, value); + + visitor_input_teardown(data, unused); + + v = visitor_input_test_init(data, "not an int"); + + visit_type_int(v, NULL, &res, &err); + error_free_or_abort(&err); } static void test_visitor_in_intList(TestInputVisitorData *data, @@ -70,6 +77,7 @@ static void test_visitor_in_intList(TestInputVisitorData *data, { int64_t value[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20}; int16List *res = NULL, *tmp; + Error *err = NULL; Visitor *v; int i = 0; @@ -84,12 +92,15 @@ static void test_visitor_in_intList(TestInputVisitorData *data, } g_assert(!tmp); - tmp = res; - while (tmp) { - res = res->next; - g_free(tmp); - tmp = res; - } + qapi_free_int16List(res); + + visitor_input_teardown(data, unused); + + v = visitor_input_test_init(data, "not an int list"); + + visit_type_int16List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); } static void test_visitor_in_bool(TestInputVisitorData *data, diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index 9adbc30a41..7b14b5a7af 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -1038,7 +1038,7 @@ static void qmp_deserialize(void **native_out, void *datap, obj = qobject_from_json(qstring_get_str(output_json)); QDECREF(output_json); - d->qiv = qmp_input_visitor_new(obj); + d->qiv = qmp_input_visitor_new(obj, true); qobject_decref(obj_orig); qobject_decref(obj); visit(qmp_input_get_visitor(d->qiv), native_out, errp); diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 0d536911c9..2a2c5243a1 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -1145,7 +1145,7 @@ void qapi_copy_SocketAddress(SocketAddress **p_dest, return; } - qiv = qmp_input_visitor_new(obj); + qiv = qmp_input_visitor_new(obj, true); iv = qmp_input_get_visitor(qiv); visit_type_SocketAddress(iv, NULL, p_dest, &error_abort); qmp_input_visitor_cleanup(qiv); |