diff --git a/MAINTAINERS b/MAINTAINERS index 992799171f..65dfdc9677 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -642,6 +642,7 @@ R: Strahinja Jankovic L: qemu-arm@nongnu.org S: Odd Fixes F: hw/*/allwinner* +F: hw/ide/ahci-allwinner.c F: include/hw/*/allwinner* F: hw/arm/cubieboard.c F: docs/system/arm/cubieboard.rst @@ -3675,6 +3676,16 @@ F: hw/core/clock-vmstate.c F: hw/core/qdev-clock.c F: docs/devel/clocks.rst +Reset framework +M: Peter Maydell +S: Maintained +F: include/hw/resettable.h +F: include/hw/core/resetcontainer.h +F: include/sysemu/reset.h +F: hw/core/reset.c +F: hw/core/resettable.c +F: hw/core/resetcontainer.c + Usermode Emulation ------------------ Overall usermode emulation diff --git a/docs/devel/qom.rst b/docs/devel/qom.rst index 9918fac7f2..0889ca949c 100644 --- a/docs/devel/qom.rst +++ b/docs/devel/qom.rst @@ -348,12 +348,14 @@ used. This does the same as OBJECT_DECLARE_SIMPLE_TYPE(), but without the 'struct MyDeviceClass' definition. To implement the type, the OBJECT_DEFINE macro family is available. -In the simple case the OBJECT_DEFINE_TYPE macro is suitable: +For the simplest case of a leaf class which doesn't need any of its +own virtual functions (i.e. which was declared with OBJECT_DECLARE_SIMPLE_TYPE) +the OBJECT_DEFINE_SIMPLE_TYPE macro is suitable: .. code-block:: c :caption: Defining a simple type - OBJECT_DEFINE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE) + OBJECT_DEFINE_SIMPLE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE) This is equivalent to the following: @@ -370,7 +372,6 @@ This is equivalent to the following: .instance_size = sizeof(MyDevice), .instance_init = my_device_init, .instance_finalize = my_device_finalize, - .class_size = sizeof(MyDeviceClass), .class_init = my_device_class_init, }; @@ -385,13 +386,36 @@ This is sufficient to get the type registered with the type system, and the three standard methods now need to be implemented along with any other logic required for the type. +If the class needs its own virtual methods, or has some other +per-class state it needs to store in its own class struct, +then you can use the OBJECT_DEFINE_TYPE macro. This does the +same thing as OBJECT_DEFINE_SIMPLE_TYPE, but it also sets the +class_size of the type to the size of the class struct. + +.. code-block:: c + :caption: Defining a type which needs a class struct + + OBJECT_DEFINE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE) + If the type needs to implement one or more interfaces, then the -OBJECT_DEFINE_TYPE_WITH_INTERFACES() macro can be used instead. -This accepts an array of interface type names. +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES() and +OBJECT_DEFINE_TYPE_WITH_INTERFACES() macros can be used instead. +These accept an array of interface type names. The difference between +them is that the former is for simple leaf classes that don't need +a class struct, and the latter is for when you will be defining +a class struct. .. code-block:: c :caption: Defining a simple type implementing interfaces + OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(MyDevice, my_device, + MY_DEVICE, DEVICE, + { TYPE_USER_CREATABLE }, + { NULL }) + +.. code-block:: c + :caption: Defining a type implementing interfaces + OBJECT_DEFINE_TYPE_WITH_INTERFACES(MyDevice, my_device, MY_DEVICE, DEVICE, { TYPE_USER_CREATABLE }, diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst index d4e79718ba..2ea85e7779 100644 --- a/docs/devel/reset.rst +++ b/docs/devel/reset.rst @@ -11,8 +11,8 @@ whole group can be reset consistently. Each individual member object does not have to care about others; in particular, problems of order (which object is reset first) are addressed. -As of now DeviceClass and BusClass implement this interface. - +The main object types which implement this interface are DeviceClass +and BusClass. Triggering reset ---------------- @@ -288,3 +288,43 @@ There is currently 2 cases where this function is used: 2. *hot bus change*; it means an existing live device is added, moved or removed in the bus hierarchy. At the moment, it occurs only in the raspi machines for changing the sdbus used by sd card. + +Reset of the complete system +---------------------------- + +Reset of the complete system is a little complicated. The typical +flow is: + +1. Code which wishes to reset the entire system does so by calling + ``qemu_system_reset_request()``. This schedules a reset, but the + reset will happen asynchronously after the function returns. + That makes this safe to call from, for example, device models. + +2. The function which is called to make the reset happen is + ``qemu_system_reset()``. Generally only core system code should + call this directly. + +3. ``qemu_system_reset()`` calls the ``MachineClass::reset`` method of + the current machine, if it has one. That method must call + ``qemu_devices_reset()``. If the machine has no reset method, + ``qemu_system_reset()`` calls ``qemu_devices_reset()`` directly. + +4. ``qemu_devices_reset()`` performs a reset of the system, using + the three-phase mechanism listed above. It resets all objects + that were registered with it using ``qemu_register_resettable()``. + It also calls all the functions registered with it using + ``qemu_register_reset()``. Those functions are called during the + "hold" phase of this reset. + +5. The most important object that this reset resets is the + 'sysbus' bus. The sysbus bus is the root of the qbus tree. This + means that all devices on the sysbus are reset, and all their + child buses, and all the devices on those child buses. + +6. Devices which are not on the qbus tree are *not* automatically + reset! (The most obvious example of this is CPU objects, but + anything that directly inherits from ``TYPE_OBJECT`` or ``TYPE_DEVICE`` + rather than from ``TYPE_SYS_BUS_DEVICE`` or some other plugs-into-a-bus + type will be in this category.) You need to therefore arrange for these + to be reset in some other way (e.g. using ``qemu_register_resettable()`` + or ``qemu_register_reset()``). diff --git a/docs/system/arm/raspi.rst b/docs/system/arm/raspi.rst index d0a6f08b2b..bb417c3424 100644 --- a/docs/system/arm/raspi.rst +++ b/docs/system/arm/raspi.rst @@ -1,5 +1,5 @@ -Raspberry Pi boards (``raspi0``, ``raspi1ap``, ``raspi2b``, ``raspi3ap``, ``raspi3b``) -====================================================================================== +Raspberry Pi boards (``raspi0``, ``raspi1ap``, ``raspi2b``, ``raspi3ap``, ``raspi3b``, ``raspi4b``) +=================================================================================================== QEMU provides models of the following Raspberry Pi boards: @@ -12,12 +12,13 @@ QEMU provides models of the following Raspberry Pi boards: Cortex-A53 (4 cores), 512 MiB of RAM ``raspi3b`` Cortex-A53 (4 cores), 1 GiB of RAM - +``raspi4b`` + Cortex-A72 (4 cores), 2 GiB of RAM Implemented devices ------------------- - * ARM1176JZF-S, Cortex-A7 or Cortex-A53 CPU + * ARM1176JZF-S, Cortex-A7, Cortex-A53 or Cortex-A72 CPU * Interrupt controller * DMA controller * Clock and reset controller (CPRMAN) @@ -35,9 +36,10 @@ Implemented devices * VideoCore firmware (property) * Peripheral SPI controller (SPI) - Missing devices --------------- * Analog to Digital Converter (ADC) * Pulse Width Modulation (PWM) + * PCIE Root Port (raspi4b) + * GENET Ethernet Controller (raspi4b) diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index d5573fd954..a0bbe76f26 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -30,9 +30,9 @@ #define SEPARATE_DMA_IRQ_MAX 10 #define ORGATED_DMA_IRQ_COUNT 4 -static void create_unimp(BCM2835PeripheralState *ps, - UnimplementedDeviceState *uds, - const char *name, hwaddr ofs, hwaddr size) +void create_unimp(BCMSocPeripheralBaseState *ps, + UnimplementedDeviceState *uds, + const char *name, hwaddr ofs, hwaddr size) { object_initialize_child(OBJECT(ps), name, uds, TYPE_UNIMPLEMENTED_DEVICE); qdev_prop_set_string(DEVICE(uds), "name", name); @@ -45,9 +45,36 @@ static void create_unimp(BCM2835PeripheralState *ps, static void bcm2835_peripherals_init(Object *obj) { BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj); + BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(obj); + + /* Random Number Generator */ + object_initialize_child(obj, "rng", &s->rng, TYPE_BCM2835_RNG); + + /* Thermal */ + object_initialize_child(obj, "thermal", &s->thermal, TYPE_BCM2835_THERMAL); + + /* GPIO */ + object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2835_GPIO); + + object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci", + OBJECT(&s_base->sdhci.sdbus)); + object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost", + OBJECT(&s_base->sdhost.sdbus)); + + /* Gated DMA interrupts */ + object_initialize_child(obj, "orgated-dma-irq", + &s_base->orgated_dma_irq, TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s_base->orgated_dma_irq), "num-lines", + ORGATED_DMA_IRQ_COUNT, &error_abort); +} + +static void raspi_peripherals_base_init(Object *obj) +{ + BCMSocPeripheralBaseState *s = BCM_SOC_PERIPHERALS_BASE(obj); + BCMSocPeripheralBaseClass *bc = BCM_SOC_PERIPHERALS_BASE_GET_CLASS(obj); /* Memory region for peripheral devices, which we export to our parent */ - memory_region_init(&s->peri_mr, obj,"bcm2835-peripherals", 0x1000000); + memory_region_init(&s->peri_mr, obj, "bcm2835-peripherals", bc->peri_size); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_mr); /* Internal memory region for peripheral bus addresses (not exported) */ @@ -81,6 +108,7 @@ static void bcm2835_peripherals_init(Object *obj) /* Framebuffer */ object_initialize_child(obj, "fb", &s->fb, TYPE_BCM2835_FB); object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size"); + object_property_add_alias(obj, "vcram-base", OBJECT(&s->fb), "vcram-base"); object_property_add_const_link(OBJECT(&s->fb), "dma-mr", OBJECT(&s->gpu_bus_mr)); @@ -98,9 +126,6 @@ static void bcm2835_peripherals_init(Object *obj) object_property_add_const_link(OBJECT(&s->property), "dma-mr", OBJECT(&s->gpu_bus_mr)); - /* Random Number Generator */ - object_initialize_child(obj, "rng", &s->rng, TYPE_BCM2835_RNG); - /* Extended Mass Media Controller */ object_initialize_child(obj, "sdhci", &s->sdhci, TYPE_SYSBUS_SDHCI); @@ -110,25 +135,9 @@ static void bcm2835_peripherals_init(Object *obj) /* DMA Channels */ object_initialize_child(obj, "dma", &s->dma, TYPE_BCM2835_DMA); - object_initialize_child(obj, "orgated-dma-irq", - &s->orgated_dma_irq, TYPE_OR_IRQ); - object_property_set_int(OBJECT(&s->orgated_dma_irq), "num-lines", - ORGATED_DMA_IRQ_COUNT, &error_abort); - object_property_add_const_link(OBJECT(&s->dma), "dma-mr", OBJECT(&s->gpu_bus_mr)); - /* Thermal */ - object_initialize_child(obj, "thermal", &s->thermal, TYPE_BCM2835_THERMAL); - - /* GPIO */ - object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2835_GPIO); - - object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci", - OBJECT(&s->sdhci.sdbus)); - object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost", - OBJECT(&s->sdhost.sdbus)); - /* Mphi */ object_initialize_child(obj, "mphi", &s->mphi, TYPE_BCM2835_MPHI); @@ -152,11 +161,76 @@ static void bcm2835_peripherals_init(Object *obj) static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) { + MemoryRegion *mphi_mr; BCM2835PeripheralState *s = BCM2835_PERIPHERALS(dev); + BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(dev); + int n; + + bcm_soc_peripherals_common_realize(dev, errp); + + /* Extended Mass Media Controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->sdhci), 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), BCM2835_IC_GPU_IRQ, + INTERRUPT_ARASANSDIO)); + + /* Connect DMA 0-12 to the interrupt controller */ + for (n = 0; n <= SEPARATE_DMA_IRQ_MAX; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_DMA0 + n)); + } + + if (!qdev_realize(DEVICE(&s_base->orgated_dma_irq), NULL, errp)) { + return; + } + for (n = 0; n < ORGATED_DMA_IRQ_COUNT; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), + SEPARATE_DMA_IRQ_MAX + 1 + n, + qdev_get_gpio_in(DEVICE(&s_base->orgated_dma_irq), n)); + } + qdev_connect_gpio_out(DEVICE(&s_base->orgated_dma_irq), 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_DMA0 + SEPARATE_DMA_IRQ_MAX + 1)); + + /* Random Number Generator */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rng), errp)) { + return; + } + memory_region_add_subregion( + &s_base->peri_mr, RNG_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0)); + + /* THERMAL */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->thermal), errp)) { + return; + } + memory_region_add_subregion(&s_base->peri_mr, THERMAL_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->thermal), 0)); + + /* Map MPHI to the peripherals memory map */ + mphi_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s_base->mphi), 0); + memory_region_add_subregion(&s_base->peri_mr, MPHI_OFFSET, mphi_mr); + + /* GPIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { + return; + } + memory_region_add_subregion( + &s_base->peri_mr, GPIO_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0)); + + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus"); +} + +void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp) +{ + BCMSocPeripheralBaseState *s = BCM_SOC_PERIPHERALS_BASE(dev); Object *obj; MemoryRegion *ram; Error *err = NULL; - uint64_t ram_size, vcram_size; + uint64_t ram_size, vcram_size, vcram_base; int n; obj = object_property_get_link(OBJECT(dev), "ram", &error_abort); @@ -260,11 +334,21 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) return; } - if (!object_property_set_uint(OBJECT(&s->fb), "vcram-base", - ram_size - vcram_size, errp)) { + vcram_base = object_property_get_uint(OBJECT(s), "vcram-base", &err); + if (err) { + error_propagate(errp, err); return; } + if (vcram_base == 0) { + vcram_base = ram_size - vcram_size; + } + vcram_base = MIN(vcram_base, UPPER_RAM_BASE - vcram_size); + + if (!object_property_set_uint(OBJECT(&s->fb), "vcram-base", vcram_base, + errp)) { + return; + } if (!sysbus_realize(SYS_BUS_DEVICE(&s->fb), errp)) { return; } @@ -285,14 +369,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0, qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY)); - /* Random Number Generator */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->rng), errp)) { - return; - } - - memory_region_add_subregion(&s->peri_mr, RNG_OFFSET, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0)); - /* Extended Mass Media Controller * * Compatible with: @@ -315,9 +391,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->peri_mr, EMMC1_OFFSET, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhci), 0)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, - qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, - INTERRUPT_ARASANSDIO)); /* SDHOST */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhost), errp)) { @@ -340,49 +413,11 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1)); - for (n = 0; n <= SEPARATE_DMA_IRQ_MAX; n++) { - sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n, - qdev_get_gpio_in_named(DEVICE(&s->ic), - BCM2835_IC_GPU_IRQ, - INTERRUPT_DMA0 + n)); - } - if (!qdev_realize(DEVICE(&s->orgated_dma_irq), NULL, errp)) { - return; - } - for (n = 0; n < ORGATED_DMA_IRQ_COUNT; n++) { - sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), - SEPARATE_DMA_IRQ_MAX + 1 + n, - qdev_get_gpio_in(DEVICE(&s->orgated_dma_irq), n)); - } - qdev_connect_gpio_out(DEVICE(&s->orgated_dma_irq), 0, - qdev_get_gpio_in_named(DEVICE(&s->ic), - BCM2835_IC_GPU_IRQ, - INTERRUPT_DMA0 + SEPARATE_DMA_IRQ_MAX + 1)); - - /* THERMAL */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->thermal), errp)) { - return; - } - memory_region_add_subregion(&s->peri_mr, THERMAL_OFFSET, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->thermal), 0)); - - /* GPIO */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { - return; - } - - memory_region_add_subregion(&s->peri_mr, GPIO_OFFSET, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0)); - - object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus"); - /* Mphi */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->mphi), errp)) { return; } - memory_region_add_subregion(&s->peri_mr, MPHI_OFFSET, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mphi), 0)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->mphi), 0, qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, INTERRUPT_HOSTPORT)); @@ -436,21 +471,27 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + BCMSocPeripheralBaseClass *bc = BCM_SOC_PERIPHERALS_BASE_CLASS(oc); + bc->peri_size = 0x1000000; dc->realize = bcm2835_peripherals_realize; } -static const TypeInfo bcm2835_peripherals_type_info = { - .name = TYPE_BCM2835_PERIPHERALS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(BCM2835PeripheralState), - .instance_init = bcm2835_peripherals_init, - .class_init = bcm2835_peripherals_class_init, +static const TypeInfo bcm2835_peripherals_types[] = { + { + .name = TYPE_BCM2835_PERIPHERALS, + .parent = TYPE_BCM_SOC_PERIPHERALS_BASE, + .instance_size = sizeof(BCM2835PeripheralState), + .instance_init = bcm2835_peripherals_init, + .class_init = bcm2835_peripherals_class_init, + }, { + .name = TYPE_BCM_SOC_PERIPHERALS_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCMSocPeripheralBaseState), + .instance_init = raspi_peripherals_base_init, + .class_size = sizeof(BCMSocPeripheralBaseClass), + .abstract = true, + } }; -static void bcm2835_peripherals_register_types(void) -{ - type_register_static(&bcm2835_peripherals_type_info); -} - -type_init(bcm2835_peripherals_register_types) +DEFINE_TYPES(bcm2835_peripherals_types) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index e3ba18a8ec..db191661f2 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -31,12 +31,12 @@ struct BCM283XClass { }; static Property bcm2836_enabled_cores_property = - DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus, 0); + DEFINE_PROP_UINT32("enabled-cpus", BCM283XBaseState, enabled_cpus, 0); -static void bcm2836_init(Object *obj) +static void bcm283x_base_init(Object *obj) { - BCM283XState *s = BCM283X(obj); - BCM283XClass *bc = BCM283X_GET_CLASS(obj); + BCM283XBaseState *s = BCM283X_BASE(obj); + BCM283XBaseClass *bc = BCM283X_BASE_GET_CLASS(obj); int n; for (n = 0; n < bc->core_count; n++) { @@ -52,6 +52,11 @@ static void bcm2836_init(Object *obj) object_initialize_child(obj, "control", &s->control, TYPE_BCM2836_CONTROL); } +} + +static void bcm283x_init(Object *obj) +{ + BCM283XState *s = BCM283X(obj); object_initialize_child(obj, "peripherals", &s->peripherals, TYPE_BCM2835_PERIPHERALS); @@ -61,108 +66,116 @@ static void bcm2836_init(Object *obj) "command-line"); object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals), "vcram-size"); + object_property_add_alias(obj, "vcram-base", OBJECT(&s->peripherals), + "vcram-base"); } -static bool bcm283x_common_realize(DeviceState *dev, Error **errp) +bool bcm283x_common_realize(DeviceState *dev, BCMSocPeripheralBaseState *ps, + Error **errp) { - BCM283XState *s = BCM283X(dev); - BCM283XClass *bc = BCM283X_GET_CLASS(dev); + BCM283XBaseState *s = BCM283X_BASE(dev); + BCM283XBaseClass *bc = BCM283X_BASE_GET_CLASS(dev); Object *obj; /* common peripherals from bcm2835 */ obj = object_property_get_link(OBJECT(dev), "ram", &error_abort); - object_property_add_const_link(OBJECT(&s->peripherals), "ram", obj); + object_property_add_const_link(OBJECT(ps), "ram", obj); - if (!sysbus_realize(SYS_BUS_DEVICE(&s->peripherals), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(ps), errp)) { return false; } - object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->peripherals), - "sd-bus"); + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(ps), "sd-bus"); - sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0, - bc->peri_base, 1); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(ps), 0, bc->peri_base, 1); return true; } static void bcm2835_realize(DeviceState *dev, Error **errp) { BCM283XState *s = BCM283X(dev); + BCM283XBaseState *s_base = BCM283X_BASE(dev); + BCMSocPeripheralBaseState *ps_base + = BCM_SOC_PERIPHERALS_BASE(&s->peripherals); - if (!bcm283x_common_realize(dev, errp)) { + if (!bcm283x_common_realize(dev, ps_base, errp)) { return; } - if (!qdev_realize(DEVICE(&s->cpu[0].core), NULL, errp)) { + if (!qdev_realize(DEVICE(&s_base->cpu[0].core), NULL, errp)) { return; } /* Connect irq/fiq outputs from the interrupt controller. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0, - qdev_get_gpio_in(DEVICE(&s->cpu[0].core), ARM_CPU_IRQ)); + qdev_get_gpio_in(DEVICE(&s_base->cpu[0].core), ARM_CPU_IRQ)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1, - qdev_get_gpio_in(DEVICE(&s->cpu[0].core), ARM_CPU_FIQ)); + qdev_get_gpio_in(DEVICE(&s_base->cpu[0].core), ARM_CPU_FIQ)); } static void bcm2836_realize(DeviceState *dev, Error **errp) { - BCM283XState *s = BCM283X(dev); - BCM283XClass *bc = BCM283X_GET_CLASS(dev); int n; + BCM283XState *s = BCM283X(dev); + BCM283XBaseState *s_base = BCM283X_BASE(dev); + BCM283XBaseClass *bc = BCM283X_BASE_GET_CLASS(dev); + BCMSocPeripheralBaseState *ps_base + = BCM_SOC_PERIPHERALS_BASE(&s->peripherals); - if (!bcm283x_common_realize(dev, errp)) { + if (!bcm283x_common_realize(dev, ps_base, errp)) { return; } /* bcm2836 interrupt controller (and mailboxes, etc.) */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->control), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s_base->control), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, bc->ctrl_base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s_base->control), 0, bc->ctrl_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0, - qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-irq", 0)); + qdev_get_gpio_in_named(DEVICE(&s_base->control), "gpu-irq", 0)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1, - qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0)); + qdev_get_gpio_in_named(DEVICE(&s_base->control), "gpu-fiq", 0)); for (n = 0; n < BCM283X_NCPUS; n++) { - object_property_set_int(OBJECT(&s->cpu[n].core), "mp-affinity", + object_property_set_int(OBJECT(&s_base->cpu[n].core), "mp-affinity", (bc->clusterid << 8) | n, &error_abort); /* set periphbase/CBAR value for CPU-local registers */ - object_property_set_int(OBJECT(&s->cpu[n].core), "reset-cbar", + object_property_set_int(OBJECT(&s_base->cpu[n].core), "reset-cbar", bc->peri_base, &error_abort); /* start powered off if not enabled */ - object_property_set_bool(OBJECT(&s->cpu[n].core), "start-powered-off", - n >= s->enabled_cpus, &error_abort); + object_property_set_bool(OBJECT(&s_base->cpu[n].core), + "start-powered-off", + n >= s_base->enabled_cpus, &error_abort); - if (!qdev_realize(DEVICE(&s->cpu[n].core), NULL, errp)) { + if (!qdev_realize(DEVICE(&s_base->cpu[n].core), NULL, errp)) { return; } /* Connect irq/fiq outputs from the interrupt controller. */ - qdev_connect_gpio_out_named(DEVICE(&s->control), "irq", n, - qdev_get_gpio_in(DEVICE(&s->cpu[n].core), ARM_CPU_IRQ)); - qdev_connect_gpio_out_named(DEVICE(&s->control), "fiq", n, - qdev_get_gpio_in(DEVICE(&s->cpu[n].core), ARM_CPU_FIQ)); + qdev_connect_gpio_out_named(DEVICE(&s_base->control), "irq", n, + qdev_get_gpio_in(DEVICE(&s_base->cpu[n].core), ARM_CPU_IRQ)); + qdev_connect_gpio_out_named(DEVICE(&s_base->control), "fiq", n, + qdev_get_gpio_in(DEVICE(&s_base->cpu[n].core), ARM_CPU_FIQ)); /* Connect timers from the CPU to the interrupt controller */ - qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_PHYS, - qdev_get_gpio_in_named(DEVICE(&s->control), "cntpnsirq", n)); - qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_VIRT, - qdev_get_gpio_in_named(DEVICE(&s->control), "cntvirq", n)); - qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_HYP, - qdev_get_gpio_in_named(DEVICE(&s->control), "cnthpirq", n)); - qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_SEC, - qdev_get_gpio_in_named(DEVICE(&s->control), "cntpsirq", n)); + qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_PHYS, + qdev_get_gpio_in_named(DEVICE(&s_base->control), "cntpnsirq", n)); + qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_VIRT, + qdev_get_gpio_in_named(DEVICE(&s_base->control), "cntvirq", n)); + qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_HYP, + qdev_get_gpio_in_named(DEVICE(&s_base->control), "cnthpirq", n)); + qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_SEC, + qdev_get_gpio_in_named(DEVICE(&s_base->control), "cntpsirq", n)); } } -static void bcm283x_class_init(ObjectClass *oc, void *data) +static void bcm283x_base_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -173,7 +186,7 @@ static void bcm283x_class_init(ObjectClass *oc, void *data) static void bcm2835_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - BCM283XClass *bc = BCM283X_CLASS(oc); + BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc); bc->cpu_type = ARM_CPU_TYPE_NAME("arm1176"); bc->core_count = 1; @@ -184,7 +197,7 @@ static void bcm2835_class_init(ObjectClass *oc, void *data) static void bcm2836_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - BCM283XClass *bc = BCM283X_CLASS(oc); + BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc); bc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); bc->core_count = BCM283X_NCPUS; @@ -198,7 +211,7 @@ static void bcm2836_class_init(ObjectClass *oc, void *data) static void bcm2837_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - BCM283XClass *bc = BCM283X_CLASS(oc); + BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc); bc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"); bc->core_count = BCM283X_NCPUS; @@ -226,11 +239,17 @@ static const TypeInfo bcm283x_types[] = { #endif }, { .name = TYPE_BCM283X, - .parent = TYPE_DEVICE, + .parent = TYPE_BCM283X_BASE, .instance_size = sizeof(BCM283XState), - .instance_init = bcm2836_init, - .class_size = sizeof(BCM283XClass), - .class_init = bcm283x_class_init, + .instance_init = bcm283x_init, + .abstract = true, + }, { + .name = TYPE_BCM283X_BASE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(BCM283XBaseState), + .instance_init = bcm283x_base_init, + .class_size = sizeof(BCM283XBaseClass), + .class_init = bcm283x_base_class_init, .abstract = true, } }; diff --git a/hw/arm/bcm2838.c b/hw/arm/bcm2838.c new file mode 100644 index 0000000000..ddb7c5f757 --- /dev/null +++ b/hw/arm/bcm2838.c @@ -0,0 +1,263 @@ +/* + * BCM2838 SoC emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "hw/arm/raspi_platform.h" +#include "hw/sysbus.h" +#include "hw/arm/bcm2838.h" +#include "trace.h" + +#define GIC400_MAINTENANCE_IRQ 9 +#define GIC400_TIMER_NS_EL2_IRQ 10 +#define GIC400_TIMER_VIRT_IRQ 11 +#define GIC400_LEGACY_FIQ 12 +#define GIC400_TIMER_S_EL1_IRQ 13 +#define GIC400_TIMER_NS_EL1_IRQ 14 +#define GIC400_LEGACY_IRQ 15 + +/* Number of external interrupt lines to configure the GIC with */ +#define GIC_NUM_IRQS 192 + +#define PPI(cpu, irq) (GIC_NUM_IRQS + (cpu) * GIC_INTERNAL + GIC_NR_SGIS + irq) + +#define GIC_BASE_OFS 0x0000 +#define GIC_DIST_OFS 0x1000 +#define GIC_CPU_OFS 0x2000 +#define GIC_VIFACE_THIS_OFS 0x4000 +#define GIC_VIFACE_OTHER_OFS(cpu) (0x5000 + (cpu) * 0x200) +#define GIC_VCPU_OFS 0x6000 + +#define VIRTUAL_PMU_IRQ 7 + +static void bcm2838_gic_set_irq(void *opaque, int irq, int level) +{ + BCM2838State *s = (BCM2838State *)opaque; + + trace_bcm2838_gic_set_irq(irq, level); + qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); +} + +static void bcm2838_init(Object *obj) +{ + BCM2838State *s = BCM2838(obj); + + object_initialize_child(obj, "peripherals", &s->peripherals, + TYPE_BCM2838_PERIPHERALS); + object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals), + "board-rev"); + object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals), + "vcram-size"); + object_property_add_alias(obj, "vcram-base", OBJECT(&s->peripherals), + "vcram-base"); + object_property_add_alias(obj, "command-line", OBJECT(&s->peripherals), + "command-line"); + + object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC); +} + +static void bcm2838_realize(DeviceState *dev, Error **errp) +{ + BCM2838State *s = BCM2838(dev); + BCM283XBaseState *s_base = BCM283X_BASE(dev); + BCM283XBaseClass *bc_base = BCM283X_BASE_GET_CLASS(dev); + BCM2838PeripheralState *ps = BCM2838_PERIPHERALS(&s->peripherals); + BCMSocPeripheralBaseState *ps_base = + BCM_SOC_PERIPHERALS_BASE(&s->peripherals); + + DeviceState *gicdev = NULL; + + if (!bcm283x_common_realize(dev, ps_base, errp)) { + return; + } + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(ps), 1, BCM2838_PERI_LOW_BASE, 1); + + /* bcm2836 interrupt controller (and mailboxes, etc.) */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s_base->control), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s_base->control), 0, bc_base->ctrl_base); + + /* Create cores */ + for (int n = 0; n < bc_base->core_count; n++) { + + object_property_set_int(OBJECT(&s_base->cpu[n].core), "mp-affinity", + (bc_base->clusterid << 8) | n, &error_abort); + + /* set periphbase/CBAR value for CPU-local registers */ + object_property_set_int(OBJECT(&s_base->cpu[n].core), "reset-cbar", + bc_base->peri_base, &error_abort); + + /* start powered off if not enabled */ + object_property_set_bool(OBJECT(&s_base->cpu[n].core), + "start-powered-off", + n >= s_base->enabled_cpus, &error_abort); + + if (!qdev_realize(DEVICE(&s_base->cpu[n].core), NULL, errp)) { + return; + } + } + + if (!object_property_set_uint(OBJECT(&s->gic), "revision", 2, errp)) { + return; + } + + if (!object_property_set_uint(OBJECT(&s->gic), "num-cpu", BCM283X_NCPUS, + errp)) { + return; + } + + if (!object_property_set_uint(OBJECT(&s->gic), "num-irq", + GIC_NUM_IRQS + GIC_INTERNAL, errp)) { + return; + } + + if (!object_property_set_bool(OBJECT(&s->gic), + "has-virtualization-extensions", true, + errp)) { + return; + } + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, + bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_DIST_OFS); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, + bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_CPU_OFS); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, + bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_VIFACE_THIS_OFS); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, + bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_VCPU_OFS); + + for (int n = 0; n < BCM283X_NCPUS; n++) { + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 4 + n, + bc_base->ctrl_base + BCM2838_GIC_BASE + + GIC_VIFACE_OTHER_OFS(n)); + } + + gicdev = DEVICE(&s->gic); + + for (int n = 0; n < BCM283X_NCPUS; n++) { + DeviceState *cpudev = DEVICE(&s_base->cpu[n]); + + /* Connect the GICv2 outputs to the CPU */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + BCM283X_NCPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + 2 * BCM283X_NCPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + 3 * BCM283X_NCPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + 4 * BCM283X_NCPUS, + qdev_get_gpio_in(gicdev, + PPI(n, GIC400_MAINTENANCE_IRQ))); + + /* Connect timers from the CPU to the interrupt controller */ + qdev_connect_gpio_out(cpudev, GTIMER_PHYS, + qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_NS_EL1_IRQ))); + qdev_connect_gpio_out(cpudev, GTIMER_VIRT, + qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_VIRT_IRQ))); + qdev_connect_gpio_out(cpudev, GTIMER_HYP, + qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_NS_EL2_IRQ))); + qdev_connect_gpio_out(cpudev, GTIMER_SEC, + qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_S_EL1_IRQ))); + /* PMU interrupt */ + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, + qdev_get_gpio_in(gicdev, PPI(n, VIRTUAL_PMU_IRQ))); + } + + /* Connect UART0 to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->uart0), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_UART0)); + + /* Connect AUX / UART1 to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->aux), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_AUX_UART1)); + + /* Connect VC mailbox to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->mboxes), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_MBOX)); + + /* Connect SD host to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->sdhost), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_SDHOST)); + + /* According to DTS, EMMC and EMMC2 share one irq */ + DeviceState *mmc_irq_orgate = DEVICE(&ps->mmc_irq_orgate); + + /* Connect EMMC and EMMC2 to the interrupt controller */ + qdev_connect_gpio_out(mmc_irq_orgate, 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_EMMC_EMMC2)); + + /* Connect USB OTG and MPHI to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->mphi), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_MPHI)); + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->dwc2), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_DWC2)); + + /* Connect DMA 0-6 to the interrupt controller */ + for (int n = GIC_SPI_INTERRUPT_DMA_0; n <= GIC_SPI_INTERRUPT_DMA_6; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->dma), + n - GIC_SPI_INTERRUPT_DMA_0, + qdev_get_gpio_in(gicdev, n)); + } + + /* According to DTS, DMA 7 and 8 share one irq */ + DeviceState *dma_7_8_irq_orgate = DEVICE(&ps->dma_7_8_irq_orgate); + + /* Connect DMA 7-8 to the interrupt controller */ + qdev_connect_gpio_out(dma_7_8_irq_orgate, 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_DMA_7_8)); + + /* According to DTS, DMA 9 and 10 share one irq */ + DeviceState *dma_9_10_irq_orgate = DEVICE(&ps->dma_9_10_irq_orgate); + + /* Connect DMA 9-10 to the interrupt controller */ + qdev_connect_gpio_out(dma_9_10_irq_orgate, 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_DMA_9_10)); + + /* Pass through inbound GPIO lines to the GIC */ + qdev_init_gpio_in(dev, bcm2838_gic_set_irq, GIC_NUM_IRQS); + + /* Pass through outbound IRQ lines from the GIC */ + qdev_pass_gpios(DEVICE(&s->gic), DEVICE(&s->peripherals), NULL); +} + +static void bcm2838_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + BCM283XBaseClass *bc_base = BCM283X_BASE_CLASS(oc); + + bc_base->cpu_type = ARM_CPU_TYPE_NAME("cortex-a72"); + bc_base->core_count = BCM283X_NCPUS; + bc_base->peri_base = 0xfe000000; + bc_base->ctrl_base = 0xff800000; + bc_base->clusterid = 0x0; + dc->realize = bcm2838_realize; +} + +static const TypeInfo bcm2838_type = { + .name = TYPE_BCM2838, + .parent = TYPE_BCM283X_BASE, + .instance_size = sizeof(BCM2838State), + .instance_init = bcm2838_init, + .class_size = sizeof(BCM283XBaseClass), + .class_init = bcm2838_class_init, +}; + +static void bcm2838_register_types(void) +{ + type_register_static(&bcm2838_type); +} + +type_init(bcm2838_register_types); diff --git a/hw/arm/bcm2838_peripherals.c b/hw/arm/bcm2838_peripherals.c new file mode 100644 index 0000000000..e28bef4a37 --- /dev/null +++ b/hw/arm/bcm2838_peripherals.c @@ -0,0 +1,224 @@ +/* + * BCM2838 peripherals emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "hw/arm/raspi_platform.h" +#include "hw/arm/bcm2838_peripherals.h" + +#define CLOCK_ISP_OFFSET 0xc11000 +#define CLOCK_ISP_SIZE 0x100 + +/* Lower peripheral base address on the VC (GPU) system bus */ +#define BCM2838_VC_PERI_LOW_BASE 0x7c000000 + +/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */ +#define BCM2835_SDHC_CAPAREG 0x52134b4 + +static void bcm2838_peripherals_init(Object *obj) +{ + BCM2838PeripheralState *s = BCM2838_PERIPHERALS(obj); + BCM2838PeripheralClass *bc = BCM2838_PERIPHERALS_GET_CLASS(obj); + BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(obj); + + /* Lower memory region for peripheral devices (exported to the Soc) */ + memory_region_init(&s->peri_low_mr, obj, "bcm2838-peripherals", + bc->peri_low_size); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_low_mr); + + /* Extended Mass Media Controller 2 */ + object_initialize_child(obj, "emmc2", &s->emmc2, TYPE_SYSBUS_SDHCI); + + /* GPIO */ + object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2838_GPIO); + + object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci", + OBJECT(&s_base->sdhci.sdbus)); + object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost", + OBJECT(&s_base->sdhost.sdbus)); + + object_initialize_child(obj, "mmc_irq_orgate", &s->mmc_irq_orgate, + TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->mmc_irq_orgate), "num-lines", 2, + &error_abort); + + object_initialize_child(obj, "dma_7_8_irq_orgate", &s->dma_7_8_irq_orgate, + TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->dma_7_8_irq_orgate), "num-lines", 2, + &error_abort); + + object_initialize_child(obj, "dma_9_10_irq_orgate", &s->dma_9_10_irq_orgate, + TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->dma_9_10_irq_orgate), "num-lines", 2, + &error_abort); +} + +static void bcm2838_peripherals_realize(DeviceState *dev, Error **errp) +{ + DeviceState *mmc_irq_orgate; + DeviceState *dma_7_8_irq_orgate; + DeviceState *dma_9_10_irq_orgate; + MemoryRegion *mphi_mr; + BCM2838PeripheralState *s = BCM2838_PERIPHERALS(dev); + BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(dev); + int n; + + bcm_soc_peripherals_common_realize(dev, errp); + + /* Map lower peripherals into the GPU address space */ + memory_region_init_alias(&s->peri_low_mr_alias, OBJECT(s), + "bcm2838-peripherals", &s->peri_low_mr, 0, + memory_region_size(&s->peri_low_mr)); + memory_region_add_subregion_overlap(&s_base->gpu_bus_mr, + BCM2838_VC_PERI_LOW_BASE, + &s->peri_low_mr_alias, 1); + + /* Extended Mass Media Controller 2 */ + object_property_set_uint(OBJECT(&s->emmc2), "sd-spec-version", 3, + &error_abort); + object_property_set_uint(OBJECT(&s->emmc2), "capareg", + BCM2835_SDHC_CAPAREG, &error_abort); + object_property_set_bool(OBJECT(&s->emmc2), "pending-insert-quirk", true, + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc2), errp)) { + return; + } + + memory_region_add_subregion(&s_base->peri_mr, EMMC2_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->emmc2), + 0)); + + /* According to DTS, EMMC and EMMC2 share one irq */ + if (!qdev_realize(DEVICE(&s->mmc_irq_orgate), NULL, errp)) { + return; + } + + mmc_irq_orgate = DEVICE(&s->mmc_irq_orgate); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc2), 0, + qdev_get_gpio_in(mmc_irq_orgate, 0)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->sdhci), 0, + qdev_get_gpio_in(mmc_irq_orgate, 1)); + + /* Connect EMMC and EMMC2 to the interrupt controller */ + qdev_connect_gpio_out(mmc_irq_orgate, 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_ARASANSDIO)); + + /* Connect DMA 0-6 to the interrupt controller */ + for (n = 0; n < 7; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA0 + n)); + } + + /* According to DTS, DMA 7 and 8 share one irq */ + if (!qdev_realize(DEVICE(&s->dma_7_8_irq_orgate), NULL, errp)) { + return; + } + dma_7_8_irq_orgate = DEVICE(&s->dma_7_8_irq_orgate); + + /* Connect DMA 7-8 to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 7, + qdev_get_gpio_in(dma_7_8_irq_orgate, 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 8, + qdev_get_gpio_in(dma_7_8_irq_orgate, 1)); + + qdev_connect_gpio_out(dma_7_8_irq_orgate, 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA7_8)); + + /* According to DTS, DMA 9 and 10 share one irq */ + if (!qdev_realize(DEVICE(&s->dma_9_10_irq_orgate), NULL, errp)) { + return; + } + dma_9_10_irq_orgate = DEVICE(&s->dma_9_10_irq_orgate); + + /* Connect DMA 9-10 to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 9, + qdev_get_gpio_in(dma_9_10_irq_orgate, 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 10, + qdev_get_gpio_in(dma_9_10_irq_orgate, 1)); + + qdev_connect_gpio_out(dma_9_10_irq_orgate, 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA9_10)); + + /* Connect DMA 11-14 to the interrupt controller */ + for (n = 11; n < 15; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA11 + n + - 11)); + } + + /* + * Connect DMA 15 to the interrupt controller, it is physically removed + * from other DMA channels and exclusively used by the GPU + */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 15, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA15)); + + /* Map MPHI to BCM2838 memory map */ + mphi_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s_base->mphi), 0); + memory_region_init_alias(&s->mphi_mr_alias, OBJECT(s), "mphi", mphi_mr, 0, + BCM2838_MPHI_SIZE); + memory_region_add_subregion(&s_base->peri_mr, BCM2838_MPHI_OFFSET, + &s->mphi_mr_alias); + + create_unimp(s_base, &s->clkisp, "bcm2835-clkisp", CLOCK_ISP_OFFSET, + CLOCK_ISP_SIZE); + + /* GPIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { + return; + } + memory_region_add_subregion( + &s_base->peri_mr, GPIO_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0)); + + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus"); + + /* BCM2838 RPiVid ASB must be mapped to prevent kernel crash */ + create_unimp(s_base, &s->asb, "bcm2838-asb", BRDG_OFFSET, 0x24); +} + +static void bcm2838_peripherals_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + BCM2838PeripheralClass *bc = BCM2838_PERIPHERALS_CLASS(oc); + BCMSocPeripheralBaseClass *bc_base = BCM_SOC_PERIPHERALS_BASE_CLASS(oc); + + bc->peri_low_size = 0x2000000; + bc_base->peri_size = 0x1800000; + dc->realize = bcm2838_peripherals_realize; +} + +static const TypeInfo bcm2838_peripherals_type_info = { + .name = TYPE_BCM2838_PERIPHERALS, + .parent = TYPE_BCM_SOC_PERIPHERALS_BASE, + .instance_size = sizeof(BCM2838PeripheralState), + .instance_init = bcm2838_peripherals_init, + .class_size = sizeof(BCM2838PeripheralClass), + .class_init = bcm2838_peripherals_class_init, +}; + +static void bcm2838_peripherals_register_types(void) +{ + type_register_static(&bcm2838_peripherals_type_info); +} + +type_init(bcm2838_peripherals_register_types) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index a16d347905..6808135c1f 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -30,6 +30,7 @@ arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubi arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c')) arm_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c')) arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c')) +arm_ss.add(when: ['CONFIG_RASPI', 'TARGET_AARCH64'], if_true: files('bcm2838.c', 'raspi4b.c')) arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c')) arm_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c')) arm_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: files('stm32f405_soc.c')) @@ -67,6 +68,7 @@ system_ss.add(when: 'CONFIG_GUMSTIX', if_true: files('gumstix.c')) system_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap2.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c')) system_ss.add(when: 'CONFIG_SPITZ', if_true: files('spitz.c')) system_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c')) system_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index cc4c4ec9bf..a7a662f40d 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -18,6 +18,8 @@ #include "qapi/error.h" #include "hw/arm/boot.h" #include "hw/arm/bcm2836.h" +#include "hw/arm/bcm2838.h" +#include "hw/arm/raspi_platform.h" #include "hw/registerfields.h" #include "qemu/error-report.h" #include "hw/boards.h" @@ -25,6 +27,9 @@ #include "hw/arm/boot.h" #include "qom/object.h" +#define TYPE_RASPI_MACHINE MACHINE_TYPE_NAME("raspi-common") +OBJECT_DECLARE_SIMPLE_TYPE(RaspiMachineState, RASPI_MACHINE) + #define SMPBOOT_ADDR 0x300 /* this should leave enough space for ATAGS */ #define MVBAR_ADDR 0x400 /* secure vectors */ #define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */ @@ -32,30 +37,12 @@ #define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */ #define SPINTABLE_ADDR 0xd8 /* Pi 3 bootloader spintable */ -/* Registered machine type (matches RPi Foundation bootloader and U-Boot) */ -#define MACH_TYPE_BCM2708 3138 - struct RaspiMachineState { /*< private >*/ - MachineState parent_obj; + RaspiBaseMachineState parent_obj; /*< public >*/ BCM283XState soc; - struct arm_boot_info binfo; }; -typedef struct RaspiMachineState RaspiMachineState; - -struct RaspiMachineClass { - /*< private >*/ - MachineClass parent_obj; - /*< public >*/ - uint32_t board_rev; -}; -typedef struct RaspiMachineClass RaspiMachineClass; - -#define TYPE_RASPI_MACHINE MACHINE_TYPE_NAME("raspi-common") -DECLARE_OBJ_CHECKERS(RaspiMachineState, RaspiMachineClass, - RASPI_MACHINE, TYPE_RASPI_MACHINE) - /* * Board revision codes: @@ -72,6 +59,7 @@ typedef enum RaspiProcessorId { PROCESSOR_ID_BCM2835 = 0, PROCESSOR_ID_BCM2836 = 1, PROCESSOR_ID_BCM2837 = 2, + PROCESSOR_ID_BCM2838 = 3, } RaspiProcessorId; static const struct { @@ -81,9 +69,10 @@ static const struct { [PROCESSOR_ID_BCM2835] = {TYPE_BCM2835, 1}, [PROCESSOR_ID_BCM2836] = {TYPE_BCM2836, BCM283X_NCPUS}, [PROCESSOR_ID_BCM2837] = {TYPE_BCM2837, BCM283X_NCPUS}, + [PROCESSOR_ID_BCM2838] = {TYPE_BCM2838, BCM283X_NCPUS}, }; -static uint64_t board_ram_size(uint32_t board_rev) +uint64_t board_ram_size(uint32_t board_rev) { assert(FIELD_EX32(board_rev, REV_CODE, STYLE)); /* Only new style */ return 256 * MiB << FIELD_EX32(board_rev, REV_CODE, MEMORY_SIZE); @@ -99,7 +88,7 @@ static RaspiProcessorId board_processor_id(uint32_t board_rev) return proc_id; } -static const char *board_soc_type(uint32_t board_rev) +const char *board_soc_type(uint32_t board_rev) { return soc_property[board_processor_id(board_rev)].type; } @@ -200,13 +189,12 @@ static void reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) cpu_set_pc(cs, info->smp_loader_start); } -static void setup_boot(MachineState *machine, RaspiProcessorId processor_id, - size_t ram_size) +static void setup_boot(MachineState *machine, ARMCPU *cpu, + RaspiProcessorId processor_id, size_t ram_size) { - RaspiMachineState *s = RASPI_MACHINE(machine); + RaspiBaseMachineState *s = RASPI_BASE_MACHINE(machine); int r; - s->binfo.board_id = MACH_TYPE_BCM2708; s->binfo.ram_size = ram_size; if (processor_id <= PROCESSOR_ID_BCM2836) { @@ -252,16 +240,17 @@ static void setup_boot(MachineState *machine, RaspiProcessorId processor_id, s->binfo.firmware_loaded = true; } - arm_load_kernel(&s->soc.cpu[0].core, machine, &s->binfo); + arm_load_kernel(cpu, machine, &s->binfo); } -static void raspi_machine_init(MachineState *machine) +void raspi_base_machine_init(MachineState *machine, + BCM283XBaseState *soc) { - RaspiMachineClass *mc = RASPI_MACHINE_GET_CLASS(machine); - RaspiMachineState *s = RASPI_MACHINE(machine); + RaspiBaseMachineClass *mc = RASPI_BASE_MACHINE_GET_CLASS(machine); uint32_t board_rev = mc->board_rev; uint64_t ram_size = board_ram_size(board_rev); - uint32_t vcram_size; + uint32_t vcram_base, vcram_size; + size_t boot_ram_size; DriveInfo *di; BlockBackend *blk; BusState *bus; @@ -279,19 +268,17 @@ static void raspi_machine_init(MachineState *machine) machine->ram, 0); /* Setup the SOC */ - object_initialize_child(OBJECT(machine), "soc", &s->soc, - board_soc_type(board_rev)); - object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(machine->ram)); - object_property_set_int(OBJECT(&s->soc), "board-rev", board_rev, + object_property_add_const_link(OBJECT(soc), "ram", OBJECT(machine->ram)); + object_property_set_int(OBJECT(soc), "board-rev", board_rev, &error_abort); - object_property_set_str(OBJECT(&s->soc), "command-line", + object_property_set_str(OBJECT(soc), "command-line", machine->kernel_cmdline, &error_abort); - qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + qdev_realize(DEVICE(soc), NULL, &error_fatal); /* Create and plug in the SD cards */ di = drive_get(IF_SD, 0, 0); blk = di ? blk_by_legacy_dinfo(di) : NULL; - bus = qdev_get_child_bus(DEVICE(&s->soc), "sd-bus"); + bus = qdev_get_child_bus(DEVICE(soc), "sd-bus"); if (bus == NULL) { error_report("No SD bus found in SOC object"); exit(1); @@ -300,19 +287,40 @@ static void raspi_machine_init(MachineState *machine) qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); - vcram_size = object_property_get_uint(OBJECT(&s->soc), "vcram-size", + vcram_size = object_property_get_uint(OBJECT(soc), "vcram-size", &error_abort); - setup_boot(machine, board_processor_id(mc->board_rev), - machine->ram_size - vcram_size); + vcram_base = object_property_get_uint(OBJECT(soc), "vcram-base", + &error_abort); + + if (vcram_base == 0) { + vcram_base = ram_size - vcram_size; + } + boot_ram_size = MIN(vcram_base, UPPER_RAM_BASE - vcram_size); + + setup_boot(machine, &soc->cpu[0].core, board_processor_id(board_rev), + boot_ram_size); } -static void raspi_machine_class_common_init(MachineClass *mc, - uint32_t board_rev) +void raspi_machine_init(MachineState *machine) +{ + RaspiMachineState *s = RASPI_MACHINE(machine); + RaspiBaseMachineState *s_base = RASPI_BASE_MACHINE(machine); + RaspiBaseMachineClass *mc = RASPI_BASE_MACHINE_GET_CLASS(machine); + BCM283XState *soc = &s->soc; + + s_base->binfo.board_id = MACH_TYPE_BCM2708; + + object_initialize_child(OBJECT(machine), "soc", soc, + board_soc_type(mc->board_rev)); + raspi_base_machine_init(machine, &soc->parent_obj); +} + +void raspi_machine_class_common_init(MachineClass *mc, + uint32_t board_rev) { mc->desc = g_strdup_printf("Raspberry Pi %s (revision 1.%u)", board_type(board_rev), FIELD_EX32(board_rev, REV_CODE, REVISION)); - mc->init = raspi_machine_init; mc->block_default_type = IF_SD; mc->no_parallel = 1; mc->no_floppy = 1; @@ -322,50 +330,57 @@ static void raspi_machine_class_common_init(MachineClass *mc, mc->default_ram_id = "ram"; }; +static void raspi_machine_class_init(MachineClass *mc, + uint32_t board_rev) +{ + raspi_machine_class_common_init(mc, board_rev); + mc->init = raspi_machine_init; +}; + static void raspi0_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0x920092; /* Revision 1.2 */ - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; static void raspi1ap_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0x900021; /* Revision 1.1 */ - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; static void raspi2b_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0xa21041; - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; #ifdef TARGET_AARCH64 static void raspi3ap_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0x9020e0; /* Revision 1.0 */ - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; static void raspi3b_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0xa02082; - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; #endif /* TARGET_AARCH64 */ @@ -394,9 +409,14 @@ static const TypeInfo raspi_machine_types[] = { #endif }, { .name = TYPE_RASPI_MACHINE, - .parent = TYPE_MACHINE, + .parent = TYPE_RASPI_BASE_MACHINE, .instance_size = sizeof(RaspiMachineState), - .class_size = sizeof(RaspiMachineClass), + .abstract = true, + }, { + .name = TYPE_RASPI_BASE_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(RaspiBaseMachineState), + .class_size = sizeof(RaspiBaseMachineClass), .abstract = true, } }; diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c new file mode 100644 index 0000000000..cb1b1f2f14 --- /dev/null +++ b/hw/arm/raspi4b.c @@ -0,0 +1,132 @@ +/* + * Raspberry Pi 4B emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "hw/arm/raspi_platform.h" +#include "hw/display/bcm2835_fb.h" +#include "hw/registerfields.h" +#include "qemu/error-report.h" +#include "sysemu/device_tree.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/arm/boot.h" +#include "qom/object.h" +#include "hw/arm/bcm2838.h" +#include + +#define TYPE_RASPI4B_MACHINE MACHINE_TYPE_NAME("raspi4b") +OBJECT_DECLARE_SIMPLE_TYPE(Raspi4bMachineState, RASPI4B_MACHINE) + +struct Raspi4bMachineState { + RaspiBaseMachineState parent_obj; + BCM2838State soc; +}; + +/* + * Add second memory region if board RAM amount exceeds VC base address + * (see https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf + * 1.2 Address Map) + */ +static int raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len) +{ + int ret; + uint32_t acells, scells; + char *nodename = g_strdup_printf("/memory@%" PRIx64, mem_base); + + acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells", + NULL, &error_fatal); + scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells", + NULL, &error_fatal); + if (acells == 0 || scells == 0) { + fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n"); + ret = -1; + } else { + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + acells, mem_base, + scells, mem_len); + } + + g_free(nodename); + return ret; +} + +static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt) +{ + uint64_t ram_size; + + /* Temporarily disable following devices until they are implemented */ + const char *nodes_to_remove[] = { + "brcm,bcm2711-pcie", + "brcm,bcm2711-rng200", + "brcm,bcm2711-thermal", + "brcm,bcm2711-genet-v5", + }; + + for (int i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) { + const char *dev_str = nodes_to_remove[i]; + + int offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); + if (offset >= 0) { + if (!fdt_nop_node(fdt, offset)) { + warn_report("bcm2711 dtc: %s has been disabled!", dev_str); + } + } + } + + ram_size = board_ram_size(info->board_id); + + if (info->ram_size > UPPER_RAM_BASE) { + raspi_add_memory_node(fdt, UPPER_RAM_BASE, ram_size - UPPER_RAM_BASE); + } +} + +static void raspi4b_machine_init(MachineState *machine) +{ + Raspi4bMachineState *s = RASPI4B_MACHINE(machine); + RaspiBaseMachineState *s_base = RASPI_BASE_MACHINE(machine); + RaspiBaseMachineClass *mc = RASPI_BASE_MACHINE_GET_CLASS(machine); + BCM2838State *soc = &s->soc; + + s_base->binfo.modify_dtb = raspi4_modify_dtb; + s_base->binfo.board_id = mc->board_rev; + + object_initialize_child(OBJECT(machine), "soc", soc, + board_soc_type(mc->board_rev)); + + raspi_base_machine_init(machine, &soc->parent_obj); +} + +static void raspi4b_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + + rmc->board_rev = 0xb03115; /* Revision 1.5, 2 Gb RAM */ + raspi_machine_class_common_init(mc, rmc->board_rev); + mc->init = raspi4b_machine_init; +} + +static const TypeInfo raspi4b_machine_type = { + .name = TYPE_RASPI4B_MACHINE, + .parent = TYPE_RASPI_BASE_MACHINE, + .instance_size = sizeof(Raspi4bMachineState), + .class_init = raspi4b_machine_class_init, +}; + +static void raspi4b_machine_register_type(void) +{ + type_register_static(&raspi4b_machine_type); +} + +type_init(raspi4b_machine_register_type) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 13dde50cba..f5709d6c14 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -664,9 +664,8 @@ static void create_pcie(SBSAMachineState *sms) } pci = PCI_HOST_BRIDGE(dev); - if (pci->bus) { - pci_init_nic_devices(pci->bus, mc->default_nic); - } + + pci_init_nic_devices(pci->bus, mc->default_nic); pci_create_simple(pci->bus, -1, "bochs-display"); diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index f470ff74ec..d1786e0da1 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" +#include "hw/or-irq.h" #include "hw/arm/stm32l4x5_soc.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" @@ -42,21 +43,24 @@ #define NUM_EXTI_IRQ 40 /* Match exti line connections with their CPU IRQ number */ /* See Vector Table (Reference Manual p.396) */ +/* + * Some IRQs are connected to the same CPU IRQ (denoted by -1) + * and require an intermediary OR gate to function correctly. + */ static const int exti_irq[NUM_EXTI_IRQ] = { 6, /* GPIO[0] */ 7, /* GPIO[1] */ 8, /* GPIO[2] */ 9, /* GPIO[3] */ 10, /* GPIO[4] */ - 23, 23, 23, 23, 23, /* GPIO[5..9] */ - 40, 40, 40, 40, 40, 40, /* GPIO[10..15] */ - 1, /* PVD */ + -1, -1, -1, -1, -1, /* GPIO[5..9] OR gate 23 */ + -1, -1, -1, -1, -1, -1, /* GPIO[10..15] OR gate 40 */ + -1, /* PVD OR gate 1 */ 67, /* OTG_FS_WKUP, Direct */ 41, /* RTC_ALARM */ 2, /* RTC_TAMP_STAMP2/CSS_LSE */ 3, /* RTC wakeup timer */ - 63, /* COMP1 */ - 63, /* COMP2 */ + -1, -1, /* COMP[1..2] OR gate 63 */ 31, /* I2C1 wakeup, Direct */ 33, /* I2C2 wakeup, Direct */ 72, /* I2C3 wakeup, Direct */ @@ -69,18 +73,39 @@ static const int exti_irq[NUM_EXTI_IRQ] = { 65, /* LPTIM1, Direct */ 66, /* LPTIM2, Direct */ 76, /* SWPMI1 wakeup, Direct */ - 1, /* PVM1 wakeup */ - 1, /* PVM2 wakeup */ - 1, /* PVM3 wakeup */ - 1, /* PVM4 wakeup */ + -1, -1, -1, -1, /* PVM[1..4] OR gate 1 */ 78 /* LCD wakeup, Direct */ }; +static const int exti_or_gates_out[NUM_EXTI_OR_GATES] = { + 23, 40, 63, 1, +}; + +static const int exti_or_gates_num_lines_in[NUM_EXTI_OR_GATES] = { + 5, 6, 2, 5, +}; + +/* 3 OR gates with consecutive inputs */ +#define NUM_EXTI_SIMPLE_OR_GATES 3 +static const int exti_or_gates_first_line_in[NUM_EXTI_SIMPLE_OR_GATES] = { + 5, 10, 21, +}; + +/* 1 OR gate with non-consecutive inputs */ +#define EXTI_OR_GATE1_NUM_LINES_IN 5 +static const int exti_or_gate1_lines_in[EXTI_OR_GATE1_NUM_LINES_IN] = { + 16, 35, 36, 37, 38, +}; + static void stm32l4x5_soc_initfn(Object *obj) { Stm32l4x5SocState *s = STM32L4X5_SOC(obj); object_initialize_child(obj, "exti", &s->exti, TYPE_STM32L4X5_EXTI); + for (unsigned i = 0; i < NUM_EXTI_OR_GATES; i++) { + object_initialize_child(obj, "exti_or_gates[*]", &s->exti_or_gates[i], + TYPE_OR_IRQ); + } object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG); s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); @@ -175,8 +200,43 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) return; } sysbus_mmio_map(busdev, 0, EXTI_ADDR); + + /* IRQs with fan-in that require an OR gate */ + for (unsigned i = 0; i < NUM_EXTI_OR_GATES; i++) { + if (!object_property_set_int(OBJECT(&s->exti_or_gates[i]), "num-lines", + exti_or_gates_num_lines_in[i], errp)) { + return; + } + if (!qdev_realize(DEVICE(&s->exti_or_gates[i]), NULL, errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->exti_or_gates[i]), 0, + qdev_get_gpio_in(armv7m, exti_or_gates_out[i])); + + if (i < NUM_EXTI_SIMPLE_OR_GATES) { + /* consecutive inputs for OR gates 23, 40, 63 */ + for (unsigned j = 0; j < exti_or_gates_num_lines_in[i]; j++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->exti), + exti_or_gates_first_line_in[i] + j, + qdev_get_gpio_in(DEVICE(&s->exti_or_gates[i]), j)); + } + } else { + /* non-consecutive inputs for OR gate 1 */ + for (unsigned j = 0; j < EXTI_OR_GATE1_NUM_LINES_IN; j++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->exti), + exti_or_gate1_lines_in[j], + qdev_get_gpio_in(DEVICE(&s->exti_or_gates[i]), j)); + } + } + } + + /* IRQs that don't require fan-in */ for (unsigned i = 0; i < NUM_EXTI_IRQ; i++) { - sysbus_connect_irq(busdev, i, qdev_get_gpio_in(armv7m, exti_irq[i])); + if (exti_irq[i] != -1) { + sysbus_connect_irq(busdev, i, + qdev_get_gpio_in(armv7m, exti_irq[i])); + } } for (unsigned i = 0; i < 16; i++) { diff --git a/hw/arm/trace-events b/hw/arm/trace-events index fd0d92762e..f1a54a02df 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -70,3 +70,6 @@ z2_aer915_event(int8_t event, int8_t len) "i2c event =0x%x len=%d bytes" xen_create_virtio_mmio_devices(int i, int irq, uint64_t base) "Created virtio-mmio device %d: irq %d base 0x%"PRIx64 xen_init_ram(uint64_t machine_ram_size) "Initialized xen ram with size 0x%"PRIx64 xen_enable_tpm(uint64_t addr) "Connected tpmdev at address 0x%"PRIx64 + +# bcm2838.c +bcm2838_gic_set_irq(int irq, int level) "gic irq:%d lvl:%d" diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 94942c55df..bfaed1aebf 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -49,6 +49,7 @@ struct VersalVirt { struct { bool secure; } cfg; + char *ospi_model; }; static void fdt_create(VersalVirt *s) @@ -638,6 +639,22 @@ static void sd_plugin_card(SDHCIState *sd, DriveInfo *di) &error_fatal); } +static char *versal_get_ospi_model(Object *obj, Error **errp) +{ + VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + + return g_strdup(s->ospi_model); +} + +static void versal_set_ospi_model(Object *obj, const char *value, Error **errp) +{ + VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + + g_free(s->ospi_model); + s->ospi_model = g_strdup(value); +} + + static void versal_virt_init(MachineState *machine) { VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(machine); @@ -732,12 +749,25 @@ static void versal_virt_init(MachineState *machine) for (i = 0; i < XLNX_VERSAL_NUM_OSPI_FLASH; i++) { BusState *spi_bus; DeviceState *flash_dev; + ObjectClass *flash_klass; qemu_irq cs_line; DriveInfo *dinfo = drive_get(IF_MTD, 0, i); spi_bus = qdev_get_child_bus(DEVICE(&s->soc.pmc.iou.ospi), "spi0"); - flash_dev = qdev_new("mt35xu01g"); + if (s->ospi_model) { + flash_klass = object_class_by_name(s->ospi_model); + if (!flash_klass || + object_class_is_abstract(flash_klass) || + !object_class_dynamic_cast(flash_klass, "m25p80-generic")) { + error_setg(&error_fatal, "'%s' is either abstract or" + " not a subtype of m25p80", s->ospi_model); + return; + } + } + + flash_dev = qdev_new(s->ospi_model ? s->ospi_model : "mt35xu01g"); + if (dinfo) { qdev_prop_set_drive_err(flash_dev, "drive", blk_by_legacy_dinfo(dinfo), &error_fatal); @@ -770,6 +800,13 @@ static void versal_virt_machine_instance_init(Object *obj) 0); } +static void versal_virt_machine_finalize(Object *obj) +{ + VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + + g_free(s->ospi_model); +} + static void versal_virt_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -781,6 +818,10 @@ static void versal_virt_machine_class_init(ObjectClass *oc, void *data) mc->default_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; mc->no_cdrom = true; mc->default_ram_id = "ddr"; + object_class_property_add_str(oc, "ospi-flash", versal_get_ospi_model, + versal_set_ospi_model); + object_class_property_set_description(oc, "ospi-flash", + "Change the OSPI Flash model"); } static const TypeInfo versal_virt_machine_init_typeinfo = { @@ -789,6 +830,7 @@ static const TypeInfo versal_virt_machine_init_typeinfo = { .class_init = versal_virt_machine_class_init, .instance_init = versal_virt_machine_instance_init, .instance_size = sizeof(VersalVirt), + .instance_finalize = versal_virt_machine_finalize, }; static void versal_virt_machine_init_register_types(void) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 0a12030a3a..08a00a6d9b 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -267,6 +267,9 @@ static const FlashPartInfo known_devices[] = { { INFO("mt25ql512ab", 0x20ba20, 0x1044, 64 << 10, 1024, ER_4K | ER_32K) }, { INFO_STACKED("mt35xu01g", 0x2c5b1b, 0x104100, 128 << 10, 1024, ER_4K | ER_32K, 2) }, + { INFO_STACKED("mt35xu02gbba", 0x2c5b1c, 0x104100, 128 << 10, 2048, + ER_4K | ER_32K, 4), + .sfdp_read = m25p80_sfdp_mt35xu02g }, { INFO_STACKED("n25q00", 0x20ba21, 0x1000, 64 << 10, 2048, ER_4K, 4) }, { INFO_STACKED("n25q00a", 0x20bb21, 0x1000, 64 << 10, 2048, ER_4K, 4) }, { INFO_STACKED("mt25ql01g", 0x20ba21, 0x1040, 64 << 10, 2048, ER_4K, 2) }, diff --git a/hw/block/m25p80_sfdp.c b/hw/block/m25p80_sfdp.c index b33811a4f5..6ee2cfaf11 100644 --- a/hw/block/m25p80_sfdp.c +++ b/hw/block/m25p80_sfdp.c @@ -57,6 +57,42 @@ static const uint8_t sfdp_n25q256a[] = { }; define_sfdp_read(n25q256a); +static const uint8_t sfdp_mt35xu02g[] = { + 0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x01, 0xff, + 0x00, 0x06, 0x01, 0x10, 0x30, 0x00, 0x00, 0xff, + 0x84, 0x00, 0x01, 0x02, 0x80, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0x8a, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x0c, 0x20, 0x11, 0xd8, + 0x0f, 0x52, 0x00, 0x00, 0x24, 0x5a, 0x99, 0x00, + 0x8b, 0x8e, 0x03, 0xe1, 0xac, 0x01, 0x27, 0x38, + 0x7a, 0x75, 0x7a, 0x75, 0xfb, 0xbd, 0xd5, 0x5c, + 0x00, 0x00, 0x70, 0xff, 0x81, 0xb0, 0x38, 0x36, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x43, 0x0e, 0xff, 0xff, 0x21, 0xdc, 0x5c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +define_sfdp_read(mt35xu02g); /* * Matronix diff --git a/hw/block/m25p80_sfdp.h b/hw/block/m25p80_sfdp.h index 011a880f66..1733b56950 100644 --- a/hw/block/m25p80_sfdp.h +++ b/hw/block/m25p80_sfdp.h @@ -16,6 +16,7 @@ #define M25P80_SFDP_MAX_SIZE (1 << 24) uint8_t m25p80_sfdp_n25q256a(uint32_t addr); +uint8_t m25p80_sfdp_mt35xu02g(uint32_t addr); uint8_t m25p80_sfdp_mx25l25635e(uint32_t addr); uint8_t m25p80_sfdp_mx25l25635f(uint32_t addr); diff --git a/hw/core/machine.c b/hw/core/machine.c index fb5afdcae4..9ac5d5389a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1577,14 +1577,13 @@ void qdev_machine_creation_done(void) /* TODO: once all bus devices are qdevified, this should be done * when bus is created by qdev.c */ /* - * TODO: If we had a main 'reset container' that the whole system - * lived in, we could reset that using the multi-phase reset - * APIs. For the moment, we just reset the sysbus, which will cause + * This is where we arrange for the sysbus to be reset when the + * whole simulation is reset. In turn, resetting the sysbus will cause * all devices hanging off it (and all their child buses, recursively) * to be reset. Note that this will *not* reset any Device objects * which are not attached to some part of the qbus tree! */ - qemu_register_reset(resettable_cold_reset_fn, sysbus_get_default()); + qemu_register_resettable(OBJECT(sysbus_get_default())); notifier_list_notify(&machine_init_done_notifiers, NULL); diff --git a/hw/core/meson.build b/hw/core/meson.build index 67dad04de5..e26f2e088c 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -4,6 +4,7 @@ hwcore_ss.add(files( 'qdev-properties.c', 'qdev.c', 'reset.c', + 'resetcontainer.c', 'resettable.c', 'vmstate-if.c', # irq.c needed for qdev GPIO handling: diff --git a/hw/core/reset.c b/hw/core/reset.c index d3263b613e..d50da7e304 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -24,64 +24,164 @@ */ #include "qemu/osdep.h" -#include "qemu/queue.h" #include "sysemu/reset.h" +#include "hw/resettable.h" +#include "hw/core/resetcontainer.h" -/* reset/shutdown handler */ +/* + * Return a pointer to the singleton container that holds all the Resettable + * items that will be reset when qemu_devices_reset() is called. + */ +static ResettableContainer *get_root_reset_container(void) +{ + static ResettableContainer *root_reset_container; -typedef struct QEMUResetEntry { - QTAILQ_ENTRY(QEMUResetEntry) entry; + if (!root_reset_container) { + root_reset_container = + RESETTABLE_CONTAINER(object_new(TYPE_RESETTABLE_CONTAINER)); + } + return root_reset_container; +} + +/* + * Reason why the currently in-progress qemu_devices_reset() was called. + * If we made at least SHUTDOWN_CAUSE_SNAPSHOT_LOAD have a corresponding + * ResetType we could perhaps avoid the need for this global. + */ +static ShutdownCause device_reset_reason; + +/* + * This is an Object which implements Resettable simply to call the + * callback function in the hold phase. + */ +#define TYPE_LEGACY_RESET "legacy-reset" +OBJECT_DECLARE_SIMPLE_TYPE(LegacyReset, LEGACY_RESET) + +struct LegacyReset { + Object parent; + ResettableState reset_state; QEMUResetHandler *func; void *opaque; bool skip_on_snapshot_load; -} QEMUResetEntry; +}; -static QTAILQ_HEAD(, QEMUResetEntry) reset_handlers = - QTAILQ_HEAD_INITIALIZER(reset_handlers); +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(LegacyReset, legacy_reset, LEGACY_RESET, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { }) + +static ResettableState *legacy_reset_get_state(Object *obj) +{ + LegacyReset *lr = LEGACY_RESET(obj); + return &lr->reset_state; +} + +static void legacy_reset_hold(Object *obj) +{ + LegacyReset *lr = LEGACY_RESET(obj); + + if (device_reset_reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD && + lr->skip_on_snapshot_load) { + return; + } + lr->func(lr->opaque); +} + +static void legacy_reset_init(Object *obj) +{ +} + +static void legacy_reset_finalize(Object *obj) +{ +} + +static void legacy_reset_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->get_state = legacy_reset_get_state; + rc->phases.hold = legacy_reset_hold; +} void qemu_register_reset(QEMUResetHandler *func, void *opaque) { - QEMUResetEntry *re = g_new0(QEMUResetEntry, 1); + Object *obj = object_new(TYPE_LEGACY_RESET); + LegacyReset *lr = LEGACY_RESET(obj); - re->func = func; - re->opaque = opaque; - QTAILQ_INSERT_TAIL(&reset_handlers, re, entry); + lr->func = func; + lr->opaque = opaque; + qemu_register_resettable(obj); } void qemu_register_reset_nosnapshotload(QEMUResetHandler *func, void *opaque) { - QEMUResetEntry *re = g_new0(QEMUResetEntry, 1); + Object *obj = object_new(TYPE_LEGACY_RESET); + LegacyReset *lr = LEGACY_RESET(obj); - re->func = func; - re->opaque = opaque; - re->skip_on_snapshot_load = true; - QTAILQ_INSERT_TAIL(&reset_handlers, re, entry); + lr->func = func; + lr->opaque = opaque; + lr->skip_on_snapshot_load = true; + qemu_register_resettable(obj); +} + +typedef struct FindLegacyInfo { + QEMUResetHandler *func; + void *opaque; + LegacyReset *lr; +} FindLegacyInfo; + +static void find_legacy_reset_cb(Object *obj, void *opaque, ResetType type) +{ + LegacyReset *lr; + FindLegacyInfo *fli = opaque; + + /* Not everything in the ResettableContainer will be a LegacyReset */ + lr = LEGACY_RESET(object_dynamic_cast(obj, TYPE_LEGACY_RESET)); + if (lr && lr->func == fli->func && lr->opaque == fli->opaque) { + fli->lr = lr; + } +} + +static LegacyReset *find_legacy_reset(QEMUResetHandler *func, void *opaque) +{ + /* + * Find the LegacyReset with the specified func and opaque, + * by getting the ResettableContainer to call our callback for + * every item in it. + */ + ResettableContainer *rootcon = get_root_reset_container(); + ResettableClass *rc = RESETTABLE_GET_CLASS(rootcon); + FindLegacyInfo fli; + + fli.func = func; + fli.opaque = opaque; + fli.lr = NULL; + rc->child_foreach(OBJECT(rootcon), find_legacy_reset_cb, + &fli, RESET_TYPE_COLD); + return fli.lr; } void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) { - QEMUResetEntry *re; + Object *obj = OBJECT(find_legacy_reset(func, opaque)); - QTAILQ_FOREACH(re, &reset_handlers, entry) { - if (re->func == func && re->opaque == opaque) { - QTAILQ_REMOVE(&reset_handlers, re, entry); - g_free(re); - return; - } + if (obj) { + qemu_unregister_resettable(obj); + object_unref(obj); } } +void qemu_register_resettable(Object *obj) +{ + resettable_container_add(get_root_reset_container(), obj); +} + +void qemu_unregister_resettable(Object *obj) +{ + resettable_container_remove(get_root_reset_container(), obj); +} + void qemu_devices_reset(ShutdownCause reason) { - QEMUResetEntry *re, *nre; + device_reset_reason = reason; - /* reset all devices */ - QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) { - if (reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD && - re->skip_on_snapshot_load) { - continue; - } - re->func(re->opaque); - } + /* Reset the simulation */ + resettable_reset(OBJECT(get_root_reset_container()), RESET_TYPE_COLD); } - diff --git a/hw/core/resetcontainer.c b/hw/core/resetcontainer.c new file mode 100644 index 0000000000..e4ece68e83 --- /dev/null +++ b/hw/core/resetcontainer.c @@ -0,0 +1,77 @@ +/* + * Reset container + * + * Copyright (c) 2024 Linaro, Ltd + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * The "reset container" is an object which implements the Resettable + * interface. It contains a list of arbitrary other objects which also + * implement Resettable. Resetting the reset container resets all the + * objects in it. + */ + +#include "qemu/osdep.h" +#include "hw/resettable.h" +#include "hw/core/resetcontainer.h" + +struct ResettableContainer { + Object parent; + ResettableState reset_state; + GPtrArray *children; +}; + +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ResettableContainer, resettable_container, RESETTABLE_CONTAINER, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { }) + +void resettable_container_add(ResettableContainer *rc, Object *obj) +{ + INTERFACE_CHECK(void, obj, TYPE_RESETTABLE_INTERFACE); + g_ptr_array_add(rc->children, obj); +} + +void resettable_container_remove(ResettableContainer *rc, Object *obj) +{ + g_ptr_array_remove(rc->children, obj); +} + +static ResettableState *resettable_container_get_state(Object *obj) +{ + ResettableContainer *rc = RESETTABLE_CONTAINER(obj); + return &rc->reset_state; +} + +static void resettable_container_child_foreach(Object *obj, + ResettableChildCallback cb, + void *opaque, ResetType type) +{ + ResettableContainer *rc = RESETTABLE_CONTAINER(obj); + unsigned int len = rc->children->len; + + for (unsigned int i = 0; i < len; i++) { + cb(g_ptr_array_index(rc->children, i), opaque, type); + /* Detect callbacks trying to unregister themselves */ + assert(len == rc->children->len); + } +} + +static void resettable_container_init(Object *obj) +{ + ResettableContainer *rc = RESETTABLE_CONTAINER(obj); + + rc->children = g_ptr_array_new(); +} + +static void resettable_container_finalize(Object *obj) +{ +} + +static void resettable_container_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->get_state = resettable_container_get_state; + rc->child_foreach = resettable_container_child_foreach; +} diff --git a/hw/gpio/bcm2838_gpio.c b/hw/gpio/bcm2838_gpio.c new file mode 100644 index 0000000000..2ddf62f695 --- /dev/null +++ b/hw/gpio/bcm2838_gpio.c @@ -0,0 +1,390 @@ +/* + * Raspberry Pi (BCM2838) GPIO Controller + * This implementation is based on bcm2835_gpio (hw/gpio/bcm2835_gpio.c) + * + * Copyright (c) 2022 Auriga LLC + * + * Authors: + * Lotosh, Aleksey + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "hw/sd/sd.h" +#include "hw/gpio/bcm2838_gpio.h" +#include "hw/irq.h" + +#define GPFSEL0 0x00 +#define GPFSEL1 0x04 +#define GPFSEL2 0x08 +#define GPFSEL3 0x0C +#define GPFSEL4 0x10 +#define GPFSEL5 0x14 +#define GPSET0 0x1C +#define GPSET1 0x20 +#define GPCLR0 0x28 +#define GPCLR1 0x2C +#define GPLEV0 0x34 +#define GPLEV1 0x38 +#define GPEDS0 0x40 +#define GPEDS1 0x44 +#define GPREN0 0x4C +#define GPREN1 0x50 +#define GPFEN0 0x58 +#define GPFEN1 0x5C +#define GPHEN0 0x64 +#define GPHEN1 0x68 +#define GPLEN0 0x70 +#define GPLEN1 0x74 +#define GPAREN0 0x7C +#define GPAREN1 0x80 +#define GPAFEN0 0x88 +#define GPAFEN1 0x8C + +#define GPIO_PUP_PDN_CNTRL_REG0 0xE4 +#define GPIO_PUP_PDN_CNTRL_REG1 0xE8 +#define GPIO_PUP_PDN_CNTRL_REG2 0xEC +#define GPIO_PUP_PDN_CNTRL_REG3 0xF0 + +#define RESET_VAL_CNTRL_REG0 0xAAA95555 +#define RESET_VAL_CNTRL_REG1 0xA0AAAAAA +#define RESET_VAL_CNTRL_REG2 0x50AAA95A +#define RESET_VAL_CNTRL_REG3 0x00055555 + +#define NUM_FSELN_IN_GPFSELN 10 +#define NUM_BITS_FSELN 3 +#define MASK_FSELN 0x7 + +#define BYTES_IN_WORD 4 + +/* bcm,function property */ +#define BCM2838_FSEL_GPIO_IN 0 +#define BCM2838_FSEL_GPIO_OUT 1 +#define BCM2838_FSEL_ALT5 2 +#define BCM2838_FSEL_ALT4 3 +#define BCM2838_FSEL_ALT0 4 +#define BCM2838_FSEL_ALT1 5 +#define BCM2838_FSEL_ALT2 6 +#define BCM2838_FSEL_ALT3 7 + +static uint32_t gpfsel_get(BCM2838GpioState *s, uint8_t reg) +{ + int i; + uint32_t value = 0; + for (i = 0; i < NUM_FSELN_IN_GPFSELN; i++) { + uint32_t index = NUM_FSELN_IN_GPFSELN * reg + i; + if (index < sizeof(s->fsel)) { + value |= (s->fsel[index] & MASK_FSELN) << (NUM_BITS_FSELN * i); + } + } + return value; +} + +static void gpfsel_set(BCM2838GpioState *s, uint8_t reg, uint32_t value) +{ + int i; + for (i = 0; i < NUM_FSELN_IN_GPFSELN; i++) { + uint32_t index = NUM_FSELN_IN_GPFSELN * reg + i; + if (index < sizeof(s->fsel)) { + int fsel = (value >> (NUM_BITS_FSELN * i)) & MASK_FSELN; + s->fsel[index] = fsel; + } + } + + /* SD controller selection (48-53) */ + if (s->sd_fsel != BCM2838_FSEL_GPIO_IN + && (s->fsel[48] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[49] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[50] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[51] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[52] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[53] == BCM2838_FSEL_GPIO_IN) + ) { + /* SDHCI controller selected */ + sdbus_reparent_card(s->sdbus_sdhost, s->sdbus_sdhci); + s->sd_fsel = BCM2838_FSEL_GPIO_IN; + } else if (s->sd_fsel != BCM2838_FSEL_ALT0 + && (s->fsel[48] == BCM2838_FSEL_ALT0) /* SD_CLK_R */ + && (s->fsel[49] == BCM2838_FSEL_ALT0) /* SD_CMD_R */ + && (s->fsel[50] == BCM2838_FSEL_ALT0) /* SD_DATA0_R */ + && (s->fsel[51] == BCM2838_FSEL_ALT0) /* SD_DATA1_R */ + && (s->fsel[52] == BCM2838_FSEL_ALT0) /* SD_DATA2_R */ + && (s->fsel[53] == BCM2838_FSEL_ALT0) /* SD_DATA3_R */ + ) { + /* SDHost controller selected */ + sdbus_reparent_card(s->sdbus_sdhci, s->sdbus_sdhost); + s->sd_fsel = BCM2838_FSEL_ALT0; + } +} + +static int gpfsel_is_out(BCM2838GpioState *s, int index) +{ + if (index >= 0 && index < BCM2838_GPIO_NUM) { + return s->fsel[index] == 1; + } + return 0; +} + +static void gpset(BCM2838GpioState *s, uint32_t val, uint8_t start, + uint8_t count, uint32_t *lev) +{ + uint32_t changes = val & ~*lev; + uint32_t cur = 1; + + int i; + for (i = 0; i < count; i++) { + if ((changes & cur) && (gpfsel_is_out(s, start + i))) { + qemu_set_irq(s->out[start + i], 1); + } + cur <<= 1; + } + + *lev |= val; +} + +static void gpclr(BCM2838GpioState *s, uint32_t val, uint8_t start, + uint8_t count, uint32_t *lev) +{ + uint32_t changes = val & *lev; + uint32_t cur = 1; + + int i; + for (i = 0; i < count; i++) { + if ((changes & cur) && (gpfsel_is_out(s, start + i))) { + qemu_set_irq(s->out[start + i], 0); + } + cur <<= 1; + } + + *lev &= ~val; +} + +static uint64_t bcm2838_gpio_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2838GpioState *s = (BCM2838GpioState *)opaque; + uint64_t value = 0; + + switch (offset) { + case GPFSEL0: + case GPFSEL1: + case GPFSEL2: + case GPFSEL3: + case GPFSEL4: + case GPFSEL5: + value = gpfsel_get(s, offset / BYTES_IN_WORD); + break; + case GPSET0: + case GPSET1: + case GPCLR0: + case GPCLR1: + /* Write Only */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Attempt reading from write only" + " register. 0x%"PRIx64" will be returned." + " Address 0x%"HWADDR_PRIx", size %u\n", + TYPE_BCM2838_GPIO, __func__, value, offset, size); + break; + case GPLEV0: + value = s->lev0; + break; + case GPLEV1: + value = s->lev1; + break; + case GPEDS0: + case GPEDS1: + case GPREN0: + case GPREN1: + case GPFEN0: + case GPFEN1: + case GPHEN0: + case GPHEN1: + case GPLEN0: + case GPLEN1: + case GPAREN0: + case GPAREN1: + case GPAFEN0: + case GPAFEN1: + /* Not implemented */ + qemu_log_mask(LOG_UNIMP, "%s: %s: not implemented for %"HWADDR_PRIx"\n", + TYPE_BCM2838_GPIO, __func__, offset); + break; + case GPIO_PUP_PDN_CNTRL_REG0: + case GPIO_PUP_PDN_CNTRL_REG1: + case GPIO_PUP_PDN_CNTRL_REG2: + case GPIO_PUP_PDN_CNTRL_REG3: + value = s->pup_cntrl_reg[(offset - GPIO_PUP_PDN_CNTRL_REG0) + / sizeof(s->pup_cntrl_reg[0])]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: bad offset %"HWADDR_PRIx"\n", + TYPE_BCM2838_GPIO, __func__, offset); + break; + } + + return value; +} + +static void bcm2838_gpio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2838GpioState *s = (BCM2838GpioState *)opaque; + + switch (offset) { + case GPFSEL0: + case GPFSEL1: + case GPFSEL2: + case GPFSEL3: + case GPFSEL4: + case GPFSEL5: + gpfsel_set(s, offset / BYTES_IN_WORD, value); + break; + case GPSET0: + gpset(s, value, 0, 32, &s->lev0); + break; + case GPSET1: + gpset(s, value, 32, 22, &s->lev1); + break; + case GPCLR0: + gpclr(s, value, 0, 32, &s->lev0); + break; + case GPCLR1: + gpclr(s, value, 32, 22, &s->lev1); + break; + case GPLEV0: + case GPLEV1: + /* Read Only */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Attempt writing 0x%"PRIx64"" + " to read only register. Ignored." + " Address 0x%"HWADDR_PRIx", size %u\n", + TYPE_BCM2838_GPIO, __func__, value, offset, size); + break; + case GPEDS0: + case GPEDS1: + case GPREN0: + case GPREN1: + case GPFEN0: + case GPFEN1: + case GPHEN0: + case GPHEN1: + case GPLEN0: + case GPLEN1: + case GPAREN0: + case GPAREN1: + case GPAFEN0: + case GPAFEN1: + /* Not implemented */ + qemu_log_mask(LOG_UNIMP, "%s: %s: not implemented for %"HWADDR_PRIx"\n", + TYPE_BCM2838_GPIO, __func__, offset); + break; + case GPIO_PUP_PDN_CNTRL_REG0: + case GPIO_PUP_PDN_CNTRL_REG1: + case GPIO_PUP_PDN_CNTRL_REG2: + case GPIO_PUP_PDN_CNTRL_REG3: + s->pup_cntrl_reg[(offset - GPIO_PUP_PDN_CNTRL_REG0) + / sizeof(s->pup_cntrl_reg[0])] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: bad offset %"HWADDR_PRIx"\n", + TYPE_BCM2838_GPIO, __func__, offset); + } + return; +} + +static void bcm2838_gpio_reset(DeviceState *dev) +{ + BCM2838GpioState *s = BCM2838_GPIO(dev); + + memset(s->fsel, 0, sizeof(s->fsel)); + + s->sd_fsel = 0; + + /* SDHCI is selected by default */ + sdbus_reparent_card(&s->sdbus, s->sdbus_sdhci); + + s->lev0 = 0; + s->lev1 = 0; + + memset(s->fsel, 0, sizeof(s->fsel)); + + s->pup_cntrl_reg[0] = RESET_VAL_CNTRL_REG0; + s->pup_cntrl_reg[1] = RESET_VAL_CNTRL_REG1; + s->pup_cntrl_reg[2] = RESET_VAL_CNTRL_REG2; + s->pup_cntrl_reg[3] = RESET_VAL_CNTRL_REG3; +} + +static const MemoryRegionOps bcm2838_gpio_ops = { + .read = bcm2838_gpio_read, + .write = bcm2838_gpio_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_bcm2838_gpio = { + .name = "bcm2838_gpio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(fsel, BCM2838GpioState, BCM2838_GPIO_NUM), + VMSTATE_UINT32(lev0, BCM2838GpioState), + VMSTATE_UINT32(lev1, BCM2838GpioState), + VMSTATE_UINT8(sd_fsel, BCM2838GpioState), + VMSTATE_UINT32_ARRAY(pup_cntrl_reg, BCM2838GpioState, + GPIO_PUP_PDN_CNTRL_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2838_gpio_init(Object *obj) +{ + BCM2838GpioState *s = BCM2838_GPIO(obj); + DeviceState *dev = DEVICE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(s), "sd-bus"); + + memory_region_init_io(&s->iomem, obj, &bcm2838_gpio_ops, s, + "bcm2838_gpio", BCM2838_GPIO_REGS_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + qdev_init_gpio_out(dev, s->out, BCM2838_GPIO_NUM); +} + +static void bcm2838_gpio_realize(DeviceState *dev, Error **errp) +{ + BCM2838GpioState *s = BCM2838_GPIO(dev); + Object *obj; + + obj = object_property_get_link(OBJECT(dev), "sdbus-sdhci", &error_abort); + s->sdbus_sdhci = SD_BUS(obj); + + obj = object_property_get_link(OBJECT(dev), "sdbus-sdhost", &error_abort); + s->sdbus_sdhost = SD_BUS(obj); +} + +static void bcm2838_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_bcm2838_gpio; + dc->realize = &bcm2838_gpio_realize; + dc->reset = &bcm2838_gpio_reset; +} + +static const TypeInfo bcm2838_gpio_info = { + .name = TYPE_BCM2838_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2838GpioState), + .instance_init = bcm2838_gpio_init, + .class_init = bcm2838_gpio_class_init, +}; + +static void bcm2838_gpio_register_types(void) +{ + type_register_static(&bcm2838_gpio_info); +} + +type_init(bcm2838_gpio_register_types) diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index 066ea96480..8a8d03d885 100644 --- a/hw/gpio/meson.build +++ b/hw/gpio/meson.build @@ -9,6 +9,9 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpio.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_gpio.c')) system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_gpio.c')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gpio.c')) -system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files( + 'bcm2835_gpio.c', + 'bcm2838_gpio.c' +)) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c')) system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c')) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 5c48f8d743..bdd9a6bbce 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -19,6 +19,8 @@ #include "trace.h" #include "hw/arm/raspi_platform.h" +#define VCHI_BUSADDR_SIZE sizeof(uint32_t) + /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) @@ -138,6 +140,13 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; + case RPI_FWREQ_GET_CLOCKS: + /* TODO: add more clock IDs if needed */ + stl_le_phys(&s->dma_as, value + 12, 0); + stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_ARM_CLK_ID); + resplen = 8; + break; + case RPI_FWREQ_SET_CLOCK_RATE: case RPI_FWREQ_SET_MAX_CLOCK_RATE: case RPI_FWREQ_SET_MIN_CLOCK_RATE: @@ -276,6 +285,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) stl_le_phys(&s->dma_as, value + 12, 0); resplen = 4; break; + case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS: stl_le_phys(&s->dma_as, value + 12, 1); resplen = 4; @@ -301,6 +311,17 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen); break; + case RPI_FWREQ_GET_THROTTLED: + stl_le_phys(&s->dma_as, value + 12, 0); + resplen = 4; + break; + + case RPI_FWREQ_VCHIQ_INIT: + stl_le_phys(&s->dma_as, + value + offsetof(rpi_firmware_prop_request_t, payload), + 0); + resplen = VCHI_BUSADDR_SIZE; + break; default: qemu_log_mask(LOG_UNIMP, "bcm2835_property: unhandled tag 0x%08x\n", tag); diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index aa44c4b20c..1585a2d399 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -36,6 +36,7 @@ #include "qemu/bcd.h" #include "qemu/module.h" #include "trace.h" +#include "sysemu/watchdog.h" #include "m48t59-internal.h" #include "migration/vmstate.h" @@ -163,8 +164,7 @@ static void watchdog_cb (void *opaque) if (NVRAM->buffer[0x1FF7] & 0x80) { NVRAM->buffer[0x1FF7] = 0x00; NVRAM->buffer[0x1FFC] &= ~0x40; - /* May it be a hw CPU Reset instead ? */ - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + watchdog_perform_action(); } else { qemu_set_irq(NVRAM->IRQ, 1); qemu_set_irq(NVRAM->IRQ, 0); diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index 837b0bdf9b..563bb4b446 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -141,6 +141,7 @@ static void pl031_write(void * opaque, hwaddr offset, g_autofree const char *qom_path = object_get_canonical_path(opaque); struct tm tm; + s->lr = value; s->tick_offset += value - pl031_get_count(s); qemu_get_timedate(&tm, s->tick_offset); diff --git a/hw/ssi/xlnx-versal-ospi.c b/hw/ssi/xlnx-versal-ospi.c index c7b95b1f37..c479138ec1 100644 --- a/hw/ssi/xlnx-versal-ospi.c +++ b/hw/ssi/xlnx-versal-ospi.c @@ -1772,6 +1772,12 @@ static void xlnx_versal_ospi_init(Object *obj) memory_region_init_io(&s->iomem_dac, obj, &ospi_dac_ops, s, TYPE_XILINX_VERSAL_OSPI "-dac", 0x20000000); sysbus_init_mmio(sbd, &s->iomem_dac); + /* + * The OSPI DMA reads flash data through the OSPI linear address space (the + * iomem_dac region), because of this the reentrancy guard needs to be + * disabled. + */ + s->iomem_dac.disable_reentrancy_guard = true; sysbus_init_irq(sbd, &s->irq); diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 6a7d5551f4..6479ab1a8b 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -18,6 +18,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" +#include "sysemu/watchdog.h" #define OSMR0 0x00 #define OSMR1 0x04 @@ -417,7 +418,7 @@ static void pxa2xx_timer_tick(void *opaque) if (t->num == 3) if (i->reset3 & 1) { i->reset3 = 0; - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + watchdog_perform_action(); } } diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index 0203bb79d8..1fc96218f8 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -35,10 +35,13 @@ #include "hw/misc/unimp.h" #include "qom/object.h" +#define TYPE_BCM_SOC_PERIPHERALS_BASE "bcm-soc-peripherals-base" +OBJECT_DECLARE_TYPE(BCMSocPeripheralBaseState, BCMSocPeripheralBaseClass, + BCM_SOC_PERIPHERALS_BASE) #define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals" OBJECT_DECLARE_SIMPLE_TYPE(BCM2835PeripheralState, BCM2835_PERIPHERALS) -struct BCM2835PeripheralState { +struct BCMSocPeripheralBaseState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ @@ -60,12 +63,9 @@ struct BCM2835PeripheralState { OrIRQState orgated_dma_irq; BCM2835ICState ic; BCM2835PropertyState property; - BCM2835RngState rng; BCM2835MboxState mboxes; SDHCIState sdhci; BCM2835SDHostState sdhost; - BCM2835GpioState gpio; - Bcm2835ThermalState thermal; UnimplementedDeviceState i2s; BCM2835SPIState spi[1]; UnimplementedDeviceState i2c[3]; @@ -79,4 +79,25 @@ struct BCM2835PeripheralState { UnimplementedDeviceState sdramc; }; +struct BCMSocPeripheralBaseClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + uint64_t peri_size; /* Peripheral range size */ +}; + +struct BCM2835PeripheralState { + /*< private >*/ + BCMSocPeripheralBaseState parent_obj; + /*< public >*/ + BCM2835RngState rng; + Bcm2835ThermalState thermal; + BCM2835GpioState gpio; +}; + +void create_unimp(BCMSocPeripheralBaseState *ps, + UnimplementedDeviceState *uds, + const char *name, hwaddr ofs, hwaddr size); +void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp); + #endif /* BCM2835_PERIPHERALS_H */ diff --git a/include/hw/arm/bcm2836.h b/include/hw/arm/bcm2836.h index 6f90cabfa3..918fb3bf14 100644 --- a/include/hw/arm/bcm2836.h +++ b/include/hw/arm/bcm2836.h @@ -17,8 +17,10 @@ #include "target/arm/cpu.h" #include "qom/object.h" +#define TYPE_BCM283X_BASE "bcm283x-base" +OBJECT_DECLARE_TYPE(BCM283XBaseState, BCM283XBaseClass, BCM283X_BASE) #define TYPE_BCM283X "bcm283x" -OBJECT_DECLARE_TYPE(BCM283XState, BCM283XClass, BCM283X) +OBJECT_DECLARE_SIMPLE_TYPE(BCM283XState, BCM283X) #define BCM283X_NCPUS 4 @@ -30,7 +32,7 @@ OBJECT_DECLARE_TYPE(BCM283XState, BCM283XClass, BCM283X) #define TYPE_BCM2836 "bcm2836" #define TYPE_BCM2837 "bcm2837" -struct BCM283XState { +struct BCM283XBaseState { /*< private >*/ DeviceState parent_obj; /*< public >*/ @@ -41,7 +43,28 @@ struct BCM283XState { ARMCPU core; } cpu[BCM283X_NCPUS]; BCM2836ControlState control; +}; + +struct BCM283XBaseClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + const char *name; + const char *cpu_type; + unsigned core_count; + hwaddr peri_base; /* Peripheral base address seen by the CPU */ + hwaddr ctrl_base; /* Interrupt controller and mailboxes etc. */ + int clusterid; +}; + +struct BCM283XState { + /*< private >*/ + BCM283XBaseState parent_obj; + /*< public >*/ BCM2835PeripheralState peripherals; }; +bool bcm283x_common_realize(DeviceState *dev, BCMSocPeripheralBaseState *ps, + Error **errp); + #endif /* BCM2836_H */ diff --git a/include/hw/arm/bcm2838.h b/include/hw/arm/bcm2838.h new file mode 100644 index 0000000000..e53c7bedf9 --- /dev/null +++ b/include/hw/arm/bcm2838.h @@ -0,0 +1,31 @@ +/* + * BCM2838 SoC emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BCM2838_H +#define BCM2838_H + +#include "hw/arm/bcm2836.h" +#include "hw/intc/arm_gic.h" +#include "hw/arm/bcm2838_peripherals.h" + +#define BCM2838_PERI_LOW_BASE 0xfc000000 +#define BCM2838_GIC_BASE 0x40000 + +#define TYPE_BCM2838 "bcm2838" + +OBJECT_DECLARE_TYPE(BCM2838State, BCM2838Class, BCM2838) + +struct BCM2838State { + /*< private >*/ + BCM283XBaseState parent_obj; + /*< public >*/ + BCM2838PeripheralState peripherals; + GICState gic; +}; + +#endif /* BCM2838_H */ diff --git a/include/hw/arm/bcm2838_peripherals.h b/include/hw/arm/bcm2838_peripherals.h new file mode 100644 index 0000000000..7ee1bd066f --- /dev/null +++ b/include/hw/arm/bcm2838_peripherals.h @@ -0,0 +1,84 @@ +/* + * BCM2838 peripherals emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BCM2838_PERIPHERALS_H +#define BCM2838_PERIPHERALS_H + +#include "hw/arm/bcm2835_peripherals.h" +#include "hw/sd/sdhci.h" +#include "hw/gpio/bcm2838_gpio.h" + +/* SPI */ +#define GIC_SPI_INTERRUPT_MBOX 33 +#define GIC_SPI_INTERRUPT_MPHI 40 +#define GIC_SPI_INTERRUPT_DWC2 73 +#define GIC_SPI_INTERRUPT_DMA_0 80 +#define GIC_SPI_INTERRUPT_DMA_6 86 +#define GIC_SPI_INTERRUPT_DMA_7_8 87 +#define GIC_SPI_INTERRUPT_DMA_9_10 88 +#define GIC_SPI_INTERRUPT_AUX_UART1 93 +#define GIC_SPI_INTERRUPT_SDHOST 120 +#define GIC_SPI_INTERRUPT_UART0 121 +#define GIC_SPI_INTERRUPT_RNG200 125 +#define GIC_SPI_INTERRUPT_EMMC_EMMC2 126 +#define GIC_SPI_INTERRUPT_PCI_INT_A 143 +#define GIC_SPI_INTERRUPT_GENET_A 157 +#define GIC_SPI_INTERRUPT_GENET_B 158 + + +/* GPU (legacy) DMA interrupts */ +#define GPU_INTERRUPT_DMA0 16 +#define GPU_INTERRUPT_DMA1 17 +#define GPU_INTERRUPT_DMA2 18 +#define GPU_INTERRUPT_DMA3 19 +#define GPU_INTERRUPT_DMA4 20 +#define GPU_INTERRUPT_DMA5 21 +#define GPU_INTERRUPT_DMA6 22 +#define GPU_INTERRUPT_DMA7_8 23 +#define GPU_INTERRUPT_DMA9_10 24 +#define GPU_INTERRUPT_DMA11 25 +#define GPU_INTERRUPT_DMA12 26 +#define GPU_INTERRUPT_DMA13 27 +#define GPU_INTERRUPT_DMA14 28 +#define GPU_INTERRUPT_DMA15 31 + +#define BCM2838_MPHI_OFFSET 0xb200 +#define BCM2838_MPHI_SIZE 0x200 + +#define TYPE_BCM2838_PERIPHERALS "bcm2838-peripherals" +OBJECT_DECLARE_TYPE(BCM2838PeripheralState, BCM2838PeripheralClass, + BCM2838_PERIPHERALS) + +struct BCM2838PeripheralState { + /*< private >*/ + BCMSocPeripheralBaseState parent_obj; + + /*< public >*/ + MemoryRegion peri_low_mr; + MemoryRegion peri_low_mr_alias; + MemoryRegion mphi_mr_alias; + + SDHCIState emmc2; + BCM2838GpioState gpio; + + OrIRQState mmc_irq_orgate; + OrIRQState dma_7_8_irq_orgate; + OrIRQState dma_9_10_irq_orgate; + + UnimplementedDeviceState asb; + UnimplementedDeviceState clkisp; +}; + +struct BCM2838PeripheralClass { + /*< private >*/ + BCMSocPeripheralBaseClass parent_class; + /*< public >*/ + uint64_t peri_low_size; /* Peripheral lower range size */ +}; + +#endif /* BCM2838_PERIPHERALS_H */ diff --git a/include/hw/arm/raspberrypi-fw-defs.h b/include/hw/arm/raspberrypi-fw-defs.h index 579cf0d554..8b404e0533 100644 --- a/include/hw/arm/raspberrypi-fw-defs.h +++ b/include/hw/arm/raspberrypi-fw-defs.h @@ -159,4 +159,15 @@ enum rpi_firmware_clk_id { RPI_FIRMWARE_NUM_CLK_ID, }; +struct rpi_firmware_property_tag_header { + uint32_t tag; + uint32_t buf_size; + uint32_t req_resp_size; +}; + +typedef struct rpi_firmware_prop_request { + struct rpi_firmware_property_tag_header hdr; + uint8_t payload[0]; +} rpi_firmware_prop_request_t; + #endif /* INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ */ diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h index ede98e63c3..7bc4807fa5 100644 --- a/include/hw/arm/raspi_platform.h +++ b/include/hw/arm/raspi_platform.h @@ -28,6 +28,42 @@ #ifndef HW_ARM_RASPI_PLATFORM_H #define HW_ARM_RASPI_PLATFORM_H +#include "hw/boards.h" +#include "hw/arm/boot.h" + +/* Registered machine type (matches RPi Foundation bootloader and U-Boot) */ +#define MACH_TYPE_BCM2708 3138 + +#define TYPE_RASPI_BASE_MACHINE MACHINE_TYPE_NAME("raspi-base") +OBJECT_DECLARE_TYPE(RaspiBaseMachineState, RaspiBaseMachineClass, + RASPI_BASE_MACHINE) + +struct RaspiBaseMachineState { + /*< private >*/ + MachineState parent_obj; + /*< public >*/ + struct arm_boot_info binfo; +}; + +struct RaspiBaseMachineClass { + /*< private >*/ + MachineClass parent_obj; + /*< public >*/ + uint32_t board_rev; +}; + +/* Common functions for raspberry pi machines */ +const char *board_soc_type(uint32_t board_rev); +void raspi_machine_init(MachineState *machine); + +typedef struct BCM283XBaseState BCM283XBaseState; +void raspi_base_machine_init(MachineState *machine, + BCM283XBaseState *soc); + +void raspi_machine_class_common_init(MachineClass *mc, + uint32_t board_rev); +uint64_t board_ram_size(uint32_t board_rev); + #define MSYNC_OFFSET 0x0000 /* Multicore Sync Block */ #define CCPT_OFFSET 0x1000 /* Compact Camera Port 2 TX */ #define INTE_OFFSET 0x2000 /* VC Interrupt controller */ @@ -37,7 +73,7 @@ #define MPHI_OFFSET 0x6000 /* Message-based Parallel Host Intf. */ #define DMA_OFFSET 0x7000 /* DMA controller, channels 0-14 */ #define ARBA_OFFSET 0x9000 -#define BRDG_OFFSET 0xa000 +#define BRDG_OFFSET 0xa000 /* RPiVid ASB for BCM2838 (BCM2711) */ #define ARM_OFFSET 0xB000 /* ARM control block */ #define ARMCTRL_OFFSET (ARM_OFFSET + 0x000) #define ARMCTRL_IC_OFFSET (ARM_OFFSET + 0x200) /* Interrupt controller */ diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h index baf70410b5..4f314b7a93 100644 --- a/include/hw/arm/stm32l4x5_soc.h +++ b/include/hw/arm/stm32l4x5_soc.h @@ -26,6 +26,7 @@ #include "exec/memory.h" #include "hw/arm/armv7m.h" +#include "hw/or-irq.h" #include "hw/misc/stm32l4x5_syscfg.h" #include "hw/misc/stm32l4x5_exti.h" #include "qom/object.h" @@ -36,12 +37,15 @@ #define TYPE_STM32L4X5XG_SOC "stm32l4x5xg-soc" OBJECT_DECLARE_TYPE(Stm32l4x5SocState, Stm32l4x5SocClass, STM32L4X5_SOC) +#define NUM_EXTI_OR_GATES 4 + struct Stm32l4x5SocState { SysBusDevice parent_obj; ARMv7MState armv7m; Stm32l4x5ExtiState exti; + OrIRQState exti_or_gates[NUM_EXTI_OR_GATES]; Stm32l4x5SyscfgState syscfg; MemoryRegion sram1; diff --git a/include/hw/core/resetcontainer.h b/include/hw/core/resetcontainer.h new file mode 100644 index 0000000000..23db0c7a88 --- /dev/null +++ b/include/hw/core/resetcontainer.h @@ -0,0 +1,48 @@ +/* + * Reset container + * + * Copyright (c) 2024 Linaro, Ltd + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_RESETCONTAINER_H +#define HW_RESETCONTAINER_H + +/* + * The "reset container" is an object which implements the Resettable + * interface. It contains a list of arbitrary other objects which also + * implement Resettable. Resetting the reset container resets all the + * objects in it. + */ + +#include "qom/object.h" + +#define TYPE_RESETTABLE_CONTAINER "resettable-container" +OBJECT_DECLARE_TYPE(ResettableContainer, ResettableContainerClass, RESETTABLE_CONTAINER) + +/** + * resettable_container_add: Add a resettable object to the container + * @rc: container + * @obj: object to add to the container + * + * Add @obj to the ResettableContainer @rc. @obj must implement the + * Resettable interface. + * + * When @rc is reset, it will reset every object that has been added + * to it, in the order they were added. + */ +void resettable_container_add(ResettableContainer *rc, Object *obj); + +/** + * resettable_container_remove: Remove an object from the container + * @rc: container + * @obj: object to remove from the container + * + * Remove @obj from the ResettableContainer @rc. @obj must have been + * previously added to this container. + */ +void resettable_container_remove(ResettableContainer *rc, Object *obj); + +#endif diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index 38671afffd..49541bf08f 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -16,6 +16,8 @@ #include "ui/console.h" #include "qom/object.h" +#define UPPER_RAM_BASE 0x40000000 + #define TYPE_BCM2835_FB "bcm2835-fb" OBJECT_DECLARE_SIMPLE_TYPE(BCM2835FBState, BCM2835_FB) diff --git a/include/hw/gpio/bcm2838_gpio.h b/include/hw/gpio/bcm2838_gpio.h new file mode 100644 index 0000000000..f2a57a697f --- /dev/null +++ b/include/hw/gpio/bcm2838_gpio.h @@ -0,0 +1,45 @@ +/* + * Raspberry Pi (BCM2838) GPIO Controller + * This implementation is based on bcm2835_gpio (hw/gpio/bcm2835_gpio.c) + * + * Copyright (c) 2022 Auriga LLC + * + * Authors: + * Lotosh, Aleksey + * + * 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 BCM2838_GPIO_H +#define BCM2838_GPIO_H + +#include "hw/sd/sd.h" +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_BCM2838_GPIO "bcm2838-gpio" +OBJECT_DECLARE_SIMPLE_TYPE(BCM2838GpioState, BCM2838_GPIO) + +#define BCM2838_GPIO_REGS_SIZE 0x1000 +#define BCM2838_GPIO_NUM 58 +#define GPIO_PUP_PDN_CNTRL_NUM 4 + +struct BCM2838GpioState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + /* SDBus selector */ + SDBus sdbus; + SDBus *sdbus_sdhci; + SDBus *sdbus_sdhost; + + uint8_t fsel[BCM2838_GPIO_NUM]; + uint32_t lev0, lev1; + uint8_t sd_fsel; + qemu_irq out[BCM2838_GPIO_NUM]; + uint32_t pup_cntrl_reg[GPIO_PUP_PDN_CNTRL_NUM]; +}; + +#endif diff --git a/include/qom/object.h b/include/qom/object.h index e9ed9550f0..13d3a655dd 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -258,6 +258,51 @@ struct Object DECLARE_INSTANCE_CHECKER(InstanceType, MODULE_OBJ_NAME, TYPE_##MODULE_OBJ_NAME) +/** + * DO_OBJECT_DEFINE_TYPE_EXTENDED: + * @ModuleObjName: the object name with initial caps + * @module_obj_name: the object name in lowercase with underscore separators + * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators + * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore + * separators + * @ABSTRACT: boolean flag to indicate whether the object can be instantiated + * @CLASS_SIZE: size of the type's class + * @...: list of initializers for "InterfaceInfo" to declare implemented interfaces + * + * This is the base macro used to implement all the OBJECT_DEFINE_* + * macros. It should never be used directly in a source file. + */ +#define DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, \ + PARENT_MODULE_OBJ_NAME, \ + ABSTRACT, CLASS_SIZE, ...) \ + static void \ + module_obj_name##_finalize(Object *obj); \ + static void \ + module_obj_name##_class_init(ObjectClass *oc, void *data); \ + static void \ + module_obj_name##_init(Object *obj); \ + \ + static const TypeInfo module_obj_name##_info = { \ + .parent = TYPE_##PARENT_MODULE_OBJ_NAME, \ + .name = TYPE_##MODULE_OBJ_NAME, \ + .instance_size = sizeof(ModuleObjName), \ + .instance_align = __alignof__(ModuleObjName), \ + .instance_init = module_obj_name##_init, \ + .instance_finalize = module_obj_name##_finalize, \ + .class_size = CLASS_SIZE, \ + .class_init = module_obj_name##_class_init, \ + .abstract = ABSTRACT, \ + .interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \ + }; \ + \ + static void \ + module_obj_name##_register_types(void) \ + { \ + type_register_static(&module_obj_name##_info); \ + } \ + type_init(module_obj_name##_register_types); + /** * OBJECT_DEFINE_TYPE_EXTENDED: * @ModuleObjName: the object name with initial caps @@ -284,32 +329,10 @@ struct Object #define OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ABSTRACT, ...) \ - static void \ - module_obj_name##_finalize(Object *obj); \ - static void \ - module_obj_name##_class_init(ObjectClass *oc, void *data); \ - static void \ - module_obj_name##_init(Object *obj); \ - \ - static const TypeInfo module_obj_name##_info = { \ - .parent = TYPE_##PARENT_MODULE_OBJ_NAME, \ - .name = TYPE_##MODULE_OBJ_NAME, \ - .instance_size = sizeof(ModuleObjName), \ - .instance_align = __alignof__(ModuleObjName), \ - .instance_init = module_obj_name##_init, \ - .instance_finalize = module_obj_name##_finalize, \ - .class_size = sizeof(ModuleObjName##Class), \ - .class_init = module_obj_name##_class_init, \ - .abstract = ABSTRACT, \ - .interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \ - }; \ - \ - static void \ - module_obj_name##_register_types(void) \ - { \ - type_register_static(&module_obj_name##_info); \ - } \ - type_init(module_obj_name##_register_types); + DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ + ABSTRACT, sizeof(ModuleObjName##Class), \ + __VA_ARGS__) /** * OBJECT_DEFINE_TYPE: @@ -368,6 +391,45 @@ struct Object MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ true, { NULL }) +/** + * OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES: + * @ModuleObjName: the object name with initial caps + * @module_obj_name: the object name in lowercase with underscore separators + * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators + * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore + * separators + * + * This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for + * the case of a non-abstract type, with interfaces, and with no requirement + * for a class struct. + */ +#define OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, \ + module_obj_name, \ + MODULE_OBJ_NAME, \ + PARENT_MODULE_OBJ_NAME, ...) \ + DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ + false, 0, __VA_ARGS__) + +/** + * OBJECT_DEFINE_SIMPLE_TYPE: + * @ModuleObjName: the object name with initial caps + * @module_obj_name: the object name in lowercase with underscore separators + * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators + * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore + * separators + * + * This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for + * the common case of a non-abstract type, without any interfaces, and with + * no requirement for a class struct. If you declared your type with + * OBJECT_DECLARE_SIMPLE_TYPE then this is probably the right choice for + * defining it. + */ +#define OBJECT_DEFINE_SIMPLE_TYPE(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME) \ + OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, { NULL }) + /** * struct TypeInfo: * @name: The name of the type. diff --git a/include/sysemu/reset.h b/include/sysemu/reset.h index 609e4d50c2..ae436044a9 100644 --- a/include/sysemu/reset.h +++ b/include/sysemu/reset.h @@ -1,3 +1,29 @@ +/* + * Reset handlers. + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2016 Red Hat, Inc. + * Copyright (c) 2024 Linaro, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #ifndef QEMU_SYSEMU_RESET_H #define QEMU_SYSEMU_RESET_H @@ -5,9 +31,96 @@ typedef void QEMUResetHandler(void *opaque); +/** + * qemu_register_resettable: Register an object to be reset + * @obj: object to be reset: it must implement the Resettable interface + * + * Register @obj on the list of objects which will be reset when the + * simulation is reset. These objects will be reset in the order + * they were added, using the three-phase Resettable protocol, + * so first all objects go through the enter phase, then all objects + * go through the hold phase, and then finally all go through the + * exit phase. + * + * It is not permitted to register or unregister reset functions or + * resettable objects from within any of the reset phase methods of @obj. + * + * We assume that the caller holds the BQL. + */ +void qemu_register_resettable(Object *obj); + +/** + * qemu_unregister_resettable: Unregister an object to be reset + * @obj: object to unregister + * + * Remove @obj from the list of objects which are reset when the + * simulation is reset. It must have been previously added to + * the list via qemu_register_resettable(). + * + * We assume that the caller holds the BQL. + */ +void qemu_unregister_resettable(Object *obj); + +/** + * qemu_register_reset: Register a callback for system reset + * @func: function to call + * @opaque: opaque data to pass to @func + * + * Register @func on the list of functions which are called when the + * entire system is reset. Functions registered with this API and + * Resettable objects registered with qemu_register_resettable() are + * handled together, in the order in which they were registered. + * Functions registered with this API are called in the 'hold' phase + * of the 3-phase reset. + * + * In general this function should not be used in new code where possible; + * for instance, device model reset is better accomplished using the + * methods on DeviceState. + * + * It is not permitted to register or unregister reset functions or + * resettable objects from within the @func callback. + * + * We assume that the caller holds the BQL. + */ void qemu_register_reset(QEMUResetHandler *func, void *opaque); + +/** + * qemu_register_reset_nosnapshotload: Register a callback for system reset + * @func: function to call + * @opaque: opaque data to pass to @func + * + * This is the same as qemu_register_reset(), except that @func is + * not called if the reason that the system is being reset is to + * put it into a clean state prior to loading a snapshot (i.e. for + * SHUTDOWN_CAUSE_SNAPSHOT_LOAD). + */ void qemu_register_reset_nosnapshotload(QEMUResetHandler *func, void *opaque); + +/** + * qemu_unregister_reset: Unregister a system reset callback + * @func: function registered with qemu_register_reset() + * @opaque: the same opaque data that was passed to qemu_register_reset() + * + * Undo the effects of a qemu_register_reset(). The @func and @opaque + * must both match the arguments originally used with qemu_register_reset(). + * + * We assume that the caller holds the BQL. + */ void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); + +/** + * qemu_devices_reset: Perform a complete system reset + * @reason: reason for the reset + * + * This function performs the low-level work needed to do a complete reset + * of the system (calling all the callbacks registered with + * qemu_register_reset() and resetting all the Resettable objects registered + * with qemu_register_resettable()). It should only be called by the code in a + * MachineClass reset method. + * + * If you want to trigger a system reset from, for instance, a device + * model, don't use this function. Use qemu_system_reset_request(). + */ void qemu_devices_reset(ShutdownCause reason); #endif diff --git a/system/bootdevice.c b/system/bootdevice.c index 2106f1026f..2579b26dc8 100644 --- a/system/bootdevice.c +++ b/system/bootdevice.c @@ -101,20 +101,23 @@ void validate_bootdevices(const char *devices, Error **errp) void restore_boot_order(void *opaque) { char *normal_boot_order = opaque; - static int first = 1; + static int bootcount; - /* Restore boot order and remove ourselves after the first boot */ - if (first) { - first = 0; + switch (bootcount++) { + case 0: + /* First boot: use the one-time config */ + return; + case 1: + /* Second boot: restore normal boot order */ + if (boot_set_handler) { + qemu_boot_set(normal_boot_order, &error_abort); + } + g_free(normal_boot_order); + return; + default: + /* Subsequent boots: keep using normal boot order */ return; } - - if (boot_set_handler) { - qemu_boot_set(normal_boot_order, &error_abort); - } - - qemu_unregister_reset(restore_boot_order, normal_boot_order); - g_free(normal_boot_order); } void check_boot_index(int32_t bootindex, Error **errp) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 8e30a7993e..0f7a44a28f 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -663,7 +663,7 @@ static void aarch64_a53_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53; cpu->midr = 0x410fd034; - cpu->revidr = 0x00000000; + cpu->revidr = 0x00000100; cpu->reset_fpsid = 0x41034070; cpu->isar.mvfr0 = 0x10110222; cpu->isar.mvfr1 = 0x12111111; diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 5eb3577bcd..ba1a27ca2b 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -711,8 +711,68 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, void *host = ptw->out_host; if (unlikely(!host)) { - fi->type = ARMFault_UnsuppAtomicUpdate; - return 0; + /* Page table in MMIO Memory Region */ + CPUState *cs = env_cpu(env); + MemTxAttrs attrs = { + .space = ptw->out_space, + .secure = arm_space_is_secure(ptw->out_space), + }; + AddressSpace *as = arm_addressspace(cs, attrs); + MemTxResult result = MEMTX_OK; + bool need_lock = !bql_locked(); + + if (need_lock) { + bql_lock(); + } + if (ptw->out_be) { + cur_val = address_space_ldq_be(as, ptw->out_phys, attrs, &result); + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + if (need_lock) { + bql_unlock(); + } + return old_val; + } + if (cur_val == old_val) { + address_space_stq_be(as, ptw->out_phys, new_val, attrs, &result); + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + if (need_lock) { + bql_unlock(); + } + return old_val; + } + cur_val = new_val; + } + } else { + cur_val = address_space_ldq_le(as, ptw->out_phys, attrs, &result); + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + if (need_lock) { + bql_unlock(); + } + return old_val; + } + if (cur_val == old_val) { + address_space_stq_le(as, ptw->out_phys, new_val, attrs, &result); + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + if (need_lock) { + bql_unlock(); + } + return old_val; + } + cur_val = new_val; + } + } + if (need_lock) { + bql_unlock(); + } + return cur_val; } /* diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index a00202df3c..989b65111c 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -501,6 +501,103 @@ class BootLinuxConsole(LinuxKernelTest): # Wait for VM to shut down gracefully self.vm.wait() + def test_arm_raspi4(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:raspi4b + :avocado: tags=device:pl011 + :avocado: tags=accel:tcg + :avocado: tags=rpi4b + + The kernel can be rebuilt using the kernel source referenced + and following the instructions on the on: + https://www.raspberrypi.org/documentation/linux/kernel/building.md + """ + + deb_url = ('http://archive.raspberrypi.org/debian/' + 'pool/main/r/raspberrypi-firmware/' + 'raspberrypi-kernel_1.20230106-1_arm64.deb') + deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img') + dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb') + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=pl011,mmio32,0xfe201000 ' + + 'console=ttyAMA0,115200 ' + + 'root=/dev/mmcblk1p2 rootwait ' + + 'dwc_otg.fiq_fsm_enable=0') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + # When PCI is supported we can add a USB controller: + # '-device', 'qemu-xhci,bus=pcie.1,id=xhci', + # '-device', 'usb-kbd,bus=xhci.0', + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + # When USB is enabled we can look for this + # console_pattern = 'Product: QEMU USB Keyboard' + # self.wait_for_console_pattern(console_pattern) + console_pattern = 'Waiting for root device' + self.wait_for_console_pattern(console_pattern) + + + def test_arm_raspi4_initrd(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:raspi4b + :avocado: tags=device:pl011 + :avocado: tags=accel:tcg + :avocado: tags=rpi4b + + The kernel can be rebuilt using the kernel source referenced + and following the instructions on the on: + https://www.raspberrypi.org/documentation/linux/kernel/building.md + """ + deb_url = ('http://archive.raspberrypi.org/debian/' + 'pool/main/r/raspberrypi-firmware/' + 'raspberrypi-kernel_1.20230106-1_arm64.deb') + deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img') + dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb') + + initrd_url = ('https://github.com/groeck/linux-build-test/raw/' + '86b2be1384d41c8c388e63078a847f1e1c4cb1de/rootfs/' + 'arm64/rootfs.cpio.gz') + initrd_hash = 'f3d4f9fa92a49aa542f1b44d34be77bbf8ca5b9d' + initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.gzip_uncompress(initrd_path_gz, initrd_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=pl011,mmio32,0xfe201000 ' + + 'console=ttyAMA0,115200 ' + + 'panic=-1 noreboot ' + + 'dwc_otg.fiq_fsm_enable=0') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + # When PCI is supported we can add a USB controller: + # '-device', 'qemu-xhci,bus=pcie.1,id=xhci', + # '-device', 'usb-kbd,bus=xhci.0', + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'BCM2835') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'cprman@7e101000') + exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') + # TODO: Raspberry Pi4 doesn't shut down properly with recent kernels + # Wait for VM to shut down gracefully + #self.vm.wait() + def test_arm_exynos4210_initrd(self): """ :avocado: tags=arch:arm diff --git a/tests/qtest/stm32l4x5_exti-test.c b/tests/qtest/stm32l4x5_exti-test.c index c390077713..81830be8ae 100644 --- a/tests/qtest/stm32l4x5_exti-test.c +++ b/tests/qtest/stm32l4x5_exti-test.c @@ -31,6 +31,7 @@ #define EXTI0_IRQ 6 #define EXTI1_IRQ 7 +#define EXTI5_9_IRQ 23 #define EXTI35_IRQ 1 static void enable_nvic_irq(unsigned int n) @@ -499,6 +500,40 @@ static void test_interrupt(void) g_assert_false(check_nvic_pending(EXTI1_IRQ)); } +static void test_orred_interrupts(void) +{ + /* + * For lines EXTI5..9 (fanned-in to NVIC irq 23), + * test that raising the line pends interrupt + * 23 in NVIC. + */ + enable_nvic_irq(EXTI5_9_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI5_9_IRQ)); + + /* Enable interrupt lines EXTI[5..9] */ + exti_writel(EXTI_IMR1, (0x1F << 5)); + + /* Configure interrupt on rising edge */ + exti_writel(EXTI_RTSR1, (0x1F << 5)); + + /* Raise GPIO line i, check that the interrupt is pending */ + for (unsigned i = 5; i < 10; i++) { + exti_set_irq(i, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 1 << i); + g_assert_true(check_nvic_pending(EXTI5_9_IRQ)); + + exti_writel(EXTI_PR1, 1 << i); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_true(check_nvic_pending(EXTI5_9_IRQ)); + + unpend_nvic_irq(EXTI5_9_IRQ); + g_assert_false(check_nvic_pending(EXTI5_9_IRQ)); + } +} + int main(int argc, char **argv) { int ret; @@ -515,6 +550,8 @@ int main(int argc, char **argv) qtest_add_func("stm32l4x5/exti/masked_interrupt", test_masked_interrupt); qtest_add_func("stm32l4x5/exti/interrupt", test_interrupt); qtest_add_func("stm32l4x5/exti/test_edge_selector", test_edge_selector); + qtest_add_func("stm32l4x5/exti/test_orred_interrupts", + test_orred_interrupts); qtest_start("-machine b-l475e-iot01a"); ret = g_test_run();