target-arm queue:

* Model the Arm "Musca" development boards: "musca-a" and "musca-b1"
  * Implement the ARMv8.3-JSConv extension
  * v8M MPU should use background region as default, not always
  * Stop unintentional sign extension in pmu_init
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAlxu9GAZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3mCyEACdpF+KWdCjVU8sJJka1dde
 C2J1xk59bxnud3iOgL0ssZmt+JXXC25CpabFrK38556O0v0QIf9HGXlMnWBD5SQI
 N+u2v5eumv6xC4al97wcDX2VRSGrdemUAvioo0IA4hXbaW8Z9TgZaXWCqHzjyOyt
 MynKHJIh9MLzudZ8NdBjDTi93KP39jIwEd2PQUxYg08/Sg8OgvlSijDM5J92luoI
 lBrgIQe0tUZKrzUs63AafHdWK4a37PAQE3FvtmwTuCXxL2PSNIcsT/zBXIVmHFWe
 Go9pSLE9i4AH5GfrDzuLhEY+/ca6EYtrF0f54kVgWDHkXzHFtWvUefNbUvN2RGjj
 fo9FkDVSDBI8hHEeZ4auiTeiseYcJaRj92xD4Bmrc7ers03rH+1HaaIj74QeU4AP
 0PjpnW+jseLCLWnwcjo412GGCrugd9aBQ1agbUnpcn8d83wLq+BhOpnvFZuyMBpN
 e20kCHnJIpxfFQKCm22OvvrwC5Xt+6utCWKqfFZcMMnD7czHFsbWwAvCWII3oIbs
 oRqvyvNV/LVqZSGfjN5Bexd3/OR1bNBmMyF9/psR7EPtM64jHzS5O7vQc5h7/xsR
 aUfKYO4SnLbx+QPnKGVdX0aOW4Jm1E4s9WjK7JUaMpLqo75zbovpzF2v0l+ztwgX
 EihFPjWL0Bivtvt67GvE5g==
 =fo9R
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190221' into staging

target-arm queue:
 * Model the Arm "Musca" development boards: "musca-a" and "musca-b1"
 * Implement the ARMv8.3-JSConv extension
 * v8M MPU should use background region as default, not always
 * Stop unintentional sign extension in pmu_init

# gpg: Signature made Thu 21 Feb 2019 18:56:32 GMT
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20190221: (21 commits)
  hw/arm/armsse: Make 0x5... alias region work for per-CPU devices
  hw/arm/musca: Wire up PL011 UARTs
  hw/arm/musca: Wire up PL031 RTC
  hw/arm/musca: Add MPCs
  hw/arm/musca: Add PPCs
  hw/arm/musca.c: Implement models of the Musca-A and -B1 boards
  hw/arm/armsse: Allow boards to specify init-svtor
  hw/arm/armsse: Document SRAM_ADDR_WIDTH property in header comment
  hw/char/pl011: Use '0x' prefix when logging hex numbers
  hw/char/pl011: Support all interrupt lines
  hw/char/pl011: Allow use as an embedded-struct device
  hw/timer/pl031: Convert to using trace events
  hw/timer/pl031: Allow use as an embedded-struct device
  hw/misc/tz-ppc: Support having unused ports in the middle of the range
  target/arm: Implement ARMv8.3-JSConv
  target/arm: Rearrange Floating-point data-processing (2 regs)
  target/arm: Split out vfp_helper.c
  target/arm: Restructure disas_fp_int_conv
  target/arm: Stop unintentional sign extension in pmu_init
  target/arm: v8M MPU should use background region as default, not always
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-02-21 18:58:35 +00:00
commit faf840a359
22 changed files with 2307 additions and 1330 deletions

View File

@ -491,6 +491,7 @@ F: hw/sd/pl181.c
F: hw/ssi/pl022.c
F: include/hw/ssi/pl022.h
F: hw/timer/pl031.c
F: include/hw/timer/pl031.h
F: include/hw/arm/primecell.h
F: hw/timer/cmsdk-apb-timer.c
F: include/hw/timer/cmsdk-apb-timer.h
@ -633,6 +634,12 @@ F: include/hw/misc/iotkit-sysinfo.h
F: hw/misc/armsse-cpuid.c
F: include/hw/misc/armsse-cpuid.h
Musca
M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org
S: Maintained
F: hw/arm/musca.c
Musicpal
M: Jan Kiszka <jan.kiszka@web.de>
M: Peter Maydell <peter.maydell@linaro.org>

View File

@ -89,6 +89,7 @@ CONFIG_TUSB6010=y
CONFIG_IMX=y
CONFIG_MAINSTONE=y
CONFIG_MPS2=y
CONFIG_MUSCA=y
CONFIG_NSERIES=y
CONFIG_RASPI=y
CONFIG_REALVIEW=y

View File

@ -35,6 +35,7 @@ obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o
obj-$(CONFIG_MPS2) += mps2.o
obj-$(CONFIG_MPS2) += mps2-tz.o
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
obj-$(CONFIG_MUSCA) += musca.o
obj-$(CONFIG_ARMSSE) += armsse.o
obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o

View File

@ -110,15 +110,16 @@ static bool irq_is_common[32] = {
/* 30, 31: reserved */
};
/* Create an alias region of @size bytes starting at @base
/*
* Create an alias region in @container of @size bytes starting at @base
* which mirrors the memory starting at @orig.
*/
static void make_alias(ARMSSE *s, MemoryRegion *mr, const char *name,
hwaddr base, hwaddr size, hwaddr orig)
static void make_alias(ARMSSE *s, MemoryRegion *mr, MemoryRegion *container,
const char *name, hwaddr base, hwaddr size, hwaddr orig)
{
memory_region_init_alias(mr, NULL, name, &s->container, orig, size);
memory_region_init_alias(mr, NULL, name, container, orig, size);
/* The alias is even lower priority than unimplemented_device regions */
memory_region_add_subregion_overlap(&s->container, base, mr, -1500);
memory_region_add_subregion_overlap(container, base, mr, -1500);
}
static void irq_status_forwarder(void *opaque, int n, int level)
@ -505,11 +506,10 @@ static void armsse_realize(DeviceState *dev, Error **errp)
* the INITSVTOR* registers before powering up the CPUs in any case,
* so the hardware's default value doesn't matter. QEMU doesn't emulate
* the control processor, so instead we behave in the way that the
* firmware does. All boards currently known about have firmware that
* sets the INITSVTOR0 and INITSVTOR1 registers to 0x10000000, like the
* IoTKit default. We can make this more configurable if necessary.
* firmware does. The initial value is configurable by the board code
* to match whatever its firmware does.
*/
qdev_prop_set_uint32(cpudev, "init-svtor", 0x10000000);
qdev_prop_set_uint32(cpudev, "init-svtor", s->init_svtor);
/*
* Start all CPUs except CPU0 powered down. In real hardware it is
* a configurable property of the SSE-200 which CPUs start powered up
@ -608,16 +608,21 @@ static void armsse_realize(DeviceState *dev, Error **errp)
}
/* Set up the big aliases first */
make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000);
make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000);
make_alias(s, &s->alias1, &s->container, "alias 1",
0x10000000, 0x10000000, 0x00000000);
make_alias(s, &s->alias2, &s->container,
"alias 2", 0x30000000, 0x10000000, 0x20000000);
/* The 0x50000000..0x5fffffff region is not a pure alias: it has
* a few extra devices that only appear there (generally the
* control interfaces for the protection controllers).
* We implement this by mapping those devices over the top of this
* alias MR at a higher priority.
* alias MR at a higher priority. Some of the devices in this range
* are per-CPU, so we must put this alias in the per-cpu containers.
*/
make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000);
for (i = 0; i < info->num_cpus; i++) {
make_alias(s, &s->alias3[i], &s->cpu_container[i],
"alias 3", 0x50000000, 0x10000000, 0x40000000);
}
/* Security controller */
object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err);
@ -762,26 +767,28 @@ static void armsse_realize(DeviceState *dev, Error **errp)
if (info->has_mhus) {
for (i = 0; i < ARRAY_SIZE(s->mhu); i++) {
char *name = g_strdup_printf("MHU%d", i);
char *port = g_strdup_printf("port[%d]", i + 3);
char *name;
char *port;
name = g_strdup_printf("MHU%d", i);
qdev_prop_set_string(DEVICE(&s->mhu[i]), "name", name);
qdev_prop_set_uint64(DEVICE(&s->mhu[i]), "size", 0x1000);
object_property_set_bool(OBJECT(&s->mhu[i]), true,
"realized", &err);
g_free(name);
if (err) {
error_propagate(errp, err);
return;
}
port = g_strdup_printf("port[%d]", i + 3);
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mhu[i]), 0);
object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr),
port, &err);
g_free(port);
if (err) {
error_propagate(errp, err);
return;
}
g_free(name);
g_free(port);
}
}
@ -1185,6 +1192,7 @@ static Property armsse_properties[] = {
DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64),
DEFINE_PROP_UINT32("MAINCLK", ARMSSE, mainclk_frq, 0),
DEFINE_PROP_UINT32("SRAM_ADDR_WIDTH", ARMSSE, sram_addr_width, 15),
DEFINE_PROP_UINT32("init-svtor", ARMSSE, init_svtor, 0x10000000),
DEFINE_PROP_END_OF_LIST()
};

669
hw/arm/musca.c Normal file
View File

@ -0,0 +1,669 @@
/*
* Arm Musca-B1 test chip board emulation
*
* Copyright (c) 2019 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.
*/
/*
* The Musca boards are a reference implementation of a system using
* the SSE-200 subsystem for embedded:
* https://developer.arm.com/products/system-design/development-boards/iot-test-chips-and-boards/musca-a-test-chip-board
* https://developer.arm.com/products/system-design/development-boards/iot-test-chips-and-boards/musca-b-test-chip-board
* We model the A and B1 variants of this board, as described in the TRMs:
* http://infocenter.arm.com/help/topic/com.arm.doc.101107_0000_00_en/index.html
* http://infocenter.arm.com/help/topic/com.arm.doc.101312_0000_00_en/index.html
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
#include "hw/arm/arm.h"
#include "hw/arm/armsse.h"
#include "hw/boards.h"
#include "hw/char/pl011.h"
#include "hw/core/split-irq.h"
#include "hw/misc/tz-mpc.h"
#include "hw/misc/tz-ppc.h"
#include "hw/misc/unimp.h"
#include "hw/timer/pl031.h"
#define MUSCA_NUMIRQ_MAX 96
#define MUSCA_PPC_MAX 3
#define MUSCA_MPC_MAX 5
typedef struct MPCInfo MPCInfo;
typedef enum MuscaType {
MUSCA_A,
MUSCA_B1,
} MuscaType;
typedef struct {
MachineClass parent;
MuscaType type;
uint32_t init_svtor;
int sram_addr_width;
int num_irqs;
const MPCInfo *mpc_info;
int num_mpcs;
} MuscaMachineClass;
typedef struct {
MachineState parent;
ARMSSE sse;
/* RAM and flash */
MemoryRegion ram[MUSCA_MPC_MAX];
SplitIRQ cpu_irq_splitter[MUSCA_NUMIRQ_MAX];
SplitIRQ sec_resp_splitter;
TZPPC ppc[MUSCA_PPC_MAX];
MemoryRegion container;
UnimplementedDeviceState eflash[2];
UnimplementedDeviceState qspi;
TZMPC mpc[MUSCA_MPC_MAX];
UnimplementedDeviceState mhu[2];
UnimplementedDeviceState pwm[3];
UnimplementedDeviceState i2s;
PL011State uart[2];
UnimplementedDeviceState i2c[2];
UnimplementedDeviceState spi;
UnimplementedDeviceState scc;
UnimplementedDeviceState timer;
PL031State rtc;
UnimplementedDeviceState pvt;
UnimplementedDeviceState sdio;
UnimplementedDeviceState gpio;
UnimplementedDeviceState cryptoisland;
} MuscaMachineState;
#define TYPE_MUSCA_MACHINE "musca"
#define TYPE_MUSCA_A_MACHINE MACHINE_TYPE_NAME("musca-a")
#define TYPE_MUSCA_B1_MACHINE MACHINE_TYPE_NAME("musca-b1")
#define MUSCA_MACHINE(obj) \
OBJECT_CHECK(MuscaMachineState, obj, TYPE_MUSCA_MACHINE)
#define MUSCA_MACHINE_GET_CLASS(obj) \
OBJECT_GET_CLASS(MuscaMachineClass, obj, TYPE_MUSCA_MACHINE)
#define MUSCA_MACHINE_CLASS(klass) \
OBJECT_CLASS_CHECK(MuscaMachineClass, klass, TYPE_MUSCA_MACHINE)
/*
* Main SYSCLK frequency in Hz
* TODO this should really be different for the two cores, but we
* don't model that in our SSE-200 model yet.
*/
#define SYSCLK_FRQ 40000000
static qemu_irq get_sse_irq_in(MuscaMachineState *mms, int irqno)
{
/* Return a qemu_irq which will signal IRQ n to all CPUs in the SSE. */
assert(irqno < MUSCA_NUMIRQ_MAX);
return qdev_get_gpio_in(DEVICE(&mms->cpu_irq_splitter[irqno]), 0);
}
/*
* Most of the devices in the Musca board sit behind Peripheral Protection
* Controllers. These data structures define the layout of which devices
* sit behind which PPCs.
* The devfn for each port is a function which creates, configures
* and initializes the device, returning the MemoryRegion which
* needs to be plugged into the downstream end of the PPC port.
*/
typedef MemoryRegion *MakeDevFn(MuscaMachineState *mms, void *opaque,
const char *name, hwaddr size);
typedef struct PPCPortInfo {
const char *name;
MakeDevFn *devfn;
void *opaque;
hwaddr addr;
hwaddr size;
} PPCPortInfo;
typedef struct PPCInfo {
const char *name;
PPCPortInfo ports[TZ_NUM_PORTS];
} PPCInfo;
static MemoryRegion *make_unimp_dev(MuscaMachineState *mms,
void *opaque, const char *name, hwaddr size)
{
/*
* Initialize, configure and realize a TYPE_UNIMPLEMENTED_DEVICE,
* and return a pointer to its MemoryRegion.
*/
UnimplementedDeviceState *uds = opaque;
sysbus_init_child_obj(OBJECT(mms), name, uds,
sizeof(UnimplementedDeviceState),
TYPE_UNIMPLEMENTED_DEVICE);
qdev_prop_set_string(DEVICE(uds), "name", name);
qdev_prop_set_uint64(DEVICE(uds), "size", size);
object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal);
return sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0);
}
typedef enum MPCInfoType {
MPC_RAM,
MPC_ROM,
MPC_CRYPTOISLAND,
} MPCInfoType;
struct MPCInfo {
const char *name;
hwaddr addr;
hwaddr size;
MPCInfoType type;
};
/* Order of the MPCs here must match the order of the bits in SECMPCINTSTATUS */
static const MPCInfo a_mpc_info[] = { {
.name = "qspi",
.type = MPC_ROM,
.addr = 0x00200000,
.size = 0x00800000,
}, {
.name = "sram",
.type = MPC_RAM,
.addr = 0x00000000,
.size = 0x00200000,
}
};
static const MPCInfo b1_mpc_info[] = { {
.name = "qspi",
.type = MPC_ROM,
.addr = 0x00000000,
.size = 0x02000000,
}, {
.name = "sram",
.type = MPC_RAM,
.addr = 0x0a400000,
.size = 0x00080000,
}, {
.name = "eflash0",
.type = MPC_ROM,
.addr = 0x0a000000,
.size = 0x00200000,
}, {
.name = "eflash1",
.type = MPC_ROM,
.addr = 0x0a200000,
.size = 0x00200000,
}, {
.name = "cryptoisland",
.type = MPC_CRYPTOISLAND,
.addr = 0x0a000000,
.size = 0x00200000,
}
};
static MemoryRegion *make_mpc(MuscaMachineState *mms, void *opaque,
const char *name, hwaddr size)
{
/*
* Create an MPC and the RAM or flash behind it.
* MPC 0: eFlash 0
* MPC 1: eFlash 1
* MPC 2: SRAM
* MPC 3: QSPI flash
* MPC 4: CryptoIsland
* For now we implement the flash regions as ROM (ie not programmable)
* (with their control interface memory regions being unimplemented
* stubs behind the PPCs).
* The whole CryptoIsland region behind its MPC is an unimplemented stub.
*/
MuscaMachineClass *mmc = MUSCA_MACHINE_GET_CLASS(mms);
TZMPC *mpc = opaque;
int i = mpc - &mms->mpc[0];
MemoryRegion *downstream;
MemoryRegion *upstream;
UnimplementedDeviceState *uds;
char *mpcname;
const MPCInfo *mpcinfo = mmc->mpc_info;
mpcname = g_strdup_printf("%s-mpc", mpcinfo[i].name);
switch (mpcinfo[i].type) {
case MPC_ROM:
downstream = &mms->ram[i];
memory_region_init_rom(downstream, NULL, mpcinfo[i].name,
mpcinfo[i].size, &error_fatal);
break;
case MPC_RAM:
downstream = &mms->ram[i];
memory_region_init_ram(downstream, NULL, mpcinfo[i].name,
mpcinfo[i].size, &error_fatal);
break;
case MPC_CRYPTOISLAND:
/* We don't implement the CryptoIsland yet */
uds = &mms->cryptoisland;
sysbus_init_child_obj(OBJECT(mms), name, uds,
sizeof(UnimplementedDeviceState),
TYPE_UNIMPLEMENTED_DEVICE);
qdev_prop_set_string(DEVICE(uds), "name", mpcinfo[i].name);
qdev_prop_set_uint64(DEVICE(uds), "size", mpcinfo[i].size);
object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal);
downstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0);
break;
default:
g_assert_not_reached();
}
sysbus_init_child_obj(OBJECT(mms), mpcname, mpc, sizeof(mms->mpc[0]),
TYPE_TZ_MPC);
object_property_set_link(OBJECT(mpc), OBJECT(downstream),
"downstream", &error_fatal);
object_property_set_bool(OBJECT(mpc), true, "realized", &error_fatal);
/* Map the upstream end of the MPC into system memory */
upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 1);
memory_region_add_subregion(get_system_memory(), mpcinfo[i].addr, upstream);
/* and connect its interrupt to the SSE-200 */
qdev_connect_gpio_out_named(DEVICE(mpc), "irq", 0,
qdev_get_gpio_in_named(DEVICE(&mms->sse),
"mpcexp_status", i));
g_free(mpcname);
/* Return the register interface MR for our caller to map behind the PPC */
return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0);
}
static MemoryRegion *make_rtc(MuscaMachineState *mms, void *opaque,
const char *name, hwaddr size)
{
PL031State *rtc = opaque;
sysbus_init_child_obj(OBJECT(mms), name, rtc, sizeof(mms->rtc), TYPE_PL031);
object_property_set_bool(OBJECT(rtc), true, "realized", &error_fatal);
sysbus_connect_irq(SYS_BUS_DEVICE(rtc), 0, get_sse_irq_in(mms, 39));
return sysbus_mmio_get_region(SYS_BUS_DEVICE(rtc), 0);
}
static MemoryRegion *make_uart(MuscaMachineState *mms, void *opaque,
const char *name, hwaddr size)
{
PL011State *uart = opaque;
int i = uart - &mms->uart[0];
int irqbase = 7 + i * 6;
SysBusDevice *s;
sysbus_init_child_obj(OBJECT(mms), name, uart, sizeof(mms->uart[0]),
TYPE_PL011);
qdev_prop_set_chr(DEVICE(uart), "chardev", serial_hd(i));
object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal);
s = SYS_BUS_DEVICE(uart);
sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqbase + 5)); /* combined */
sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqbase + 0)); /* RX */
sysbus_connect_irq(s, 2, get_sse_irq_in(mms, irqbase + 1)); /* TX */
sysbus_connect_irq(s, 3, get_sse_irq_in(mms, irqbase + 2)); /* RT */
sysbus_connect_irq(s, 4, get_sse_irq_in(mms, irqbase + 3)); /* MS */
sysbus_connect_irq(s, 5, get_sse_irq_in(mms, irqbase + 4)); /* E */
return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0);
}
static MemoryRegion *make_musca_a_devs(MuscaMachineState *mms, void *opaque,
const char *name, hwaddr size)
{
/*
* Create the container MemoryRegion for all the devices that live
* behind the Musca-A PPC's single port. These devices don't have a PPC
* port each, but we use the PPCPortInfo struct as a convenient way
* to describe them. Note that addresses here are relative to the base
* address of the PPC port region: 0x40100000, and devices appear both
* at the 0x4... NS region and the 0x5... S region.
*/
int i;
MemoryRegion *container = &mms->container;
const PPCPortInfo devices[] = {
{ "uart0", make_uart, &mms->uart[0], 0x1000, 0x1000 },
{ "uart1", make_uart, &mms->uart[1], 0x2000, 0x1000 },
{ "spi", make_unimp_dev, &mms->spi, 0x3000, 0x1000 },
{ "i2c0", make_unimp_dev, &mms->i2c[0], 0x4000, 0x1000 },
{ "i2c1", make_unimp_dev, &mms->i2c[1], 0x5000, 0x1000 },
{ "i2s", make_unimp_dev, &mms->i2s, 0x6000, 0x1000 },
{ "pwm0", make_unimp_dev, &mms->pwm[0], 0x7000, 0x1000 },
{ "rtc", make_rtc, &mms->rtc, 0x8000, 0x1000 },
{ "qspi", make_unimp_dev, &mms->qspi, 0xa000, 0x1000 },
{ "timer", make_unimp_dev, &mms->timer, 0xb000, 0x1000 },
{ "scc", make_unimp_dev, &mms->scc, 0xc000, 0x1000 },
{ "pwm1", make_unimp_dev, &mms->pwm[1], 0xe000, 0x1000 },
{ "pwm2", make_unimp_dev, &mms->pwm[2], 0xf000, 0x1000 },
{ "gpio", make_unimp_dev, &mms->gpio, 0x10000, 0x1000 },
{ "mpc0", make_mpc, &mms->mpc[0], 0x12000, 0x1000 },
{ "mpc1", make_mpc, &mms->mpc[1], 0x13000, 0x1000 },
};
memory_region_init(container, OBJECT(mms), "musca-device-container", size);
for (i = 0; i < ARRAY_SIZE(devices); i++) {
const PPCPortInfo *pinfo = &devices[i];
MemoryRegion *mr;
mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size);
memory_region_add_subregion(container, pinfo->addr, mr);
}
return &mms->container;
}
static void musca_init(MachineState *machine)
{
MuscaMachineState *mms = MUSCA_MACHINE(machine);
MuscaMachineClass *mmc = MUSCA_MACHINE_GET_CLASS(mms);
MachineClass *mc = MACHINE_GET_CLASS(machine);
MemoryRegion *system_memory = get_system_memory();
DeviceState *ssedev;
DeviceState *dev_splitter;
const PPCInfo *ppcs;
int num_ppcs;
int i;
assert(mmc->num_irqs <= MUSCA_NUMIRQ_MAX);
assert(mmc->num_mpcs <= MUSCA_MPC_MAX);
if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
error_report("This board can only be used with CPU %s",
mc->default_cpu_type);
exit(1);
}
sysbus_init_child_obj(OBJECT(machine), "sse-200", &mms->sse,
sizeof(mms->sse), TYPE_SSE200);
ssedev = DEVICE(&mms->sse);
object_property_set_link(OBJECT(&mms->sse), OBJECT(system_memory),
"memory", &error_fatal);
qdev_prop_set_uint32(ssedev, "EXP_NUMIRQ", mmc->num_irqs);
qdev_prop_set_uint32(ssedev, "init-svtor", mmc->init_svtor);
qdev_prop_set_uint32(ssedev, "SRAM_ADDR_WIDTH", mmc->sram_addr_width);
qdev_prop_set_uint32(ssedev, "MAINCLK", SYSCLK_FRQ);
object_property_set_bool(OBJECT(&mms->sse), true, "realized",
&error_fatal);
/*
* We need to create splitters to feed the IRQ inputs
* for each CPU in the SSE-200 from each device in the board.
*/
for (i = 0; i < mmc->num_irqs; i++) {
char *name = g_strdup_printf("musca-irq-splitter%d", i);
SplitIRQ *splitter = &mms->cpu_irq_splitter[i];
object_initialize_child(OBJECT(machine), name,
splitter, sizeof(*splitter),
TYPE_SPLIT_IRQ, &error_fatal, NULL);
g_free(name);
object_property_set_int(OBJECT(splitter), 2, "num-lines",
&error_fatal);
object_property_set_bool(OBJECT(splitter), true, "realized",
&error_fatal);
qdev_connect_gpio_out(DEVICE(splitter), 0,
qdev_get_gpio_in_named(ssedev, "EXP_IRQ", i));
qdev_connect_gpio_out(DEVICE(splitter), 1,
qdev_get_gpio_in_named(ssedev,
"EXP_CPU1_IRQ", i));
}
/*
* The sec_resp_cfg output from the SSE-200 must be split into multiple
* lines, one for each of the PPCs we create here.
*/
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_fatal);
object_property_set_int(OBJECT(&mms->sec_resp_splitter),
ARRAY_SIZE(mms->ppc), "num-lines", &error_fatal);
object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true,
"realized", &error_fatal);
dev_splitter = DEVICE(&mms->sec_resp_splitter);
qdev_connect_gpio_out_named(ssedev, "sec_resp_cfg", 0,
qdev_get_gpio_in(dev_splitter, 0));
/*
* Most of the devices in the board are behind Peripheral Protection
* Controllers. The required order for initializing things is:
* + initialize the PPC
* + initialize, configure and realize downstream devices
* + connect downstream device MemoryRegions to the PPC
* + realize the PPC
* + map the PPC's MemoryRegions to the places in the address map
* where the downstream devices should appear
* + wire up the PPC's control lines to the SSE object
*
* The PPC mapping differs for the -A and -B1 variants; the -A version
* is much simpler, using only a single port of a single PPC and putting
* all the devices behind that.
*/
const PPCInfo a_ppcs[] = { {
.name = "ahb_ppcexp0",
.ports = {
{ "musca-devices", make_musca_a_devs, 0, 0x40100000, 0x100000 },
},
},
};
/*
* Devices listed with an 0x4.. address appear in both the NS 0x4.. region
* and the 0x5.. S region. Devices listed with an 0x5.. address appear
* only in the S region.
*/
const PPCInfo b1_ppcs[] = { {
.name = "apb_ppcexp0",
.ports = {
{ "eflash0", make_unimp_dev, &mms->eflash[0],
0x52400000, 0x1000 },
{ "eflash1", make_unimp_dev, &mms->eflash[1],
0x52500000, 0x1000 },
{ "qspi", make_unimp_dev, &mms->qspi, 0x42800000, 0x100000 },
{ "mpc0", make_mpc, &mms->mpc[0], 0x52000000, 0x1000 },
{ "mpc1", make_mpc, &mms->mpc[1], 0x52100000, 0x1000 },
{ "mpc2", make_mpc, &mms->mpc[2], 0x52200000, 0x1000 },
{ "mpc3", make_mpc, &mms->mpc[3], 0x52300000, 0x1000 },
{ "mhu0", make_unimp_dev, &mms->mhu[0], 0x42600000, 0x100000 },
{ "mhu1", make_unimp_dev, &mms->mhu[1], 0x42700000, 0x100000 },
{ }, /* port 9: unused */
{ }, /* port 10: unused */
{ }, /* port 11: unused */
{ }, /* port 12: unused */
{ }, /* port 13: unused */
{ "mpc4", make_mpc, &mms->mpc[4], 0x52e00000, 0x1000 },
},
}, {
.name = "apb_ppcexp1",
.ports = {
{ "pwm0", make_unimp_dev, &mms->pwm[0], 0x40101000, 0x1000 },
{ "pwm1", make_unimp_dev, &mms->pwm[1], 0x40102000, 0x1000 },
{ "pwm2", make_unimp_dev, &mms->pwm[2], 0x40103000, 0x1000 },
{ "i2s", make_unimp_dev, &mms->i2s, 0x40104000, 0x1000 },
{ "uart0", make_uart, &mms->uart[0], 0x40105000, 0x1000 },
{ "uart1", make_uart, &mms->uart[1], 0x40106000, 0x1000 },
{ "i2c0", make_unimp_dev, &mms->i2c[0], 0x40108000, 0x1000 },
{ "i2c1", make_unimp_dev, &mms->i2c[1], 0x40109000, 0x1000 },
{ "spi", make_unimp_dev, &mms->spi, 0x4010a000, 0x1000 },
{ "scc", make_unimp_dev, &mms->scc, 0x5010b000, 0x1000 },
{ "timer", make_unimp_dev, &mms->timer, 0x4010c000, 0x1000 },
{ "rtc", make_rtc, &mms->rtc, 0x4010d000, 0x1000 },
{ "pvt", make_unimp_dev, &mms->pvt, 0x4010e000, 0x1000 },
{ "sdio", make_unimp_dev, &mms->sdio, 0x4010f000, 0x1000 },
},
}, {
.name = "ahb_ppcexp0",
.ports = {
{ }, /* port 0: unused */
{ "gpio", make_unimp_dev, &mms->gpio, 0x41000000, 0x1000 },
},
},
};
switch (mmc->type) {
case MUSCA_A:
ppcs = a_ppcs;
num_ppcs = ARRAY_SIZE(a_ppcs);
break;
case MUSCA_B1:
ppcs = b1_ppcs;
num_ppcs = ARRAY_SIZE(b1_ppcs);
break;
default:
g_assert_not_reached();
}
assert(num_ppcs <= MUSCA_PPC_MAX);
for (i = 0; i < num_ppcs; i++) {
const PPCInfo *ppcinfo = &ppcs[i];
TZPPC *ppc = &mms->ppc[i];
DeviceState *ppcdev;
int port;
char *gpioname;
sysbus_init_child_obj(OBJECT(machine), ppcinfo->name, ppc,
sizeof(TZPPC), TYPE_TZ_PPC);
ppcdev = DEVICE(ppc);
for (port = 0; port < TZ_NUM_PORTS; port++) {
const PPCPortInfo *pinfo = &ppcinfo->ports[port];
MemoryRegion *mr;
char *portname;
if (!pinfo->devfn) {
continue;
}
mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size);
portname = g_strdup_printf("port[%d]", port);
object_property_set_link(OBJECT(ppc), OBJECT(mr),
portname, &error_fatal);
g_free(portname);
}
object_property_set_bool(OBJECT(ppc), true, "realized", &error_fatal);
for (port = 0; port < TZ_NUM_PORTS; port++) {
const PPCPortInfo *pinfo = &ppcinfo->ports[port];
if (!pinfo->devfn) {
continue;
}
sysbus_mmio_map(SYS_BUS_DEVICE(ppc), port, pinfo->addr);
gpioname = g_strdup_printf("%s_nonsec", ppcinfo->name);
qdev_connect_gpio_out_named(ssedev, gpioname, port,
qdev_get_gpio_in_named(ppcdev,
"cfg_nonsec",
port));
g_free(gpioname);
gpioname = g_strdup_printf("%s_ap", ppcinfo->name);
qdev_connect_gpio_out_named(ssedev, gpioname, port,
qdev_get_gpio_in_named(ppcdev,
"cfg_ap", port));
g_free(gpioname);
}
gpioname = g_strdup_printf("%s_irq_enable", ppcinfo->name);
qdev_connect_gpio_out_named(ssedev, gpioname, 0,
qdev_get_gpio_in_named(ppcdev,
"irq_enable", 0));
g_free(gpioname);
gpioname = g_strdup_printf("%s_irq_clear", ppcinfo->name);
qdev_connect_gpio_out_named(ssedev, gpioname, 0,
qdev_get_gpio_in_named(ppcdev,
"irq_clear", 0));
g_free(gpioname);
gpioname = g_strdup_printf("%s_irq_status", ppcinfo->name);
qdev_connect_gpio_out_named(ppcdev, "irq", 0,
qdev_get_gpio_in_named(ssedev,
gpioname, 0));
g_free(gpioname);
qdev_connect_gpio_out(dev_splitter, i,
qdev_get_gpio_in_named(ppcdev,
"cfg_sec_resp", 0));
}
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x2000000);
}
static void musca_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->default_cpus = 2;
mc->min_cpus = mc->default_cpus;
mc->max_cpus = mc->default_cpus;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
mc->init = musca_init;
}
static void musca_a_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc);
mc->desc = "ARM Musca-A board (dual Cortex-M33)";
mmc->type = MUSCA_A;
mmc->init_svtor = 0x10200000;
mmc->sram_addr_width = 15;
mmc->num_irqs = 64;
mmc->mpc_info = a_mpc_info;
mmc->num_mpcs = ARRAY_SIZE(a_mpc_info);
}
static void musca_b1_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc);
mc->desc = "ARM Musca-B1 board (dual Cortex-M33)";
mmc->type = MUSCA_B1;
/*
* This matches the DAPlink firmware which boots from QSPI. There
* is also a firmware blob which boots from the eFlash, which
* uses init_svtor = 0x1A000000. QEMU doesn't currently support that,
* though we could in theory expose a machine property on the command
* line to allow the user to request eFlash boot.
*/
mmc->init_svtor = 0x10000000;
mmc->sram_addr_width = 17;
mmc->num_irqs = 96;
mmc->mpc_info = b1_mpc_info;
mmc->num_mpcs = ARRAY_SIZE(b1_mpc_info);
}
static const TypeInfo musca_info = {
.name = TYPE_MUSCA_MACHINE,
.parent = TYPE_MACHINE,
.abstract = true,
.instance_size = sizeof(MuscaMachineState),
.class_size = sizeof(MuscaMachineClass),
.class_init = musca_class_init,
};
static const TypeInfo musca_a_info = {
.name = TYPE_MUSCA_A_MACHINE,
.parent = TYPE_MUSCA_MACHINE,
.class_init = musca_a_class_init,
};
static const TypeInfo musca_b1_info = {
.name = TYPE_MUSCA_B1_MACHINE,
.parent = TYPE_MUSCA_MACHINE,
.class_init = musca_b1_class_init,
};
static void musca_machine_init(void)
{
type_register_static(&musca_info);
type_register_static(&musca_a_info);
type_register_static(&musca_b1_info);
}
type_init(musca_machine_init);

View File

@ -7,40 +7,24 @@
* This code is licensed under the GPL.
*/
/*
* QEMU interface:
* + sysbus MMIO region 0: device registers
* + sysbus IRQ 0: UARTINTR (combined interrupt line)
* + sysbus IRQ 1: UARTRXINTR (receive FIFO interrupt line)
* + sysbus IRQ 2: UARTTXINTR (transmit FIFO interrupt line)
* + sysbus IRQ 3: UARTRTINTR (receive timeout interrupt line)
* + sysbus IRQ 4: UARTMSINTR (momem status interrupt line)
* + sysbus IRQ 5: UARTEINTR (error interrupt line)
*/
#include "qemu/osdep.h"
#include "hw/char/pl011.h"
#include "hw/sysbus.h"
#include "chardev/char-fe.h"
#include "qemu/log.h"
#include "trace.h"
#define TYPE_PL011 "pl011"
#define PL011(obj) OBJECT_CHECK(PL011State, (obj), TYPE_PL011)
typedef struct PL011State {
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t readbuff;
uint32_t flags;
uint32_t lcr;
uint32_t rsr;
uint32_t cr;
uint32_t dmacr;
uint32_t int_enabled;
uint32_t int_level;
uint32_t read_fifo[16];
uint32_t ilpr;
uint32_t ibrd;
uint32_t fbrd;
uint32_t ifl;
int read_pos;
int read_count;
int read_trigger;
CharBackend chr;
qemu_irq irq;
const unsigned char *id;
} PL011State;
#define PL011_INT_TX 0x20
#define PL011_INT_RX 0x10
@ -49,18 +33,46 @@ typedef struct PL011State {
#define PL011_FLAG_TXFF 0x20
#define PL011_FLAG_RXFE 0x10
/* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */
#define INT_OE (1 << 10)
#define INT_BE (1 << 9)
#define INT_PE (1 << 8)
#define INT_FE (1 << 7)
#define INT_RT (1 << 6)
#define INT_TX (1 << 5)
#define INT_RX (1 << 4)
#define INT_DSR (1 << 3)
#define INT_DCD (1 << 2)
#define INT_CTS (1 << 1)
#define INT_RI (1 << 0)
#define INT_E (INT_OE | INT_BE | INT_PE | INT_FE)
#define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS)
static const unsigned char pl011_id_arm[8] =
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
static const unsigned char pl011_id_luminary[8] =
{ 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
/* Which bits in the interrupt status matter for each outbound IRQ line ? */
static const uint32_t irqmask[] = {
INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */
INT_RX,
INT_TX,
INT_RT,
INT_MS,
INT_E,
};
static void pl011_update(PL011State *s)
{
uint32_t flags;
int i;
flags = s->int_level & s->int_enabled;
trace_pl011_irq_state(flags != 0);
qemu_set_irq(s->irq, flags != 0);
for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
qemu_set_irq(s->irq[i], (flags & irqmask[i]) != 0);
}
}
static uint64_t pl011_read(void *opaque, hwaddr offset,
@ -131,7 +143,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset,
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"pl011_read: Bad offset %x\n", (int)offset);
"pl011_read: Bad offset 0x%x\n", (int)offset);
r = 0;
break;
}
@ -220,7 +232,7 @@ static void pl011_write(void *opaque, hwaddr offset,
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"pl011_write: Bad offset %x\n", (int)offset);
"pl011_write: Bad offset 0x%x\n", (int)offset);
}
}
@ -311,10 +323,13 @@ static void pl011_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
PL011State *s = PL011(obj);
int i;
memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
sysbus_init_irq(sbd, &s->irq[i]);
}
s->read_trigger = 1;
s->ifl = 0x12;
@ -357,7 +372,7 @@ static void pl011_luminary_init(Object *obj)
}
static const TypeInfo pl011_luminary_info = {
.name = "pl011_luminary",
.name = TYPE_PL011_LUMINARY,
.parent = TYPE_PL011,
.instance_init = pl011_luminary_init,
};

View File

@ -181,6 +181,21 @@ static const MemoryRegionOps tz_ppc_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
static bool tz_ppc_dummy_accepts(void *opaque, hwaddr addr,
unsigned size, bool is_write,
MemTxAttrs attrs)
{
/*
* Board code should never map the upstream end of an unused port,
* so we should never try to make a memory access to it.
*/
g_assert_not_reached();
}
static const MemoryRegionOps tz_ppc_dummy_ops = {
.valid.accepts = tz_ppc_dummy_accepts,
};
static void tz_ppc_reset(DeviceState *dev)
{
TZPPC *s = TZ_PPC(dev);
@ -210,16 +225,33 @@ static void tz_ppc_realize(DeviceState *dev, Error **errp)
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
TZPPC *s = TZ_PPC(dev);
int i;
int max_port = 0;
/* We can't create the upstream end of the port until realize,
* as we don't know the size of the MR used as the downstream until then.
*/
for (i = 0; i < TZ_NUM_PORTS; i++) {
if (s->port[i].downstream) {
max_port = i;
}
}
for (i = 0; i <= max_port; i++) {
TZPPCPort *port = &s->port[i];
char *name;
uint64_t size;
if (!port->downstream) {
/*
* Create dummy sysbus MMIO region so the sysbus region
* numbering doesn't get out of sync with the port numbers.
* The size is entirely arbitrary.
*/
name = g_strdup_printf("tz-ppc-dummy-port[%d]", i);
memory_region_init_io(&port->upstream, obj, &tz_ppc_dummy_ops,
port, name, 0x10000);
sysbus_init_mmio(sbd, &port->upstream);
g_free(name);
continue;
}

View File

@ -12,20 +12,13 @@
*/
#include "qemu/osdep.h"
#include "hw/timer/pl031.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "qemu/cutils.h"
#include "qemu/log.h"
//#define DEBUG_PL031
#ifdef DEBUG_PL031
#define DPRINTF(fmt, ...) \
do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do {} while(0)
#endif
#include "trace.h"
#define RTC_DR 0x00 /* Data read register */
#define RTC_MR 0x04 /* Match register */
@ -36,30 +29,6 @@ do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0)
#define RTC_MIS 0x18 /* Masked interrupt status register */
#define RTC_ICR 0x1c /* Interrupt clear register */
#define TYPE_PL031 "pl031"
#define PL031(obj) OBJECT_CHECK(PL031State, (obj), TYPE_PL031)
typedef struct PL031State {
SysBusDevice parent_obj;
MemoryRegion iomem;
QEMUTimer *timer;
qemu_irq irq;
/* Needed to preserve the tick_count across migration, even if the
* absolute value of the rtc_clock is different on the source and
* destination.
*/
uint32_t tick_offset_vmstate;
uint32_t tick_offset;
uint32_t mr;
uint32_t lr;
uint32_t cr;
uint32_t im;
uint32_t is;
} PL031State;
static const unsigned char pl031_id[] = {
0x31, 0x10, 0x14, 0x00, /* Device ID */
0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */
@ -67,7 +36,10 @@ static const unsigned char pl031_id[] = {
static void pl031_update(PL031State *s)
{
qemu_set_irq(s->irq, s->is & s->im);
uint32_t flags = s->is & s->im;
trace_pl031_irq_state(flags);
qemu_set_irq(s->irq, flags);
}
static void pl031_interrupt(void * opaque)
@ -75,7 +47,7 @@ static void pl031_interrupt(void * opaque)
PL031State *s = (PL031State *)opaque;
s->is = 1;
DPRINTF("Alarm raised\n");
trace_pl031_alarm_raised();
pl031_update(s);
}
@ -92,7 +64,7 @@ static void pl031_set_alarm(PL031State *s)
/* The timer wraps around. This subtraction also wraps in the same way,
and gives correct results when alarm < now_ticks. */
ticks = s->mr - pl031_get_count(s);
DPRINTF("Alarm set in %ud ticks\n", ticks);
trace_pl031_set_alarm(ticks);
if (ticks == 0) {
timer_del(s->timer);
pl031_interrupt(s);
@ -106,38 +78,49 @@ static uint64_t pl031_read(void *opaque, hwaddr offset,
unsigned size)
{
PL031State *s = (PL031State *)opaque;
if (offset >= 0xfe0 && offset < 0x1000)
return pl031_id[(offset - 0xfe0) >> 2];
uint64_t r;
switch (offset) {
case RTC_DR:
return pl031_get_count(s);
r = pl031_get_count(s);
break;
case RTC_MR:
return s->mr;
r = s->mr;
break;
case RTC_IMSC:
return s->im;
r = s->im;
break;
case RTC_RIS:
return s->is;
r = s->is;
break;
case RTC_LR:
return s->lr;
r = s->lr;
break;
case RTC_CR:
/* RTC is permanently enabled. */
return 1;
r = 1;
break;
case RTC_MIS:
return s->is & s->im;
r = s->is & s->im;
break;
case 0xfe0 ... 0xfff:
r = pl031_id[(offset - 0xfe0) >> 2];
break;
case RTC_ICR:
qemu_log_mask(LOG_GUEST_ERROR,
"pl031: read of write-only register at offset 0x%x\n",
(int)offset);
r = 0;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"pl031_read: Bad offset 0x%x\n", (int)offset);
r = 0;
break;
}
return 0;
trace_pl031_read(offset, r);
return r;
}
static void pl031_write(void * opaque, hwaddr offset,
@ -145,6 +128,7 @@ static void pl031_write(void * opaque, hwaddr offset,
{
PL031State *s = (PL031State *)opaque;
trace_pl031_write(offset, value);
switch (offset) {
case RTC_LR:
@ -157,7 +141,6 @@ static void pl031_write(void * opaque, hwaddr offset,
break;
case RTC_IMSC:
s->im = value & 1;
DPRINTF("Interrupt mask %d\n", s->im);
pl031_update(s);
break;
case RTC_ICR:
@ -165,7 +148,6 @@ static void pl031_write(void * opaque, hwaddr offset,
cleared when bit 0 of the written value is set. However the
arm926e documentation (DDI0287B) states that the interrupt is
cleared when any value is written. */
DPRINTF("Interrupt cleared");
s->is = 0;
pl031_update(s);
break;

View File

@ -77,3 +77,9 @@ xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec
nrf51_timer_read(uint64_t addr, uint32_t value, unsigned size) "read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
nrf51_timer_write(uint64_t addr, uint32_t value, unsigned size) "write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
# hw/timer/pl031.c
pl031_irq_state(int level) "irq state %d"
pl031_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
pl031_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
pl031_alarm_raised(void) "alarm raised"
pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks"

View File

@ -46,6 +46,10 @@
* being the same for both, to avoid having to have separate Property
* lists for different variants. This restriction can be relaxed later
* if necessary.)
* + QOM property "SRAM_ADDR_WIDTH" sets the number of bits used for the
* address of each SRAM bank (and thus the total amount of internal SRAM)
* + QOM property "init-svtor" sets the initial value of the CPU SVTOR register
* (where it expects to load the PC and SP from the vector table on reset)
* + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts for CPU 0,
* which are wired to its NVIC lines 32 .. n+32
* + Named GPIO inputs "EXP_CPU1_IRQ" 0..n are the expansion interrupts for
@ -182,7 +186,7 @@ typedef struct ARMSSE {
MemoryRegion cpu_container[SSE_MAX_CPUS];
MemoryRegion alias1;
MemoryRegion alias2;
MemoryRegion alias3;
MemoryRegion alias3[SSE_MAX_CPUS];
MemoryRegion sram[MAX_SRAM_BANKS];
qemu_irq *exp_irqs[SSE_MAX_CPUS];
@ -202,6 +206,7 @@ typedef struct ARMSSE {
uint32_t exp_numirq;
uint32_t mainclk_frq;
uint32_t sram_addr_width;
uint32_t init_svtor;
} ARMSSE;
typedef struct ARMSSEInfo ARMSSEInfo;

View File

@ -15,6 +15,40 @@
#ifndef HW_PL011_H
#define HW_PL011_H
#include "hw/sysbus.h"
#include "chardev/char-fe.h"
#define TYPE_PL011 "pl011"
#define PL011(obj) OBJECT_CHECK(PL011State, (obj), TYPE_PL011)
/* This shares the same struct (and cast macro) as the base pl011 device */
#define TYPE_PL011_LUMINARY "pl011_luminary"
typedef struct PL011State {
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t readbuff;
uint32_t flags;
uint32_t lcr;
uint32_t rsr;
uint32_t cr;
uint32_t dmacr;
uint32_t int_enabled;
uint32_t int_level;
uint32_t read_fifo[16];
uint32_t ilpr;
uint32_t ibrd;
uint32_t fbrd;
uint32_t ifl;
int read_pos;
int read_count;
int read_trigger;
CharBackend chr;
qemu_irq irq[6];
const unsigned char *id;
} PL011State;
static inline DeviceState *pl011_create(hwaddr addr,
qemu_irq irq,
Chardev *chr)

View File

@ -38,7 +38,13 @@
*
* QEMU interface:
* + sysbus MMIO regions 0..15: MemoryRegions defining the upstream end
* of each of the 16 ports of the PPC
* of each of the 16 ports of the PPC. When a port is unused (i.e. no
* downstream MemoryRegion is connected to it) at the end of the 0..15
* range then no sysbus MMIO region is created for its upstream. When an
* unused port lies in the middle of the range with other used ports at
* higher port numbers, a dummy MMIO region is created to ensure that
* port N's upstream is always sysbus MMIO region N. Dummy regions should
* not be mapped, and will assert if any access is made to them.
* + Property "port[0..15]": MemoryRegion defining the downstream device(s)
* for each of the 16 ports of the PPC
* + Named GPIO inputs "cfg_nonsec[0..15]": set to 1 if the port should be

44
include/hw/timer/pl031.h Normal file
View File

@ -0,0 +1,44 @@
/*
* ARM AMBA PrimeCell PL031 RTC
*
* Copyright (c) 2007 CodeSourcery
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#ifndef HW_TIMER_PL031
#define HW_TIMER_PL031
#include "hw/sysbus.h"
#define TYPE_PL031 "pl031"
#define PL031(obj) OBJECT_CHECK(PL031State, (obj), TYPE_PL031)
typedef struct PL031State {
SysBusDevice parent_obj;
MemoryRegion iomem;
QEMUTimer *timer;
qemu_irq irq;
/*
* Needed to preserve the tick_count across migration, even if the
* absolute value of the rtc_clock is different on the source and
* destination.
*/
uint32_t tick_offset_vmstate;
uint32_t tick_offset;
uint32_t mr;
uint32_t lr;
uint32_t cr;
uint32_t im;
uint32_t is;
} PL031State;
#endif

View File

@ -5,7 +5,7 @@ obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o
obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += neon_helper.o iwmmxt_helper.o vec_helper.o
obj-y += neon_helper.o iwmmxt_helper.o vec_helper.o vfp_helper.o
obj-y += gdbstub.o
obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o
obj-$(TARGET_AARCH64) += pauth_helper.o

View File

@ -2001,6 +2001,7 @@ static void arm_max_initfn(Object *obj)
cpu->isar.id_isar5 = t;
t = cpu->isar.id_isar6;
t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1);
t = FIELD_DP32(t, ID_ISAR6, DP, 1);
cpu->isar.id_isar6 = t;

View File

@ -3273,6 +3273,11 @@ static inline bool isar_feature_aa32_vcma(const ARMISARegisters *id)
return FIELD_EX32(id->id_isar5, ID_ISAR5, VCMA) != 0;
}
static inline bool isar_feature_aa32_jscvt(const ARMISARegisters *id)
{
return FIELD_EX32(id->id_isar6, ID_ISAR6, JSCVT) != 0;
}
static inline bool isar_feature_aa32_dp(const ARMISARegisters *id)
{
return FIELD_EX32(id->id_isar6, ID_ISAR6, DP) != 0;
@ -3351,6 +3356,11 @@ static inline bool isar_feature_aa64_dp(const ARMISARegisters *id)
return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, DP) != 0;
}
static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id)
{
return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0;
}
static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id)
{
return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0;

View File

@ -311,6 +311,7 @@ static void aarch64_max_initfn(Object *obj)
cpu->isar.id_aa64isar0 = t;
t = cpu->isar.id_aa64isar1;
t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1);
t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1);
t = FIELD_DP64(t, ID_AA64ISAR1, APA, 1); /* PAuth, architected only */
t = FIELD_DP64(t, ID_AA64ISAR1, API, 0);
@ -344,6 +345,7 @@ static void aarch64_max_initfn(Object *obj)
cpu->isar.id_isar5 = u;
u = cpu->isar.id_isar6;
u = FIELD_DP32(u, ID_ISAR6, JSCVT, 1);
u = FIELD_DP32(u, ID_ISAR6, DP, 1);
cpu->isar.id_isar6 = u;

File diff suppressed because it is too large Load Diff

View File

@ -218,6 +218,9 @@ DEF_HELPER_FLAGS_2(rintd_exact, TCG_CALL_NO_RWG, f64, f64, ptr)
DEF_HELPER_FLAGS_2(rints, TCG_CALL_NO_RWG, f32, f32, ptr)
DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, ptr)
DEF_HELPER_FLAGS_2(vjcvt, TCG_CALL_NO_RWG, i32, f64, env)
DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, ptr)
/* neon_helper.c */
DEF_HELPER_FLAGS_3(neon_qadd_u8, TCG_CALL_NO_RWG, i32, env, i32, i32)
DEF_HELPER_FLAGS_3(neon_qadd_s8, TCG_CALL_NO_RWG, i32, env, i32, i32)

View File

@ -6526,6 +6526,24 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof)
}
}
static void handle_fjcvtzs(DisasContext *s, int rd, int rn)
{
TCGv_i64 t = read_fp_dreg(s, rn);
TCGv_ptr fpstatus = get_fpstatus_ptr(false);
gen_helper_fjcvtzs(t, t, fpstatus);
tcg_temp_free_ptr(fpstatus);
tcg_gen_ext32u_i64(cpu_reg(s, rd), t);
tcg_gen_extrh_i64_i32(cpu_ZF, t);
tcg_gen_movi_i32(cpu_CF, 0);
tcg_gen_movi_i32(cpu_NF, 0);
tcg_gen_movi_i32(cpu_VF, 0);
tcg_temp_free_i64(t);
}
/* Floating point <-> integer conversions
* 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0
* +----+---+---+-----------+------+---+-------+-----+-------------+----+----+
@ -6541,68 +6559,80 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn)
int type = extract32(insn, 22, 2);
bool sbit = extract32(insn, 29, 1);
bool sf = extract32(insn, 31, 1);
bool itof = false;
if (sbit) {
unallocated_encoding(s);
return;
goto do_unallocated;
}
if (opcode > 5) {
/* FMOV */
bool itof = opcode & 1;
if (rmode >= 2) {
unallocated_encoding(s);
return;
}
switch (sf << 3 | type << 1 | rmode) {
case 0x0: /* 32 bit */
case 0xa: /* 64 bit */
case 0xd: /* 64 bit to top half of quad */
break;
case 0x6: /* 16-bit float, 32-bit int */
case 0xe: /* 16-bit float, 64-bit int */
if (dc_isar_feature(aa64_fp16, s)) {
break;
}
/* fallthru */
default:
/* all other sf/type/rmode combinations are invalid */
unallocated_encoding(s);
return;
}
if (!fp_access_check(s)) {
return;
}
handle_fmov(s, rd, rn, type, itof);
} else {
/* actual FP conversions */
bool itof = extract32(opcode, 1, 1);
if (rmode != 0 && opcode > 1) {
unallocated_encoding(s);
return;
switch (opcode) {
case 2: /* SCVTF */
case 3: /* UCVTF */
itof = true;
/* fallthru */
case 4: /* FCVTAS */
case 5: /* FCVTAU */
if (rmode != 0) {
goto do_unallocated;
}
/* fallthru */
case 0: /* FCVT[NPMZ]S */
case 1: /* FCVT[NPMZ]U */
switch (type) {
case 0: /* float32 */
case 1: /* float64 */
break;
case 3: /* float16 */
if (dc_isar_feature(aa64_fp16, s)) {
break;
if (!dc_isar_feature(aa64_fp16, s)) {
goto do_unallocated;
}
/* fallthru */
break;
default:
unallocated_encoding(s);
return;
goto do_unallocated;
}
if (!fp_access_check(s)) {
return;
}
handle_fpfpcvt(s, rd, rn, opcode, itof, rmode, 64, sf, type);
break;
default:
switch (sf << 7 | type << 5 | rmode << 3 | opcode) {
case 0b01100110: /* FMOV half <-> 32-bit int */
case 0b01100111:
case 0b11100110: /* FMOV half <-> 64-bit int */
case 0b11100111:
if (!dc_isar_feature(aa64_fp16, s)) {
goto do_unallocated;
}
/* fallthru */
case 0b00000110: /* FMOV 32-bit */
case 0b00000111:
case 0b10100110: /* FMOV 64-bit */
case 0b10100111:
case 0b11001110: /* FMOV top half of 128-bit */
case 0b11001111:
if (!fp_access_check(s)) {
return;
}
itof = opcode & 1;
handle_fmov(s, rd, rn, type, itof);
break;
case 0b00111110: /* FJCVTZS */
if (!dc_isar_feature(aa64_jscvt, s)) {
goto do_unallocated;
} else if (fp_access_check(s)) {
handle_fjcvtzs(s, rd, rn);
}
break;
default:
do_unallocated:
unallocated_encoding(s);
return;
}
break;
}
}

View File

@ -3639,52 +3639,115 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
}
} else {
/* data processing */
bool rd_is_dp = dp;
bool rm_is_dp = dp;
bool no_output = false;
/* The opcode is in bits 23, 21, 20 and 6. */
op = ((insn >> 20) & 8) | ((insn >> 19) & 6) | ((insn >> 6) & 1);
if (dp) {
if (op == 15) {
/* rn is opcode */
rn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1);
} else {
/* rn is register number */
VFP_DREG_N(rn, insn);
}
rn = VFP_SREG_N(insn);
if (op == 15 && (rn == 15 || ((rn & 0x1c) == 0x18) ||
((rn & 0x1e) == 0x6))) {
/* Integer or single/half precision destination. */
rd = VFP_SREG_D(insn);
} else {
VFP_DREG_D(rd, insn);
}
if (op == 15 &&
(((rn & 0x1c) == 0x10) || ((rn & 0x14) == 0x14) ||
((rn & 0x1e) == 0x4))) {
/* VCVT from int or half precision is always from S reg
* regardless of dp bit. VCVT with immediate frac_bits
* has same format as SREG_M.
if (op == 15) {
/* rn is opcode, encoded as per VFP_SREG_N. */
switch (rn) {
case 0x00: /* vmov */
case 0x01: /* vabs */
case 0x02: /* vneg */
case 0x03: /* vsqrt */
break;
case 0x04: /* vcvtb.f64.f16, vcvtb.f32.f16 */
case 0x05: /* vcvtt.f64.f16, vcvtt.f32.f16 */
/*
* VCVTB, VCVTT: only present with the halfprec extension
* UNPREDICTABLE if bit 8 is set prior to ARMv8
* (we choose to UNDEF)
*/
rm = VFP_SREG_M(insn);
} else {
VFP_DREG_M(rm, insn);
if ((dp && !arm_dc_feature(s, ARM_FEATURE_V8)) ||
!arm_dc_feature(s, ARM_FEATURE_VFP_FP16)) {
return 1;
}
rm_is_dp = false;
break;
case 0x06: /* vcvtb.f16.f32, vcvtb.f16.f64 */
case 0x07: /* vcvtt.f16.f32, vcvtt.f16.f64 */
if ((dp && !arm_dc_feature(s, ARM_FEATURE_V8)) ||
!arm_dc_feature(s, ARM_FEATURE_VFP_FP16)) {
return 1;
}
rd_is_dp = false;
break;
case 0x08: case 0x0a: /* vcmp, vcmpz */
case 0x09: case 0x0b: /* vcmpe, vcmpez */
no_output = true;
break;
case 0x0c: /* vrintr */
case 0x0d: /* vrintz */
case 0x0e: /* vrintx */
break;
case 0x0f: /* vcvt double<->single */
rd_is_dp = !dp;
break;
case 0x10: /* vcvt.fxx.u32 */
case 0x11: /* vcvt.fxx.s32 */
rm_is_dp = false;
break;
case 0x18: /* vcvtr.u32.fxx */
case 0x19: /* vcvtz.u32.fxx */
case 0x1a: /* vcvtr.s32.fxx */
case 0x1b: /* vcvtz.s32.fxx */
rd_is_dp = false;
break;
case 0x14: /* vcvt fp <-> fixed */
case 0x15:
case 0x16:
case 0x17:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) {
return 1;
}
/* Immediate frac_bits has same format as SREG_M. */
rm_is_dp = false;
break;
case 0x13: /* vjcvt */
if (!dp || !dc_isar_feature(aa32_jscvt, s)) {
return 1;
}
rd_is_dp = false;
break;
default:
return 1;
}
} else if (dp) {
/* rn is register number */
VFP_DREG_N(rn, insn);
}
if (rd_is_dp) {
VFP_DREG_D(rd, insn);
} else {
rd = VFP_SREG_D(insn);
}
if (rm_is_dp) {
VFP_DREG_M(rm, insn);
} else {
rn = VFP_SREG_N(insn);
if (op == 15 && rn == 15) {
/* Double precision destination. */
VFP_DREG_D(rd, insn);
} else {
rd = VFP_SREG_D(insn);
}
/* NB that we implicitly rely on the encoding for the frac_bits
* in VCVT of fixed to float being the same as that of an SREG_M
*/
rm = VFP_SREG_M(insn);
}
veclen = s->vec_len;
if (op == 15 && rn > 3)
if (op == 15 && rn > 3) {
veclen = 0;
}
/* Shut up compiler warnings. */
delta_m = 0;
@ -3720,55 +3783,28 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
/* Load the initial operands. */
if (op == 15) {
switch (rn) {
case 16:
case 17:
/* Integer source */
gen_mov_F0_vreg(0, rm);
break;
case 8:
case 9:
/* Compare */
case 0x08: case 0x09: /* Compare */
gen_mov_F0_vreg(dp, rd);
gen_mov_F1_vreg(dp, rm);
break;
case 10:
case 11:
/* Compare with zero */
case 0x0a: case 0x0b: /* Compare with zero */
gen_mov_F0_vreg(dp, rd);
gen_vfp_F1_ld0(dp);
break;
case 20:
case 21:
case 22:
case 23:
case 28:
case 29:
case 30:
case 31:
case 0x14: /* vcvt fp <-> fixed */
case 0x15:
case 0x16:
case 0x17:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
/* Source and destination the same. */
gen_mov_F0_vreg(dp, rd);
break;
case 4:
case 5:
case 6:
case 7:
/* VCVTB, VCVTT: only present with the halfprec extension
* UNPREDICTABLE if bit 8 is set prior to ARMv8
* (we choose to UNDEF)
*/
if ((dp && !arm_dc_feature(s, ARM_FEATURE_V8)) ||
!arm_dc_feature(s, ARM_FEATURE_VFP_FP16)) {
return 1;
}
if (!extract32(rn, 1, 1)) {
/* Half precision source. */
gen_mov_F0_vreg(0, rm);
break;
}
/* Otherwise fall through */
default:
/* One source operand. */
gen_mov_F0_vreg(dp, rm);
gen_mov_F0_vreg(rm_is_dp, rm);
break;
}
} else {
@ -4047,10 +4083,11 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
break;
}
case 15: /* single<->double conversion */
if (dp)
if (dp) {
gen_helper_vfp_fcvtsd(cpu_F0s, cpu_F0d, cpu_env);
else
} else {
gen_helper_vfp_fcvtds(cpu_F0d, cpu_F0s, cpu_env);
}
break;
case 16: /* fuito */
gen_vfp_uito(dp, 0);
@ -4058,28 +4095,19 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
case 17: /* fsito */
gen_vfp_sito(dp, 0);
break;
case 19: /* vjcvt */
gen_helper_vjcvt(cpu_F0s, cpu_F0d, cpu_env);
break;
case 20: /* fshto */
if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) {
return 1;
}
gen_vfp_shto(dp, 16 - rm, 0);
break;
case 21: /* fslto */
if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) {
return 1;
}
gen_vfp_slto(dp, 32 - rm, 0);
break;
case 22: /* fuhto */
if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) {
return 1;
}
gen_vfp_uhto(dp, 16 - rm, 0);
break;
case 23: /* fulto */
if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) {
return 1;
}
gen_vfp_ulto(dp, 32 - rm, 0);
break;
case 24: /* ftoui */
@ -4095,57 +4123,34 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
gen_vfp_tosiz(dp, 0);
break;
case 28: /* ftosh */
if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) {
return 1;
}
gen_vfp_tosh(dp, 16 - rm, 0);
break;
case 29: /* ftosl */
if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) {
return 1;
}
gen_vfp_tosl(dp, 32 - rm, 0);
break;
case 30: /* ftouh */
if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) {
return 1;
}
gen_vfp_touh(dp, 16 - rm, 0);
break;
case 31: /* ftoul */
if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) {
return 1;
}
gen_vfp_toul(dp, 32 - rm, 0);
break;
default: /* undefined */
return 1;
g_assert_not_reached();
}
break;
default: /* undefined */
return 1;
}
/* Write back the result. */
if (op == 15 && (rn >= 8 && rn <= 11)) {
/* Comparison, do nothing. */
} else if (op == 15 && dp && ((rn & 0x1c) == 0x18 ||
(rn & 0x1e) == 0x6)) {
/* VCVT double to int: always integer result.
* VCVT double to half precision is always a single
* precision result.
*/
gen_mov_vreg_F0(0, rd);
} else if (op == 15 && rn == 15) {
/* conversion */
gen_mov_vreg_F0(!dp, rd);
} else {
gen_mov_vreg_F0(dp, rd);
/* Write back the result, if any. */
if (!no_output) {
gen_mov_vreg_F0(rd_is_dp, rd);
}
/* break out of the loop if we have finished */
if (veclen == 0)
if (veclen == 0) {
break;
}
if (op == 15 && delta_m == 0) {
/* single source one-many */

1176
target/arm/vfp_helper.c Normal file

File diff suppressed because it is too large Load Diff