target-arm queue:
* virt-acpi-build: add always-on property for timer * various fixes for EL2 and EL3 behaviour * arm: virt-acpi: each MADT.GICC entry as enabled unconditionally * target-arm: Don't report presence of EL2 if it doesn't exist * raspi: add raspberry pi 2 machine -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJWsk26AAoJEDwlJe0UNgzeWwAP/j5AWKgqsi9hpskq1BVtjc8P yZZlsRcSN8oyKMRoCXUCXztQhiBSYVABeeABH5JWsoxCK1CA6vBqkOl89ENfhwNZ TmeRh4BLCwfW+YIb9OG84JoVpCbvt6M7KYpCA3DJBy5GLOsO5vxd3E3BLN7O+dKH ljEcF0l+SlJAupK692A34nyS01dCs91xwP/kigOSPoVjpkUBOmSHrQhD4wTHjunr xj6fgc6rLqnG9TrNUkTn8aIB8lZ1j9VmJUetbp0x/Zo7TKcC8KObh+pYk3hik7JK PLhNJU0tPe55PEx+RLi8i6vYZp8Fqsl5qskjshl3uBtvK5TR0doxbIAu89raapWL Bg7xesN2H6Z+w0tq6TRLU4FhL+MtN0+wQy6PJ55NOkHVgZv9/i5keH4Xe/vZwMZd YGVhhrSSjfGmR878Rh6s7v1ZNFhAJenti4icd66e82InPl6UHf2SC34dLBpzW6SF qnmIt3BCHIDruwJKmWF71RAjmd9eR4xSEw1wcqoZF7UqghnQOBSuszEv8dlvRmES zNYgDqOjxQ79X3zmtXLKQ8UFoGQqhy3E2DF6lsHgxHCwcwHKKuro6Q3LQWnLZvau e5CVQShK2lU8o2dwMLmXakjoFGleCG0OsJBz9Ls89w1YV/c4X/UPFXwAJMhJ5P9v cYtw9ol7WJmT5yLxQGtI =Ul6h -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20160203' into staging target-arm queue: * virt-acpi-build: add always-on property for timer * various fixes for EL2 and EL3 behaviour * arm: virt-acpi: each MADT.GICC entry as enabled unconditionally * target-arm: Don't report presence of EL2 if it doesn't exist * raspi: add raspberry pi 2 machine # gpg: Signature made Wed 03 Feb 2016 18:58:02 GMT using RSA key ID 14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" * remotes/pmaydell/tags/pull-target-arm-20160203: raspi: add raspberry pi 2 machine arm/boot: move highbank secure board setup code to common routine bcm2836: add bcm2836 SoC device bcm2836_control: add bcm2836 ARM control logic bcm2835_peripherals: add rollup device for bcm2835 peripherals bcm2835_ic: add bcm2835 interrupt controller bcm2835_property: add bcm2835 property channel bcm2835_mbox: add BCM2835 mailboxes target-arm: Don't report presence of EL2 if it doesn't exist libvixl: Avoid std::abs() of 64-bit type arm: virt-acpi: each MADT.GICC entry as enabled unconditionally target-arm: Implement the S2 MMU inputsize > pamax check target-arm: Rename check_s2_startlevel to check_s2_mmu_setup target-arm: Apply S2 MMU startlevel table size check to AArch64 hw/arm: Setup EL1 and EL2 in AArch64 mode for 64bit Linux boots target-arm: Make various system registers visible to EL3 virt-acpi-build: add always-on property for timer Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
		
						commit
						071aacc9c9
					
				@ -79,6 +79,7 @@ CONFIG_TUSB6010=y
 | 
				
			|||||||
CONFIG_IMX=y
 | 
					CONFIG_IMX=y
 | 
				
			||||||
CONFIG_MAINSTONE=y
 | 
					CONFIG_MAINSTONE=y
 | 
				
			||||||
CONFIG_NSERIES=y
 | 
					CONFIG_NSERIES=y
 | 
				
			||||||
 | 
					CONFIG_RASPI=y
 | 
				
			||||||
CONFIG_REALVIEW=y
 | 
					CONFIG_REALVIEW=y
 | 
				
			||||||
CONFIG_ZAURUS=y
 | 
					CONFIG_ZAURUS=y
 | 
				
			||||||
CONFIG_ZYNQ=y
 | 
					CONFIG_ZYNQ=y
 | 
				
			||||||
 | 
				
			|||||||
@ -2688,8 +2688,12 @@ void Disassembler::AppendRegisterNameToOutput(const Instruction* instr,
 | 
				
			|||||||
void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr,
 | 
					void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr,
 | 
				
			||||||
                                                  int64_t offset) {
 | 
					                                                  int64_t offset) {
 | 
				
			||||||
  USE(instr);
 | 
					  USE(instr);
 | 
				
			||||||
 | 
					  uint64_t abs_offset = offset;
 | 
				
			||||||
  char sign = (offset < 0) ? '-' : '+';
 | 
					  char sign = (offset < 0) ? '-' : '+';
 | 
				
			||||||
  AppendToOutput("#%c0x%" PRIx64, sign, std::abs(offset));
 | 
					  if (offset < 0) {
 | 
				
			||||||
 | 
					    abs_offset = -abs_offset;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  AppendToOutput("#%c0x%" PRIx64, sign, abs_offset);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@ obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
 | 
				
			|||||||
obj-$(CONFIG_DIGIC) += digic.o
 | 
					obj-$(CONFIG_DIGIC) += digic.o
 | 
				
			||||||
obj-y += omap1.o omap2.o strongarm.o
 | 
					obj-y += omap1.o omap2.o strongarm.o
 | 
				
			||||||
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
 | 
					obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
 | 
				
			||||||
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
 | 
					obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
 | 
				
			||||||
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
 | 
					obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
 | 
				
			||||||
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
 | 
					obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										204
									
								
								hw/arm/bcm2835_peripherals.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								hw/arm/bcm2835_peripherals.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,204 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
 | 
				
			||||||
 | 
					 * Written by Andrew Baumann
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/arm/bcm2835_peripherals.h"
 | 
				
			||||||
 | 
					#include "hw/misc/bcm2835_mbox_defs.h"
 | 
				
			||||||
 | 
					#include "hw/arm/raspi_platform.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Peripheral base address on the VC (GPU) system bus */
 | 
				
			||||||
 | 
					#define BCM2835_VC_PERI_BASE 0x7e000000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */
 | 
				
			||||||
 | 
					#define BCM2835_SDHC_CAPAREG 0x52034b4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_peripherals_init(Object *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Memory region for peripheral devices, which we export to our parent */
 | 
				
			||||||
 | 
					    memory_region_init(&s->peri_mr, obj,"bcm2835-peripherals", 0x1000000);
 | 
				
			||||||
 | 
					    object_property_add_child(obj, "peripheral-io", OBJECT(&s->peri_mr), NULL);
 | 
				
			||||||
 | 
					    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_mr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Internal memory region for peripheral bus addresses (not exported) */
 | 
				
			||||||
 | 
					    memory_region_init(&s->gpu_bus_mr, obj, "bcm2835-gpu", (uint64_t)1 << 32);
 | 
				
			||||||
 | 
					    object_property_add_child(obj, "gpu-bus", OBJECT(&s->gpu_bus_mr), NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Internal memory region for request/response communication with
 | 
				
			||||||
 | 
					     * mailbox-addressable peripherals (not exported)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    memory_region_init(&s->mbox_mr, obj, "bcm2835-mbox",
 | 
				
			||||||
 | 
					                       MBOX_CHAN_COUNT << MBOX_AS_CHAN_SHIFT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Interrupt Controller */
 | 
				
			||||||
 | 
					    object_initialize(&s->ic, sizeof(s->ic), TYPE_BCM2835_IC);
 | 
				
			||||||
 | 
					    object_property_add_child(obj, "ic", OBJECT(&s->ic), NULL);
 | 
				
			||||||
 | 
					    qdev_set_parent_bus(DEVICE(&s->ic), sysbus_get_default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* UART0 */
 | 
				
			||||||
 | 
					    s->uart0 = SYS_BUS_DEVICE(object_new("pl011"));
 | 
				
			||||||
 | 
					    object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL);
 | 
				
			||||||
 | 
					    qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Mailboxes */
 | 
				
			||||||
 | 
					    object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX);
 | 
				
			||||||
 | 
					    object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL);
 | 
				
			||||||
 | 
					    qdev_set_parent_bus(DEVICE(&s->mboxes), sysbus_get_default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr",
 | 
				
			||||||
 | 
					                                   OBJECT(&s->mbox_mr), &error_abort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Property channel */
 | 
				
			||||||
 | 
					    object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY);
 | 
				
			||||||
 | 
					    object_property_add_child(obj, "property", OBJECT(&s->property), NULL);
 | 
				
			||||||
 | 
					    qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_property_add_const_link(OBJECT(&s->property), "dma-mr",
 | 
				
			||||||
 | 
					                                   OBJECT(&s->gpu_bus_mr), &error_abort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Extended Mass Media Controller */
 | 
				
			||||||
 | 
					    object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
 | 
				
			||||||
 | 
					    object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
 | 
				
			||||||
 | 
					    qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835PeripheralState *s = BCM2835_PERIPHERALS(dev);
 | 
				
			||||||
 | 
					    Object *obj;
 | 
				
			||||||
 | 
					    MemoryRegion *ram;
 | 
				
			||||||
 | 
					    Error *err = NULL;
 | 
				
			||||||
 | 
					    uint32_t ram_size;
 | 
				
			||||||
 | 
					    int n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    obj = object_property_get_link(OBJECT(dev), "ram", &err);
 | 
				
			||||||
 | 
					    if (obj == NULL) {
 | 
				
			||||||
 | 
					        error_setg(errp, "%s: required ram link not found: %s",
 | 
				
			||||||
 | 
					                   __func__, error_get_pretty(err));
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ram = MEMORY_REGION(obj);
 | 
				
			||||||
 | 
					    ram_size = memory_region_size(ram);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Map peripherals and RAM into the GPU address space. */
 | 
				
			||||||
 | 
					    memory_region_init_alias(&s->peri_mr_alias, OBJECT(s),
 | 
				
			||||||
 | 
					                             "bcm2835-peripherals", &s->peri_mr, 0,
 | 
				
			||||||
 | 
					                             memory_region_size(&s->peri_mr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_add_subregion_overlap(&s->gpu_bus_mr, BCM2835_VC_PERI_BASE,
 | 
				
			||||||
 | 
					                                        &s->peri_mr_alias, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* RAM is aliased four times (different cache configurations) on the GPU */
 | 
				
			||||||
 | 
					    for (n = 0; n < 4; n++) {
 | 
				
			||||||
 | 
					        memory_region_init_alias(&s->ram_alias[n], OBJECT(s),
 | 
				
			||||||
 | 
					                                 "bcm2835-gpu-ram-alias[*]", ram, 0, ram_size);
 | 
				
			||||||
 | 
					        memory_region_add_subregion_overlap(&s->gpu_bus_mr, (hwaddr)n << 30,
 | 
				
			||||||
 | 
					                                            &s->ram_alias[n], 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Interrupt Controller */
 | 
				
			||||||
 | 
					    object_property_set_bool(OBJECT(&s->ic), true, "realized", &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET,
 | 
				
			||||||
 | 
					                sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0));
 | 
				
			||||||
 | 
					    sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* UART0 */
 | 
				
			||||||
 | 
					    object_property_set_bool(OBJECT(s->uart0), true, "realized", &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_add_subregion(&s->peri_mr, UART0_OFFSET,
 | 
				
			||||||
 | 
					                                sysbus_mmio_get_region(s->uart0, 0));
 | 
				
			||||||
 | 
					    sysbus_connect_irq(s->uart0, 0,
 | 
				
			||||||
 | 
					        qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
 | 
				
			||||||
 | 
					                               INTERRUPT_UART));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Mailboxes */
 | 
				
			||||||
 | 
					    object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_add_subregion(&s->peri_mr, ARMCTRL_0_SBM_OFFSET,
 | 
				
			||||||
 | 
					                sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mboxes), 0));
 | 
				
			||||||
 | 
					    sysbus_connect_irq(SYS_BUS_DEVICE(&s->mboxes), 0,
 | 
				
			||||||
 | 
					        qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
 | 
				
			||||||
 | 
					                               INTERRUPT_ARM_MAILBOX));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Property channel */
 | 
				
			||||||
 | 
					    object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_property_set_bool(OBJECT(&s->property), true, "realized", &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_add_subregion(&s->mbox_mr,
 | 
				
			||||||
 | 
					                MBOX_CHAN_PROPERTY << MBOX_AS_CHAN_SHIFT,
 | 
				
			||||||
 | 
					                sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->property), 0));
 | 
				
			||||||
 | 
					    sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0,
 | 
				
			||||||
 | 
					                      qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Extended Mass Media Controller */
 | 
				
			||||||
 | 
					    object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
 | 
				
			||||||
 | 
					                            &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_add_subregion(&s->peri_mr, EMMC_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));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceClass *dc = DEVICE_CLASS(oc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 void bcm2835_peripherals_register_types(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type_register_static(&bcm2835_peripherals_type_info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type_init(bcm2835_peripherals_register_types)
 | 
				
			||||||
							
								
								
									
										165
									
								
								hw/arm/bcm2836.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								hw/arm/bcm2836.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,165 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
 | 
				
			||||||
 | 
					 * Written by Andrew Baumann
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/arm/bcm2836.h"
 | 
				
			||||||
 | 
					#include "hw/arm/raspi_platform.h"
 | 
				
			||||||
 | 
					#include "hw/sysbus.h"
 | 
				
			||||||
 | 
					#include "exec/address-spaces.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Peripheral base address seen by the CPU */
 | 
				
			||||||
 | 
					#define BCM2836_PERI_BASE       0x3F000000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* "QA7" (Pi2) interrupt controller and mailboxes etc. */
 | 
				
			||||||
 | 
					#define BCM2836_CONTROL_BASE    0x40000000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_init(Object *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2836State *s = BCM2836(obj);
 | 
				
			||||||
 | 
					    int n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (n = 0; n < BCM2836_NCPUS; n++) {
 | 
				
			||||||
 | 
					        object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
 | 
				
			||||||
 | 
					                          "cortex-a15-" TYPE_ARM_CPU);
 | 
				
			||||||
 | 
					        object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
 | 
				
			||||||
 | 
					                                  &error_abort);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_initialize(&s->control, sizeof(s->control), TYPE_BCM2836_CONTROL);
 | 
				
			||||||
 | 
					    object_property_add_child(obj, "control", OBJECT(&s->control), NULL);
 | 
				
			||||||
 | 
					    qdev_set_parent_bus(DEVICE(&s->control), sysbus_get_default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_initialize(&s->peripherals, sizeof(s->peripherals),
 | 
				
			||||||
 | 
					                      TYPE_BCM2835_PERIPHERALS);
 | 
				
			||||||
 | 
					    object_property_add_child(obj, "peripherals", OBJECT(&s->peripherals),
 | 
				
			||||||
 | 
					                              &error_abort);
 | 
				
			||||||
 | 
					    qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_realize(DeviceState *dev, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2836State *s = BCM2836(dev);
 | 
				
			||||||
 | 
					    Object *obj;
 | 
				
			||||||
 | 
					    Error *err = NULL;
 | 
				
			||||||
 | 
					    int n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* common peripherals from bcm2835 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    obj = object_property_get_link(OBJECT(dev), "ram", &err);
 | 
				
			||||||
 | 
					    if (obj == NULL) {
 | 
				
			||||||
 | 
					        error_setg(errp, "%s: required ram link not found: %s",
 | 
				
			||||||
 | 
					                   __func__, error_get_pretty(err));
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_property_add_const_link(OBJECT(&s->peripherals), "ram", obj, &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_property_set_bool(OBJECT(&s->peripherals), true, "realized", &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0,
 | 
				
			||||||
 | 
					                            BCM2836_PERI_BASE, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* bcm2836 interrupt controller (and mailboxes, etc.) */
 | 
				
			||||||
 | 
					    object_property_set_bool(OBJECT(&s->control), true, "realized", &err);
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					        error_propagate(errp, err);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, BCM2836_CONTROL_BASE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0,
 | 
				
			||||||
 | 
					        qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-irq", 0));
 | 
				
			||||||
 | 
					    sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1,
 | 
				
			||||||
 | 
					        qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (n = 0; n < BCM2836_NCPUS; n++) {
 | 
				
			||||||
 | 
					        /* Mirror bcm2836, which has clusterid set to 0xf
 | 
				
			||||||
 | 
					         * TODO: this should be converted to a property of ARM_CPU
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        s->cpus[n].mp_affinity = 0xF00 | n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* set periphbase/CBAR value for CPU-local registers */
 | 
				
			||||||
 | 
					        object_property_set_int(OBJECT(&s->cpus[n]),
 | 
				
			||||||
 | 
					                                BCM2836_PERI_BASE + MCORE_OFFSET,
 | 
				
			||||||
 | 
					                                "reset-cbar", &err);
 | 
				
			||||||
 | 
					        if (err) {
 | 
				
			||||||
 | 
					            error_propagate(errp, err);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* start powered off if not enabled */
 | 
				
			||||||
 | 
					        object_property_set_bool(OBJECT(&s->cpus[n]), n >= s->enabled_cpus,
 | 
				
			||||||
 | 
					                                 "start-powered-off", &err);
 | 
				
			||||||
 | 
					        if (err) {
 | 
				
			||||||
 | 
					            error_propagate(errp, err);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        object_property_set_bool(OBJECT(&s->cpus[n]), true, "realized", &err);
 | 
				
			||||||
 | 
					        if (err) {
 | 
				
			||||||
 | 
					            error_propagate(errp, err);
 | 
				
			||||||
 | 
					            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->cpus[n]), ARM_CPU_IRQ));
 | 
				
			||||||
 | 
					        qdev_connect_gpio_out_named(DEVICE(&s->control), "fiq", n,
 | 
				
			||||||
 | 
					                qdev_get_gpio_in(DEVICE(&s->cpus[n]), ARM_CPU_FIQ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Connect timers from the CPU to the interrupt controller */
 | 
				
			||||||
 | 
					        qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_PHYS,
 | 
				
			||||||
 | 
					                qdev_get_gpio_in_named(DEVICE(&s->control), "cntpsirq", n));
 | 
				
			||||||
 | 
					        qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_VIRT,
 | 
				
			||||||
 | 
					                qdev_get_gpio_in_named(DEVICE(&s->control), "cntvirq", n));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static Property bcm2836_props[] = {
 | 
				
			||||||
 | 
					    DEFINE_PROP_UINT32("enabled-cpus", BCM2836State, enabled_cpus, BCM2836_NCPUS),
 | 
				
			||||||
 | 
					    DEFINE_PROP_END_OF_LIST()
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_class_init(ObjectClass *oc, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceClass *dc = DEVICE_CLASS(oc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dc->props = bcm2836_props;
 | 
				
			||||||
 | 
					    dc->realize = bcm2836_realize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * Reason: creates an ARM CPU, thus use after free(), see
 | 
				
			||||||
 | 
					     * arm_cpu_class_init()
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    dc->cannot_destroy_with_object_finalize_yet = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const TypeInfo bcm2836_type_info = {
 | 
				
			||||||
 | 
					    .name = TYPE_BCM2836,
 | 
				
			||||||
 | 
					    .parent = TYPE_SYS_BUS_DEVICE,
 | 
				
			||||||
 | 
					    .instance_size = sizeof(BCM2836State),
 | 
				
			||||||
 | 
					    .instance_init = bcm2836_init,
 | 
				
			||||||
 | 
					    .class_init = bcm2836_class_init,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_register_types(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type_register_static(&bcm2836_type_info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type_init(bcm2836_register_types)
 | 
				
			||||||
@ -178,6 +178,57 @@ static void default_write_secondary(ARMCPU *cpu,
 | 
				
			|||||||
                     smpboot, fixupcontext);
 | 
					                     smpboot, fixupcontext);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
 | 
				
			||||||
 | 
					                                            const struct arm_boot_info *info,
 | 
				
			||||||
 | 
					                                            hwaddr mvbar_addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int n;
 | 
				
			||||||
 | 
					    uint32_t mvbar_blob[] = {
 | 
				
			||||||
 | 
					        /* mvbar_addr: secure monitor vectors
 | 
				
			||||||
 | 
					         * Default unimplemented and unused vectors to spin. Makes it
 | 
				
			||||||
 | 
					         * easier to debug (as opposed to the CPU running away).
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        0xeafffffe, /* (spin) */
 | 
				
			||||||
 | 
					        0xeafffffe, /* (spin) */
 | 
				
			||||||
 | 
					        0xe1b0f00e, /* movs pc, lr ;SMC exception return */
 | 
				
			||||||
 | 
					        0xeafffffe, /* (spin) */
 | 
				
			||||||
 | 
					        0xeafffffe, /* (spin) */
 | 
				
			||||||
 | 
					        0xeafffffe, /* (spin) */
 | 
				
			||||||
 | 
					        0xeafffffe, /* (spin) */
 | 
				
			||||||
 | 
					        0xeafffffe, /* (spin) */
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    uint32_t board_setup_blob[] = {
 | 
				
			||||||
 | 
					        /* board setup addr */
 | 
				
			||||||
 | 
					        0xe3a00e00 + (mvbar_addr >> 4), /* mov r0, #mvbar_addr */
 | 
				
			||||||
 | 
					        0xee0c0f30, /* mcr     p15, 0, r0, c12, c0, 1 ;set MVBAR */
 | 
				
			||||||
 | 
					        0xee110f11, /* mrc     p15, 0, r0, c1 , c1, 0 ;read SCR */
 | 
				
			||||||
 | 
					        0xe3800031, /* orr     r0, #0x31              ;enable AW, FW, NS */
 | 
				
			||||||
 | 
					        0xee010f11, /* mcr     p15, 0, r0, c1, c1, 0  ;write SCR */
 | 
				
			||||||
 | 
					        0xe1a0100e, /* mov     r1, lr                 ;save LR across SMC */
 | 
				
			||||||
 | 
					        0xe1600070, /* smc     #0                     ;call monitor to flush SCR */
 | 
				
			||||||
 | 
					        0xe1a0f001, /* mov     pc, r1                 ;return */
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* check that mvbar_addr is correctly aligned and relocatable (using MOV) */
 | 
				
			||||||
 | 
					    assert((mvbar_addr & 0x1f) == 0 && (mvbar_addr >> 4) < 0x100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* check that these blobs don't overlap */
 | 
				
			||||||
 | 
					    assert((mvbar_addr + sizeof(mvbar_blob) <= info->board_setup_addr)
 | 
				
			||||||
 | 
					          || (info->board_setup_addr + sizeof(board_setup_blob) <= mvbar_addr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (n = 0; n < ARRAY_SIZE(mvbar_blob); n++) {
 | 
				
			||||||
 | 
					        mvbar_blob[n] = tswap32(mvbar_blob[n]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rom_add_blob_fixed("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob),
 | 
				
			||||||
 | 
					                       mvbar_addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
 | 
				
			||||||
 | 
					        board_setup_blob[n] = tswap32(board_setup_blob[n]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rom_add_blob_fixed("board-setup", board_setup_blob,
 | 
				
			||||||
 | 
					                       sizeof(board_setup_blob), info->board_setup_addr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void default_reset_secondary(ARMCPU *cpu,
 | 
					static void default_reset_secondary(ARMCPU *cpu,
 | 
				
			||||||
                                    const struct arm_boot_info *info)
 | 
					                                    const struct arm_boot_info *info)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -488,7 +539,9 @@ static void do_cpu_reset(void *opaque)
 | 
				
			|||||||
                 * adjust.
 | 
					                 * adjust.
 | 
				
			||||||
                 */
 | 
					                 */
 | 
				
			||||||
                if (env->aarch64) {
 | 
					                if (env->aarch64) {
 | 
				
			||||||
 | 
					                    env->cp15.scr_el3 |= SCR_RW;
 | 
				
			||||||
                    if (arm_feature(env, ARM_FEATURE_EL2)) {
 | 
					                    if (arm_feature(env, ARM_FEATURE_EL2)) {
 | 
				
			||||||
 | 
					                        env->cp15.hcr_el2 |= HCR_RW;
 | 
				
			||||||
                        env->pstate = PSTATE_MODE_EL2h;
 | 
					                        env->pstate = PSTATE_MODE_EL2h;
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        env->pstate = PSTATE_MODE_EL1h;
 | 
					                        env->pstate = PSTATE_MODE_EL1h;
 | 
				
			||||||
 | 
				
			|||||||
@ -35,49 +35,16 @@
 | 
				
			|||||||
#define MPCORE_PERIPHBASE       0xfff10000
 | 
					#define MPCORE_PERIPHBASE       0xfff10000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MVBAR_ADDR              0x200
 | 
					#define MVBAR_ADDR              0x200
 | 
				
			||||||
 | 
					#define BOARD_SETUP_ADDR        (MVBAR_ADDR + 8 * sizeof(uint32_t))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define NIRQ_GIC                160
 | 
					#define NIRQ_GIC                160
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Board init.  */
 | 
					/* Board init.  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* MVBAR_ADDR is limited by precision of movw */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
QEMU_BUILD_BUG_ON(MVBAR_ADDR >= (1 << 16));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define ARMV7_IMM16(x) (extract32((x),  0, 12) | \
 | 
					 | 
				
			||||||
                        extract32((x), 12,  4) << 16)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void hb_write_board_setup(ARMCPU *cpu,
 | 
					static void hb_write_board_setup(ARMCPU *cpu,
 | 
				
			||||||
                                 const struct arm_boot_info *info)
 | 
					                                 const struct arm_boot_info *info)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int n;
 | 
					    arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
 | 
				
			||||||
    uint32_t board_setup_blob[] = {
 | 
					 | 
				
			||||||
        /* MVBAR_ADDR */
 | 
					 | 
				
			||||||
        /* Default unimplemented and unused vectors to spin. Makes it
 | 
					 | 
				
			||||||
         * easier to debug (as opposed to the CPU running away).
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        0xeafffffe, /* notused1: b notused */
 | 
					 | 
				
			||||||
        0xeafffffe, /* notused2: b notused */
 | 
					 | 
				
			||||||
        0xe1b0f00e, /* smc: movs pc, lr - exception return */
 | 
					 | 
				
			||||||
        0xeafffffe, /* prefetch_abort: b prefetch_abort */
 | 
					 | 
				
			||||||
        0xeafffffe, /* data_abort: b data_abort */
 | 
					 | 
				
			||||||
        0xeafffffe, /* notused3: b notused3 */
 | 
					 | 
				
			||||||
        0xeafffffe, /* irq: b irq */
 | 
					 | 
				
			||||||
        0xeafffffe, /* fiq: b fiq */
 | 
					 | 
				
			||||||
#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t))
 | 
					 | 
				
			||||||
        0xe3000000 + ARMV7_IMM16(MVBAR_ADDR), /* movw r0, MVBAR_ADDR */
 | 
					 | 
				
			||||||
        0xee0c0f30, /* mcr p15, 0, r0, c12, c0, 1 - set MVBAR */
 | 
					 | 
				
			||||||
        0xee110f11, /* mrc p15, 0, r0, c1 , c1, 0 - get SCR */
 | 
					 | 
				
			||||||
        0xe3810001, /* orr r0, #1 - set NS */
 | 
					 | 
				
			||||||
        0xee010f11, /* mcr p15, 0, r0, c1 , c1, 0 - set SCR */
 | 
					 | 
				
			||||||
        0xe1600070, /* smc - go to monitor mode to flush NS change */
 | 
					 | 
				
			||||||
        0xe12fff1e, /* bx lr - return to caller */
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
 | 
					 | 
				
			||||||
        board_setup_blob[n] = tswap32(board_setup_blob[n]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rom_add_blob_fixed("board-setup", board_setup_blob,
 | 
					 | 
				
			||||||
                       sizeof(board_setup_blob), MVBAR_ADDR);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
 | 
					static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										152
									
								
								hw/arm/raspi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								hw/arm/raspi.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,152 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Rasperry Pi 2 emulation Copyright (c) 2015, Microsoft
 | 
				
			||||||
 | 
					 * Written by Andrew Baumann
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/arm/bcm2836.h"
 | 
				
			||||||
 | 
					#include "qemu/error-report.h"
 | 
				
			||||||
 | 
					#include "hw/boards.h"
 | 
				
			||||||
 | 
					#include "hw/loader.h"
 | 
				
			||||||
 | 
					#include "hw/arm/arm.h"
 | 
				
			||||||
 | 
					#include "sysemu/sysemu.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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 */
 | 
				
			||||||
 | 
					#define FIRMWARE_ADDR   0x8000 /* Pi loads kernel.img here by default */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Table of Linux board IDs for different Pi versions */
 | 
				
			||||||
 | 
					static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct RasPiState {
 | 
				
			||||||
 | 
					    BCM2836State soc;
 | 
				
			||||||
 | 
					    MemoryRegion ram;
 | 
				
			||||||
 | 
					} RasPiState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    static const uint32_t smpboot[] = {
 | 
				
			||||||
 | 
					        0xe1a0e00f, /*    mov     lr, pc */
 | 
				
			||||||
 | 
					        0xe3a0fe00 + (BOARDSETUP_ADDR >> 4), /* mov pc, BOARDSETUP_ADDR */
 | 
				
			||||||
 | 
					        0xee100fb0, /*    mrc     p15, 0, r0, c0, c0, 5;get core ID */
 | 
				
			||||||
 | 
					        0xe7e10050, /*    ubfx    r0, r0, #0, #2       ;extract LSB */
 | 
				
			||||||
 | 
					        0xe59f5014, /*    ldr     r5, =0x400000CC      ;load mbox base */
 | 
				
			||||||
 | 
					        0xe320f001, /* 1: yield */
 | 
				
			||||||
 | 
					        0xe7953200, /*    ldr     r3, [r5, r0, lsl #4] ;read mbox for our core*/
 | 
				
			||||||
 | 
					        0xe3530000, /*    cmp     r3, #0               ;spin while zero */
 | 
				
			||||||
 | 
					        0x0afffffb, /*    beq     1b */
 | 
				
			||||||
 | 
					        0xe7853200, /*    str     r3, [r5, r0, lsl #4] ;clear mbox */
 | 
				
			||||||
 | 
					        0xe12fff13, /*    bx      r3                   ;jump to target */
 | 
				
			||||||
 | 
					        0x400000cc, /* (constant: mailbox 3 read/clear base) */
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* check that we don't overrun board setup vectors */
 | 
				
			||||||
 | 
					    QEMU_BUILD_BUG_ON(SMPBOOT_ADDR + sizeof(smpboot) > MVBAR_ADDR);
 | 
				
			||||||
 | 
					    /* check that board setup address is correctly relocated */
 | 
				
			||||||
 | 
					    QEMU_BUILD_BUG_ON((BOARDSETUP_ADDR & 0xf) != 0
 | 
				
			||||||
 | 
					                      || (BOARDSETUP_ADDR >> 4) >= 0x100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),
 | 
				
			||||||
 | 
					                       info->smp_loader_start);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    CPUState *cs = CPU(cpu);
 | 
				
			||||||
 | 
					    cpu_set_pc(cs, info->smp_loader_start);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void setup_boot(MachineState *machine, int version, size_t ram_size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    static struct arm_boot_info binfo;
 | 
				
			||||||
 | 
					    int r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    binfo.board_id = raspi_boardid[version];
 | 
				
			||||||
 | 
					    binfo.ram_size = ram_size;
 | 
				
			||||||
 | 
					    binfo.nb_cpus = smp_cpus;
 | 
				
			||||||
 | 
					    binfo.board_setup_addr = BOARDSETUP_ADDR;
 | 
				
			||||||
 | 
					    binfo.write_board_setup = write_board_setup;
 | 
				
			||||||
 | 
					    binfo.secure_board_setup = true;
 | 
				
			||||||
 | 
					    binfo.secure_boot = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Pi2 requires SMP setup */
 | 
				
			||||||
 | 
					    if (version == 2) {
 | 
				
			||||||
 | 
					        binfo.smp_loader_start = SMPBOOT_ADDR;
 | 
				
			||||||
 | 
					        binfo.write_secondary_boot = write_smpboot;
 | 
				
			||||||
 | 
					        binfo.secondary_cpu_reset_hook = reset_secondary;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* If the user specified a "firmware" image (e.g. UEFI), we bypass
 | 
				
			||||||
 | 
					     * the normal Linux boot process
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    if (machine->firmware) {
 | 
				
			||||||
 | 
					        /* load the firmware image (typically kernel.img) */
 | 
				
			||||||
 | 
					        r = load_image_targphys(machine->firmware, FIRMWARE_ADDR,
 | 
				
			||||||
 | 
					                                ram_size - FIRMWARE_ADDR);
 | 
				
			||||||
 | 
					        if (r < 0) {
 | 
				
			||||||
 | 
					            error_report("Failed to load firmware from %s", machine->firmware);
 | 
				
			||||||
 | 
					            exit(1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binfo.entry = FIRMWARE_ADDR;
 | 
				
			||||||
 | 
					        binfo.firmware_loaded = true;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        binfo.kernel_filename = machine->kernel_filename;
 | 
				
			||||||
 | 
					        binfo.kernel_cmdline = machine->kernel_cmdline;
 | 
				
			||||||
 | 
					        binfo.initrd_filename = machine->initrd_filename;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    arm_load_kernel(ARM_CPU(first_cpu), &binfo);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void raspi2_init(MachineState *machine)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    RasPiState *s = g_new0(RasPiState, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836);
 | 
				
			||||||
 | 
					    object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
 | 
				
			||||||
 | 
					                              &error_abort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Allocate and map RAM */
 | 
				
			||||||
 | 
					    memory_region_allocate_system_memory(&s->ram, OBJECT(machine), "ram",
 | 
				
			||||||
 | 
					                                         machine->ram_size);
 | 
				
			||||||
 | 
					    /* FIXME: Remove when we have custom CPU address space support */
 | 
				
			||||||
 | 
					    memory_region_add_subregion_overlap(get_system_memory(), 0, &s->ram, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Setup the SOC */
 | 
				
			||||||
 | 
					    object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram),
 | 
				
			||||||
 | 
					                                   &error_abort);
 | 
				
			||||||
 | 
					    object_property_set_int(OBJECT(&s->soc), smp_cpus, "enabled-cpus",
 | 
				
			||||||
 | 
					                            &error_abort);
 | 
				
			||||||
 | 
					    object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setup_boot(machine, 2, machine->ram_size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void raspi2_machine_init(MachineClass *mc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    mc->desc = "Raspberry Pi 2";
 | 
				
			||||||
 | 
					    mc->init = raspi2_init;
 | 
				
			||||||
 | 
					    mc->block_default_type = IF_SD;
 | 
				
			||||||
 | 
					    mc->no_parallel = 1;
 | 
				
			||||||
 | 
					    mc->no_floppy = 1;
 | 
				
			||||||
 | 
					    mc->no_cdrom = 1;
 | 
				
			||||||
 | 
					    mc->max_cpus = BCM2836_NCPUS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* XXX: Temporary restriction in RAM size from the full 1GB. Since
 | 
				
			||||||
 | 
					     * we do not yet support the framebuffer / GPU, we need to limit
 | 
				
			||||||
 | 
					     * RAM usable by the OS to sit below the peripherals.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					DEFINE_MACHINE("raspi2", raspi2_machine_init)
 | 
				
			||||||
@ -46,20 +46,6 @@
 | 
				
			|||||||
#define ARM_SPI_BASE 32
 | 
					#define ARM_SPI_BASE 32
 | 
				
			||||||
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
 | 
					#define ACPI_POWER_BUTTON_DEVICE "PWRB"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct VirtAcpiCpuInfo {
 | 
					 | 
				
			||||||
    DECLARE_BITMAP(found_cpus, VIRT_ACPI_CPU_ID_LIMIT);
 | 
					 | 
				
			||||||
} VirtAcpiCpuInfo;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void virt_acpi_get_cpu_info(VirtAcpiCpuInfo *cpuinfo)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    CPUState *cpu;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    memset(cpuinfo->found_cpus, 0, sizeof cpuinfo->found_cpus);
 | 
					 | 
				
			||||||
    CPU_FOREACH(cpu) {
 | 
					 | 
				
			||||||
        set_bit(cpu->cpu_index, cpuinfo->found_cpus);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
 | 
					static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    uint16_t i;
 | 
					    uint16_t i;
 | 
				
			||||||
@ -443,7 +429,7 @@ build_gtdt(GArray *table_data, GArray *linker)
 | 
				
			|||||||
    gtdt->secure_el1_flags = ACPI_EDGE_SENSITIVE;
 | 
					    gtdt->secure_el1_flags = ACPI_EDGE_SENSITIVE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    gtdt->non_secure_el1_interrupt = ARCH_TIMER_NS_EL1_IRQ + 16;
 | 
					    gtdt->non_secure_el1_interrupt = ARCH_TIMER_NS_EL1_IRQ + 16;
 | 
				
			||||||
    gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE;
 | 
					    gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE | ACPI_GTDT_ALWAYS_ON;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    gtdt->virtual_timer_interrupt = ARCH_TIMER_VIRT_IRQ + 16;
 | 
					    gtdt->virtual_timer_interrupt = ARCH_TIMER_VIRT_IRQ + 16;
 | 
				
			||||||
    gtdt->virtual_timer_flags = ACPI_EDGE_SENSITIVE;
 | 
					    gtdt->virtual_timer_flags = ACPI_EDGE_SENSITIVE;
 | 
				
			||||||
@ -458,8 +444,7 @@ build_gtdt(GArray *table_data, GArray *linker)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* MADT */
 | 
					/* MADT */
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
 | 
					build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
 | 
				
			||||||
           VirtAcpiCpuInfo *cpuinfo)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int madt_start = table_data->len;
 | 
					    int madt_start = table_data->len;
 | 
				
			||||||
    const MemMapEntry *memmap = guest_info->memmap;
 | 
					    const MemMapEntry *memmap = guest_info->memmap;
 | 
				
			||||||
@ -489,10 +474,8 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
 | 
				
			|||||||
        gicc->cpu_interface_number = i;
 | 
					        gicc->cpu_interface_number = i;
 | 
				
			||||||
        gicc->arm_mpidr = armcpu->mp_affinity;
 | 
					        gicc->arm_mpidr = armcpu->mp_affinity;
 | 
				
			||||||
        gicc->uid = i;
 | 
					        gicc->uid = i;
 | 
				
			||||||
        if (test_bit(i, cpuinfo->found_cpus)) {
 | 
					 | 
				
			||||||
        gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED);
 | 
					        gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (guest_info->gic_version == 3) {
 | 
					    if (guest_info->gic_version == 3) {
 | 
				
			||||||
        AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data,
 | 
					        AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data,
 | 
				
			||||||
@ -599,11 +582,8 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    GArray *table_offsets;
 | 
					    GArray *table_offsets;
 | 
				
			||||||
    unsigned dsdt, rsdt;
 | 
					    unsigned dsdt, rsdt;
 | 
				
			||||||
    VirtAcpiCpuInfo cpuinfo;
 | 
					 | 
				
			||||||
    GArray *tables_blob = tables->table_data;
 | 
					    GArray *tables_blob = tables->table_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    virt_acpi_get_cpu_info(&cpuinfo);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    table_offsets = g_array_new(false, true /* clear */,
 | 
					    table_offsets = g_array_new(false, true /* clear */,
 | 
				
			||||||
                                        sizeof(uint32_t));
 | 
					                                        sizeof(uint32_t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -630,7 +610,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
 | 
				
			|||||||
    build_fadt(tables_blob, tables->linker, dsdt);
 | 
					    build_fadt(tables_blob, tables->linker, dsdt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    acpi_add_table(table_offsets, tables_blob);
 | 
					    acpi_add_table(table_offsets, tables_blob);
 | 
				
			||||||
    build_madt(tables_blob, tables->linker, guest_info, &cpuinfo);
 | 
					    build_madt(tables_blob, tables->linker, guest_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    acpi_add_table(table_offsets, tables_blob);
 | 
					    acpi_add_table(table_offsets, tables_blob);
 | 
				
			||||||
    build_gtdt(tables_blob, tables->linker);
 | 
					    build_gtdt(tables_blob, tables->linker);
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@ obj-$(CONFIG_GRLIB) += grlib_irqmp.o
 | 
				
			|||||||
obj-$(CONFIG_IOAPIC) += ioapic.o
 | 
					obj-$(CONFIG_IOAPIC) += ioapic.o
 | 
				
			||||||
obj-$(CONFIG_OMAP) += omap_intc.o
 | 
					obj-$(CONFIG_OMAP) += omap_intc.o
 | 
				
			||||||
obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
 | 
					obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
 | 
				
			||||||
obj-$(CONFIG_SH4) += sh_intc.o
 | 
					obj-$(CONFIG_SH4) += sh_intc.o
 | 
				
			||||||
obj-$(CONFIG_XICS) += xics.o
 | 
					obj-$(CONFIG_XICS) += xics.o
 | 
				
			||||||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
 | 
					obj-$(CONFIG_XICS_KVM) += xics_kvm.o
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										236
									
								
								hw/intc/bcm2835_ic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								hw/intc/bcm2835_ic.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,236 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 * Heavily based on pl190.c, copyright terms below:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Arm PrimeCell PL190 Vector Interrupt Controller
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (c) 2006 CodeSourcery.
 | 
				
			||||||
 | 
					 * Written by Paul Brook
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This code is licensed under the GPL.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/intc/bcm2835_ic.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GPU_IRQS 64
 | 
				
			||||||
 | 
					#define ARM_IRQS 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IRQ_PENDING_BASIC       0x00 /* IRQ basic pending */
 | 
				
			||||||
 | 
					#define IRQ_PENDING_1           0x04 /* IRQ pending 1 */
 | 
				
			||||||
 | 
					#define IRQ_PENDING_2           0x08 /* IRQ pending 2 */
 | 
				
			||||||
 | 
					#define FIQ_CONTROL             0x0C /* FIQ register */
 | 
				
			||||||
 | 
					#define IRQ_ENABLE_1            0x10 /* Interrupt enable register 1 */
 | 
				
			||||||
 | 
					#define IRQ_ENABLE_2            0x14 /* Interrupt enable register 2 */
 | 
				
			||||||
 | 
					#define IRQ_ENABLE_BASIC        0x18 /* Base interrupt enable register */
 | 
				
			||||||
 | 
					#define IRQ_DISABLE_1           0x1C /* Interrupt disable register 1 */
 | 
				
			||||||
 | 
					#define IRQ_DISABLE_2           0x20 /* Interrupt disable register 2 */
 | 
				
			||||||
 | 
					#define IRQ_DISABLE_BASIC       0x24 /* Base interrupt disable register */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Update interrupts.  */
 | 
				
			||||||
 | 
					static void bcm2835_ic_update(BCM2835ICState *s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    bool set = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (s->fiq_enable) {
 | 
				
			||||||
 | 
					        if (s->fiq_select >= GPU_IRQS) {
 | 
				
			||||||
 | 
					            /* ARM IRQ */
 | 
				
			||||||
 | 
					            set = extract32(s->arm_irq_level, s->fiq_select - GPU_IRQS, 1);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            set = extract64(s->gpu_irq_level, s->fiq_select, 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    qemu_set_irq(s->fiq, set);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    set = (s->gpu_irq_level & s->gpu_irq_enable)
 | 
				
			||||||
 | 
					        || (s->arm_irq_level & s->arm_irq_enable);
 | 
				
			||||||
 | 
					    qemu_set_irq(s->irq, set);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_ic_set_gpu_irq(void *opaque, int irq, int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835ICState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(irq >= 0 && irq < 64);
 | 
				
			||||||
 | 
					    s->gpu_irq_level = deposit64(s->gpu_irq_level, irq, 1, level != 0);
 | 
				
			||||||
 | 
					    bcm2835_ic_update(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_ic_set_arm_irq(void *opaque, int irq, int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835ICState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(irq >= 0 && irq < 8);
 | 
				
			||||||
 | 
					    s->arm_irq_level = deposit32(s->arm_irq_level, irq, 1, level != 0);
 | 
				
			||||||
 | 
					    bcm2835_ic_update(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const int irq_dups[] = { 7, 9, 10, 18, 19, 53, 54, 55, 56, 57, 62 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint64_t bcm2835_ic_read(void *opaque, hwaddr offset, unsigned size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835ICState *s = opaque;
 | 
				
			||||||
 | 
					    uint32_t res = 0;
 | 
				
			||||||
 | 
					    uint64_t gpu_pending = s->gpu_irq_level & s->gpu_irq_enable;
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (offset) {
 | 
				
			||||||
 | 
					    case IRQ_PENDING_BASIC:
 | 
				
			||||||
 | 
					        /* bits 0-7: ARM irqs */
 | 
				
			||||||
 | 
					        res = s->arm_irq_level & s->arm_irq_enable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* bits 8 & 9: pending registers 1 & 2 */
 | 
				
			||||||
 | 
					        res |= (((uint32_t)gpu_pending) != 0) << 8;
 | 
				
			||||||
 | 
					        res |= ((gpu_pending >> 32) != 0) << 9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* bits 10-20: selected GPU IRQs */
 | 
				
			||||||
 | 
					        for (i = 0; i < ARRAY_SIZE(irq_dups); i++) {
 | 
				
			||||||
 | 
					            res |= extract64(gpu_pending, irq_dups[i], 1) << (i + 10);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_PENDING_1:
 | 
				
			||||||
 | 
					        res = gpu_pending;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_PENDING_2:
 | 
				
			||||||
 | 
					        res = gpu_pending >> 32;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case FIQ_CONTROL:
 | 
				
			||||||
 | 
					        res = (s->fiq_enable << 7) | s->fiq_select;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_ENABLE_1:
 | 
				
			||||||
 | 
					        res = s->gpu_irq_enable;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_ENABLE_2:
 | 
				
			||||||
 | 
					        res = s->gpu_irq_enable >> 32;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_ENABLE_BASIC:
 | 
				
			||||||
 | 
					        res = s->arm_irq_enable;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_DISABLE_1:
 | 
				
			||||||
 | 
					        res = ~s->gpu_irq_enable;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_DISABLE_2:
 | 
				
			||||||
 | 
					        res = ~s->gpu_irq_enable >> 32;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_DISABLE_BASIC:
 | 
				
			||||||
 | 
					        res = ~s->arm_irq_enable;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 | 
				
			||||||
 | 
					                      __func__, offset);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_ic_write(void *opaque, hwaddr offset, uint64_t val,
 | 
				
			||||||
 | 
					                             unsigned size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835ICState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (offset) {
 | 
				
			||||||
 | 
					    case FIQ_CONTROL:
 | 
				
			||||||
 | 
					        s->fiq_select = extract32(val, 0, 7);
 | 
				
			||||||
 | 
					        s->fiq_enable = extract32(val, 7, 1);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_ENABLE_1:
 | 
				
			||||||
 | 
					        s->gpu_irq_enable |= val;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_ENABLE_2:
 | 
				
			||||||
 | 
					        s->gpu_irq_enable |= val << 32;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_ENABLE_BASIC:
 | 
				
			||||||
 | 
					        s->arm_irq_enable |= val & 0xff;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_DISABLE_1:
 | 
				
			||||||
 | 
					        s->gpu_irq_enable &= ~val;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_DISABLE_2:
 | 
				
			||||||
 | 
					        s->gpu_irq_enable &= ~(val << 32);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case IRQ_DISABLE_BASIC:
 | 
				
			||||||
 | 
					        s->arm_irq_enable &= ~val & 0xff;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 | 
				
			||||||
 | 
					                      __func__, offset);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    bcm2835_ic_update(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const MemoryRegionOps bcm2835_ic_ops = {
 | 
				
			||||||
 | 
					    .read = bcm2835_ic_read,
 | 
				
			||||||
 | 
					    .write = bcm2835_ic_write,
 | 
				
			||||||
 | 
					    .endianness = DEVICE_NATIVE_ENDIAN,
 | 
				
			||||||
 | 
					    .valid.min_access_size = 4,
 | 
				
			||||||
 | 
					    .valid.max_access_size = 4,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_ic_reset(DeviceState *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835ICState *s = BCM2835_IC(d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->gpu_irq_enable = 0;
 | 
				
			||||||
 | 
					    s->arm_irq_enable = 0;
 | 
				
			||||||
 | 
					    s->fiq_enable = false;
 | 
				
			||||||
 | 
					    s->fiq_select = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_ic_init(Object *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835ICState *s = BCM2835_IC(obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_init_io(&s->iomem, obj, &bcm2835_ic_ops, s, TYPE_BCM2835_IC,
 | 
				
			||||||
 | 
					                          0x200);
 | 
				
			||||||
 | 
					    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_gpu_irq,
 | 
				
			||||||
 | 
					                            BCM2835_IC_GPU_IRQ, GPU_IRQS);
 | 
				
			||||||
 | 
					    qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_arm_irq,
 | 
				
			||||||
 | 
					                            BCM2835_IC_ARM_IRQ, ARM_IRQS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
 | 
				
			||||||
 | 
					    sysbus_init_irq(SYS_BUS_DEVICE(s), &s->fiq);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const VMStateDescription vmstate_bcm2835_ic = {
 | 
				
			||||||
 | 
					    .name = TYPE_BCM2835_IC,
 | 
				
			||||||
 | 
					    .version_id = 1,
 | 
				
			||||||
 | 
					    .minimum_version_id = 1,
 | 
				
			||||||
 | 
					    .fields = (VMStateField[]) {
 | 
				
			||||||
 | 
					        VMSTATE_UINT64(gpu_irq_level, BCM2835ICState),
 | 
				
			||||||
 | 
					        VMSTATE_UINT64(gpu_irq_enable, BCM2835ICState),
 | 
				
			||||||
 | 
					        VMSTATE_UINT8(arm_irq_level, BCM2835ICState),
 | 
				
			||||||
 | 
					        VMSTATE_UINT8(arm_irq_enable, BCM2835ICState),
 | 
				
			||||||
 | 
					        VMSTATE_BOOL(fiq_enable, BCM2835ICState),
 | 
				
			||||||
 | 
					        VMSTATE_UINT8(fiq_select, BCM2835ICState),
 | 
				
			||||||
 | 
					        VMSTATE_END_OF_LIST()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_ic_class_init(ObjectClass *klass, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dc->reset = bcm2835_ic_reset;
 | 
				
			||||||
 | 
					    dc->vmsd = &vmstate_bcm2835_ic;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static TypeInfo bcm2835_ic_info = {
 | 
				
			||||||
 | 
					    .name          = TYPE_BCM2835_IC,
 | 
				
			||||||
 | 
					    .parent        = TYPE_SYS_BUS_DEVICE,
 | 
				
			||||||
 | 
					    .instance_size = sizeof(BCM2835ICState),
 | 
				
			||||||
 | 
					    .class_init    = bcm2835_ic_class_init,
 | 
				
			||||||
 | 
					    .instance_init = bcm2835_ic_init,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_ic_register_types(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type_register_static(&bcm2835_ic_info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type_init(bcm2835_ic_register_types)
 | 
				
			||||||
							
								
								
									
										303
									
								
								hw/intc/bcm2836_control.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								hw/intc/bcm2836_control.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,303 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Rasperry Pi 2 emulation ARM control logic module.
 | 
				
			||||||
 | 
					 * Copyright (c) 2015, Microsoft
 | 
				
			||||||
 | 
					 * Written by Andrew Baumann
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * At present, only implements interrupt routing, and mailboxes (i.e.,
 | 
				
			||||||
 | 
					 * not local timer, PMU interrupt, or AXI counters).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Ref:
 | 
				
			||||||
 | 
					 * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/intc/bcm2836_control.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define REG_GPU_ROUTE           0x0c
 | 
				
			||||||
 | 
					#define REG_TIMERCONTROL        0x40
 | 
				
			||||||
 | 
					#define REG_MBOXCONTROL         0x50
 | 
				
			||||||
 | 
					#define REG_IRQSRC              0x60
 | 
				
			||||||
 | 
					#define REG_FIQSRC              0x70
 | 
				
			||||||
 | 
					#define REG_MBOX0_WR            0x80
 | 
				
			||||||
 | 
					#define REG_MBOX0_RDCLR         0xc0
 | 
				
			||||||
 | 
					#define REG_LIMIT              0x100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
 | 
				
			||||||
 | 
					#define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IRQ_CNTPSIRQ    0
 | 
				
			||||||
 | 
					#define IRQ_CNTPNSIRQ   1
 | 
				
			||||||
 | 
					#define IRQ_CNTHPIRQ    2
 | 
				
			||||||
 | 
					#define IRQ_CNTVIRQ     3
 | 
				
			||||||
 | 
					#define IRQ_MAILBOX0    4
 | 
				
			||||||
 | 
					#define IRQ_MAILBOX1    5
 | 
				
			||||||
 | 
					#define IRQ_MAILBOX2    6
 | 
				
			||||||
 | 
					#define IRQ_MAILBOX3    7
 | 
				
			||||||
 | 
					#define IRQ_GPU         8
 | 
				
			||||||
 | 
					#define IRQ_PMU         9
 | 
				
			||||||
 | 
					#define IRQ_AXI         10
 | 
				
			||||||
 | 
					#define IRQ_TIMER       11
 | 
				
			||||||
 | 
					#define IRQ_MAX         IRQ_TIMER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
 | 
				
			||||||
 | 
					                          uint32_t controlreg, uint8_t controlidx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (FIQ_BIT(controlreg, controlidx)) {
 | 
				
			||||||
 | 
					        /* deliver a FIQ */
 | 
				
			||||||
 | 
					        s->fiqsrc[core] |= (uint32_t)1 << irq;
 | 
				
			||||||
 | 
					    } else if (IRQ_BIT(controlreg, controlidx)) {
 | 
				
			||||||
 | 
					        /* deliver an IRQ */
 | 
				
			||||||
 | 
					        s->irqsrc[core] |= (uint32_t)1 << irq;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* the interrupt is masked */
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Update interrupts.  */
 | 
				
			||||||
 | 
					static void bcm2836_control_update(BCM2836ControlState *s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int i, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* reset pending IRQs/FIQs */
 | 
				
			||||||
 | 
					    for (i = 0; i < BCM2836_NCORES; i++) {
 | 
				
			||||||
 | 
					        s->irqsrc[i] = s->fiqsrc[i] = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* apply routing logic, update status regs */
 | 
				
			||||||
 | 
					    if (s->gpu_irq) {
 | 
				
			||||||
 | 
					        assert(s->route_gpu_irq < BCM2836_NCORES);
 | 
				
			||||||
 | 
					        s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (s->gpu_fiq) {
 | 
				
			||||||
 | 
					        assert(s->route_gpu_fiq < BCM2836_NCORES);
 | 
				
			||||||
 | 
					        s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (i = 0; i < BCM2836_NCORES; i++) {
 | 
				
			||||||
 | 
					        /* handle local timer interrupts for this core */
 | 
				
			||||||
 | 
					        if (s->timerirqs[i]) {
 | 
				
			||||||
 | 
					            assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */
 | 
				
			||||||
 | 
					            for (j = 0; j <= IRQ_CNTVIRQ; j++) {
 | 
				
			||||||
 | 
					                if ((s->timerirqs[i] & (1 << j)) != 0) {
 | 
				
			||||||
 | 
					                    /* local interrupt j is set */
 | 
				
			||||||
 | 
					                    deliver_local(s, i, j, s->timercontrol[i], j);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* handle mailboxes for this core */
 | 
				
			||||||
 | 
					        for (j = 0; j < BCM2836_MBPERCORE; j++) {
 | 
				
			||||||
 | 
					            if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) {
 | 
				
			||||||
 | 
					                /* mailbox j is set */
 | 
				
			||||||
 | 
					                deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* call set_irq appropriately for each output */
 | 
				
			||||||
 | 
					    for (i = 0; i < BCM2836_NCORES; i++) {
 | 
				
			||||||
 | 
					        qemu_set_irq(s->irq[i], s->irqsrc[i] != 0);
 | 
				
			||||||
 | 
					        qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq,
 | 
				
			||||||
 | 
					                                          int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2836ControlState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(core >= 0 && core < BCM2836_NCORES);
 | 
				
			||||||
 | 
					    assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bcm2836_control_update(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* XXX: the following wrapper functions are a kludgy workaround,
 | 
				
			||||||
 | 
					 * needed because I can't seem to pass useful information in the "irq"
 | 
				
			||||||
 | 
					 * parameter when using named interrupts. Feel free to clean this up!
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_set_local_irq0(void *opaque, int core, int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    bcm2836_control_set_local_irq(opaque, core, 0, level);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_set_local_irq1(void *opaque, int core, int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    bcm2836_control_set_local_irq(opaque, core, 1, level);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_set_local_irq2(void *opaque, int core, int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    bcm2836_control_set_local_irq(opaque, core, 2, level);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_set_local_irq3(void *opaque, int core, int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    bcm2836_control_set_local_irq(opaque, core, 3, level);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2836ControlState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->gpu_irq = level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bcm2836_control_update(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2836ControlState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->gpu_fiq = level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bcm2836_control_update(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2836ControlState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (offset == REG_GPU_ROUTE) {
 | 
				
			||||||
 | 
					        assert(s->route_gpu_fiq < BCM2836_NCORES
 | 
				
			||||||
 | 
					               && s->route_gpu_irq < BCM2836_NCORES);
 | 
				
			||||||
 | 
					        return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
 | 
				
			||||||
 | 
					    } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
 | 
				
			||||||
 | 
					        return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
 | 
				
			||||||
 | 
					    } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
 | 
				
			||||||
 | 
					        return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2];
 | 
				
			||||||
 | 
					    } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) {
 | 
				
			||||||
 | 
					        return s->irqsrc[(offset - REG_IRQSRC) >> 2];
 | 
				
			||||||
 | 
					    } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) {
 | 
				
			||||||
 | 
					        return s->fiqsrc[(offset - REG_FIQSRC) >> 2];
 | 
				
			||||||
 | 
					    } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
 | 
				
			||||||
 | 
					        return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2];
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 | 
				
			||||||
 | 
					                      __func__, offset);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_write(void *opaque, hwaddr offset,
 | 
				
			||||||
 | 
					                                  uint64_t val, unsigned size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2836ControlState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (offset == REG_GPU_ROUTE) {
 | 
				
			||||||
 | 
					        s->route_gpu_irq = val & 0x3;
 | 
				
			||||||
 | 
					        s->route_gpu_fiq = (val >> 2) & 0x3;
 | 
				
			||||||
 | 
					    } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
 | 
				
			||||||
 | 
					        s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
 | 
				
			||||||
 | 
					    } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
 | 
				
			||||||
 | 
					        s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff;
 | 
				
			||||||
 | 
					    } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) {
 | 
				
			||||||
 | 
					        s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val;
 | 
				
			||||||
 | 
					    } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
 | 
				
			||||||
 | 
					        s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 | 
				
			||||||
 | 
					                      __func__, offset);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bcm2836_control_update(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const MemoryRegionOps bcm2836_control_ops = {
 | 
				
			||||||
 | 
					    .read = bcm2836_control_read,
 | 
				
			||||||
 | 
					    .write = bcm2836_control_write,
 | 
				
			||||||
 | 
					    .endianness = DEVICE_NATIVE_ENDIAN,
 | 
				
			||||||
 | 
					    .valid.min_access_size = 4,
 | 
				
			||||||
 | 
					    .valid.max_access_size = 4,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_reset(DeviceState *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2836ControlState *s = BCM2836_CONTROL(d);
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->route_gpu_irq = s->route_gpu_fiq = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (i = 0; i < BCM2836_NCORES; i++) {
 | 
				
			||||||
 | 
					        s->timercontrol[i] = 0;
 | 
				
			||||||
 | 
					        s->mailboxcontrol[i] = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) {
 | 
				
			||||||
 | 
					        s->mailboxes[i] = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_init(Object *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2836ControlState *s = BCM2836_CONTROL(obj);
 | 
				
			||||||
 | 
					    DeviceState *dev = DEVICE(obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s,
 | 
				
			||||||
 | 
					                          TYPE_BCM2836_CONTROL, REG_LIMIT);
 | 
				
			||||||
 | 
					    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* inputs from each CPU core */
 | 
				
			||||||
 | 
					    qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq",
 | 
				
			||||||
 | 
					                            BCM2836_NCORES);
 | 
				
			||||||
 | 
					    qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq",
 | 
				
			||||||
 | 
					                            BCM2836_NCORES);
 | 
				
			||||||
 | 
					    qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq",
 | 
				
			||||||
 | 
					                            BCM2836_NCORES);
 | 
				
			||||||
 | 
					    qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq",
 | 
				
			||||||
 | 
					                            BCM2836_NCORES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* IRQ and FIQ inputs from upstream bcm2835 controller */
 | 
				
			||||||
 | 
					    qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1);
 | 
				
			||||||
 | 
					    qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* outputs to CPU cores */
 | 
				
			||||||
 | 
					    qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
 | 
				
			||||||
 | 
					    qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const VMStateDescription vmstate_bcm2836_control = {
 | 
				
			||||||
 | 
					    .name = TYPE_BCM2836_CONTROL,
 | 
				
			||||||
 | 
					    .version_id = 1,
 | 
				
			||||||
 | 
					    .minimum_version_id = 1,
 | 
				
			||||||
 | 
					    .fields = (VMStateField[]) {
 | 
				
			||||||
 | 
					        VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
 | 
				
			||||||
 | 
					                             BCM2836_NCORES * BCM2836_MBPERCORE),
 | 
				
			||||||
 | 
					        VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState),
 | 
				
			||||||
 | 
					        VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState),
 | 
				
			||||||
 | 
					        VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES),
 | 
				
			||||||
 | 
					        VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
 | 
				
			||||||
 | 
					                             BCM2836_NCORES),
 | 
				
			||||||
 | 
					        VMSTATE_END_OF_LIST()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_class_init(ObjectClass *klass, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dc->reset = bcm2836_control_reset;
 | 
				
			||||||
 | 
					    dc->vmsd = &vmstate_bcm2836_control;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static TypeInfo bcm2836_control_info = {
 | 
				
			||||||
 | 
					    .name          = TYPE_BCM2836_CONTROL,
 | 
				
			||||||
 | 
					    .parent        = TYPE_SYS_BUS_DEVICE,
 | 
				
			||||||
 | 
					    .instance_size = sizeof(BCM2836ControlState),
 | 
				
			||||||
 | 
					    .class_init    = bcm2836_control_class_init,
 | 
				
			||||||
 | 
					    .instance_init = bcm2836_control_init,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2836_control_register_types(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type_register_static(&bcm2836_control_info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type_init(bcm2836_control_register_types)
 | 
				
			||||||
@ -36,6 +36,8 @@ obj-$(CONFIG_OMAP) += omap_gpmc.o
 | 
				
			|||||||
obj-$(CONFIG_OMAP) += omap_l4.o
 | 
					obj-$(CONFIG_OMAP) += omap_l4.o
 | 
				
			||||||
obj-$(CONFIG_OMAP) += omap_sdrc.o
 | 
					obj-$(CONFIG_OMAP) += omap_sdrc.o
 | 
				
			||||||
obj-$(CONFIG_OMAP) += omap_tap.o
 | 
					obj-$(CONFIG_OMAP) += omap_tap.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_RASPI) += bcm2835_mbox.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_RASPI) += bcm2835_property.o
 | 
				
			||||||
obj-$(CONFIG_SLAVIO) += slavio_misc.o
 | 
					obj-$(CONFIG_SLAVIO) += slavio_misc.o
 | 
				
			||||||
obj-$(CONFIG_ZYNQ) += zynq_slcr.o
 | 
					obj-$(CONFIG_ZYNQ) += zynq_slcr.o
 | 
				
			||||||
obj-$(CONFIG_ZYNQ) += zynq-xadc.o
 | 
					obj-$(CONFIG_ZYNQ) += zynq-xadc.o
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										333
									
								
								hw/misc/bcm2835_mbox.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								hw/misc/bcm2835_mbox.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,333 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file models the system mailboxes, which are used for
 | 
				
			||||||
 | 
					 * communication with low-bandwidth GPU peripherals. Refs:
 | 
				
			||||||
 | 
					 *   https://github.com/raspberrypi/firmware/wiki/Mailboxes
 | 
				
			||||||
 | 
					 *   https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/misc/bcm2835_mbox.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAIL0_PEEK   0x90
 | 
				
			||||||
 | 
					#define MAIL0_SENDER 0x94
 | 
				
			||||||
 | 
					#define MAIL1_STATUS 0xb8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Mailbox status register */
 | 
				
			||||||
 | 
					#define MAIL0_STATUS 0x98
 | 
				
			||||||
 | 
					#define ARM_MS_FULL       0x80000000
 | 
				
			||||||
 | 
					#define ARM_MS_EMPTY      0x40000000
 | 
				
			||||||
 | 
					#define ARM_MS_LEVEL      0x400000FF /* Max. value depends on mailbox depth */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* MAILBOX config/status register */
 | 
				
			||||||
 | 
					#define MAIL0_CONFIG 0x9c
 | 
				
			||||||
 | 
					/* ANY write to this register clears the error bits! */
 | 
				
			||||||
 | 
					#define ARM_MC_IHAVEDATAIRQEN    0x00000001 /* mbox irq enable:  has data */
 | 
				
			||||||
 | 
					#define ARM_MC_IHAVESPACEIRQEN   0x00000002 /* mbox irq enable:  has space */
 | 
				
			||||||
 | 
					#define ARM_MC_OPPISEMPTYIRQEN   0x00000004 /* mbox irq enable: Opp is empty */
 | 
				
			||||||
 | 
					#define ARM_MC_MAIL_CLEAR        0x00000008 /* mbox clear write 1, then  0 */
 | 
				
			||||||
 | 
					#define ARM_MC_IHAVEDATAIRQPEND  0x00000010 /* mbox irq pending:  has space */
 | 
				
			||||||
 | 
					#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mbox irq pending: Opp is empty */
 | 
				
			||||||
 | 
					#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mbox irq pending */
 | 
				
			||||||
 | 
					/* Bit 7 is unused */
 | 
				
			||||||
 | 
					#define ARM_MC_ERRNOOWN   0x00000100 /* error : none owner read from mailbox */
 | 
				
			||||||
 | 
					#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */
 | 
				
			||||||
 | 
					#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mbox_update_status(BCM2835Mbox *mb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    mb->status &= ~(ARM_MS_EMPTY | ARM_MS_FULL);
 | 
				
			||||||
 | 
					    if (mb->count == 0) {
 | 
				
			||||||
 | 
					        mb->status |= ARM_MS_EMPTY;
 | 
				
			||||||
 | 
					    } else if (mb->count == MBOX_SIZE) {
 | 
				
			||||||
 | 
					        mb->status |= ARM_MS_FULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mbox_reset(BCM2835Mbox *mb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mb->count = 0;
 | 
				
			||||||
 | 
					    mb->config = 0;
 | 
				
			||||||
 | 
					    for (n = 0; n < MBOX_SIZE; n++) {
 | 
				
			||||||
 | 
					        mb->reg[n] = MBOX_INVALID_DATA;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    mbox_update_status(mb);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint32_t mbox_pull(BCM2835Mbox *mb, int index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int n;
 | 
				
			||||||
 | 
					    uint32_t val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(mb->count > 0);
 | 
				
			||||||
 | 
					    assert(index < mb->count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val = mb->reg[index];
 | 
				
			||||||
 | 
					    for (n = index + 1; n < mb->count; n++) {
 | 
				
			||||||
 | 
					        mb->reg[n - 1] = mb->reg[n];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    mb->count--;
 | 
				
			||||||
 | 
					    mb->reg[mb->count] = MBOX_INVALID_DATA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mbox_update_status(mb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mbox_push(BCM2835Mbox *mb, uint32_t val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(mb->count < MBOX_SIZE);
 | 
				
			||||||
 | 
					    mb->reg[mb->count++] = val;
 | 
				
			||||||
 | 
					    mbox_update_status(mb);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_mbox_update(BCM2835MboxState *s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    uint32_t value;
 | 
				
			||||||
 | 
					    bool set;
 | 
				
			||||||
 | 
					    int n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->mbox_irq_disabled = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Get pending responses and put them in the vc->arm mbox,
 | 
				
			||||||
 | 
					     * as long as it's not full
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    for (n = 0; n < MBOX_CHAN_COUNT; n++) {
 | 
				
			||||||
 | 
					        while (s->available[n] && !(s->mbox[0].status & ARM_MS_FULL)) {
 | 
				
			||||||
 | 
					            value = ldl_phys(&s->mbox_as, n << MBOX_AS_CHAN_SHIFT);
 | 
				
			||||||
 | 
					            assert(value != MBOX_INVALID_DATA); /* Pending interrupt but no data */
 | 
				
			||||||
 | 
					            mbox_push(&s->mbox[0], value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* TODO (?): Try to push pending requests from the arm->vc mbox */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Re-enable calls from the IRQ routine */
 | 
				
			||||||
 | 
					    s->mbox_irq_disabled = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Update ARM IRQ status */
 | 
				
			||||||
 | 
					    set = false;
 | 
				
			||||||
 | 
					    s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQPEND;
 | 
				
			||||||
 | 
					    if (!(s->mbox[0].status & ARM_MS_EMPTY)) {
 | 
				
			||||||
 | 
					        s->mbox[0].config |= ARM_MC_IHAVEDATAIRQPEND;
 | 
				
			||||||
 | 
					        if (s->mbox[0].config & ARM_MC_IHAVEDATAIRQEN) {
 | 
				
			||||||
 | 
					            set = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    qemu_set_irq(s->arm_irq, set);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_mbox_set_irq(void *opaque, int irq, int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835MboxState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->available[irq] = level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* avoid recursively calling bcm2835_mbox_update when the interrupt
 | 
				
			||||||
 | 
					     * status changes due to the ldl_phys call within that function
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    if (!s->mbox_irq_disabled) {
 | 
				
			||||||
 | 
					        bcm2835_mbox_update(s);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint64_t bcm2835_mbox_read(void *opaque, hwaddr offset, unsigned size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835MboxState *s = opaque;
 | 
				
			||||||
 | 
					    uint32_t res = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    offset &= 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (offset) {
 | 
				
			||||||
 | 
					    case 0x80 ... 0x8c: /* MAIL0_READ */
 | 
				
			||||||
 | 
					        if (s->mbox[0].status & ARM_MS_EMPTY) {
 | 
				
			||||||
 | 
					            res = MBOX_INVALID_DATA;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            res = mbox_pull(&s->mbox[0], 0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case MAIL0_PEEK:
 | 
				
			||||||
 | 
					        res = s->mbox[0].reg[0];
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case MAIL0_SENDER:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case MAIL0_STATUS:
 | 
				
			||||||
 | 
					        res = s->mbox[0].status;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case MAIL0_CONFIG:
 | 
				
			||||||
 | 
					        res = s->mbox[0].config;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case MAIL1_STATUS:
 | 
				
			||||||
 | 
					        res = s->mbox[1].status;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 | 
				
			||||||
 | 
					                      __func__, offset);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bcm2835_mbox_update(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_mbox_write(void *opaque, hwaddr offset,
 | 
				
			||||||
 | 
					                               uint64_t value, unsigned size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835MboxState *s = opaque;
 | 
				
			||||||
 | 
					    hwaddr childaddr;
 | 
				
			||||||
 | 
					    uint8_t ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    offset &= 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (offset) {
 | 
				
			||||||
 | 
					    case MAIL0_SENDER:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case MAIL0_CONFIG:
 | 
				
			||||||
 | 
					        s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQEN;
 | 
				
			||||||
 | 
					        s->mbox[0].config |= value & ARM_MC_IHAVEDATAIRQEN;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 0xa0 ... 0xac: /* MAIL1_WRITE */
 | 
				
			||||||
 | 
					        if (s->mbox[1].status & ARM_MS_FULL) {
 | 
				
			||||||
 | 
					            /* Mailbox full */
 | 
				
			||||||
 | 
					            qemu_log_mask(LOG_GUEST_ERROR, "%s: mailbox full\n", __func__);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            ch = value & 0xf;
 | 
				
			||||||
 | 
					            if (ch < MBOX_CHAN_COUNT) {
 | 
				
			||||||
 | 
					                childaddr = ch << MBOX_AS_CHAN_SHIFT;
 | 
				
			||||||
 | 
					                if (ldl_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) {
 | 
				
			||||||
 | 
					                    /* Child busy, push delayed. Push it in the arm->vc mbox */
 | 
				
			||||||
 | 
					                    mbox_push(&s->mbox[1], value);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    /* Push it directly to the child device */
 | 
				
			||||||
 | 
					                    stl_phys(&s->mbox_as, childaddr, value);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                /* Invalid channel number */
 | 
				
			||||||
 | 
					                qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid channel %u\n",
 | 
				
			||||||
 | 
					                              __func__, ch);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 | 
				
			||||||
 | 
					                      __func__, offset);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bcm2835_mbox_update(s);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const MemoryRegionOps bcm2835_mbox_ops = {
 | 
				
			||||||
 | 
					    .read = bcm2835_mbox_read,
 | 
				
			||||||
 | 
					    .write = bcm2835_mbox_write,
 | 
				
			||||||
 | 
					    .endianness = DEVICE_NATIVE_ENDIAN,
 | 
				
			||||||
 | 
					    .valid.min_access_size = 4,
 | 
				
			||||||
 | 
					    .valid.max_access_size = 4,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* vmstate of a single mailbox */
 | 
				
			||||||
 | 
					static const VMStateDescription vmstate_bcm2835_mbox_box = {
 | 
				
			||||||
 | 
					    .name = TYPE_BCM2835_MBOX "_box",
 | 
				
			||||||
 | 
					    .version_id = 1,
 | 
				
			||||||
 | 
					    .minimum_version_id = 1,
 | 
				
			||||||
 | 
					    .fields = (VMStateField[]) {
 | 
				
			||||||
 | 
					        VMSTATE_UINT32_ARRAY(reg, BCM2835Mbox, MBOX_SIZE),
 | 
				
			||||||
 | 
					        VMSTATE_UINT32(count, BCM2835Mbox),
 | 
				
			||||||
 | 
					        VMSTATE_UINT32(status, BCM2835Mbox),
 | 
				
			||||||
 | 
					        VMSTATE_UINT32(config, BCM2835Mbox),
 | 
				
			||||||
 | 
					        VMSTATE_END_OF_LIST()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* vmstate of the entire device */
 | 
				
			||||||
 | 
					static const VMStateDescription vmstate_bcm2835_mbox = {
 | 
				
			||||||
 | 
					    .name = TYPE_BCM2835_MBOX,
 | 
				
			||||||
 | 
					    .version_id = 1,
 | 
				
			||||||
 | 
					    .minimum_version_id = 1,
 | 
				
			||||||
 | 
					    .minimum_version_id_old = 1,
 | 
				
			||||||
 | 
					    .fields      = (VMStateField[]) {
 | 
				
			||||||
 | 
					        VMSTATE_BOOL_ARRAY(available, BCM2835MboxState, MBOX_CHAN_COUNT),
 | 
				
			||||||
 | 
					        VMSTATE_STRUCT_ARRAY(mbox, BCM2835MboxState, 2, 1,
 | 
				
			||||||
 | 
					                             vmstate_bcm2835_mbox_box, BCM2835Mbox),
 | 
				
			||||||
 | 
					        VMSTATE_END_OF_LIST()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_mbox_init(Object *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835MboxState *s = BCM2835_MBOX(obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_init_io(&s->iomem, obj, &bcm2835_mbox_ops, s,
 | 
				
			||||||
 | 
					                          TYPE_BCM2835_MBOX, 0x400);
 | 
				
			||||||
 | 
					    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
 | 
				
			||||||
 | 
					    sysbus_init_irq(SYS_BUS_DEVICE(s), &s->arm_irq);
 | 
				
			||||||
 | 
					    qdev_init_gpio_in(DEVICE(s), bcm2835_mbox_set_irq, MBOX_CHAN_COUNT);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_mbox_reset(DeviceState *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835MboxState *s = BCM2835_MBOX(dev);
 | 
				
			||||||
 | 
					    int n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mbox_reset(&s->mbox[0]);
 | 
				
			||||||
 | 
					    mbox_reset(&s->mbox[1]);
 | 
				
			||||||
 | 
					    s->mbox_irq_disabled = false;
 | 
				
			||||||
 | 
					    for (n = 0; n < MBOX_CHAN_COUNT; n++) {
 | 
				
			||||||
 | 
					        s->available[n] = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_mbox_realize(DeviceState *dev, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835MboxState *s = BCM2835_MBOX(dev);
 | 
				
			||||||
 | 
					    Object *obj;
 | 
				
			||||||
 | 
					    Error *err = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    obj = object_property_get_link(OBJECT(dev), "mbox-mr", &err);
 | 
				
			||||||
 | 
					    if (obj == NULL) {
 | 
				
			||||||
 | 
					        error_setg(errp, "%s: required mbox-mr link not found: %s",
 | 
				
			||||||
 | 
					                   __func__, error_get_pretty(err));
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->mbox_mr = MEMORY_REGION(obj);
 | 
				
			||||||
 | 
					    address_space_init(&s->mbox_as, s->mbox_mr, NULL);
 | 
				
			||||||
 | 
					    bcm2835_mbox_reset(dev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_mbox_class_init(ObjectClass *klass, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dc->realize = bcm2835_mbox_realize;
 | 
				
			||||||
 | 
					    dc->reset = bcm2835_mbox_reset;
 | 
				
			||||||
 | 
					    dc->vmsd = &vmstate_bcm2835_mbox;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static TypeInfo bcm2835_mbox_info = {
 | 
				
			||||||
 | 
					    .name          = TYPE_BCM2835_MBOX,
 | 
				
			||||||
 | 
					    .parent        = TYPE_SYS_BUS_DEVICE,
 | 
				
			||||||
 | 
					    .instance_size = sizeof(BCM2835MboxState),
 | 
				
			||||||
 | 
					    .class_init    = bcm2835_mbox_class_init,
 | 
				
			||||||
 | 
					    .instance_init = bcm2835_mbox_init,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_mbox_register_types(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type_register_static(&bcm2835_mbox_info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type_init(bcm2835_mbox_register_types)
 | 
				
			||||||
							
								
								
									
										287
									
								
								hw/misc/bcm2835_property.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								hw/misc/bcm2835_property.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,287 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/misc/bcm2835_property.h"
 | 
				
			||||||
 | 
					#include "hw/misc/bcm2835_mbox_defs.h"
 | 
				
			||||||
 | 
					#include "sysemu/dma.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    uint32_t tag;
 | 
				
			||||||
 | 
					    uint32_t bufsize;
 | 
				
			||||||
 | 
					    uint32_t tot_len;
 | 
				
			||||||
 | 
					    size_t resplen;
 | 
				
			||||||
 | 
					    uint32_t tmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    value &= ~0xf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->addr = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tot_len = ldl_phys(&s->dma_as, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* @(addr + 4) : Buffer response code */
 | 
				
			||||||
 | 
					    value = s->addr + 8;
 | 
				
			||||||
 | 
					    while (value + 8 <= s->addr + tot_len) {
 | 
				
			||||||
 | 
					        tag = ldl_phys(&s->dma_as, value);
 | 
				
			||||||
 | 
					        bufsize = ldl_phys(&s->dma_as, value + 4);
 | 
				
			||||||
 | 
					        /* @(value + 8) : Request/response indicator */
 | 
				
			||||||
 | 
					        resplen = 0;
 | 
				
			||||||
 | 
					        switch (tag) {
 | 
				
			||||||
 | 
					        case 0x00000000: /* End tag */
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 0x00000001: /* Get firmware revision */
 | 
				
			||||||
 | 
					            stl_phys(&s->dma_as, value + 12, 346337);
 | 
				
			||||||
 | 
					            resplen = 4;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 0x00010001: /* Get board model */
 | 
				
			||||||
 | 
					            qemu_log_mask(LOG_UNIMP,
 | 
				
			||||||
 | 
					                          "bcm2835_property: %x get board model NYI\n", tag);
 | 
				
			||||||
 | 
					            resplen = 4;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 0x00010002: /* Get board revision */
 | 
				
			||||||
 | 
					            qemu_log_mask(LOG_UNIMP,
 | 
				
			||||||
 | 
					                          "bcm2835_property: %x get board revision NYI\n", tag);
 | 
				
			||||||
 | 
					            resplen = 4;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 0x00010003: /* Get board MAC address */
 | 
				
			||||||
 | 
					            resplen = sizeof(s->macaddr.a);
 | 
				
			||||||
 | 
					            dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 0x00010004: /* Get board serial */
 | 
				
			||||||
 | 
					            qemu_log_mask(LOG_UNIMP,
 | 
				
			||||||
 | 
					                          "bcm2835_property: %x get board serial NYI\n", tag);
 | 
				
			||||||
 | 
					            resplen = 8;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 0x00010005: /* Get ARM memory */
 | 
				
			||||||
 | 
					            /* base */
 | 
				
			||||||
 | 
					            stl_phys(&s->dma_as, value + 12, 0);
 | 
				
			||||||
 | 
					            /* size */
 | 
				
			||||||
 | 
					            stl_phys(&s->dma_as, value + 16, s->ram_size);
 | 
				
			||||||
 | 
					            resplen = 8;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case 0x00028001: /* Set power state */
 | 
				
			||||||
 | 
					            /* Assume that whatever device they asked for exists,
 | 
				
			||||||
 | 
					             * and we'll just claim we set it to the desired state
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
					            tmp = ldl_phys(&s->dma_as, value + 16);
 | 
				
			||||||
 | 
					            stl_phys(&s->dma_as, value + 16, (tmp & 1));
 | 
				
			||||||
 | 
					            resplen = 8;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Clocks */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 0x00030001: /* Get clock state */
 | 
				
			||||||
 | 
					            stl_phys(&s->dma_as, value + 16, 0x1);
 | 
				
			||||||
 | 
					            resplen = 8;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 0x00038001: /* Set clock state */
 | 
				
			||||||
 | 
					            qemu_log_mask(LOG_UNIMP,
 | 
				
			||||||
 | 
					                          "bcm2835_property: %x set clock state NYI\n", tag);
 | 
				
			||||||
 | 
					            resplen = 8;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 0x00030002: /* Get clock rate */
 | 
				
			||||||
 | 
					        case 0x00030004: /* Get max clock rate */
 | 
				
			||||||
 | 
					        case 0x00030007: /* Get min clock rate */
 | 
				
			||||||
 | 
					            switch (ldl_phys(&s->dma_as, value + 12)) {
 | 
				
			||||||
 | 
					            case 1: /* EMMC */
 | 
				
			||||||
 | 
					                stl_phys(&s->dma_as, value + 16, 50000000);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 2: /* UART */
 | 
				
			||||||
 | 
					                stl_phys(&s->dma_as, value + 16, 3000000);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                stl_phys(&s->dma_as, value + 16, 700000000);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            resplen = 8;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 0x00038002: /* Set clock rate */
 | 
				
			||||||
 | 
					        case 0x00038004: /* Set max clock rate */
 | 
				
			||||||
 | 
					        case 0x00038007: /* Set min clock rate */
 | 
				
			||||||
 | 
					            qemu_log_mask(LOG_UNIMP,
 | 
				
			||||||
 | 
					                          "bcm2835_property: %x set clock rates NYI\n", tag);
 | 
				
			||||||
 | 
					            resplen = 8;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Temperature */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 0x00030006: /* Get temperature */
 | 
				
			||||||
 | 
					            stl_phys(&s->dma_as, value + 16, 25000);
 | 
				
			||||||
 | 
					            resplen = 8;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 0x0003000A: /* Get max temperature */
 | 
				
			||||||
 | 
					            stl_phys(&s->dma_as, value + 16, 99000);
 | 
				
			||||||
 | 
					            resplen = 8;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 0x00060001: /* Get DMA channels */
 | 
				
			||||||
 | 
					            /* channels 2-5 */
 | 
				
			||||||
 | 
					            stl_phys(&s->dma_as, value + 12, 0x003C);
 | 
				
			||||||
 | 
					            resplen = 4;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 0x00050001: /* Get command line */
 | 
				
			||||||
 | 
					            resplen = 0;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            qemu_log_mask(LOG_GUEST_ERROR,
 | 
				
			||||||
 | 
					                          "bcm2835_property: unhandled tag %08x\n", tag);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (tag == 0) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stl_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
 | 
				
			||||||
 | 
					        value += bufsize + 12;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Buffer response code */
 | 
				
			||||||
 | 
					    stl_phys(&s->dma_as, s->addr + 4, (1 << 31));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
 | 
				
			||||||
 | 
					                                      unsigned size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835PropertyState *s = opaque;
 | 
				
			||||||
 | 
					    uint32_t res = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (offset) {
 | 
				
			||||||
 | 
					    case MBOX_AS_DATA:
 | 
				
			||||||
 | 
					        res = MBOX_CHAN_PROPERTY | s->addr;
 | 
				
			||||||
 | 
					        s->pending = false;
 | 
				
			||||||
 | 
					        qemu_set_irq(s->mbox_irq, 0);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case MBOX_AS_PENDING:
 | 
				
			||||||
 | 
					        res = s->pending;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 | 
				
			||||||
 | 
					                      __func__, offset);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_property_write(void *opaque, hwaddr offset,
 | 
				
			||||||
 | 
					                                   uint64_t value, unsigned size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835PropertyState *s = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (offset) {
 | 
				
			||||||
 | 
					    case MBOX_AS_DATA:
 | 
				
			||||||
 | 
					        /* bcm2835_mbox should check our pending status before pushing */
 | 
				
			||||||
 | 
					        assert(!s->pending);
 | 
				
			||||||
 | 
					        s->pending = true;
 | 
				
			||||||
 | 
					        bcm2835_property_mbox_push(s, value);
 | 
				
			||||||
 | 
					        qemu_set_irq(s->mbox_irq, 1);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
 | 
				
			||||||
 | 
					                      __func__, offset);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const MemoryRegionOps bcm2835_property_ops = {
 | 
				
			||||||
 | 
					    .read = bcm2835_property_read,
 | 
				
			||||||
 | 
					    .write = bcm2835_property_write,
 | 
				
			||||||
 | 
					    .endianness = DEVICE_NATIVE_ENDIAN,
 | 
				
			||||||
 | 
					    .valid.min_access_size = 4,
 | 
				
			||||||
 | 
					    .valid.max_access_size = 4,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const VMStateDescription vmstate_bcm2835_property = {
 | 
				
			||||||
 | 
					    .name = TYPE_BCM2835_PROPERTY,
 | 
				
			||||||
 | 
					    .version_id = 1,
 | 
				
			||||||
 | 
					    .minimum_version_id = 1,
 | 
				
			||||||
 | 
					    .fields      = (VMStateField[]) {
 | 
				
			||||||
 | 
					        VMSTATE_MACADDR(macaddr, BCM2835PropertyState),
 | 
				
			||||||
 | 
					        VMSTATE_UINT32(addr, BCM2835PropertyState),
 | 
				
			||||||
 | 
					        VMSTATE_BOOL(pending, BCM2835PropertyState),
 | 
				
			||||||
 | 
					        VMSTATE_END_OF_LIST()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_property_init(Object *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835PropertyState *s = BCM2835_PROPERTY(obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s,
 | 
				
			||||||
 | 
					                          TYPE_BCM2835_PROPERTY, 0x10);
 | 
				
			||||||
 | 
					    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
 | 
				
			||||||
 | 
					    sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_property_reset(DeviceState *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->pending = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_property_realize(DeviceState *dev, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
 | 
				
			||||||
 | 
					    Object *obj;
 | 
				
			||||||
 | 
					    Error *err = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
 | 
				
			||||||
 | 
					    if (obj == NULL) {
 | 
				
			||||||
 | 
					        error_setg(errp, "%s: required dma-mr link not found: %s",
 | 
				
			||||||
 | 
					                   __func__, error_get_pretty(err));
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->dma_mr = MEMORY_REGION(obj);
 | 
				
			||||||
 | 
					    address_space_init(&s->dma_as, s->dma_mr, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* TODO: connect to MAC address of USB NIC device, once we emulate it */
 | 
				
			||||||
 | 
					    qemu_macaddr_default_if_unset(&s->macaddr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bcm2835_property_reset(dev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static Property bcm2835_property_props[] = {
 | 
				
			||||||
 | 
					    DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0),
 | 
				
			||||||
 | 
					    DEFINE_PROP_END_OF_LIST()
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_property_class_init(ObjectClass *klass, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dc->props = bcm2835_property_props;
 | 
				
			||||||
 | 
					    dc->realize = bcm2835_property_realize;
 | 
				
			||||||
 | 
					    dc->vmsd = &vmstate_bcm2835_property;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static TypeInfo bcm2835_property_info = {
 | 
				
			||||||
 | 
					    .name          = TYPE_BCM2835_PROPERTY,
 | 
				
			||||||
 | 
					    .parent        = TYPE_SYS_BUS_DEVICE,
 | 
				
			||||||
 | 
					    .instance_size = sizeof(BCM2835PropertyState),
 | 
				
			||||||
 | 
					    .class_init    = bcm2835_property_class_init,
 | 
				
			||||||
 | 
					    .instance_init = bcm2835_property_init,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm2835_property_register_types(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type_register_static(&bcm2835_property_info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type_init(bcm2835_property_register_types)
 | 
				
			||||||
@ -122,6 +122,11 @@ struct arm_boot_info {
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info);
 | 
					void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Write a secure board setup routine with a dummy handler for SMCs */
 | 
				
			||||||
 | 
					void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
 | 
				
			||||||
 | 
					                                            const struct arm_boot_info *info,
 | 
				
			||||||
 | 
					                                            hwaddr mvbar_addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Multiplication factor to convert from system clock ticks to qemu timer
 | 
					/* Multiplication factor to convert from system clock ticks to qemu timer
 | 
				
			||||||
   ticks.  */
 | 
					   ticks.  */
 | 
				
			||||||
extern int system_clock_scale;
 | 
					extern int system_clock_scale;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										42
									
								
								include/hw/arm/bcm2835_peripherals.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								include/hw/arm/bcm2835_peripherals.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
 | 
				
			||||||
 | 
					 * Written by Andrew Baumann
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef BCM2835_PERIPHERALS_H
 | 
				
			||||||
 | 
					#define BCM2835_PERIPHERALS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "qemu-common.h"
 | 
				
			||||||
 | 
					#include "exec/address-spaces.h"
 | 
				
			||||||
 | 
					#include "hw/sysbus.h"
 | 
				
			||||||
 | 
					#include "hw/intc/bcm2835_ic.h"
 | 
				
			||||||
 | 
					#include "hw/misc/bcm2835_property.h"
 | 
				
			||||||
 | 
					#include "hw/misc/bcm2835_mbox.h"
 | 
				
			||||||
 | 
					#include "hw/sd/sdhci.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals"
 | 
				
			||||||
 | 
					#define BCM2835_PERIPHERALS(obj) \
 | 
				
			||||||
 | 
					    OBJECT_CHECK(BCM2835PeripheralState, (obj), TYPE_BCM2835_PERIPHERALS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct BCM2835PeripheralState {
 | 
				
			||||||
 | 
					    /*< private >*/
 | 
				
			||||||
 | 
					    SysBusDevice parent_obj;
 | 
				
			||||||
 | 
					    /*< public >*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MemoryRegion peri_mr, peri_mr_alias, gpu_bus_mr, mbox_mr;
 | 
				
			||||||
 | 
					    MemoryRegion ram_alias[4];
 | 
				
			||||||
 | 
					    qemu_irq irq, fiq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SysBusDevice *uart0;
 | 
				
			||||||
 | 
					    BCM2835ICState ic;
 | 
				
			||||||
 | 
					    BCM2835PropertyState property;
 | 
				
			||||||
 | 
					    BCM2835MboxState mboxes;
 | 
				
			||||||
 | 
					    SDHCIState sdhci;
 | 
				
			||||||
 | 
					} BCM2835PeripheralState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* BCM2835_PERIPHERALS_H */
 | 
				
			||||||
							
								
								
									
										35
									
								
								include/hw/arm/bcm2836.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								include/hw/arm/bcm2836.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
 | 
				
			||||||
 | 
					 * Written by Andrew Baumann
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef BCM2836_H
 | 
				
			||||||
 | 
					#define BCM2836_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/arm/arm.h"
 | 
				
			||||||
 | 
					#include "hw/arm/bcm2835_peripherals.h"
 | 
				
			||||||
 | 
					#include "hw/intc/bcm2836_control.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TYPE_BCM2836 "bcm2836"
 | 
				
			||||||
 | 
					#define BCM2836(obj) OBJECT_CHECK(BCM2836State, (obj), TYPE_BCM2836)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BCM2836_NCPUS 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct BCM2836State {
 | 
				
			||||||
 | 
					    /*< private >*/
 | 
				
			||||||
 | 
					    DeviceState parent_obj;
 | 
				
			||||||
 | 
					    /*< public >*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uint32_t enabled_cpus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ARMCPU cpus[BCM2836_NCPUS];
 | 
				
			||||||
 | 
					    BCM2836ControlState control;
 | 
				
			||||||
 | 
					    BCM2835PeripheralState peripherals;
 | 
				
			||||||
 | 
					} BCM2836State;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* BCM2836_H */
 | 
				
			||||||
							
								
								
									
										128
									
								
								include/hw/arm/raspi_platform.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								include/hw/arm/raspi_platform.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * bcm2708 aka bcm2835/2836 aka Raspberry Pi/Pi2 SoC platform defines
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * These definitions are derived from those in Raspbian Linux at
 | 
				
			||||||
 | 
					 * arch/arm/mach-{bcm2708,bcm2709}/include/mach/platform.h
 | 
				
			||||||
 | 
					 * where they carry the following notice:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2010 Broadcom
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program; if not, write to the Free Software
 | 
				
			||||||
 | 
					 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MCORE_OFFSET            0x0000   /* Fake frame buffer device
 | 
				
			||||||
 | 
					                                          * (the multicore sync block) */
 | 
				
			||||||
 | 
					#define IC0_OFFSET              0x2000
 | 
				
			||||||
 | 
					#define ST_OFFSET               0x3000   /* System Timer */
 | 
				
			||||||
 | 
					#define MPHI_OFFSET             0x6000   /* Message-based Parallel Host Intf. */
 | 
				
			||||||
 | 
					#define DMA_OFFSET              0x7000   /* DMA controller, channels 0-14 */
 | 
				
			||||||
 | 
					#define ARM_OFFSET              0xB000   /* BCM2708 ARM control block */
 | 
				
			||||||
 | 
					#define ARMCTRL_OFFSET          (ARM_OFFSET + 0x000)
 | 
				
			||||||
 | 
					#define ARMCTRL_IC_OFFSET       (ARM_OFFSET + 0x200) /* Interrupt controller */
 | 
				
			||||||
 | 
					#define ARMCTRL_TIMER0_1_OFFSET (ARM_OFFSET + 0x400) /* Timer 0 and 1 */
 | 
				
			||||||
 | 
					#define ARMCTRL_0_SBM_OFFSET    (ARM_OFFSET + 0x800) /* User 0 (ARM) Semaphores
 | 
				
			||||||
 | 
					                                                      * Doorbells & Mailboxes */
 | 
				
			||||||
 | 
					#define PM_OFFSET               0x100000 /* Power Management, Reset controller
 | 
				
			||||||
 | 
					                                          * and Watchdog registers */
 | 
				
			||||||
 | 
					#define PCM_CLOCK_OFFSET        0x101098
 | 
				
			||||||
 | 
					#define RNG_OFFSET              0x104000
 | 
				
			||||||
 | 
					#define GPIO_OFFSET             0x200000
 | 
				
			||||||
 | 
					#define UART0_OFFSET            0x201000
 | 
				
			||||||
 | 
					#define MMCI0_OFFSET            0x202000
 | 
				
			||||||
 | 
					#define I2S_OFFSET              0x203000
 | 
				
			||||||
 | 
					#define SPI0_OFFSET             0x204000
 | 
				
			||||||
 | 
					#define BSC0_OFFSET             0x205000 /* BSC0 I2C/TWI */
 | 
				
			||||||
 | 
					#define UART1_OFFSET            0x215000
 | 
				
			||||||
 | 
					#define EMMC_OFFSET             0x300000
 | 
				
			||||||
 | 
					#define SMI_OFFSET              0x600000
 | 
				
			||||||
 | 
					#define BSC1_OFFSET             0x804000 /* BSC1 I2C/TWI */
 | 
				
			||||||
 | 
					#define USB_OFFSET              0x980000 /* DTC_OTG USB controller */
 | 
				
			||||||
 | 
					#define DMA15_OFFSET            0xE05000 /* DMA controller, channel 15 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* GPU interrupts */
 | 
				
			||||||
 | 
					#define INTERRUPT_TIMER0               0
 | 
				
			||||||
 | 
					#define INTERRUPT_TIMER1               1
 | 
				
			||||||
 | 
					#define INTERRUPT_TIMER2               2
 | 
				
			||||||
 | 
					#define INTERRUPT_TIMER3               3
 | 
				
			||||||
 | 
					#define INTERRUPT_CODEC0               4
 | 
				
			||||||
 | 
					#define INTERRUPT_CODEC1               5
 | 
				
			||||||
 | 
					#define INTERRUPT_CODEC2               6
 | 
				
			||||||
 | 
					#define INTERRUPT_JPEG                 7
 | 
				
			||||||
 | 
					#define INTERRUPT_ISP                  8
 | 
				
			||||||
 | 
					#define INTERRUPT_USB                  9
 | 
				
			||||||
 | 
					#define INTERRUPT_3D                   10
 | 
				
			||||||
 | 
					#define INTERRUPT_TRANSPOSER           11
 | 
				
			||||||
 | 
					#define INTERRUPT_MULTICORESYNC0       12
 | 
				
			||||||
 | 
					#define INTERRUPT_MULTICORESYNC1       13
 | 
				
			||||||
 | 
					#define INTERRUPT_MULTICORESYNC2       14
 | 
				
			||||||
 | 
					#define INTERRUPT_MULTICORESYNC3       15
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA0                 16
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA1                 17
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA2                 18
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA3                 19
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA4                 20
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA5                 21
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA6                 22
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA7                 23
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA8                 24
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA9                 25
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA10                26
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA11                27
 | 
				
			||||||
 | 
					#define INTERRUPT_DMA12                28
 | 
				
			||||||
 | 
					#define INTERRUPT_AUX                  29
 | 
				
			||||||
 | 
					#define INTERRUPT_ARM                  30
 | 
				
			||||||
 | 
					#define INTERRUPT_VPUDMA               31
 | 
				
			||||||
 | 
					#define INTERRUPT_HOSTPORT             32
 | 
				
			||||||
 | 
					#define INTERRUPT_VIDEOSCALER          33
 | 
				
			||||||
 | 
					#define INTERRUPT_CCP2TX               34
 | 
				
			||||||
 | 
					#define INTERRUPT_SDC                  35
 | 
				
			||||||
 | 
					#define INTERRUPT_DSI0                 36
 | 
				
			||||||
 | 
					#define INTERRUPT_AVE                  37
 | 
				
			||||||
 | 
					#define INTERRUPT_CAM0                 38
 | 
				
			||||||
 | 
					#define INTERRUPT_CAM1                 39
 | 
				
			||||||
 | 
					#define INTERRUPT_HDMI0                40
 | 
				
			||||||
 | 
					#define INTERRUPT_HDMI1                41
 | 
				
			||||||
 | 
					#define INTERRUPT_PIXELVALVE1          42
 | 
				
			||||||
 | 
					#define INTERRUPT_I2CSPISLV            43
 | 
				
			||||||
 | 
					#define INTERRUPT_DSI1                 44
 | 
				
			||||||
 | 
					#define INTERRUPT_PWA0                 45
 | 
				
			||||||
 | 
					#define INTERRUPT_PWA1                 46
 | 
				
			||||||
 | 
					#define INTERRUPT_CPR                  47
 | 
				
			||||||
 | 
					#define INTERRUPT_SMI                  48
 | 
				
			||||||
 | 
					#define INTERRUPT_GPIO0                49
 | 
				
			||||||
 | 
					#define INTERRUPT_GPIO1                50
 | 
				
			||||||
 | 
					#define INTERRUPT_GPIO2                51
 | 
				
			||||||
 | 
					#define INTERRUPT_GPIO3                52
 | 
				
			||||||
 | 
					#define INTERRUPT_I2C                  53
 | 
				
			||||||
 | 
					#define INTERRUPT_SPI                  54
 | 
				
			||||||
 | 
					#define INTERRUPT_I2SPCM               55
 | 
				
			||||||
 | 
					#define INTERRUPT_SDIO                 56
 | 
				
			||||||
 | 
					#define INTERRUPT_UART                 57
 | 
				
			||||||
 | 
					#define INTERRUPT_SLIMBUS              58
 | 
				
			||||||
 | 
					#define INTERRUPT_VEC                  59
 | 
				
			||||||
 | 
					#define INTERRUPT_CPG                  60
 | 
				
			||||||
 | 
					#define INTERRUPT_RNG                  61
 | 
				
			||||||
 | 
					#define INTERRUPT_ARASANSDIO           62
 | 
				
			||||||
 | 
					#define INTERRUPT_AVSPMON              63
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ARM CPU IRQs use a private number space */
 | 
				
			||||||
 | 
					#define INTERRUPT_ARM_TIMER            0
 | 
				
			||||||
 | 
					#define INTERRUPT_ARM_MAILBOX          1
 | 
				
			||||||
 | 
					#define INTERRUPT_ARM_DOORBELL_0       2
 | 
				
			||||||
 | 
					#define INTERRUPT_ARM_DOORBELL_1       3
 | 
				
			||||||
 | 
					#define INTERRUPT_VPU0_HALTED          4
 | 
				
			||||||
 | 
					#define INTERRUPT_VPU1_HALTED          5
 | 
				
			||||||
 | 
					#define INTERRUPT_ILLEGAL_TYPE0        6
 | 
				
			||||||
 | 
					#define INTERRUPT_ILLEGAL_TYPE1        7
 | 
				
			||||||
@ -23,7 +23,6 @@
 | 
				
			|||||||
#include "qemu-common.h"
 | 
					#include "qemu-common.h"
 | 
				
			||||||
#include "hw/arm/virt.h"
 | 
					#include "hw/arm/virt.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define VIRT_ACPI_CPU_ID_LIMIT 8
 | 
					 | 
				
			||||||
#define ACPI_GICC_ENABLED 1
 | 
					#define ACPI_GICC_ENABLED 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct VirtGuestInfo {
 | 
					typedef struct VirtGuestInfo {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										33
									
								
								include/hw/intc/bcm2835_ic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								include/hw/intc/bcm2835_ic.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef BCM2835_IC_H
 | 
				
			||||||
 | 
					#define BCM2835_IC_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/sysbus.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TYPE_BCM2835_IC "bcm2835-ic"
 | 
				
			||||||
 | 
					#define BCM2835_IC(obj) OBJECT_CHECK(BCM2835ICState, (obj), TYPE_BCM2835_IC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BCM2835_IC_GPU_IRQ "gpu-irq"
 | 
				
			||||||
 | 
					#define BCM2835_IC_ARM_IRQ "arm-irq"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct BCM2835ICState {
 | 
				
			||||||
 | 
					    /*< private >*/
 | 
				
			||||||
 | 
					    SysBusDevice busdev;
 | 
				
			||||||
 | 
					    /*< public >*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MemoryRegion iomem;
 | 
				
			||||||
 | 
					    qemu_irq irq;
 | 
				
			||||||
 | 
					    qemu_irq fiq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* 64 GPU IRQs + 8 ARM IRQs = 72 total (GPU first) */
 | 
				
			||||||
 | 
					    uint64_t gpu_irq_level, gpu_irq_enable;
 | 
				
			||||||
 | 
					    uint8_t arm_irq_level, arm_irq_enable;
 | 
				
			||||||
 | 
					    bool fiq_enable;
 | 
				
			||||||
 | 
					    uint8_t fiq_select;
 | 
				
			||||||
 | 
					} BCM2835ICState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										51
									
								
								include/hw/intc/bcm2836_control.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								include/hw/intc/bcm2836_control.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
 | 
				
			||||||
 | 
					 * Written by Andrew Baumann
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef BCM2836_CONTROL_H
 | 
				
			||||||
 | 
					#define BCM2836_CONTROL_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/sysbus.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 4 mailboxes per core, for 16 total */
 | 
				
			||||||
 | 
					#define BCM2836_NCORES 4
 | 
				
			||||||
 | 
					#define BCM2836_MBPERCORE 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TYPE_BCM2836_CONTROL "bcm2836-control"
 | 
				
			||||||
 | 
					#define BCM2836_CONTROL(obj) \
 | 
				
			||||||
 | 
					    OBJECT_CHECK(BCM2836ControlState, (obj), TYPE_BCM2836_CONTROL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct BCM2836ControlState {
 | 
				
			||||||
 | 
					    /*< private >*/
 | 
				
			||||||
 | 
					    SysBusDevice busdev;
 | 
				
			||||||
 | 
					    /*< public >*/
 | 
				
			||||||
 | 
					    MemoryRegion iomem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* mailbox state */
 | 
				
			||||||
 | 
					    uint32_t mailboxes[BCM2836_NCORES * BCM2836_MBPERCORE];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* interrupt routing/control registers */
 | 
				
			||||||
 | 
					    uint8_t route_gpu_irq, route_gpu_fiq;
 | 
				
			||||||
 | 
					    uint32_t timercontrol[BCM2836_NCORES];
 | 
				
			||||||
 | 
					    uint32_t mailboxcontrol[BCM2836_NCORES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* interrupt status regs (derived from input pins; not visible to user) */
 | 
				
			||||||
 | 
					    bool gpu_irq, gpu_fiq;
 | 
				
			||||||
 | 
					    uint8_t timerirqs[BCM2836_NCORES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* interrupt source registers, post-routing (also input-derived; visible) */
 | 
				
			||||||
 | 
					    uint32_t irqsrc[BCM2836_NCORES];
 | 
				
			||||||
 | 
					    uint32_t fiqsrc[BCM2836_NCORES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* outputs to CPU cores */
 | 
				
			||||||
 | 
					    qemu_irq irq[BCM2836_NCORES];
 | 
				
			||||||
 | 
					    qemu_irq fiq[BCM2836_NCORES];
 | 
				
			||||||
 | 
					} BCM2836ControlState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										38
									
								
								include/hw/misc/bcm2835_mbox.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								include/hw/misc/bcm2835_mbox.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef BCM2835_MBOX_H
 | 
				
			||||||
 | 
					#define BCM2835_MBOX_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "bcm2835_mbox_defs.h"
 | 
				
			||||||
 | 
					#include "hw/sysbus.h"
 | 
				
			||||||
 | 
					#include "exec/address-spaces.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TYPE_BCM2835_MBOX "bcm2835-mbox"
 | 
				
			||||||
 | 
					#define BCM2835_MBOX(obj) \
 | 
				
			||||||
 | 
					        OBJECT_CHECK(BCM2835MboxState, (obj), TYPE_BCM2835_MBOX)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    uint32_t reg[MBOX_SIZE];
 | 
				
			||||||
 | 
					    uint32_t count;
 | 
				
			||||||
 | 
					    uint32_t status;
 | 
				
			||||||
 | 
					    uint32_t config;
 | 
				
			||||||
 | 
					} BCM2835Mbox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    /*< private >*/
 | 
				
			||||||
 | 
					    SysBusDevice busdev;
 | 
				
			||||||
 | 
					    /*< public >*/
 | 
				
			||||||
 | 
					    MemoryRegion *mbox_mr;
 | 
				
			||||||
 | 
					    AddressSpace mbox_as;
 | 
				
			||||||
 | 
					    MemoryRegion iomem;
 | 
				
			||||||
 | 
					    qemu_irq arm_irq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool mbox_irq_disabled;
 | 
				
			||||||
 | 
					    bool available[MBOX_CHAN_COUNT];
 | 
				
			||||||
 | 
					    BCM2835Mbox mbox[2];
 | 
				
			||||||
 | 
					} BCM2835MboxState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										27
									
								
								include/hw/misc/bcm2835_mbox_defs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								include/hw/misc/bcm2835_mbox_defs.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef BCM2835_MBOX_DEFS_H
 | 
				
			||||||
 | 
					#define BCM2835_MBOX_DEFS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Constants shared with the ARM identifying separate mailbox channels */
 | 
				
			||||||
 | 
					#define MBOX_CHAN_POWER    0 /* for use by the power management interface */
 | 
				
			||||||
 | 
					#define MBOX_CHAN_FB       1 /* for use by the frame buffer */
 | 
				
			||||||
 | 
					#define MBOX_CHAN_VCHIQ    3 /* for use by the VCHIQ interface */
 | 
				
			||||||
 | 
					#define MBOX_CHAN_PROPERTY 8 /* for use by the property channel */
 | 
				
			||||||
 | 
					#define MBOX_CHAN_COUNT    9
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MBOX_SIZE          32
 | 
				
			||||||
 | 
					#define MBOX_INVALID_DATA  0x0f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Layout of the private address space used for communication between
 | 
				
			||||||
 | 
					 * the mbox device emulation, and child devices: each channel occupies
 | 
				
			||||||
 | 
					 * 16 bytes of address space, but only two registers are presently defined.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define MBOX_AS_CHAN_SHIFT 4
 | 
				
			||||||
 | 
					#define MBOX_AS_DATA       0 /* request / response data (RW at offset 0) */
 | 
				
			||||||
 | 
					#define MBOX_AS_PENDING    4 /* pending response status (RO at offset 4) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* BCM2835_MBOX_DEFS_H */
 | 
				
			||||||
							
								
								
									
										31
									
								
								include/hw/misc/bcm2835_property.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								include/hw/misc/bcm2835_property.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Raspberry Pi emulation (c) 2012 Gregory Estrade
 | 
				
			||||||
 | 
					 * This code is licensed under the GNU GPLv2 and later.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef BCM2835_PROPERTY_H
 | 
				
			||||||
 | 
					#define BCM2835_PROPERTY_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "hw/sysbus.h"
 | 
				
			||||||
 | 
					#include "exec/address-spaces.h"
 | 
				
			||||||
 | 
					#include "net/net.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TYPE_BCM2835_PROPERTY "bcm2835-property"
 | 
				
			||||||
 | 
					#define BCM2835_PROPERTY(obj) \
 | 
				
			||||||
 | 
					        OBJECT_CHECK(BCM2835PropertyState, (obj), TYPE_BCM2835_PROPERTY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    /*< private >*/
 | 
				
			||||||
 | 
					    SysBusDevice busdev;
 | 
				
			||||||
 | 
					    /*< public >*/
 | 
				
			||||||
 | 
					    MemoryRegion *dma_mr;
 | 
				
			||||||
 | 
					    AddressSpace dma_as;
 | 
				
			||||||
 | 
					    MemoryRegion iomem;
 | 
				
			||||||
 | 
					    qemu_irq mbox_irq;
 | 
				
			||||||
 | 
					    MACAddr macaddr;
 | 
				
			||||||
 | 
					    uint32_t ram_size;
 | 
				
			||||||
 | 
					    uint32_t addr;
 | 
				
			||||||
 | 
					    bool pending;
 | 
				
			||||||
 | 
					} BCM2835PropertyState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@ -650,6 +650,15 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
 | 
				
			|||||||
        cpu->id_aa64pfr0 &= ~0xf000;
 | 
					        cpu->id_aa64pfr0 &= ~0xf000;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!arm_feature(env, ARM_FEATURE_EL2)) {
 | 
				
			||||||
 | 
					        /* Disable the hypervisor feature bits in the processor feature
 | 
				
			||||||
 | 
					         * registers if we don't have EL2. These are id_pfr1[15:12] and
 | 
				
			||||||
 | 
					         * id_aa64pfr0_el1[11:8].
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        cpu->id_aa64pfr0 &= ~0xf00;
 | 
				
			||||||
 | 
					        cpu->id_pfr1 &= ~0xf000;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!cpu->has_mpu) {
 | 
					    if (!cpu->has_mpu) {
 | 
				
			||||||
        unset_feature(env, ARM_FEATURE_MPU);
 | 
					        unset_feature(env, ARM_FEATURE_MPU);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -3167,6 +3167,35 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
 | 
				
			|||||||
      .type = ARM_CP_ALIAS,
 | 
					      .type = ARM_CP_ALIAS,
 | 
				
			||||||
      .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]),
 | 
					      .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]),
 | 
				
			||||||
      .access = PL2_RW, .accessfn = fpexc32_access },
 | 
					      .access = PL2_RW, .accessfn = fpexc32_access },
 | 
				
			||||||
 | 
					    { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64,
 | 
				
			||||||
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0,
 | 
				
			||||||
 | 
					      .access = PL2_RW, .resetvalue = 0,
 | 
				
			||||||
 | 
					      .writefn = dacr_write, .raw_writefn = raw_write,
 | 
				
			||||||
 | 
					      .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) },
 | 
				
			||||||
 | 
					    { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64,
 | 
				
			||||||
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 0, .opc2 = 1,
 | 
				
			||||||
 | 
					      .access = PL2_RW, .resetvalue = 0,
 | 
				
			||||||
 | 
					      .fieldoffset = offsetof(CPUARMState, cp15.ifsr32_el2) },
 | 
				
			||||||
 | 
					    { .name = "SPSR_IRQ", .state = ARM_CP_STATE_AA64,
 | 
				
			||||||
 | 
					      .type = ARM_CP_ALIAS,
 | 
				
			||||||
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 0,
 | 
				
			||||||
 | 
					      .access = PL2_RW,
 | 
				
			||||||
 | 
					      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_IRQ]) },
 | 
				
			||||||
 | 
					    { .name = "SPSR_ABT", .state = ARM_CP_STATE_AA64,
 | 
				
			||||||
 | 
					      .type = ARM_CP_ALIAS,
 | 
				
			||||||
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 1,
 | 
				
			||||||
 | 
					      .access = PL2_RW,
 | 
				
			||||||
 | 
					      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_ABT]) },
 | 
				
			||||||
 | 
					    { .name = "SPSR_UND", .state = ARM_CP_STATE_AA64,
 | 
				
			||||||
 | 
					      .type = ARM_CP_ALIAS,
 | 
				
			||||||
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 2,
 | 
				
			||||||
 | 
					      .access = PL2_RW,
 | 
				
			||||||
 | 
					      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_UND]) },
 | 
				
			||||||
 | 
					    { .name = "SPSR_FIQ", .state = ARM_CP_STATE_AA64,
 | 
				
			||||||
 | 
					      .type = ARM_CP_ALIAS,
 | 
				
			||||||
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 3,
 | 
				
			||||||
 | 
					      .access = PL2_RW,
 | 
				
			||||||
 | 
					      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_FIQ]) },
 | 
				
			||||||
    REGINFO_SENTINEL
 | 
					    REGINFO_SENTINEL
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -3294,11 +3323,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
 | 
				
			|||||||
      .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0,
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0,
 | 
				
			||||||
      .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2),
 | 
					      .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2),
 | 
				
			||||||
      .writefn = hcr_write },
 | 
					      .writefn = hcr_write },
 | 
				
			||||||
    { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64,
 | 
					 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0,
 | 
					 | 
				
			||||||
      .access = PL2_RW, .resetvalue = 0,
 | 
					 | 
				
			||||||
      .writefn = dacr_write, .raw_writefn = raw_write,
 | 
					 | 
				
			||||||
      .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) },
 | 
					 | 
				
			||||||
    { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64,
 | 
					    { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64,
 | 
				
			||||||
      .type = ARM_CP_ALIAS,
 | 
					      .type = ARM_CP_ALIAS,
 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1,
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1,
 | 
				
			||||||
@ -3308,10 +3332,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
 | 
				
			|||||||
      .type = ARM_CP_ALIAS,
 | 
					      .type = ARM_CP_ALIAS,
 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0,
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0,
 | 
				
			||||||
      .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) },
 | 
					      .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) },
 | 
				
			||||||
    { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64,
 | 
					 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 0, .opc2 = 1,
 | 
					 | 
				
			||||||
      .access = PL2_RW, .resetvalue = 0,
 | 
					 | 
				
			||||||
      .fieldoffset = offsetof(CPUARMState, cp15.ifsr32_el2) },
 | 
					 | 
				
			||||||
    { .name = "FAR_EL2", .state = ARM_CP_STATE_AA64,
 | 
					    { .name = "FAR_EL2", .state = ARM_CP_STATE_AA64,
 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0,
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0,
 | 
				
			||||||
      .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) },
 | 
					      .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) },
 | 
				
			||||||
@ -3320,26 +3340,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
 | 
				
			|||||||
      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0,
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0,
 | 
				
			||||||
      .access = PL2_RW,
 | 
					      .access = PL2_RW,
 | 
				
			||||||
      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) },
 | 
					      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) },
 | 
				
			||||||
    { .name = "SPSR_IRQ", .state = ARM_CP_STATE_AA64,
 | 
					 | 
				
			||||||
      .type = ARM_CP_ALIAS,
 | 
					 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 0,
 | 
					 | 
				
			||||||
      .access = PL2_RW,
 | 
					 | 
				
			||||||
      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_IRQ]) },
 | 
					 | 
				
			||||||
    { .name = "SPSR_ABT", .state = ARM_CP_STATE_AA64,
 | 
					 | 
				
			||||||
      .type = ARM_CP_ALIAS,
 | 
					 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 1,
 | 
					 | 
				
			||||||
      .access = PL2_RW,
 | 
					 | 
				
			||||||
      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_ABT]) },
 | 
					 | 
				
			||||||
    { .name = "SPSR_UND", .state = ARM_CP_STATE_AA64,
 | 
					 | 
				
			||||||
      .type = ARM_CP_ALIAS,
 | 
					 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 2,
 | 
					 | 
				
			||||||
      .access = PL2_RW,
 | 
					 | 
				
			||||||
      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_UND]) },
 | 
					 | 
				
			||||||
    { .name = "SPSR_FIQ", .state = ARM_CP_STATE_AA64,
 | 
					 | 
				
			||||||
      .type = ARM_CP_ALIAS,
 | 
					 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 3,
 | 
					 | 
				
			||||||
      .access = PL2_RW,
 | 
					 | 
				
			||||||
      .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_FIQ]) },
 | 
					 | 
				
			||||||
    { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64,
 | 
					    { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64,
 | 
				
			||||||
      .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0,
 | 
					      .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0,
 | 
				
			||||||
      .access = PL2_RW, .writefn = vbar_write,
 | 
					      .access = PL2_RW, .writefn = vbar_write,
 | 
				
			||||||
@ -6763,24 +6763,34 @@ typedef enum {
 | 
				
			|||||||
} MMUFaultType;
 | 
					} MMUFaultType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * check_s2_startlevel
 | 
					 * check_s2_mmu_setup
 | 
				
			||||||
 * @cpu:        ARMCPU
 | 
					 * @cpu:        ARMCPU
 | 
				
			||||||
 * @is_aa64:    True if the translation regime is in AArch64 state
 | 
					 * @is_aa64:    True if the translation regime is in AArch64 state
 | 
				
			||||||
 * @startlevel: Suggested starting level
 | 
					 * @startlevel: Suggested starting level
 | 
				
			||||||
 * @inputsize:  Bitsize of IPAs
 | 
					 * @inputsize:  Bitsize of IPAs
 | 
				
			||||||
 * @stride:     Page-table stride (See the ARM ARM)
 | 
					 * @stride:     Page-table stride (See the ARM ARM)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Returns true if the suggested starting level is OK and false otherwise.
 | 
					 * Returns true if the suggested S2 translation parameters are OK and
 | 
				
			||||||
 | 
					 * false otherwise.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static bool check_s2_startlevel(ARMCPU *cpu, bool is_aa64, int level,
 | 
					static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level,
 | 
				
			||||||
                               int inputsize, int stride)
 | 
					                               int inputsize, int stride)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    const int grainsize = stride + 3;
 | 
				
			||||||
 | 
					    int startsizecheck;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Negative levels are never allowed.  */
 | 
					    /* Negative levels are never allowed.  */
 | 
				
			||||||
    if (level < 0) {
 | 
					    if (level < 0) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    startsizecheck = inputsize - ((3 - level) * stride + grainsize);
 | 
				
			||||||
 | 
					    if (startsizecheck < 1 || startsizecheck > stride + 4) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (is_aa64) {
 | 
					    if (is_aa64) {
 | 
				
			||||||
 | 
					        CPUARMState *env = &cpu->env;
 | 
				
			||||||
        unsigned int pamax = arm_pamax(cpu);
 | 
					        unsigned int pamax = arm_pamax(cpu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        switch (stride) {
 | 
					        switch (stride) {
 | 
				
			||||||
@ -6802,21 +6812,20 @@ static bool check_s2_startlevel(ARMCPU *cpu, bool is_aa64, int level,
 | 
				
			|||||||
        default:
 | 
					        default:
 | 
				
			||||||
            g_assert_not_reached();
 | 
					            g_assert_not_reached();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        const int grainsize = stride + 3;
 | 
					 | 
				
			||||||
        int startsizecheck;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Inputsize checks.  */
 | 
				
			||||||
 | 
					        if (inputsize > pamax &&
 | 
				
			||||||
 | 
					            (arm_el_is_aa64(env, 1) || inputsize > 40)) {
 | 
				
			||||||
 | 
					            /* This is CONSTRAINED UNPREDICTABLE and we choose to fault.  */
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
        /* AArch32 only supports 4KB pages. Assert on that.  */
 | 
					        /* AArch32 only supports 4KB pages. Assert on that.  */
 | 
				
			||||||
        assert(stride == 9);
 | 
					        assert(stride == 9);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (level == 0) {
 | 
					        if (level == 0) {
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        startsizecheck = inputsize - ((3 - level) * stride + grainsize);
 | 
					 | 
				
			||||||
        if (startsizecheck < 1 || startsizecheck > stride + 4) {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -7013,8 +7022,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Check that the starting level is valid. */
 | 
					        /* Check that the starting level is valid. */
 | 
				
			||||||
        ok = check_s2_startlevel(cpu, va_size == 64, level,
 | 
					        ok = check_s2_mmu_setup(cpu, va_size == 64, level, inputsize, stride);
 | 
				
			||||||
                                 inputsize, stride);
 | 
					 | 
				
			||||||
        if (!ok) {
 | 
					        if (!ok) {
 | 
				
			||||||
            /* AArch64 reports these as level 0 faults.
 | 
					            /* AArch64 reports these as level 0 faults.
 | 
				
			||||||
             * AArch32 reports these as level 1 faults.
 | 
					             * AArch32 reports these as level 1 faults.
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user