summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--disas/libvixl/vixl/a64/disasm-a64.cc6
-rw-r--r--hw/arm/Makefile.objs1
-rw-r--r--hw/arm/bcm2835_peripherals.c204
-rw-r--r--hw/arm/bcm2836.c165
-rw-r--r--hw/arm/boot.c53
-rw-r--r--hw/arm/highbank.c37
-rw-r--r--hw/arm/raspi.c152
-rw-r--r--hw/arm/virt-acpi-build.c28
-rw-r--r--hw/intc/Makefile.objs1
-rw-r--r--hw/intc/bcm2835_ic.c236
-rw-r--r--hw/intc/bcm2836_control.c303
-rw-r--r--hw/misc/Makefile.objs2
-rw-r--r--hw/misc/bcm2835_mbox.c333
-rw-r--r--hw/misc/bcm2835_property.c287
-rw-r--r--include/hw/arm/arm.h5
-rw-r--r--include/hw/arm/bcm2835_peripherals.h42
-rw-r--r--include/hw/arm/bcm2836.h35
-rw-r--r--include/hw/arm/raspi_platform.h128
-rw-r--r--include/hw/arm/virt-acpi-build.h1
-rw-r--r--include/hw/intc/bcm2835_ic.h33
-rw-r--r--include/hw/intc/bcm2836_control.h51
-rw-r--r--include/hw/misc/bcm2835_mbox.h38
-rw-r--r--include/hw/misc/bcm2835_mbox_defs.h27
-rw-r--r--include/hw/misc/bcm2835_property.h31
-rw-r--r--target-arm/cpu.c9
-rw-r--r--target-arm/helper.c94
27 files changed, 2199 insertions, 104 deletions
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index d9b90a50d6..a9f82a1032 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -79,6 +79,7 @@ CONFIG_TUSB6010=y
CONFIG_IMX=y
CONFIG_MAINSTONE=y
CONFIG_NSERIES=y
+CONFIG_RASPI=y
CONFIG_REALVIEW=y
CONFIG_ZAURUS=y
CONFIG_ZYNQ=y
diff --git a/disas/libvixl/vixl/a64/disasm-a64.cc b/disas/libvixl/vixl/a64/disasm-a64.cc
index 20caba4317..7a58a5c087 100644
--- a/disas/libvixl/vixl/a64/disasm-a64.cc
+++ b/disas/libvixl/vixl/a64/disasm-a64.cc
@@ -2688,8 +2688,12 @@ void Disassembler::AppendRegisterNameToOutput(const Instruction* instr,
void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr,
int64_t offset) {
USE(instr);
+ uint64_t abs_offset = offset;
char sign = (offset < 0) ? '-' : '+';
- AppendToOutput("#%c0x%" PRIx64, sign, std::abs(offset));
+ if (offset < 0) {
+ abs_offset = -abs_offset;
+ }
+ AppendToOutput("#%c0x%" PRIx64, sign, abs_offset);
}
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2195b60fac..a711e4df61 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -11,6 +11,7 @@ obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-$(CONFIG_DIGIC) += digic.o
obj-y += omap1.o omap2.o strongarm.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_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
new file mode 100644
index 0000000000..18b72ecb69
--- /dev/null
+++ b/hw/arm/bcm2835_peripherals.c
@@ -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)
diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c
new file mode 100644
index 0000000000..69c7438317
--- /dev/null
+++ b/hw/arm/bcm2836.c
@@ -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)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 7742dd3cb6..cce8c7cd1c 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -178,6 +178,57 @@ static void default_write_secondary(ARMCPU *cpu,
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,
const struct arm_boot_info *info)
{
@@ -488,7 +539,9 @@ static void do_cpu_reset(void *opaque)
* adjust.
*/
if (env->aarch64) {
+ env->cp15.scr_el3 |= SCR_RW;
if (arm_feature(env, ARM_FEATURE_EL2)) {
+ env->cp15.hcr_el2 |= HCR_RW;
env->pstate = PSTATE_MODE_EL2h;
} else {
env->pstate = PSTATE_MODE_EL1h;
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index 620b52631a..e25cf5e3f3 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -35,49 +35,16 @@
#define MPCORE_PERIPHBASE 0xfff10000
#define MVBAR_ADDR 0x200
+#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t))
#define NIRQ_GIC 160
/* 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,
const struct arm_boot_info *info)
{
- int n;
- 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);
+ arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
}
static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c
new file mode 100644
index 0000000000..0c9427c40e
--- /dev/null
+++ b/hw/arm/raspi.c
@@ -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)
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 87fbe7c97d..26146919cd 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -46,20 +46,6 @@
#define ARM_SPI_BASE 32
#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)
{
uint16_t i;
@@ -443,7 +429,7 @@ build_gtdt(GArray *table_data, GArray *linker)
gtdt->secure_el1_flags = ACPI_EDGE_SENSITIVE;
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_flags = ACPI_EDGE_SENSITIVE;
@@ -458,8 +444,7 @@ build_gtdt(GArray *table_data, GArray *linker)
/* MADT */
static void
-build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
- VirtAcpiCpuInfo *cpuinfo)
+build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
{
int madt_start = table_data->len;
const MemMapEntry *memmap = guest_info->memmap;
@@ -489,9 +474,7 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
gicc->cpu_interface_number = i;
gicc->arm_mpidr = armcpu->mp_affinity;
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) {
@@ -599,11 +582,8 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
{
GArray *table_offsets;
unsigned dsdt, rsdt;
- VirtAcpiCpuInfo cpuinfo;
GArray *tables_blob = tables->table_data;
- virt_acpi_get_cpu_info(&cpuinfo);
-
table_offsets = g_array_new(false, true /* clear */,
sizeof(uint32_t));
@@ -630,7 +610,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
build_fadt(tables_blob, tables->linker, dsdt);
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);
build_gtdt(tables_blob, tables->linker);
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 004b0c25e4..6a13a39519 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -24,6 +24,7 @@ obj-$(CONFIG_GRLIB) += grlib_irqmp.o
obj-$(CONFIG_IOAPIC) += ioapic.o
obj-$(CONFIG_OMAP) += omap_intc.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_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c
new file mode 100644
index 0000000000..005a72b1e2
--- /dev/null
+++ b/hw/intc/bcm2835_ic.c
@@ -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)
diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
new file mode 100644
index 0000000000..ad622aa99f
--- /dev/null
+++ b/hw/intc/bcm2836_control.c
@@ -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)
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index d4765c2516..ea6cd3c9ff 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -36,6 +36,8 @@ obj-$(CONFIG_OMAP) += omap_gpmc.o
obj-$(CONFIG_OMAP) += omap_l4.o
obj-$(CONFIG_OMAP) += omap_sdrc.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_ZYNQ) += zynq_slcr.o
obj-$(CONFIG_ZYNQ) += zynq-xadc.o
diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c
new file mode 100644
index 0000000000..df1d6e6ad6
--- /dev/null
+++ b/hw/misc/bcm2835_mbox.c
@@ -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)
diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c
new file mode 100644
index 0000000000..e42b43e72d
--- /dev/null
+++ b/hw/misc/bcm2835_property.c
@@ -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)
diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h
index c26b0e357f..52ecf4aa8f 100644
--- a/include/hw/arm/arm.h
+++ b/include/hw/arm/arm.h
@@ -122,6 +122,11 @@ struct arm_boot_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
ticks. */
extern int system_clock_scale;
diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
new file mode 100644
index 0000000000..5d888dca53
--- /dev/null
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -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 */
diff --git a/include/hw/arm/bcm2836.h b/include/hw/arm/bcm2836.h
new file mode 100644
index 0000000000..76de1996af
--- /dev/null
+++ b/include/hw/arm/bcm2836.h
@@ -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 */
diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h
new file mode 100644
index 0000000000..6467e88ae6
--- /dev/null
+++ b/include/hw/arm/raspi_platform.h
@@ -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
diff --git a/include/hw/arm/virt-acpi-build.h b/include/hw/arm/virt-acpi-build.h
index 744b666385..7d3700ebf6 100644
--- a/include/hw/arm/virt-acpi-build.h
+++ b/include/hw/arm/virt-acpi-build.h
@@ -23,7 +23,6 @@
#include "qemu-common.h"
#include "hw/arm/virt.h"
-#define VIRT_ACPI_CPU_ID_LIMIT 8
#define ACPI_GICC_ENABLED 1
typedef struct VirtGuestInfo {
diff --git a/include/hw/intc/bcm2835_ic.h b/include/hw/intc/bcm2835_ic.h
new file mode 100644
index 0000000000..fb75fa0064
--- /dev/null
+++ b/include/hw/intc/bcm2835_ic.h
@@ -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
diff --git a/include/hw/intc/bcm2836_control.h b/include/hw/intc/bcm2836_control.h
new file mode 100644
index 0000000000..613f3c4186
--- /dev/null
+++ b/include/hw/intc/bcm2836_control.h
@@ -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
diff --git a/include/hw/misc/bcm2835_mbox.h b/include/hw/misc/bcm2835_mbox.h
new file mode 100644
index 0000000000..f4e9ff9ef6
--- /dev/null
+++ b/include/hw/misc/bcm2835_mbox.h
@@ -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
diff --git a/include/hw/misc/bcm2835_mbox_defs.h b/include/hw/misc/bcm2835_mbox_defs.h
new file mode 100644
index 0000000000..a18e520b22
--- /dev/null
+++ b/include/hw/misc/bcm2835_mbox_defs.h
@@ -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 */
diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h
new file mode 100644
index 0000000000..fcf5f3deca
--- /dev/null
+++ b/include/hw/misc/bcm2835_property.h
@@ -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
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 0e582c4410..7ddbf3d19b 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -650,6 +650,15 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
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) {
unset_feature(env, ARM_FEATURE_MPU);
}
diff --git a/target-arm/helper.c b/target-arm/helper.c
index ae024869d0..5ea507f5bc 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -3167,6 +3167,35 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
.type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]),
.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
};
@@ -3294,11 +3323,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2),
.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,
.type = ARM_CP_ALIAS,
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1,
@@ -3308,10 +3332,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.type = ARM_CP_ALIAS,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0,
.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,
.opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0,
.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,
.access = PL2_RW,
.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,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0,
.access = PL2_RW, .writefn = vbar_write,
@@ -6763,24 +6763,34 @@ typedef enum {
} MMUFaultType;
/*
- * check_s2_startlevel
+ * check_s2_mmu_setup
* @cpu: ARMCPU
* @is_aa64: True if the translation regime is in AArch64 state
* @startlevel: Suggested starting level
* @inputsize: Bitsize of IPAs
* @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,
- int inputsize, int stride)
+static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level,
+ int inputsize, int stride)
{
+ const int grainsize = stride + 3;
+ int startsizecheck;
+
/* Negative levels are never allowed. */
if (level < 0) {
return false;
}
+ startsizecheck = inputsize - ((3 - level) * stride + grainsize);
+ if (startsizecheck < 1 || startsizecheck > stride + 4) {
+ return false;
+ }
+
if (is_aa64) {
+ CPUARMState *env = &cpu->env;
unsigned int pamax = arm_pamax(cpu);
switch (stride) {
@@ -6802,21 +6812,20 @@ static bool check_s2_startlevel(ARMCPU *cpu, bool is_aa64, int level,
default:
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. */
assert(stride == 9);
if (level == 0) {
return false;
}
-
- startsizecheck = inputsize - ((3 - level) * stride + grainsize);
- if (startsizecheck < 1 || startsizecheck > stride + 4) {
- return false;
- }
}
return true;
}
@@ -7013,8 +7022,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
}
/* Check that the starting level is valid. */
- ok = check_s2_startlevel(cpu, va_size == 64, level,
- inputsize, stride);
+ ok = check_s2_mmu_setup(cpu, va_size == 64, level, inputsize, stride);
if (!ok) {
/* AArch64 reports these as level 0 faults.
* AArch32 reports these as level 1 faults.