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:
Peter Maydell 2016-02-04 11:06:35 +00:00
commit 071aacc9c9
27 changed files with 2199 additions and 104 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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

View 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
View 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)

View File

@ -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;

View File

@ -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
View 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)

View File

@ -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);

View File

@ -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
View 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
View 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)

View File

@ -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
View 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
View 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)

View File

@ -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;

View 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
View 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 */

View 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

View File

@ -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 {

View 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

View 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

View 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

View 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 */

View 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

View File

@ -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);
}

View File

@ -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.