mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180824-1' into staging
target-arm queue: * Fix rounding errors in scaling float-to-int and int-to-float operations * Connect virtualization-related IRQs and memory regions of GICv2 in boards that use Cortex-A7 or Cortex-A15 * Support taking exceptions to AArch32 Hyp mode * Clear CPSR.IL and CPSR.J on 32-bit exception entry (a minor bug fix that won't affect non-buggy guest code) * mps2-an505: Implement various missing devices: dual timer, watchdogs, counters in the FPGAIO registers, some missing ID/control registers, TrustZone Master Security Controllers, PL081 DMA controllers, PL022 SPI controllers * correct ID register values for mps2-an385, -an511, -an505 * fix some hardcoded tabs in untouched backwaters of the target/arm codebase * raspi: Refactor framebuffer property handling code and implement support for the virtual framebuffer/viewport # gpg: Signature made Fri 24 Aug 2018 13:19:04 BST # gpg: using RSA key 3C2525ED14360CDE # 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>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20180824-1: (52 commits) hw/arm/mps2: Fix ID register errors on AN511 and AN385 hw/display/bcm2835_fb: Validate bcm2835_fb_mbox_push() config hw/display/bcm2835_fb: Validate config settings hw/display/bcm2835_fb: Fix handling of virtual framebuffer hw/display/bcm2835_fb: Abstract out calculation of pitch, size hw/display/bcm2835_fb: Reset resolution, etc correctly hw/display/bcm2835_fb: Drop unused size and pitch fields hw/misc/bcm2835_property: Track fb settings using BCM2835FBConfig hw/misc/bcm2835_fb: Move config fields to their own struct target/arm: Remove a handful of stray tabs target/arm: Untabify iwmmxt_helper.c target/arm: Untabify translate.c hw/arm/mps2-tz: Fix MPS2 SCC config register values hw/arm/mps2-tz: Instantiate SPI controllers hw/ssi/pl022: Correct wrong DMACR and ICR handling hw/ssi/pl022: Correct wrong value for PL022_INT_RT hw/ssi/pl022: Use DeviceState::realize rather than SysBusDevice::init hw/ssi/pl022: Don't directly call vmstate_register() hw/ssi/pl022: Set up reset function in class init hw/ssi/pl022: Allow use as embedded-struct device ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+10
@@ -451,10 +451,14 @@ F: hw/gpio/pl061.c
|
||||
F: hw/input/pl050.c
|
||||
F: hw/intc/pl190.c
|
||||
F: hw/sd/pl181.c
|
||||
F: hw/ssi/pl022.c
|
||||
F: include/hw/ssi/pl022.h
|
||||
F: hw/timer/pl031.c
|
||||
F: include/hw/arm/primecell.h
|
||||
F: hw/timer/cmsdk-apb-timer.c
|
||||
F: include/hw/timer/cmsdk-apb-timer.h
|
||||
F: hw/timer/cmsdk-apb-dualtimer.c
|
||||
F: include/hw/timer/cmsdk-apb-dualtimer.h
|
||||
F: hw/char/cmsdk-apb-uart.c
|
||||
F: include/hw/char/cmsdk-apb-uart.h
|
||||
F: hw/watchdog/cmsdk-apb-watchdog.c
|
||||
@@ -463,6 +467,8 @@ F: hw/misc/tz-ppc.c
|
||||
F: include/hw/misc/tz-ppc.h
|
||||
F: hw/misc/tz-mpc.c
|
||||
F: include/hw/misc/tz-mpc.h
|
||||
F: hw/misc/tz-msc.c
|
||||
F: include/hw/misc/tz-msc.h
|
||||
|
||||
ARM cores
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -537,6 +543,10 @@ F: hw/misc/mps2-*.c
|
||||
F: include/hw/misc/mps2-*.h
|
||||
F: hw/arm/iotkit.c
|
||||
F: include/hw/arm/iotkit.h
|
||||
F: hw/misc/iotkit-sysctl.c
|
||||
F: include/hw/misc/iotkit-sysctl.h
|
||||
F: hw/misc/iotkit-sysinfo.c
|
||||
F: include/hw/misc/iotkit-sysinfo.h
|
||||
|
||||
Musicpal
|
||||
M: Jan Kiszka <jan.kiszka@web.de>
|
||||
|
||||
@@ -103,6 +103,7 @@ CONFIG_STM32F2XX_SPI=y
|
||||
CONFIG_STM32F205_SOC=y
|
||||
|
||||
CONFIG_CMSDK_APB_TIMER=y
|
||||
CONFIG_CMSDK_APB_DUALTIMER=y
|
||||
CONFIG_CMSDK_APB_UART=y
|
||||
CONFIG_CMSDK_APB_WATCHDOG=y
|
||||
|
||||
@@ -110,9 +111,12 @@ CONFIG_MPS2_FPGAIO=y
|
||||
CONFIG_MPS2_SCC=y
|
||||
|
||||
CONFIG_TZ_MPC=y
|
||||
CONFIG_TZ_MSC=y
|
||||
CONFIG_TZ_PPC=y
|
||||
CONFIG_IOTKIT=y
|
||||
CONFIG_IOTKIT_SECCTL=y
|
||||
CONFIG_IOTKIT_SYSCTL=y
|
||||
CONFIG_IOTKIT_SYSINFO=y
|
||||
|
||||
CONFIG_VERSATILE=y
|
||||
CONFIG_VERSATILE_PCI=y
|
||||
|
||||
+455
-124
File diff suppressed because it is too large
Load Diff
@@ -736,6 +736,17 @@ static void do_cpu_reset(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
if (!env->aarch64 && !info->secure_boot &&
|
||||
arm_feature(env, ARM_FEATURE_EL2)) {
|
||||
/*
|
||||
* This is an AArch32 boot not to Secure state, and
|
||||
* we have Hyp mode available, so boot the kernel into
|
||||
* Hyp mode. This is not how the CPU comes out of reset,
|
||||
* so we need to manually put it there.
|
||||
*/
|
||||
cpsr_write(env, ARM_CPU_MODE_HYP, CPSR_M, CPSRWriteRaw);
|
||||
}
|
||||
|
||||
if (cs == first_cpu) {
|
||||
AddressSpace *as = arm_boot_address_space(cpu, info);
|
||||
|
||||
|
||||
@@ -207,6 +207,10 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp)
|
||||
irq = qdev_get_gpio_in(d, ARM_CPU_IRQ);
|
||||
sysbus_connect_irq(sbd, i, irq);
|
||||
sysbus_connect_irq(sbd, i + smp_cpus, qdev_get_gpio_in(d, ARM_CPU_FIQ));
|
||||
sysbus_connect_irq(sbd, i + 2 * smp_cpus,
|
||||
qdev_get_gpio_in(d, ARM_CPU_VIRQ));
|
||||
sysbus_connect_irq(sbd, i + 3 * smp_cpus,
|
||||
qdev_get_gpio_in(d, ARM_CPU_VFIQ));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -209,6 +209,10 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_connect_irq(sbd, i, irq);
|
||||
irq = qdev_get_gpio_in(d, ARM_CPU_FIQ);
|
||||
sysbus_connect_irq(sbd, i + smp_cpus, irq);
|
||||
irq = qdev_get_gpio_in(d, ARM_CPU_VIRQ);
|
||||
sysbus_connect_irq(sbd, i + 2 * smp_cpus, irq);
|
||||
irq = qdev_get_gpio_in(d, ARM_CPU_VFIQ);
|
||||
sysbus_connect_irq(sbd, i + 3 * smp_cpus, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -243,6 +243,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
|
||||
int n;
|
||||
qemu_irq cpu_irq[4];
|
||||
qemu_irq cpu_fiq[4];
|
||||
qemu_irq cpu_virq[4];
|
||||
qemu_irq cpu_vfiq[4];
|
||||
MemoryRegion *sysram;
|
||||
MemoryRegion *dram;
|
||||
MemoryRegion *sysmem;
|
||||
@@ -282,6 +284,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
|
||||
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
||||
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
|
||||
cpu_fiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ);
|
||||
cpu_virq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VIRQ);
|
||||
cpu_vfiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VFIQ);
|
||||
}
|
||||
|
||||
sysmem = get_system_memory();
|
||||
@@ -329,6 +333,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
sysbus_connect_irq(busdev, n, cpu_irq[n]);
|
||||
sysbus_connect_irq(busdev, n + smp_cpus, cpu_fiq[n]);
|
||||
sysbus_connect_irq(busdev, n + 2 * smp_cpus, cpu_virq[n]);
|
||||
sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]);
|
||||
}
|
||||
|
||||
for (n = 0; n < 128; n++) {
|
||||
|
||||
+97
-17
@@ -16,9 +16,11 @@
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/arm/iotkit.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/arm/arm.h"
|
||||
|
||||
/* Clock frequency in HZ of the 32KHz "slow clock" */
|
||||
#define S32KCLK (32 * 1000)
|
||||
|
||||
/* Create an alias region of @size bytes starting at @base
|
||||
* which mirrors the memory starting at @orig.
|
||||
*/
|
||||
@@ -138,8 +140,23 @@ static void iotkit_init(Object *obj)
|
||||
TYPE_CMSDK_APB_TIMER);
|
||||
sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1),
|
||||
TYPE_CMSDK_APB_TIMER);
|
||||
sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer),
|
||||
TYPE_CMSDK_APB_TIMER);
|
||||
sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer),
|
||||
TYPE_UNIMPLEMENTED_DEVICE);
|
||||
TYPE_CMSDK_APB_DUALTIMER);
|
||||
sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog,
|
||||
sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG);
|
||||
sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog,
|
||||
sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG);
|
||||
sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog,
|
||||
sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG);
|
||||
sysbus_init_child_obj(obj, "iotkit-sysctl", &s->sysctl,
|
||||
sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL);
|
||||
sysbus_init_child_obj(obj, "iotkit-sysinfo", &s->sysinfo,
|
||||
sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO);
|
||||
object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate,
|
||||
sizeof(s->nmi_orgate), TYPE_OR_IRQ,
|
||||
&error_abort, NULL);
|
||||
object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate,
|
||||
sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ,
|
||||
&error_abort, NULL);
|
||||
@@ -154,8 +171,6 @@ static void iotkit_init(Object *obj)
|
||||
TYPE_SPLIT_IRQ, &error_abort, NULL);
|
||||
g_free(name);
|
||||
}
|
||||
sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer),
|
||||
TYPE_UNIMPLEMENTED_DEVICE);
|
||||
}
|
||||
|
||||
static void iotkit_exp_irq(void *opaque, int n, int level)
|
||||
@@ -390,13 +405,15 @@ static void iotkit_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
qdev_prop_set_string(DEVICE(&s->dualtimer), "name", "Dual timer");
|
||||
qdev_prop_set_uint64(DEVICE(&s->dualtimer), "size", 0x1000);
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq);
|
||||
object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->armv7m), 5));
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0);
|
||||
object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err);
|
||||
if (err) {
|
||||
@@ -462,13 +479,14 @@ static void iotkit_realize(DeviceState *dev, Error **errp)
|
||||
/* Devices behind APB PPC1:
|
||||
* 0x4002f000: S32K timer
|
||||
*/
|
||||
qdev_prop_set_string(DEVICE(&s->s32ktimer), "name", "S32KTIMER");
|
||||
qdev_prop_set_uint64(DEVICE(&s->s32ktimer), "size", 0x1000);
|
||||
qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK);
|
||||
object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->armv7m), 2));
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0);
|
||||
object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err);
|
||||
if (err) {
|
||||
@@ -501,19 +519,66 @@ static void iotkit_realize(DeviceState *dev, Error **errp)
|
||||
qdev_get_gpio_in_named(dev_apb_ppc1,
|
||||
"cfg_sec_resp", 0));
|
||||
|
||||
/* Using create_unimplemented_device() maps the stub into the
|
||||
* system address space rather than into our container, but the
|
||||
* overall effect to the guest is the same.
|
||||
*/
|
||||
create_unimplemented_device("SYSINFO", 0x40020000, 0x1000);
|
||||
object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
/* System information registers */
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000);
|
||||
/* System control registers */
|
||||
object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000);
|
||||
|
||||
create_unimplemented_device("SYSCONTROL", 0x50021000, 0x1000);
|
||||
create_unimplemented_device("S32KWATCHDOG", 0x5002e000, 0x1000);
|
||||
/* This OR gate wires together outputs from the secure watchdogs to NMI */
|
||||
object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0));
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK);
|
||||
object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0));
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000);
|
||||
|
||||
/* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */
|
||||
|
||||
create_unimplemented_device("NS watchdog", 0x40081000, 0x1000);
|
||||
create_unimplemented_device("S watchdog", 0x50081000, 0x1000);
|
||||
qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq);
|
||||
object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->armv7m), 1));
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000);
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq);
|
||||
object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1));
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) {
|
||||
Object *splitter = OBJECT(&s->ppc_irq_splitter[i]);
|
||||
@@ -602,6 +667,21 @@ static void iotkit_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
iotkit_forward_sec_resp_cfg(s);
|
||||
|
||||
/* Forward the MSC related signals */
|
||||
qdev_pass_gpios(dev_secctl, dev, "mscexp_status");
|
||||
qdev_pass_gpios(dev_secctl, dev, "mscexp_clear");
|
||||
qdev_pass_gpios(dev_secctl, dev, "mscexp_ns");
|
||||
qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->armv7m), 11));
|
||||
|
||||
/*
|
||||
* Expose our container region to the board model; this corresponds
|
||||
* to the AHB Slave Expansion ports which allow bus master devices
|
||||
* (eg DMA controllers) in the board model to make transactions into
|
||||
* devices in the IoTKit.
|
||||
*/
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
|
||||
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq;
|
||||
}
|
||||
|
||||
|
||||
+127
-15
@@ -45,7 +45,10 @@
|
||||
#include "hw/misc/mps2-scc.h"
|
||||
#include "hw/misc/mps2-fpgaio.h"
|
||||
#include "hw/misc/tz-mpc.h"
|
||||
#include "hw/misc/tz-msc.h"
|
||||
#include "hw/arm/iotkit.h"
|
||||
#include "hw/dma/pl080.h"
|
||||
#include "hw/ssi/pl022.h"
|
||||
#include "hw/devices.h"
|
||||
#include "net/net.h"
|
||||
#include "hw/core/split-irq.h"
|
||||
@@ -71,12 +74,13 @@ typedef struct {
|
||||
MPS2FPGAIO fpgaio;
|
||||
TZPPC ppc[5];
|
||||
TZMPC ssram_mpc[3];
|
||||
UnimplementedDeviceState spi[5];
|
||||
PL022State spi[5];
|
||||
UnimplementedDeviceState i2c[4];
|
||||
UnimplementedDeviceState i2s_audio;
|
||||
UnimplementedDeviceState gpio[4];
|
||||
UnimplementedDeviceState dma[4];
|
||||
UnimplementedDeviceState gfx;
|
||||
PL080State dma[4];
|
||||
TZMSC msc[4];
|
||||
CMSDKAPBUART uart[5];
|
||||
SplitIRQ sec_resp_splitter;
|
||||
qemu_or_irq uart_irq_orgate;
|
||||
@@ -188,7 +192,7 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque,
|
||||
sccdev = DEVICE(scc);
|
||||
qdev_set_parent_bus(sccdev, sysbus_get_default());
|
||||
qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2);
|
||||
qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008);
|
||||
qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008);
|
||||
qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id);
|
||||
object_property_set_bool(OBJECT(scc), true, "realized", &error_fatal);
|
||||
return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0);
|
||||
@@ -263,6 +267,89 @@ static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque,
|
||||
return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0);
|
||||
}
|
||||
|
||||
static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque,
|
||||
const char *name, hwaddr size)
|
||||
{
|
||||
PL080State *dma = opaque;
|
||||
int i = dma - &mms->dma[0];
|
||||
SysBusDevice *s;
|
||||
char *mscname = g_strdup_printf("%s-msc", name);
|
||||
TZMSC *msc = &mms->msc[i];
|
||||
DeviceState *iotkitdev = DEVICE(&mms->iotkit);
|
||||
MemoryRegion *msc_upstream;
|
||||
MemoryRegion *msc_downstream;
|
||||
|
||||
/*
|
||||
* Each DMA device is a PL081 whose transaction master interface
|
||||
* is guarded by a Master Security Controller. The downstream end of
|
||||
* the MSC connects to the IoTKit AHB Slave Expansion port, so the
|
||||
* DMA devices can see all devices and memory that the CPU does.
|
||||
*/
|
||||
sysbus_init_child_obj(OBJECT(mms), mscname, msc, sizeof(*msc), TYPE_TZ_MSC);
|
||||
msc_downstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(&mms->iotkit), 0);
|
||||
object_property_set_link(OBJECT(msc), OBJECT(msc_downstream),
|
||||
"downstream", &error_fatal);
|
||||
object_property_set_link(OBJECT(msc), OBJECT(mms),
|
||||
"idau", &error_fatal);
|
||||
object_property_set_bool(OBJECT(msc), true, "realized", &error_fatal);
|
||||
|
||||
qdev_connect_gpio_out_named(DEVICE(msc), "irq", 0,
|
||||
qdev_get_gpio_in_named(iotkitdev,
|
||||
"mscexp_status", i));
|
||||
qdev_connect_gpio_out_named(iotkitdev, "mscexp_clear", i,
|
||||
qdev_get_gpio_in_named(DEVICE(msc),
|
||||
"irq_clear", 0));
|
||||
qdev_connect_gpio_out_named(iotkitdev, "mscexp_ns", i,
|
||||
qdev_get_gpio_in_named(DEVICE(msc),
|
||||
"cfg_nonsec", 0));
|
||||
qdev_connect_gpio_out(DEVICE(&mms->sec_resp_splitter),
|
||||
ARRAY_SIZE(mms->ppc) + i,
|
||||
qdev_get_gpio_in_named(DEVICE(msc),
|
||||
"cfg_sec_resp", 0));
|
||||
msc_upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(msc), 0);
|
||||
|
||||
sysbus_init_child_obj(OBJECT(mms), name, dma, sizeof(*dma), TYPE_PL081);
|
||||
object_property_set_link(OBJECT(dma), OBJECT(msc_upstream),
|
||||
"downstream", &error_fatal);
|
||||
object_property_set_bool(OBJECT(dma), true, "realized", &error_fatal);
|
||||
|
||||
s = SYS_BUS_DEVICE(dma);
|
||||
/* Wire up DMACINTR, DMACINTERR, DMACINTTC */
|
||||
sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev,
|
||||
"EXP_IRQ", 58 + i * 3));
|
||||
sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev,
|
||||
"EXP_IRQ", 56 + i * 3));
|
||||
sysbus_connect_irq(s, 2, qdev_get_gpio_in_named(iotkitdev,
|
||||
"EXP_IRQ", 57 + i * 3));
|
||||
|
||||
return sysbus_mmio_get_region(s, 0);
|
||||
}
|
||||
|
||||
static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque,
|
||||
const char *name, hwaddr size)
|
||||
{
|
||||
/*
|
||||
* The AN505 has five PL022 SPI controllers.
|
||||
* One of these should have the LCD controller behind it; the others
|
||||
* are connected only to the FPGA's "general purpose SPI connector"
|
||||
* or "shield" expansion connectors.
|
||||
* Note that if we do implement devices behind SPI, the chip select
|
||||
* lines are set via the "MISC" register in the MPS2 FPGAIO device.
|
||||
*/
|
||||
PL022State *spi = opaque;
|
||||
int i = spi - &mms->spi[0];
|
||||
DeviceState *iotkitdev = DEVICE(&mms->iotkit);
|
||||
SysBusDevice *s;
|
||||
|
||||
sysbus_init_child_obj(OBJECT(mms), name, spi, sizeof(mms->spi[0]),
|
||||
TYPE_PL022);
|
||||
object_property_set_bool(OBJECT(spi), true, "realized", &error_fatal);
|
||||
s = SYS_BUS_DEVICE(spi);
|
||||
sysbus_connect_irq(s, 0,
|
||||
qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 51 + i));
|
||||
return sysbus_mmio_get_region(s, 0);
|
||||
}
|
||||
|
||||
static void mps2tz_common_init(MachineState *machine)
|
||||
{
|
||||
MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine);
|
||||
@@ -289,13 +376,14 @@ static void mps2tz_common_init(MachineState *machine)
|
||||
&error_fatal);
|
||||
|
||||
/* The sec_resp_cfg output from the IoTKit must be split into multiple
|
||||
* lines, one for each of the PPCs we create here.
|
||||
* lines, one for each of the PPCs we create here, plus one per MSC.
|
||||
*/
|
||||
object_initialize(&mms->sec_resp_splitter, sizeof(mms->sec_resp_splitter),
|
||||
TYPE_SPLIT_IRQ);
|
||||
object_property_add_child(OBJECT(machine), "sec-resp-splitter",
|
||||
OBJECT(&mms->sec_resp_splitter), &error_abort);
|
||||
object_property_set_int(OBJECT(&mms->sec_resp_splitter), 5,
|
||||
object_property_set_int(OBJECT(&mms->sec_resp_splitter),
|
||||
ARRAY_SIZE(mms->ppc) + ARRAY_SIZE(mms->msc),
|
||||
"num-lines", &error_fatal);
|
||||
object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true,
|
||||
"realized", &error_fatal);
|
||||
@@ -360,11 +448,11 @@ static void mps2tz_common_init(MachineState *machine)
|
||||
}, {
|
||||
.name = "apb_ppcexp1",
|
||||
.ports = {
|
||||
{ "spi0", make_unimp_dev, &mms->spi[0], 0x40205000, 0x1000 },
|
||||
{ "spi1", make_unimp_dev, &mms->spi[1], 0x40206000, 0x1000 },
|
||||
{ "spi2", make_unimp_dev, &mms->spi[2], 0x40209000, 0x1000 },
|
||||
{ "spi3", make_unimp_dev, &mms->spi[3], 0x4020a000, 0x1000 },
|
||||
{ "spi4", make_unimp_dev, &mms->spi[4], 0x4020b000, 0x1000 },
|
||||
{ "spi0", make_spi, &mms->spi[0], 0x40205000, 0x1000 },
|
||||
{ "spi1", make_spi, &mms->spi[1], 0x40206000, 0x1000 },
|
||||
{ "spi2", make_spi, &mms->spi[2], 0x40209000, 0x1000 },
|
||||
{ "spi3", make_spi, &mms->spi[3], 0x4020a000, 0x1000 },
|
||||
{ "spi4", make_spi, &mms->spi[4], 0x4020b000, 0x1000 },
|
||||
{ "uart0", make_uart, &mms->uart[0], 0x40200000, 0x1000 },
|
||||
{ "uart1", make_uart, &mms->uart[1], 0x40201000, 0x1000 },
|
||||
{ "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 },
|
||||
@@ -396,10 +484,10 @@ static void mps2tz_common_init(MachineState *machine)
|
||||
}, {
|
||||
.name = "ahb_ppcexp1",
|
||||
.ports = {
|
||||
{ "dma0", make_unimp_dev, &mms->dma[0], 0x40110000, 0x1000 },
|
||||
{ "dma1", make_unimp_dev, &mms->dma[1], 0x40111000, 0x1000 },
|
||||
{ "dma2", make_unimp_dev, &mms->dma[2], 0x40112000, 0x1000 },
|
||||
{ "dma3", make_unimp_dev, &mms->dma[3], 0x40113000, 0x1000 },
|
||||
{ "dma0", make_dma, &mms->dma[0], 0x40110000, 0x1000 },
|
||||
{ "dma1", make_dma, &mms->dma[1], 0x40111000, 0x1000 },
|
||||
{ "dma2", make_dma, &mms->dma[2], 0x40112000, 0x1000 },
|
||||
{ "dma3", make_dma, &mms->dma[3], 0x40113000, 0x1000 },
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -480,12 +568,32 @@ static void mps2tz_common_init(MachineState *machine)
|
||||
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000);
|
||||
}
|
||||
|
||||
static void mps2_tz_idau_check(IDAUInterface *ii, uint32_t address,
|
||||
int *iregion, bool *exempt, bool *ns, bool *nsc)
|
||||
{
|
||||
/*
|
||||
* The MPS2 TZ FPGA images have IDAUs in them which are connected to
|
||||
* the Master Security Controllers. Thes have the same logic as
|
||||
* is used by the IoTKit for the IDAU connected to the CPU, except
|
||||
* that MSCs don't care about the NSC attribute.
|
||||
*/
|
||||
int region = extract32(address, 28, 4);
|
||||
|
||||
*ns = !(region & 1);
|
||||
*nsc = false;
|
||||
/* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */
|
||||
*exempt = (address & 0xeff00000) == 0xe0000000;
|
||||
*iregion = region;
|
||||
}
|
||||
|
||||
static void mps2tz_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc);
|
||||
|
||||
mc->init = mps2tz_common_init;
|
||||
mc->max_cpus = 1;
|
||||
iic->check = mps2_tz_idau_check;
|
||||
}
|
||||
|
||||
static void mps2tz_an505_class_init(ObjectClass *oc, void *data)
|
||||
@@ -496,7 +604,7 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data)
|
||||
mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33";
|
||||
mmc->fpga_type = FPGA_AN505;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
|
||||
mmc->scc_id = 0x41040000 | (505 << 4);
|
||||
mmc->scc_id = 0x41045050;
|
||||
}
|
||||
|
||||
static const TypeInfo mps2tz_info = {
|
||||
@@ -506,6 +614,10 @@ static const TypeInfo mps2tz_info = {
|
||||
.instance_size = sizeof(MPS2TZMachineState),
|
||||
.class_size = sizeof(MPS2TZMachineClass),
|
||||
.class_init = mps2tz_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_IDAU_INTERFACE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static const TypeInfo mps2tz_an505_info = {
|
||||
|
||||
+14
-3
@@ -34,6 +34,7 @@
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/char/cmsdk-apb-uart.h"
|
||||
#include "hw/timer/cmsdk-apb-timer.h"
|
||||
#include "hw/timer/cmsdk-apb-dualtimer.h"
|
||||
#include "hw/misc/mps2-scc.h"
|
||||
#include "hw/devices.h"
|
||||
#include "net/net.h"
|
||||
@@ -64,6 +65,7 @@ typedef struct {
|
||||
MemoryRegion blockram_m3;
|
||||
MemoryRegion sram;
|
||||
MPS2SCC scc;
|
||||
CMSDKAPBDualTimer dualtimer;
|
||||
} MPS2MachineState;
|
||||
|
||||
#define TYPE_MPS2_MACHINE "mps2"
|
||||
@@ -297,11 +299,20 @@ static void mps2_common_init(MachineState *machine)
|
||||
cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ);
|
||||
cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ);
|
||||
|
||||
sysbus_init_child_obj(OBJECT(mms), "dualtimer", &mms->dualtimer,
|
||||
sizeof(mms->dualtimer), TYPE_CMSDK_APB_DUALTIMER);
|
||||
qdev_prop_set_uint32(DEVICE(&mms->dualtimer), "pclk-frq", SYSCLK_FRQ);
|
||||
object_property_set_bool(OBJECT(&mms->dualtimer), true, "realized",
|
||||
&error_fatal);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 0,
|
||||
qdev_get_gpio_in(armv7m, 10));
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&mms->dualtimer), 0, 0x40002000);
|
||||
|
||||
object_initialize(&mms->scc, sizeof(mms->scc), TYPE_MPS2_SCC);
|
||||
sccdev = DEVICE(&mms->scc);
|
||||
qdev_set_parent_bus(sccdev, sysbus_get_default());
|
||||
qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2);
|
||||
qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008);
|
||||
qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008);
|
||||
qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id);
|
||||
object_property_set_bool(OBJECT(&mms->scc), true, "realized",
|
||||
&error_fatal);
|
||||
@@ -336,7 +347,7 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data)
|
||||
mc->desc = "ARM MPS2 with AN385 FPGA image for Cortex-M3";
|
||||
mmc->fpga_type = FPGA_AN385;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
|
||||
mmc->scc_id = 0x41040000 | (385 << 4);
|
||||
mmc->scc_id = 0x41043850;
|
||||
}
|
||||
|
||||
static void mps2_an511_class_init(ObjectClass *oc, void *data)
|
||||
@@ -347,7 +358,7 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data)
|
||||
mc->desc = "ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3";
|
||||
mmc->fpga_type = FPGA_AN511;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
|
||||
mmc->scc_id = 0x4104000 | (511 << 4);
|
||||
mmc->scc_id = 0x41045110;
|
||||
}
|
||||
|
||||
static const TypeInfo mps2_info = {
|
||||
|
||||
+59
-5
@@ -172,6 +172,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
MachineState parent;
|
||||
bool secure;
|
||||
bool virt;
|
||||
} VexpressMachineState;
|
||||
|
||||
#define TYPE_VEXPRESS_MACHINE "vexpress"
|
||||
@@ -203,7 +204,7 @@ struct VEDBoardInfo {
|
||||
};
|
||||
|
||||
static void init_cpus(const char *cpu_type, const char *privdev,
|
||||
hwaddr periphbase, qemu_irq *pic, bool secure)
|
||||
hwaddr periphbase, qemu_irq *pic, bool secure, bool virt)
|
||||
{
|
||||
DeviceState *dev;
|
||||
SysBusDevice *busdev;
|
||||
@@ -216,6 +217,11 @@ static void init_cpus(const char *cpu_type, const char *privdev,
|
||||
if (!secure) {
|
||||
object_property_set_bool(cpuobj, false, "has_el3", NULL);
|
||||
}
|
||||
if (!virt) {
|
||||
if (object_property_find(cpuobj, "has_el2", NULL)) {
|
||||
object_property_set_bool(cpuobj, false, "has_el2", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
|
||||
object_property_set_int(cpuobj, periphbase,
|
||||
@@ -251,6 +257,10 @@ static void init_cpus(const char *cpu_type, const char *privdev,
|
||||
sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
|
||||
sysbus_connect_irq(busdev, n + smp_cpus,
|
||||
qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
|
||||
sysbus_connect_irq(busdev, n + 2 * smp_cpus,
|
||||
qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
|
||||
sysbus_connect_irq(busdev, n + 3 * smp_cpus,
|
||||
qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +295,8 @@ static void a9_daughterboard_init(const VexpressMachineState *vms,
|
||||
memory_region_add_subregion(sysmem, 0x60000000, ram);
|
||||
|
||||
/* 0x1e000000 A9MPCore (SCU) private memory region */
|
||||
init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic, vms->secure);
|
||||
init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic,
|
||||
vms->secure, vms->virt);
|
||||
|
||||
/* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
|
||||
|
||||
@@ -366,7 +377,8 @@ static void a15_daughterboard_init(const VexpressMachineState *vms,
|
||||
memory_region_add_subregion(sysmem, 0x80000000, ram);
|
||||
|
||||
/* 0x2c000000 A15MPCore private memory region (GIC) */
|
||||
init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure);
|
||||
init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure,
|
||||
vms->virt);
|
||||
|
||||
/* A15 daughterboard peripherals: */
|
||||
|
||||
@@ -701,8 +713,8 @@ static void vexpress_common_init(MachineState *machine)
|
||||
daughterboard->bootinfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
|
||||
daughterboard->bootinfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
|
||||
daughterboard->bootinfo.modify_dtb = vexpress_modify_dtb;
|
||||
/* Indicate that when booting Linux we should be in secure state */
|
||||
daughterboard->bootinfo.secure_boot = true;
|
||||
/* When booting Linux we should be in secure state if the CPU has one. */
|
||||
daughterboard->bootinfo.secure_boot = vms->secure;
|
||||
arm_load_kernel(ARM_CPU(first_cpu), &daughterboard->bootinfo);
|
||||
}
|
||||
|
||||
@@ -720,6 +732,20 @@ static void vexpress_set_secure(Object *obj, bool value, Error **errp)
|
||||
vms->secure = value;
|
||||
}
|
||||
|
||||
static bool vexpress_get_virt(Object *obj, Error **errp)
|
||||
{
|
||||
VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
|
||||
|
||||
return vms->virt;
|
||||
}
|
||||
|
||||
static void vexpress_set_virt(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
|
||||
|
||||
vms->virt = value;
|
||||
}
|
||||
|
||||
static void vexpress_instance_init(Object *obj)
|
||||
{
|
||||
VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
|
||||
@@ -734,6 +760,32 @@ static void vexpress_instance_init(Object *obj)
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void vexpress_a15_instance_init(Object *obj)
|
||||
{
|
||||
VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
|
||||
|
||||
/*
|
||||
* For the vexpress-a15, EL2 is by default enabled if EL3 is,
|
||||
* but can also be specifically set to on or off.
|
||||
*/
|
||||
vms->virt = true;
|
||||
object_property_add_bool(obj, "virtualization", vexpress_get_virt,
|
||||
vexpress_set_virt, NULL);
|
||||
object_property_set_description(obj, "virtualization",
|
||||
"Set on/off to enable/disable the ARM "
|
||||
"Virtualization Extensions "
|
||||
"(defaults to same as 'secure')",
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void vexpress_a9_instance_init(Object *obj)
|
||||
{
|
||||
VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
|
||||
|
||||
/* The A9 doesn't have the virt extensions */
|
||||
vms->virt = false;
|
||||
}
|
||||
|
||||
static void vexpress_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
@@ -780,12 +832,14 @@ static const TypeInfo vexpress_a9_info = {
|
||||
.name = TYPE_VEXPRESS_A9_MACHINE,
|
||||
.parent = TYPE_VEXPRESS_MACHINE,
|
||||
.class_init = vexpress_a9_class_init,
|
||||
.instance_init = vexpress_a9_instance_init,
|
||||
};
|
||||
|
||||
static const TypeInfo vexpress_a15_info = {
|
||||
.name = TYPE_VEXPRESS_A15_MACHINE,
|
||||
.parent = TYPE_VEXPRESS_MACHINE,
|
||||
.class_init = vexpress_a15_class_init,
|
||||
.instance_init = vexpress_a15_instance_init,
|
||||
};
|
||||
|
||||
static void vexpress_machine_init(void)
|
||||
|
||||
+28
-3
@@ -53,6 +53,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
|
||||
int i;
|
||||
Error *err = NULL;
|
||||
bool has_el3;
|
||||
bool has_el2 = false;
|
||||
Object *cpuobj;
|
||||
|
||||
gicdev = DEVICE(&s->gic);
|
||||
@@ -67,6 +68,10 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
|
||||
has_el3 = object_property_find(cpuobj, "has_el3", NULL) &&
|
||||
object_property_get_bool(cpuobj, "has_el3", &error_abort);
|
||||
qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3);
|
||||
/* Similarly for virtualization support */
|
||||
has_el2 = object_property_find(cpuobj, "has_el2", NULL) &&
|
||||
object_property_get_bool(cpuobj, "has_el2", &error_abort);
|
||||
qdev_prop_set_bit(gicdev, "has-virtualization-extensions", has_el2);
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
|
||||
@@ -103,20 +108,40 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
|
||||
qdev_get_gpio_in(gicdev,
|
||||
ppibase + timer_irq[irq]));
|
||||
}
|
||||
if (has_el2) {
|
||||
/* Connect the GIC maintenance interrupt to PPI ID 25 */
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(gicdev), i + 4 * s->num_cpu,
|
||||
qdev_get_gpio_in(gicdev, ppibase + 25));
|
||||
}
|
||||
}
|
||||
|
||||
/* Memory map (addresses are offsets from PERIPHBASE):
|
||||
* 0x0000-0x0fff -- reserved
|
||||
* 0x1000-0x1fff -- GIC Distributor
|
||||
* 0x2000-0x3fff -- GIC CPU interface
|
||||
* 0x4000-0x4fff -- GIC virtual interface control (not modelled)
|
||||
* 0x5000-0x5fff -- GIC virtual interface control (not modelled)
|
||||
* 0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
|
||||
* 0x4000-0x4fff -- GIC virtual interface control for this CPU
|
||||
* 0x5000-0x51ff -- GIC virtual interface control for CPU 0
|
||||
* 0x5200-0x53ff -- GIC virtual interface control for CPU 1
|
||||
* 0x5400-0x55ff -- GIC virtual interface control for CPU 2
|
||||
* 0x5600-0x57ff -- GIC virtual interface control for CPU 3
|
||||
* 0x6000-0x7fff -- GIC virtual CPU interface
|
||||
*/
|
||||
memory_region_add_subregion(&s->container, 0x1000,
|
||||
sysbus_mmio_get_region(busdev, 0));
|
||||
memory_region_add_subregion(&s->container, 0x2000,
|
||||
sysbus_mmio_get_region(busdev, 1));
|
||||
if (has_el2) {
|
||||
memory_region_add_subregion(&s->container, 0x4000,
|
||||
sysbus_mmio_get_region(busdev, 2));
|
||||
memory_region_add_subregion(&s->container, 0x6000,
|
||||
sysbus_mmio_get_region(busdev, 3));
|
||||
for (i = 0; i < s->num_cpu; i++) {
|
||||
hwaddr base = 0x5000 + i * 0x200;
|
||||
MemoryRegion *mr = sysbus_mmio_get_region(busdev,
|
||||
4 + s->num_cpu + i);
|
||||
memory_region_add_subregion(&s->container, base, mr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Property a15mp_priv_properties[] = {
|
||||
|
||||
+130
-88
@@ -34,6 +34,13 @@
|
||||
#define DEFAULT_VCRAM_SIZE 0x4000000
|
||||
#define BCM2835_FB_OFFSET 0x00100000
|
||||
|
||||
/* Maximum permitted framebuffer size; experimentally determined on an rpi2 */
|
||||
#define XRES_MAX 3840
|
||||
#define YRES_MAX 2560
|
||||
/* Framebuffer size used if guest requests zero size */
|
||||
#define XRES_SMALL 592
|
||||
#define YRES_SMALL 488
|
||||
|
||||
static void fb_invalidate_display(void *opaque)
|
||||
{
|
||||
BCM2835FBState *s = BCM2835_FB(opaque);
|
||||
@@ -52,7 +59,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
|
||||
int bpp = surface_bits_per_pixel(surface);
|
||||
|
||||
while (width--) {
|
||||
switch (s->bpp) {
|
||||
switch (s->config.bpp) {
|
||||
case 8:
|
||||
/* lookup palette starting at video ram base
|
||||
* TODO: cache translation, rather than doing this each time!
|
||||
@@ -91,7 +98,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->pixo == 0) {
|
||||
if (s->config.pixo == 0) {
|
||||
/* swap to BGR pixel format */
|
||||
uint8_t tmp = r;
|
||||
r = b;
|
||||
@@ -126,6 +133,18 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
|
||||
}
|
||||
}
|
||||
|
||||
static bool fb_use_offsets(BCM2835FBConfig *config)
|
||||
{
|
||||
/*
|
||||
* Return true if we should use the viewport offsets.
|
||||
* Experimentally, the hardware seems to do this only if the
|
||||
* viewport size is larger than the physical screen. (It doesn't
|
||||
* prevent the guest setting this silly viewport setting, though...)
|
||||
*/
|
||||
return config->xres_virtual > config->xres &&
|
||||
config->yres_virtual > config->yres;
|
||||
}
|
||||
|
||||
static void fb_update_display(void *opaque)
|
||||
{
|
||||
BCM2835FBState *s = opaque;
|
||||
@@ -134,13 +153,19 @@ static void fb_update_display(void *opaque)
|
||||
int last = 0;
|
||||
int src_width = 0;
|
||||
int dest_width = 0;
|
||||
uint32_t xoff = 0, yoff = 0;
|
||||
|
||||
if (s->lock || !s->xres) {
|
||||
if (s->lock || !s->config.xres) {
|
||||
return;
|
||||
}
|
||||
|
||||
src_width = s->xres * (s->bpp >> 3);
|
||||
dest_width = s->xres;
|
||||
src_width = bcm2835_fb_get_pitch(&s->config);
|
||||
if (fb_use_offsets(&s->config)) {
|
||||
xoff = s->config.xoffset;
|
||||
yoff = s->config.yoffset;
|
||||
}
|
||||
|
||||
dest_width = s->config.xres;
|
||||
|
||||
switch (surface_bits_per_pixel(surface)) {
|
||||
case 0:
|
||||
@@ -165,89 +190,104 @@ static void fb_update_display(void *opaque)
|
||||
}
|
||||
|
||||
if (s->invalidate) {
|
||||
framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base,
|
||||
s->yres, src_width);
|
||||
hwaddr base = s->config.base + xoff + yoff * src_width;
|
||||
framebuffer_update_memory_section(&s->fbsection, s->dma_mr,
|
||||
base,
|
||||
s->config.yres, src_width);
|
||||
}
|
||||
|
||||
framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
|
||||
framebuffer_update_display(surface, &s->fbsection,
|
||||
s->config.xres, s->config.yres,
|
||||
src_width, dest_width, 0, s->invalidate,
|
||||
draw_line_src16, s, &first, &last);
|
||||
|
||||
if (first >= 0) {
|
||||
dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1);
|
||||
dpy_gfx_update(s->con, 0, first, s->config.xres,
|
||||
last - first + 1);
|
||||
}
|
||||
|
||||
s->invalidate = false;
|
||||
}
|
||||
|
||||
static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
|
||||
void bcm2835_fb_validate_config(BCM2835FBConfig *config)
|
||||
{
|
||||
value &= ~0xf;
|
||||
/*
|
||||
* Validate the config, and clip any bogus values into range,
|
||||
* as the hardware does. Note that fb_update_display() relies on
|
||||
* this happening to prevent it from performing out-of-range
|
||||
* accesses on redraw.
|
||||
*/
|
||||
config->xres = MIN(config->xres, XRES_MAX);
|
||||
config->xres_virtual = MIN(config->xres_virtual, XRES_MAX);
|
||||
config->yres = MIN(config->yres, YRES_MAX);
|
||||
config->yres_virtual = MIN(config->yres_virtual, YRES_MAX);
|
||||
|
||||
/*
|
||||
* These are not minima: a 40x40 framebuffer will be accepted.
|
||||
* They're only used as defaults if the guest asks for zero size.
|
||||
*/
|
||||
if (config->xres == 0) {
|
||||
config->xres = XRES_SMALL;
|
||||
}
|
||||
if (config->yres == 0) {
|
||||
config->yres = YRES_SMALL;
|
||||
}
|
||||
if (config->xres_virtual == 0) {
|
||||
config->xres_virtual = config->xres;
|
||||
}
|
||||
if (config->yres_virtual == 0) {
|
||||
config->yres_virtual = config->yres;
|
||||
}
|
||||
|
||||
if (fb_use_offsets(config)) {
|
||||
/* Clip the offsets so the viewport is within the physical screen */
|
||||
config->xoffset = MIN(config->xoffset,
|
||||
config->xres_virtual - config->xres);
|
||||
config->yoffset = MIN(config->yoffset,
|
||||
config->yres_virtual - config->yres);
|
||||
}
|
||||
}
|
||||
|
||||
void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig)
|
||||
{
|
||||
s->lock = true;
|
||||
|
||||
s->xres = ldl_le_phys(&s->dma_as, value);
|
||||
s->yres = ldl_le_phys(&s->dma_as, value + 4);
|
||||
s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
|
||||
s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
|
||||
s->bpp = ldl_le_phys(&s->dma_as, value + 20);
|
||||
s->xoffset = ldl_le_phys(&s->dma_as, value + 24);
|
||||
s->yoffset = ldl_le_phys(&s->dma_as, value + 28);
|
||||
|
||||
s->base = s->vcram_base | (value & 0xc0000000);
|
||||
s->base += BCM2835_FB_OFFSET;
|
||||
|
||||
/* TODO - Manage properly virtual resolution */
|
||||
|
||||
s->pitch = s->xres * (s->bpp >> 3);
|
||||
s->size = s->yres * s->pitch;
|
||||
|
||||
stl_le_phys(&s->dma_as, value + 16, s->pitch);
|
||||
stl_le_phys(&s->dma_as, value + 32, s->base);
|
||||
stl_le_phys(&s->dma_as, value + 36, s->size);
|
||||
s->config = *newconfig;
|
||||
|
||||
s->invalidate = true;
|
||||
qemu_console_resize(s->con, s->xres, s->yres);
|
||||
qemu_console_resize(s->con, s->config.xres, s->config.yres);
|
||||
s->lock = false;
|
||||
}
|
||||
|
||||
void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
|
||||
uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
|
||||
uint32_t *pixo, uint32_t *alpha)
|
||||
static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
|
||||
{
|
||||
s->lock = true;
|
||||
uint32_t pitch;
|
||||
uint32_t size;
|
||||
BCM2835FBConfig newconf;
|
||||
|
||||
/* TODO: input validation! */
|
||||
if (xres) {
|
||||
s->xres = *xres;
|
||||
}
|
||||
if (yres) {
|
||||
s->yres = *yres;
|
||||
}
|
||||
if (xoffset) {
|
||||
s->xoffset = *xoffset;
|
||||
}
|
||||
if (yoffset) {
|
||||
s->yoffset = *yoffset;
|
||||
}
|
||||
if (bpp) {
|
||||
s->bpp = *bpp;
|
||||
}
|
||||
if (pixo) {
|
||||
s->pixo = *pixo;
|
||||
}
|
||||
if (alpha) {
|
||||
s->alpha = *alpha;
|
||||
}
|
||||
value &= ~0xf;
|
||||
|
||||
/* TODO - Manage properly virtual resolution */
|
||||
newconf.xres = ldl_le_phys(&s->dma_as, value);
|
||||
newconf.yres = ldl_le_phys(&s->dma_as, value + 4);
|
||||
newconf.xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
|
||||
newconf.yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newconf.bpp = ldl_le_phys(&s->dma_as, value + 20);
|
||||
newconf.xoffset = ldl_le_phys(&s->dma_as, value + 24);
|
||||
newconf.yoffset = ldl_le_phys(&s->dma_as, value + 28);
|
||||
|
||||
s->pitch = s->xres * (s->bpp >> 3);
|
||||
s->size = s->yres * s->pitch;
|
||||
newconf.base = s->vcram_base | (value & 0xc0000000);
|
||||
newconf.base += BCM2835_FB_OFFSET;
|
||||
|
||||
s->invalidate = true;
|
||||
qemu_console_resize(s->con, s->xres, s->yres);
|
||||
s->lock = false;
|
||||
bcm2835_fb_validate_config(&newconf);
|
||||
|
||||
pitch = bcm2835_fb_get_pitch(&newconf);
|
||||
size = bcm2835_fb_get_size(&newconf);
|
||||
|
||||
stl_le_phys(&s->dma_as, value + 16, pitch);
|
||||
stl_le_phys(&s->dma_as, value + 32, newconf.base);
|
||||
stl_le_phys(&s->dma_as, value + 36, size);
|
||||
|
||||
bcm2835_fb_reconfigure(s, &newconf);
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size)
|
||||
@@ -312,18 +352,17 @@ static const VMStateDescription vmstate_bcm2835_fb = {
|
||||
VMSTATE_BOOL(lock, BCM2835FBState),
|
||||
VMSTATE_BOOL(invalidate, BCM2835FBState),
|
||||
VMSTATE_BOOL(pending, BCM2835FBState),
|
||||
VMSTATE_UINT32(xres, BCM2835FBState),
|
||||
VMSTATE_UINT32(yres, BCM2835FBState),
|
||||
VMSTATE_UINT32(xres_virtual, BCM2835FBState),
|
||||
VMSTATE_UINT32(yres_virtual, BCM2835FBState),
|
||||
VMSTATE_UINT32(xoffset, BCM2835FBState),
|
||||
VMSTATE_UINT32(yoffset, BCM2835FBState),
|
||||
VMSTATE_UINT32(bpp, BCM2835FBState),
|
||||
VMSTATE_UINT32(base, BCM2835FBState),
|
||||
VMSTATE_UINT32(pitch, BCM2835FBState),
|
||||
VMSTATE_UINT32(size, BCM2835FBState),
|
||||
VMSTATE_UINT32(pixo, BCM2835FBState),
|
||||
VMSTATE_UINT32(alpha, BCM2835FBState),
|
||||
VMSTATE_UINT32(config.xres, BCM2835FBState),
|
||||
VMSTATE_UINT32(config.yres, BCM2835FBState),
|
||||
VMSTATE_UINT32(config.xres_virtual, BCM2835FBState),
|
||||
VMSTATE_UINT32(config.yres_virtual, BCM2835FBState),
|
||||
VMSTATE_UINT32(config.xoffset, BCM2835FBState),
|
||||
VMSTATE_UINT32(config.yoffset, BCM2835FBState),
|
||||
VMSTATE_UINT32(config.bpp, BCM2835FBState),
|
||||
VMSTATE_UINT32(config.base, BCM2835FBState),
|
||||
VMSTATE_UNUSED(8), /* Was pitch and size */
|
||||
VMSTATE_UINT32(config.pixo, BCM2835FBState),
|
||||
VMSTATE_UINT32(config.alpha, BCM2835FBState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
@@ -349,13 +388,7 @@ static void bcm2835_fb_reset(DeviceState *dev)
|
||||
|
||||
s->pending = false;
|
||||
|
||||
s->xres_virtual = s->xres;
|
||||
s->yres_virtual = s->yres;
|
||||
s->xoffset = 0;
|
||||
s->yoffset = 0;
|
||||
s->base = s->vcram_base + BCM2835_FB_OFFSET;
|
||||
s->pitch = s->xres * (s->bpp >> 3);
|
||||
s->size = s->yres * s->pitch;
|
||||
s->config = s->initial_config;
|
||||
|
||||
s->invalidate = true;
|
||||
s->lock = false;
|
||||
@@ -379,24 +412,33 @@ static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fill in the parts of initial_config that are not set by QOM properties */
|
||||
s->initial_config.xres_virtual = s->initial_config.xres;
|
||||
s->initial_config.yres_virtual = s->initial_config.yres;
|
||||
s->initial_config.xoffset = 0;
|
||||
s->initial_config.yoffset = 0;
|
||||
s->initial_config.base = s->vcram_base + BCM2835_FB_OFFSET;
|
||||
|
||||
s->dma_mr = MEMORY_REGION(obj);
|
||||
address_space_init(&s->dma_as, s->dma_mr, NULL);
|
||||
|
||||
bcm2835_fb_reset(dev);
|
||||
|
||||
s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
|
||||
qemu_console_resize(s->con, s->xres, s->yres);
|
||||
qemu_console_resize(s->con, s->config.xres, s->config.yres);
|
||||
}
|
||||
|
||||
static Property bcm2835_fb_props[] = {
|
||||
DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
|
||||
DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
|
||||
DEFAULT_VCRAM_SIZE),
|
||||
DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640),
|
||||
DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480),
|
||||
DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16),
|
||||
DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */
|
||||
DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */
|
||||
DEFINE_PROP_UINT32("xres", BCM2835FBState, initial_config.xres, 640),
|
||||
DEFINE_PROP_UINT32("yres", BCM2835FBState, initial_config.yres, 480),
|
||||
DEFINE_PROP_UINT32("bpp", BCM2835FBState, initial_config.bpp, 16),
|
||||
DEFINE_PROP_UINT32("pixo", BCM2835FBState,
|
||||
initial_config.pixo, 1), /* 1=RGB, 0=BGR */
|
||||
DEFINE_PROP_UINT32("alpha", BCM2835FBState,
|
||||
initial_config.alpha, 2), /* alpha ignored */
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -2084,7 +2084,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp)
|
||||
for (i = 0; i < s->num_cpu; i++) {
|
||||
memory_region_init_io(&s->vifaceiomem[i + 1], OBJECT(s),
|
||||
&gic_viface_ops, &s->backref[i],
|
||||
"gic_viface", 0x1000);
|
||||
"gic_viface", 0x200);
|
||||
sysbus_init_mmio(sbd, &s->vifaceiomem[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,8 +64,11 @@ obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o
|
||||
obj-$(CONFIG_MPS2_SCC) += mps2-scc.o
|
||||
|
||||
obj-$(CONFIG_TZ_MPC) += tz-mpc.o
|
||||
obj-$(CONFIG_TZ_MSC) += tz-msc.o
|
||||
obj-$(CONFIG_TZ_PPC) += tz-ppc.o
|
||||
obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o
|
||||
obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o
|
||||
obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o
|
||||
|
||||
obj-$(CONFIG_PVPANIC) += pvpanic.o
|
||||
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
|
||||
|
||||
+60
-63
@@ -21,11 +21,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
||||
uint32_t tmp;
|
||||
int n;
|
||||
uint32_t offset, length, color;
|
||||
uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha;
|
||||
uint32_t tmp_xres, tmp_yres, tmp_xoffset, tmp_yoffset;
|
||||
uint32_t tmp_bpp, tmp_pixo, tmp_alpha;
|
||||
uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL,
|
||||
*newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL;
|
||||
|
||||
/*
|
||||
* Copy the current state of the framebuffer config; we will update
|
||||
* this copy as we process tags and then ask the framebuffer to use
|
||||
* it at the end.
|
||||
*/
|
||||
BCM2835FBConfig fbconfig = s->fbdev->config;
|
||||
bool fbconfig_updated = false;
|
||||
|
||||
value &= ~0xf;
|
||||
|
||||
@@ -141,12 +144,9 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
||||
/* Frame buffer */
|
||||
|
||||
case 0x00040001: /* Allocate buffer */
|
||||
stl_le_phys(&s->dma_as, value + 12, s->fbdev->base);
|
||||
tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres;
|
||||
tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres;
|
||||
tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp;
|
||||
stl_le_phys(&s->dma_as, value + 12, fbconfig.base);
|
||||
stl_le_phys(&s->dma_as, value + 16,
|
||||
tmp_xres * tmp_yres * tmp_bpp / 8);
|
||||
bcm2835_fb_get_size(&fbconfig));
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00048001: /* Release buffer */
|
||||
@@ -155,86 +155,85 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
||||
case 0x00040002: /* Blank screen */
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040003: /* Get display width/height */
|
||||
case 0x00040004:
|
||||
tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres;
|
||||
tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres;
|
||||
stl_le_phys(&s->dma_as, value + 12, tmp_xres);
|
||||
stl_le_phys(&s->dma_as, value + 16, tmp_yres);
|
||||
case 0x00044003: /* Test physical display width/height */
|
||||
case 0x00044004: /* Test virtual display width/height */
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00044003: /* Test display width/height */
|
||||
case 0x00044004:
|
||||
case 0x00048003: /* Set physical display width/height */
|
||||
fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12);
|
||||
fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16);
|
||||
bcm2835_fb_validate_config(&fbconfig);
|
||||
fbconfig_updated = true;
|
||||
/* fall through */
|
||||
case 0x00040003: /* Get physical display width/height */
|
||||
stl_le_phys(&s->dma_as, value + 12, fbconfig.xres);
|
||||
stl_le_phys(&s->dma_as, value + 16, fbconfig.yres);
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00048003: /* Set display width/height */
|
||||
case 0x00048004:
|
||||
xres = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newxres = &xres;
|
||||
yres = ldl_le_phys(&s->dma_as, value + 16);
|
||||
newyres = &yres;
|
||||
case 0x00048004: /* Set virtual display width/height */
|
||||
fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12);
|
||||
fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16);
|
||||
bcm2835_fb_validate_config(&fbconfig);
|
||||
fbconfig_updated = true;
|
||||
/* fall through */
|
||||
case 0x00040004: /* Get virtual display width/height */
|
||||
stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual);
|
||||
stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual);
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00040005: /* Get depth */
|
||||
tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp;
|
||||
stl_le_phys(&s->dma_as, value + 12, tmp_bpp);
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00044005: /* Test depth */
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00048005: /* Set depth */
|
||||
bpp = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newbpp = &bpp;
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040006: /* Get pixel order */
|
||||
tmp_pixo = newpixo != NULL ? *newpixo : s->fbdev->pixo;
|
||||
stl_le_phys(&s->dma_as, value + 12, tmp_pixo);
|
||||
fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12);
|
||||
bcm2835_fb_validate_config(&fbconfig);
|
||||
fbconfig_updated = true;
|
||||
/* fall through */
|
||||
case 0x00040005: /* Get depth */
|
||||
stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp);
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00044006: /* Test pixel order */
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00048006: /* Set pixel order */
|
||||
pixo = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newpixo = &pixo;
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040007: /* Get alpha */
|
||||
tmp_alpha = newalpha != NULL ? *newalpha : s->fbdev->alpha;
|
||||
stl_le_phys(&s->dma_as, value + 12, tmp_alpha);
|
||||
fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12);
|
||||
bcm2835_fb_validate_config(&fbconfig);
|
||||
fbconfig_updated = true;
|
||||
/* fall through */
|
||||
case 0x00040006: /* Get pixel order */
|
||||
stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo);
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00044007: /* Test pixel alpha */
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00048007: /* Set alpha */
|
||||
alpha = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newalpha = α
|
||||
fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12);
|
||||
bcm2835_fb_validate_config(&fbconfig);
|
||||
fbconfig_updated = true;
|
||||
/* fall through */
|
||||
case 0x00040007: /* Get alpha */
|
||||
stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha);
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040008: /* Get pitch */
|
||||
tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres;
|
||||
tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp;
|
||||
stl_le_phys(&s->dma_as, value + 12, tmp_xres * tmp_bpp / 8);
|
||||
stl_le_phys(&s->dma_as, value + 12,
|
||||
bcm2835_fb_get_pitch(&fbconfig));
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040009: /* Get virtual offset */
|
||||
tmp_xoffset = newxoffset != NULL ? *newxoffset : s->fbdev->xoffset;
|
||||
tmp_yoffset = newyoffset != NULL ? *newyoffset : s->fbdev->yoffset;
|
||||
stl_le_phys(&s->dma_as, value + 12, tmp_xoffset);
|
||||
stl_le_phys(&s->dma_as, value + 16, tmp_yoffset);
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00044009: /* Test virtual offset */
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00048009: /* Set virtual offset */
|
||||
xoffset = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newxoffset = &xoffset;
|
||||
yoffset = ldl_le_phys(&s->dma_as, value + 16);
|
||||
newyoffset = &yoffset;
|
||||
fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12);
|
||||
fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16);
|
||||
bcm2835_fb_validate_config(&fbconfig);
|
||||
fbconfig_updated = true;
|
||||
/* fall through */
|
||||
case 0x00040009: /* Get virtual offset */
|
||||
stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset);
|
||||
stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset);
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x0004000a: /* Get/Test/Set overscan */
|
||||
@@ -285,10 +284,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
||||
}
|
||||
|
||||
/* Reconfigure framebuffer if required */
|
||||
if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo
|
||||
|| newalpha) {
|
||||
bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset,
|
||||
newyoffset, newbpp, newpixo, newalpha);
|
||||
if (fbconfig_updated) {
|
||||
bcm2835_fb_reconfigure(s->fbdev, &fbconfig);
|
||||
}
|
||||
|
||||
/* Buffer response code */
|
||||
|
||||
+65
-8
@@ -190,12 +190,13 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr,
|
||||
r = s->apbexp[offset_to_ppc_idx(offset)].sp;
|
||||
break;
|
||||
case A_SECMSCINTSTAT:
|
||||
r = s->secmscintstat;
|
||||
break;
|
||||
case A_SECMSCINTEN:
|
||||
r = s->secmscinten;
|
||||
break;
|
||||
case A_NSMSCEXP:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"IoTKit SecCtl S block read: "
|
||||
"unimplemented offset 0x%x\n", offset);
|
||||
r = 0;
|
||||
r = s->nsmscexp;
|
||||
break;
|
||||
case A_PID4:
|
||||
case A_PID5:
|
||||
@@ -291,6 +292,23 @@ static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc)
|
||||
qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1));
|
||||
}
|
||||
|
||||
static void iotkit_secctl_update_mscexp_irqs(qemu_irq *msc_irqs, uint32_t value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IOTS_NUM_EXP_MSC; i++) {
|
||||
qemu_set_irq(msc_irqs[i], extract32(value, i + 16, 1));
|
||||
}
|
||||
}
|
||||
|
||||
static void iotkit_secctl_update_msc_irq(IoTKitSecCtl *s)
|
||||
{
|
||||
/* Update the combined MSC IRQ, based on S_MSCEXP_STATUS and S_MSCEXP_EN */
|
||||
bool level = s->secmscintstat & s->secmscinten;
|
||||
|
||||
qemu_set_irq(s->msc_irq, level);
|
||||
}
|
||||
|
||||
static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr,
|
||||
uint64_t value,
|
||||
unsigned size, MemTxAttrs attrs)
|
||||
@@ -370,10 +388,15 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr,
|
||||
iotkit_secctl_ppc_sp_write(ppc, value);
|
||||
break;
|
||||
case A_SECMSCINTCLR:
|
||||
iotkit_secctl_update_mscexp_irqs(s->mscexp_clear, value);
|
||||
break;
|
||||
case A_SECMSCINTEN:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"IoTKit SecCtl S block write: "
|
||||
"unimplemented offset 0x%x\n", offset);
|
||||
s->secmscinten = value;
|
||||
iotkit_secctl_update_msc_irq(s);
|
||||
break;
|
||||
case A_NSMSCEXP:
|
||||
s->nsmscexp = value;
|
||||
iotkit_secctl_update_mscexp_irqs(s->mscexp_ns, value);
|
||||
break;
|
||||
case A_SECMPCINTSTATUS:
|
||||
case A_SECPPCINTSTAT:
|
||||
@@ -381,7 +404,6 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr,
|
||||
case A_BRGINTSTAT:
|
||||
case A_AHBNSPPC0:
|
||||
case A_AHBSPPPC0:
|
||||
case A_NSMSCEXP:
|
||||
case A_PID4:
|
||||
case A_PID5:
|
||||
case A_PID6:
|
||||
@@ -588,6 +610,14 @@ static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level)
|
||||
s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level);
|
||||
}
|
||||
|
||||
static void iotkit_secctl_mscexp_status(void *opaque, int n, int level)
|
||||
{
|
||||
IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
|
||||
|
||||
s->secmscintstat = deposit32(s->secmscintstat, n + 16, 1, !!level);
|
||||
iotkit_secctl_update_msc_irq(s);
|
||||
}
|
||||
|
||||
static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level)
|
||||
{
|
||||
IoTKitSecCtlPPC *ppc = opaque;
|
||||
@@ -660,6 +690,14 @@ static void iotkit_secctl_init(Object *obj)
|
||||
qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status,
|
||||
"mpcexp_status", IOTS_NUM_EXP_MPC);
|
||||
|
||||
qdev_init_gpio_in_named(dev, iotkit_secctl_mscexp_status,
|
||||
"mscexp_status", IOTS_NUM_EXP_MSC);
|
||||
qdev_init_gpio_out_named(dev, s->mscexp_clear, "mscexp_clear",
|
||||
IOTS_NUM_EXP_MSC);
|
||||
qdev_init_gpio_out_named(dev, s->mscexp_ns, "mscexp_ns",
|
||||
IOTS_NUM_EXP_MSC);
|
||||
qdev_init_gpio_out_named(dev, &s->msc_irq, "msc_irq", 1);
|
||||
|
||||
memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops,
|
||||
s, "iotkit-secctl-s-regs", 0x1000);
|
||||
memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops,
|
||||
@@ -690,6 +728,24 @@ static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = {
|
||||
}
|
||||
};
|
||||
|
||||
static bool needed_always(void *opaque)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static const VMStateDescription iotkit_secctl_msc_vmstate = {
|
||||
.name = "iotkit-secctl/msc",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = needed_always,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(secmscintstat, IoTKitSecCtl),
|
||||
VMSTATE_UINT32(secmscinten, IoTKitSecCtl),
|
||||
VMSTATE_UINT32(nsmscexp, IoTKitSecCtl),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription iotkit_secctl_vmstate = {
|
||||
.name = "iotkit-secctl",
|
||||
.version_id = 1,
|
||||
@@ -710,6 +766,7 @@ static const VMStateDescription iotkit_secctl_vmstate = {
|
||||
},
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&iotkit_secctl_mpcintstatus_vmstate,
|
||||
&iotkit_secctl_msc_vmstate,
|
||||
NULL
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* ARM IoTKit system control element
|
||||
*
|
||||
* Copyright (c) 2018 Linaro Limited
|
||||
* Written by Peter Maydell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a model of the "system control element" which is part of the
|
||||
* Arm IoTKit and documented in
|
||||
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
|
||||
* Specifically, it implements the "system control register" blocks.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "trace.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/misc/iotkit-sysctl.h"
|
||||
|
||||
REG32(SECDBGSTAT, 0x0)
|
||||
REG32(SECDBGSET, 0x4)
|
||||
REG32(SECDBGCLR, 0x8)
|
||||
REG32(RESET_SYNDROME, 0x100)
|
||||
REG32(RESET_MASK, 0x104)
|
||||
REG32(SWRESET, 0x108)
|
||||
FIELD(SWRESET, SWRESETREQ, 9, 1)
|
||||
REG32(GRETREG, 0x10c)
|
||||
REG32(INITSVRTOR0, 0x110)
|
||||
REG32(CPUWAIT, 0x118)
|
||||
REG32(BUSWAIT, 0x11c)
|
||||
REG32(WICCTRL, 0x120)
|
||||
REG32(PID4, 0xfd0)
|
||||
REG32(PID5, 0xfd4)
|
||||
REG32(PID6, 0xfd8)
|
||||
REG32(PID7, 0xfdc)
|
||||
REG32(PID0, 0xfe0)
|
||||
REG32(PID1, 0xfe4)
|
||||
REG32(PID2, 0xfe8)
|
||||
REG32(PID3, 0xfec)
|
||||
REG32(CID0, 0xff0)
|
||||
REG32(CID1, 0xff4)
|
||||
REG32(CID2, 0xff8)
|
||||
REG32(CID3, 0xffc)
|
||||
|
||||
/* PID/CID values */
|
||||
static const int sysctl_id[] = {
|
||||
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
|
||||
0x54, 0xb8, 0x0b, 0x00, /* PID0..PID3 */
|
||||
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
|
||||
};
|
||||
|
||||
static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque);
|
||||
uint64_t r;
|
||||
|
||||
switch (offset) {
|
||||
case A_SECDBGSTAT:
|
||||
r = s->secure_debug;
|
||||
break;
|
||||
case A_RESET_SYNDROME:
|
||||
r = s->reset_syndrome;
|
||||
break;
|
||||
case A_RESET_MASK:
|
||||
r = s->reset_mask;
|
||||
break;
|
||||
case A_GRETREG:
|
||||
r = s->gretreg;
|
||||
break;
|
||||
case A_INITSVRTOR0:
|
||||
r = s->initsvrtor0;
|
||||
break;
|
||||
case A_CPUWAIT:
|
||||
r = s->cpuwait;
|
||||
break;
|
||||
case A_BUSWAIT:
|
||||
/* In IoTKit BUSWAIT is reserved, R/O, zero */
|
||||
r = 0;
|
||||
break;
|
||||
case A_WICCTRL:
|
||||
r = s->wicctrl;
|
||||
break;
|
||||
case A_PID4 ... A_CID3:
|
||||
r = sysctl_id[(offset - A_PID4) / 4];
|
||||
break;
|
||||
case A_SECDBGSET:
|
||||
case A_SECDBGCLR:
|
||||
case A_SWRESET:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"IoTKit SysCtl read: read of WO offset %x\n",
|
||||
(int)offset);
|
||||
r = 0;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"IoTKit SysCtl read: bad offset %x\n", (int)offset);
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
trace_iotkit_sysctl_read(offset, r, size);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void iotkit_sysctl_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque);
|
||||
|
||||
trace_iotkit_sysctl_write(offset, value, size);
|
||||
|
||||
/*
|
||||
* Most of the state here has to do with control of reset and
|
||||
* similar kinds of power up -- for instance the guest can ask
|
||||
* what the reason for the last reset was, or forbid reset for
|
||||
* some causes (like the non-secure watchdog). Most of this is
|
||||
* not relevant to QEMU, which doesn't really model anything other
|
||||
* than a full power-on reset.
|
||||
* We just model the registers as reads-as-written.
|
||||
*/
|
||||
|
||||
switch (offset) {
|
||||
case A_RESET_SYNDROME:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"IoTKit SysCtl RESET_SYNDROME unimplemented\n");
|
||||
s->reset_syndrome = value;
|
||||
break;
|
||||
case A_RESET_MASK:
|
||||
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl RESET_MASK unimplemented\n");
|
||||
s->reset_mask = value;
|
||||
break;
|
||||
case A_GRETREG:
|
||||
/*
|
||||
* General retention register, which is only reset by a power-on
|
||||
* reset. Technically this implementation is complete, since
|
||||
* QEMU only supports power-on resets...
|
||||
*/
|
||||
s->gretreg = value;
|
||||
break;
|
||||
case A_INITSVRTOR0:
|
||||
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl INITSVRTOR0 unimplemented\n");
|
||||
s->initsvrtor0 = value;
|
||||
break;
|
||||
case A_CPUWAIT:
|
||||
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CPUWAIT unimplemented\n");
|
||||
s->cpuwait = value;
|
||||
break;
|
||||
case A_WICCTRL:
|
||||
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl WICCTRL unimplemented\n");
|
||||
s->wicctrl = value;
|
||||
break;
|
||||
case A_SECDBGSET:
|
||||
/* write-1-to-set */
|
||||
qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SECDBGSET unimplemented\n");
|
||||
s->secure_debug |= value;
|
||||
break;
|
||||
case A_SECDBGCLR:
|
||||
/* write-1-to-clear */
|
||||
s->secure_debug &= ~value;
|
||||
break;
|
||||
case A_SWRESET:
|
||||
/* One w/o bit to request a reset; all other bits reserved */
|
||||
if (value & R_SWRESET_SWRESETREQ_MASK) {
|
||||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||
}
|
||||
break;
|
||||
case A_BUSWAIT: /* In IoTKit BUSWAIT is reserved, R/O, zero */
|
||||
case A_SECDBGSTAT:
|
||||
case A_PID4 ... A_CID3:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"IoTKit SysCtl write: write of RO offset %x\n",
|
||||
(int)offset);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"IoTKit SysCtl write: bad offset %x\n", (int)offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps iotkit_sysctl_ops = {
|
||||
.read = iotkit_sysctl_read,
|
||||
.write = iotkit_sysctl_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
/* byte/halfword accesses are just zero-padded on reads and writes */
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static void iotkit_sysctl_reset(DeviceState *dev)
|
||||
{
|
||||
IoTKitSysCtl *s = IOTKIT_SYSCTL(dev);
|
||||
|
||||
trace_iotkit_sysctl_reset();
|
||||
s->secure_debug = 0;
|
||||
s->reset_syndrome = 1;
|
||||
s->reset_mask = 0;
|
||||
s->gretreg = 0;
|
||||
s->initsvrtor0 = 0x10000000;
|
||||
s->cpuwait = 0;
|
||||
s->wicctrl = 0;
|
||||
}
|
||||
|
||||
static void iotkit_sysctl_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
IoTKitSysCtl *s = IOTKIT_SYSCTL(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &iotkit_sysctl_ops,
|
||||
s, "iotkit-sysctl", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static const VMStateDescription iotkit_sysctl_vmstate = {
|
||||
.name = "iotkit-sysctl",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(secure_debug, IoTKitSysCtl),
|
||||
VMSTATE_UINT32(reset_syndrome, IoTKitSysCtl),
|
||||
VMSTATE_UINT32(reset_mask, IoTKitSysCtl),
|
||||
VMSTATE_UINT32(gretreg, IoTKitSysCtl),
|
||||
VMSTATE_UINT32(initsvrtor0, IoTKitSysCtl),
|
||||
VMSTATE_UINT32(cpuwait, IoTKitSysCtl),
|
||||
VMSTATE_UINT32(wicctrl, IoTKitSysCtl),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void iotkit_sysctl_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &iotkit_sysctl_vmstate;
|
||||
dc->reset = iotkit_sysctl_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo iotkit_sysctl_info = {
|
||||
.name = TYPE_IOTKIT_SYSCTL,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IoTKitSysCtl),
|
||||
.instance_init = iotkit_sysctl_init,
|
||||
.class_init = iotkit_sysctl_class_init,
|
||||
};
|
||||
|
||||
static void iotkit_sysctl_register_types(void)
|
||||
{
|
||||
type_register_static(&iotkit_sysctl_info);
|
||||
}
|
||||
|
||||
type_init(iotkit_sysctl_register_types);
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* ARM IoTKit system information block
|
||||
*
|
||||
* Copyright (c) 2018 Linaro Limited
|
||||
* Written by Peter Maydell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a model of the "system information block" which is part of the
|
||||
* Arm IoTKit and documented in
|
||||
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
|
||||
* It consists of 2 read-only version/config registers, plus the
|
||||
* usual ID registers.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "trace.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/misc/iotkit-sysinfo.h"
|
||||
|
||||
REG32(SYS_VERSION, 0x0)
|
||||
REG32(SYS_CONFIG, 0x4)
|
||||
REG32(PID4, 0xfd0)
|
||||
REG32(PID5, 0xfd4)
|
||||
REG32(PID6, 0xfd8)
|
||||
REG32(PID7, 0xfdc)
|
||||
REG32(PID0, 0xfe0)
|
||||
REG32(PID1, 0xfe4)
|
||||
REG32(PID2, 0xfe8)
|
||||
REG32(PID3, 0xfec)
|
||||
REG32(CID0, 0xff0)
|
||||
REG32(CID1, 0xff4)
|
||||
REG32(CID2, 0xff8)
|
||||
REG32(CID3, 0xffc)
|
||||
|
||||
/* PID/CID values */
|
||||
static const int sysinfo_id[] = {
|
||||
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
|
||||
0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */
|
||||
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
|
||||
};
|
||||
|
||||
static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
uint64_t r;
|
||||
|
||||
switch (offset) {
|
||||
case A_SYS_VERSION:
|
||||
r = 0x41743;
|
||||
break;
|
||||
|
||||
case A_SYS_CONFIG:
|
||||
r = 0x31;
|
||||
break;
|
||||
case A_PID4 ... A_CID3:
|
||||
r = sysinfo_id[(offset - A_PID4) / 4];
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"IoTKit SysInfo read: bad offset %x\n", (int)offset);
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
trace_iotkit_sysinfo_read(offset, r, size);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void iotkit_sysinfo_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
trace_iotkit_sysinfo_write(offset, value, size);
|
||||
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"IoTKit SysInfo: write to RO offset 0x%x\n", (int)offset);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps iotkit_sysinfo_ops = {
|
||||
.read = iotkit_sysinfo_read,
|
||||
.write = iotkit_sysinfo_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
/* byte/halfword accesses are just zero-padded on reads and writes */
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static void iotkit_sysinfo_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
IoTKitSysInfo *s = IOTKIT_SYSINFO(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &iotkit_sysinfo_ops,
|
||||
s, "iotkit-sysinfo", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
/*
|
||||
* This device has no guest-modifiable state and so it
|
||||
* does not need a reset function or VMState.
|
||||
*/
|
||||
}
|
||||
|
||||
static const TypeInfo iotkit_sysinfo_info = {
|
||||
.name = TYPE_IOTKIT_SYSINFO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IoTKitSysInfo),
|
||||
.instance_init = iotkit_sysinfo_init,
|
||||
.class_init = iotkit_sysinfo_class_init,
|
||||
};
|
||||
|
||||
static void iotkit_sysinfo_register_types(void)
|
||||
{
|
||||
type_register_static(&iotkit_sysinfo_info);
|
||||
}
|
||||
|
||||
type_init(iotkit_sysinfo_register_types);
|
||||
+143
-3
@@ -22,6 +22,7 @@
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/misc/mps2-fpgaio.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
REG32(LED0, 0)
|
||||
REG32(BUTTON, 8)
|
||||
@@ -32,10 +33,92 @@ REG32(PRESCALE, 0x1c)
|
||||
REG32(PSCNTR, 0x20)
|
||||
REG32(MISC, 0x4c)
|
||||
|
||||
static uint32_t counter_from_tickoff(int64_t now, int64_t tick_offset, int frq)
|
||||
{
|
||||
return muldiv64(now - tick_offset, frq, NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
static int64_t tickoff_from_counter(int64_t now, uint32_t count, int frq)
|
||||
{
|
||||
return now - muldiv64(count, NANOSECONDS_PER_SECOND, frq);
|
||||
}
|
||||
|
||||
static void resync_counter(MPS2FPGAIO *s)
|
||||
{
|
||||
/*
|
||||
* Update s->counter and s->pscntr to their true current values
|
||||
* by calculating how many times PSCNTR has ticked since the
|
||||
* last time we did a resync.
|
||||
*/
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t elapsed = now - s->pscntr_sync_ticks;
|
||||
|
||||
/*
|
||||
* Round elapsed down to a whole number of PSCNTR ticks, so we don't
|
||||
* lose time if we do multiple resyncs in a single tick.
|
||||
*/
|
||||
uint64_t ticks = muldiv64(elapsed, s->prescale_clk, NANOSECONDS_PER_SECOND);
|
||||
|
||||
/*
|
||||
* Work out what PSCNTR and COUNTER have moved to. We assume that
|
||||
* PSCNTR reloads from PRESCALE one tick-period after it hits zero,
|
||||
* and that COUNTER increments at the same moment.
|
||||
*/
|
||||
if (ticks == 0) {
|
||||
/* We haven't ticked since the last time we were asked */
|
||||
return;
|
||||
} else if (ticks < s->pscntr) {
|
||||
/* We haven't yet reached zero, just reduce the PSCNTR */
|
||||
s->pscntr -= ticks;
|
||||
} else {
|
||||
if (s->prescale == 0) {
|
||||
/*
|
||||
* If the reload value is zero then the PSCNTR will stick
|
||||
* at zero once it reaches it, and so we will increment
|
||||
* COUNTER every tick after that.
|
||||
*/
|
||||
s->counter += ticks - s->pscntr;
|
||||
s->pscntr = 0;
|
||||
} else {
|
||||
/*
|
||||
* This is the complicated bit. This ASCII art diagram gives an
|
||||
* example with PRESCALE==5 PSCNTR==7:
|
||||
*
|
||||
* ticks 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
||||
* PSCNTR 7 6 5 4 3 2 1 0 5 4 3 2 1 0 5
|
||||
* cinc 1 2
|
||||
* y 0 1 2 3 4 5 6 7 8 9 10 11 12
|
||||
* x 0 1 2 3 4 5 0 1 2 3 4 5 0
|
||||
*
|
||||
* where x = y % (s->prescale + 1)
|
||||
* and so PSCNTR = s->prescale - x
|
||||
* and COUNTER is incremented by y / (s->prescale + 1)
|
||||
*
|
||||
* The case where PSCNTR < PRESCALE works out the same,
|
||||
* though we must be careful to calculate y as 64-bit unsigned
|
||||
* for all parts of the expression.
|
||||
* y < 0 is not possible because that implies ticks < s->pscntr.
|
||||
*/
|
||||
uint64_t y = ticks - s->pscntr + s->prescale;
|
||||
s->pscntr = s->prescale - (y % (s->prescale + 1));
|
||||
s->counter += y / (s->prescale + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Only advance the sync time to the timestamp of the last PSCNTR tick,
|
||||
* not all the way to 'now', so we don't lose time if we do multiple
|
||||
* resyncs in a single tick.
|
||||
*/
|
||||
s->pscntr_sync_ticks += muldiv64(ticks, NANOSECONDS_PER_SECOND,
|
||||
s->prescale_clk);
|
||||
}
|
||||
|
||||
static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
|
||||
uint64_t r;
|
||||
int64_t now;
|
||||
|
||||
switch (offset) {
|
||||
case A_LED0:
|
||||
@@ -54,12 +137,20 @@ static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
|
||||
r = s->misc;
|
||||
break;
|
||||
case A_CLK1HZ:
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
r = counter_from_tickoff(now, s->clk1hz_tick_offset, 1);
|
||||
break;
|
||||
case A_CLK100HZ:
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
r = counter_from_tickoff(now, s->clk100hz_tick_offset, 100);
|
||||
break;
|
||||
case A_COUNTER:
|
||||
resync_counter(s);
|
||||
r = s->counter;
|
||||
break;
|
||||
case A_PSCNTR:
|
||||
/* These are all upcounters of various frequencies. */
|
||||
qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n");
|
||||
r = 0;
|
||||
resync_counter(s);
|
||||
r = s->pscntr;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
@@ -76,6 +167,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
|
||||
int64_t now;
|
||||
|
||||
trace_mps2_fpgaio_write(offset, value, size);
|
||||
|
||||
@@ -89,6 +181,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
s->led0 = value & 0x3;
|
||||
break;
|
||||
case A_PRESCALE:
|
||||
resync_counter(s);
|
||||
s->prescale = value;
|
||||
break;
|
||||
case A_MISC:
|
||||
@@ -100,6 +193,22 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
"MPS2 FPGAIO: MISC control bits unimplemented\n");
|
||||
s->misc = value;
|
||||
break;
|
||||
case A_CLK1HZ:
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s->clk1hz_tick_offset = tickoff_from_counter(now, value, 1);
|
||||
break;
|
||||
case A_CLK100HZ:
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s->clk100hz_tick_offset = tickoff_from_counter(now, value, 100);
|
||||
break;
|
||||
case A_COUNTER:
|
||||
resync_counter(s);
|
||||
s->counter = value;
|
||||
break;
|
||||
case A_PSCNTR:
|
||||
resync_counter(s);
|
||||
s->pscntr = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset);
|
||||
@@ -116,11 +225,17 @@ static const MemoryRegionOps mps2_fpgaio_ops = {
|
||||
static void mps2_fpgaio_reset(DeviceState *dev)
|
||||
{
|
||||
MPS2FPGAIO *s = MPS2_FPGAIO(dev);
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
trace_mps2_fpgaio_reset();
|
||||
s->led0 = 0;
|
||||
s->prescale = 0;
|
||||
s->misc = 0;
|
||||
s->clk1hz_tick_offset = tickoff_from_counter(now, 0, 1);
|
||||
s->clk100hz_tick_offset = tickoff_from_counter(now, 0, 100);
|
||||
s->counter = 0;
|
||||
s->pscntr = 0;
|
||||
s->pscntr_sync_ticks = now;
|
||||
}
|
||||
|
||||
static void mps2_fpgaio_init(Object *obj)
|
||||
@@ -133,6 +248,27 @@ static void mps2_fpgaio_init(Object *obj)
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static bool mps2_fpgaio_counters_needed(void *opaque)
|
||||
{
|
||||
/* Currently vmstate.c insists all subsections have a 'needed' function */
|
||||
return true;
|
||||
}
|
||||
|
||||
static const VMStateDescription mps2_fpgaio_counters_vmstate = {
|
||||
.name = "mps2-fpgaio/counters",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.needed = mps2_fpgaio_counters_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(clk1hz_tick_offset, MPS2FPGAIO),
|
||||
VMSTATE_INT64(clk100hz_tick_offset, MPS2FPGAIO),
|
||||
VMSTATE_UINT32(counter, MPS2FPGAIO),
|
||||
VMSTATE_UINT32(pscntr, MPS2FPGAIO),
|
||||
VMSTATE_INT64(pscntr_sync_ticks, MPS2FPGAIO),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription mps2_fpgaio_vmstate = {
|
||||
.name = "mps2-fpgaio",
|
||||
.version_id = 1,
|
||||
@@ -142,6 +278,10 @@ static const VMStateDescription mps2_fpgaio_vmstate = {
|
||||
VMSTATE_UINT32(prescale, MPS2FPGAIO),
|
||||
VMSTATE_UINT32(misc, MPS2FPGAIO),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&mps2_fpgaio_counters_vmstate,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user