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_MAINSTONE=y
|
||||
CONFIG_NSERIES=y
|
||||
CONFIG_RASPI=y
|
||||
CONFIG_REALVIEW=y
|
||||
CONFIG_ZAURUS=y
|
||||
CONFIG_ZYNQ=y
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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)
|
||||
|
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 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);
|
||||
|
@ -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
|
||||
|
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_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
|
||||
|
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);
|
||||
|
||||
/* 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;
|
||||
|
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 "hw/arm/virt.h"
|
||||
|
||||
#define VIRT_ACPI_CPU_ID_LIMIT 8
|
||||
#define ACPI_GICC_ENABLED 1
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user