target-arm queue:

* hw/arm/smmuv3: Fix up L1STD_SPAN decoding
  * xlnx-zynqmp: Support Xilinx ZynqMP CAN controllers
  * sbsa-ref: allow to use Cortex-A53/57/72 cpus
  * Various minor code cleanups
  * hw/intc/armv7m_nvic: Make all of system PPB range be RAZWI/BusFault
  * Implement more pieces of ARMv8.1M support
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl/SCqMZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3r/TD/9VVmbjY4riSUeqbpX2CIG9
 EmT+hVCjVOdKAv/SfBqs9Wz0W92lff8bZp1IGrtPNQCRt1tTA+u8/7Et6dgBN2zv
 wPDsg85mRIyPYwtF3k25MakeitfH6Xv2W+u5QXF0CxvY1E5eljYd/xYktsW1d3Iv
 B68uOBjY9fuLMH1SWVYTccIWurjMPz4/nocQm1V9I1/CiimgTY5wmmmCTO3mnQyw
 Dx16t350WNFWjMAsTw9rnklvkgsJr4Euf8LFfJ7PTniT0Lh0mkEQfWnUIWp8uga3
 OD2oP5kDM1CJWSCkmT/vxKR/eLL7IesJNl6PzMlp+4ogeyn3r/QKRCpcagoinT2m
 17n8l2eVp46+HE/BYDE86LD8ebMQOCO+DyAg7QB98oJH8Vlp8o/gNHnipTPw5GMJ
 nrR+MDMIYmfPAFqpXrKkzYAI2FtHs8rdwP0BxscQvveEjY1L8RFeEf75B68MMH8b
 mlhOmUXPWqm+B7aidV5DgdcFoJccdoRQ6ZQdAWuBQa2ARLF5r+YJEJCa7N6cRAEj
 /6SSpjSqf3NmTQ0OaJ3KzZDlQ4yG2GXfN9oKN9RLlVdAGyket+YhiSA6EErc3b54
 /KO0rVadjX5mnhIB9ty0WftTsBKcs8+UFDS77x/ie0me7jY/y3pL/HDRowwq06zX
 2bv9Cd4s95ncKTl3XBMdyg==
 =wscB
 -----END PGP SIGNATURE-----

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

target-arm queue:
 * hw/arm/smmuv3: Fix up L1STD_SPAN decoding
 * xlnx-zynqmp: Support Xilinx ZynqMP CAN controllers
 * sbsa-ref: allow to use Cortex-A53/57/72 cpus
 * Various minor code cleanups
 * hw/intc/armv7m_nvic: Make all of system PPB range be RAZWI/BusFault
 * Implement more pieces of ARMv8.1M support

# gpg: Signature made Thu 10 Dec 2020 11:46:43 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-20201210: (36 commits)
  hw/arm/armv7m: Correct typo in QOM object name
  hw/intc/armv7m_nvic: Implement read/write for RAS register block
  target/arm: Implement M-profile "minimal RAS implementation"
  hw/intc/armv7m_nvic: Fix "return from inactive handler" check
  target/arm: Implement CCR_S.TRD behaviour for SG insns
  hw/intc/armv7m_nvic: Support v8.1M CCR.TRD bit
  target/arm: Implement new v8.1M VLLDM and VLSTM encodings
  target/arm: Implement new v8.1M NOCP check for exception return
  target/arm: Implement v8.1M REVIDR register
  target/arm: In v8.1M, don't set HFSR.FORCED on vector table fetch failures
  target/arm: For v8.1M, always clear R0-R3, R12, APSR, EPSR on exception entry
  hw/intc/armv7m_nvic: Update FPDSCR masking for v8.1M
  target/arm: Implement FPCXT_S fp system register
  target/arm: Factor out preserve-fp-state from full_vfp_access_check()
  target/arm: Use new FPCR_NZCV_MASK constant
  target/arm: Implement M-profile FPSCR_nzcvqc
  target/arm: Implement VLDR/VSTR system register
  target/arm: Move general-use constant expanders up in translate.c
  target/arm: Refactor M-profile VMSR/VMRS handling
  target/arm: Enforce M-profile VMRS/VMSR register restrictions
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2020-12-10 11:48:25 +00:00
commit 180834dcb8
34 changed files with 2713 additions and 153 deletions

View File

@ -1563,6 +1563,14 @@ F: hw/net/opencores_eth.c
Devices
-------
Xilinx CAN
M: Vikram Garhwal <fnu.vikram@xilinx.com>
M: Francisco Iglesias <francisco.iglesias@xilinx.com>
S: Maintained
F: hw/net/can/xlnx-*
F: include/hw/net/xlnx-*
F: tests/qtest/xlnx-can-test*
EDU
M: Jiri Slaby <jslaby@suse.cz>
S: Maintained

View File

@ -80,3 +80,4 @@ config XILINX_AXI
config XLNX_ZYNQMP
bool
select REGISTER
select CAN_BUS

View File

@ -136,7 +136,7 @@ static void armv7m_instance_init(Object *obj)
memory_region_init(&s->container, obj, "armv7m-container", UINT64_MAX);
object_initialize_child(obj, "nvnic", &s->nvic, TYPE_NVIC);
object_initialize_child(obj, "nvic", &s->nvic, TYPE_NVIC);
object_property_add_alias(obj, "num-irq",
OBJECT(&s->nvic), "num-irq");
@ -225,7 +225,7 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(sbd, 0,
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
memory_region_add_subregion(&s->container, 0xe000e000,
memory_region_add_subregion(&s->container, 0xe0000000,
sysbus_mmio_get_region(sbd, 0));
for (i = 0; i < ARRAY_SIZE(s->bitband); i++) {

View File

@ -143,6 +143,24 @@ static const int sbsa_ref_irqmap[] = {
[SBSA_GWDT] = 16,
};
static const char * const valid_cpus[] = {
ARM_CPU_TYPE_NAME("cortex-a53"),
ARM_CPU_TYPE_NAME("cortex-a57"),
ARM_CPU_TYPE_NAME("cortex-a72"),
};
static bool cpu_type_valid(const char *cpu)
{
int i;
for (i = 0; i < ARRAY_SIZE(valid_cpus); i++) {
if (strcmp(cpu, valid_cpus[i]) == 0) {
return true;
}
}
return false;
}
static uint64_t sbsa_ref_cpu_mp_affinity(SBSAMachineState *sms, int idx)
{
uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER;
@ -649,9 +667,8 @@ static void sbsa_ref_init(MachineState *machine)
const CPUArchIdList *possible_cpus;
int n, sbsa_max_cpus;
if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a57"))) {
error_report("sbsa-ref: CPU type other than the built-in "
"cortex-a57 not supported");
if (!cpu_type_valid(machine->cpu_type)) {
error_report("mach-virt: CPU type %s not supported", machine->cpu_type);
exit(1);
}

View File

@ -633,6 +633,6 @@ static inline uint64_t l1std_l2ptr(STEDesc *desc)
return hi << 32 | lo;
}
#define L1STD_SPAN(stm) (extract32((stm)->word[0], 0, 4))
#define L1STD_SPAN(stm) (extract32((stm)->word[0], 0, 5))
#endif

View File

@ -25,6 +25,7 @@
#include "sysemu/qtest.h"
#include "sysemu/device_tree.h"
#include "qom/object.h"
#include "net/can_emu.h"
struct XlnxZCU102 {
MachineState parent_obj;
@ -34,6 +35,8 @@ struct XlnxZCU102 {
bool secure;
bool virt;
CanBusState *canbus[XLNX_ZYNQMP_NUM_CAN];
struct arm_boot_info binfo;
};
@ -125,6 +128,14 @@ static void xlnx_zcu102_init(MachineState *machine)
object_property_set_bool(OBJECT(&s->soc), "virtualization", s->virt,
&error_fatal);
for (i = 0; i < XLNX_ZYNQMP_NUM_CAN; i++) {
gchar *bus_name = g_strdup_printf("canbus%d", i);
object_property_set_link(OBJECT(&s->soc), bus_name,
OBJECT(s->canbus[i]), &error_fatal);
g_free(bus_name);
}
qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
/* Create and plug in the SD cards */
@ -208,6 +219,15 @@ static void xlnx_zcu102_machine_instance_init(Object *obj)
s->secure = false;
/* Default to virt (EL2) being disabled */
s->virt = false;
object_property_add_link(obj, "xlnx-zcu102.canbus0", TYPE_CAN_BUS,
(Object **)&s->canbus[0],
object_property_allow_set_link,
0);
object_property_add_link(obj, "xlnx-zcu102.canbus1", TYPE_CAN_BUS,
(Object **)&s->canbus[1],
object_property_allow_set_link,
0);
}
static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data)

View File

@ -81,6 +81,14 @@ static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = {
21, 22,
};
static const uint64_t can_addr[XLNX_ZYNQMP_NUM_CAN] = {
0xFF060000, 0xFF070000,
};
static const int can_intr[XLNX_ZYNQMP_NUM_CAN] = {
23, 24,
};
static const uint64_t sdhci_addr[XLNX_ZYNQMP_NUM_SDHCI] = {
0xFF160000, 0xFF170000,
};
@ -243,6 +251,11 @@ static void xlnx_zynqmp_init(Object *obj)
TYPE_CADENCE_UART);
}
for (i = 0; i < XLNX_ZYNQMP_NUM_CAN; i++) {
object_initialize_child(obj, "can[*]", &s->can[i],
TYPE_XLNX_ZYNQMP_CAN);
}
object_initialize_child(obj, "sata", &s->sata, TYPE_SYSBUS_AHCI);
for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
@ -482,6 +495,23 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
gic_spi[uart_intr[i]]);
}
for (i = 0; i < XLNX_ZYNQMP_NUM_CAN; i++) {
object_property_set_int(OBJECT(&s->can[i]), "ext_clk_freq",
XLNX_ZYNQMP_CAN_REF_CLK, &error_abort);
object_property_set_link(OBJECT(&s->can[i]), "canbus",
OBJECT(s->canbus[i]), &error_fatal);
sysbus_realize(SYS_BUS_DEVICE(&s->can[i]), &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->can[i]), 0, can_addr[i]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->can[i]), 0,
gic_spi[can_intr[i]]);
}
object_property_set_int(OBJECT(&s->sata), "num-ports", SATA_NUM_PORTS,
&error_abort);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->sata), errp)) {
@ -619,6 +649,10 @@ static Property xlnx_zynqmp_props[] = {
DEFINE_PROP_BOOL("has_rpu", XlnxZynqMPState, has_rpu, false),
DEFINE_PROP_LINK("ddr-ram", XlnxZynqMPState, ddr_ram, TYPE_MEMORY_REGION,
MemoryRegion *),
DEFINE_PROP_LINK("canbus0", XlnxZynqMPState, canbus[0], TYPE_CAN_BUS,
CanBusState *),
DEFINE_PROP_LINK("canbus1", XlnxZynqMPState, canbus[1], TYPE_CAN_BUS,
CanBusState *),
DEFINE_PROP_END_OF_LIST()
};

View File

@ -832,10 +832,40 @@ int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure)
{
NVICState *s = (NVICState *)opaque;
VecInfo *vec = NULL;
int ret;
int ret = 0;
assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq);
trace_nvic_complete_irq(irq, secure);
if (secure && exc_is_banked(irq)) {
vec = &s->sec_vectors[irq];
} else {
vec = &s->vectors[irq];
}
/*
* Identify illegal exception return cases. We can't immediately
* return at this point because we still need to deactivate
* (either this exception or NMI/HardFault) first.
*/
if (!exc_is_banked(irq) && exc_targets_secure(s, irq) != secure) {
/*
* Return from a configurable exception targeting the opposite
* security state from the one we're trying to complete it for.
* Clear vec because it's not really the VecInfo for this
* (irq, secstate) so we mustn't deactivate it.
*/
ret = -1;
vec = NULL;
} else if (!vec->active) {
/* Return from an inactive interrupt */
ret = -1;
} else {
/* Legal return, we will return the RETTOBASE bit value to the caller */
ret = nvic_rettobase(s);
}
/*
* For negative priorities, v8M will forcibly deactivate the appropriate
* NMI or HardFault regardless of what interrupt we're being asked to
@ -865,32 +895,7 @@ int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure)
}
if (!vec) {
if (secure && exc_is_banked(irq)) {
vec = &s->sec_vectors[irq];
} else {
vec = &s->vectors[irq];
}
}
trace_nvic_complete_irq(irq, secure);
if (!vec->active) {
/* Tell the caller this was an illegal exception return */
return -1;
}
/*
* If this is a configurable exception and it is currently
* targeting the opposite security state from the one we're trying
* to complete it for, this counts as an illegal exception return.
* We still need to deactivate whatever vector the logic above has
* selected, though, as it might not be the same as the one for the
* requested exception number.
*/
if (!exc_is_banked(irq) && exc_targets_secure(s, irq) != secure) {
ret = -1;
} else {
ret = nvic_rettobase(s);
return ret;
}
vec->active = 0;
@ -1025,6 +1030,11 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
}
return val;
}
case 0xcfc:
if (!arm_feature(&cpu->env, ARM_FEATURE_V8_1M)) {
goto bad_offset;
}
return cpu->revidr;
case 0xd00: /* CPUID Base. */
return cpu->midr;
case 0xd04: /* Interrupt Control State (ICSR) */
@ -1090,8 +1100,9 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
}
return cpu->env.v7m.scr[attrs.secure];
case 0xd14: /* Configuration Control. */
/* The BFHFNMIGN bit is the only non-banked bit; we
* keep it in the non-secure copy of the register.
/*
* Non-banked bits: BFHFNMIGN (stored in the NS copy of the register)
* and TRD (stored in the S copy of the register)
*/
val = cpu->env.v7m.ccr[attrs.secure];
val |= cpu->env.v7m.ccr[M_REG_NS] & R_V7M_CCR_BFHFNMIGN_MASK;
@ -1472,6 +1483,12 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
return 0;
}
return cpu->env.v7m.sfar;
case 0xf04: /* RFSR */
if (!cpu_isar_feature(aa32_ras, cpu)) {
goto bad_offset;
}
/* We provide minimal-RAS only: RFSR is RAZ/WI */
return 0;
case 0xf34: /* FPCCR */
if (!cpu_isar_feature(aa32_vfp_simd, cpu)) {
return 0;
@ -1600,6 +1617,7 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
R_V7M_AIRCR_PRIGROUP_SHIFT,
R_V7M_AIRCR_PRIGROUP_LENGTH);
}
/* AIRCR.IESB is RAZ/WI because we implement only minimal RAS */
if (attrs.secure) {
/* These bits are only writable by secure */
cpu->env.v7m.aircr = value &
@ -1634,17 +1652,25 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
cpu->env.v7m.scr[attrs.secure] = value;
break;
case 0xd14: /* Configuration Control. */
{
uint32_t mask;
if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) {
goto bad_offset;
}
/* Enforce RAZ/WI on reserved and must-RAZ/WI bits */
value &= (R_V7M_CCR_STKALIGN_MASK |
R_V7M_CCR_BFHFNMIGN_MASK |
R_V7M_CCR_DIV_0_TRP_MASK |
R_V7M_CCR_UNALIGN_TRP_MASK |
R_V7M_CCR_USERSETMPEND_MASK |
R_V7M_CCR_NONBASETHRDENA_MASK);
mask = R_V7M_CCR_STKALIGN_MASK |
R_V7M_CCR_BFHFNMIGN_MASK |
R_V7M_CCR_DIV_0_TRP_MASK |
R_V7M_CCR_UNALIGN_TRP_MASK |
R_V7M_CCR_USERSETMPEND_MASK |
R_V7M_CCR_NONBASETHRDENA_MASK;
if (arm_feature(&cpu->env, ARM_FEATURE_V8_1M) && attrs.secure) {
/* TRD is always RAZ/WI from NS */
mask |= R_V7M_CCR_TRD_MASK;
}
value &= mask;
if (arm_feature(&cpu->env, ARM_FEATURE_V8)) {
/* v8M makes NONBASETHRDENA and STKALIGN be RES1 */
@ -1661,6 +1687,7 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
cpu->env.v7m.ccr[attrs.secure] = value;
break;
}
case 0xd24: /* System Handler Control and State (SHCSR) */
if (!arm_feature(&cpu->env, ARM_FEATURE_V7)) {
goto bad_offset;
@ -2006,6 +2033,12 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
}
break;
}
case 0xf04: /* RFSR */
if (!cpu_isar_feature(aa32_ras, cpu)) {
goto bad_offset;
}
/* We provide minimal-RAS only: RFSR is RAZ/WI */
break;
case 0xf34: /* FPCCR */
if (cpu_isar_feature(aa32_vfp_simd, cpu)) {
/* Not all bits here are banked. */
@ -2068,7 +2101,14 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
break;
case 0xf3c: /* FPDSCR */
if (cpu_isar_feature(aa32_vfp_simd, cpu)) {
value &= 0x07c00000;
uint32_t mask = FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK;
if (cpu_isar_feature(any_fp16, cpu)) {
mask |= FPCR_FZ16;
}
value &= mask;
if (cpu_isar_feature(aa32_lob, cpu)) {
value |= 4 << FPCR_LTPSIZE_SHIFT;
}
cpu->env.v7m.fpdscr[attrs.secure] = value;
}
break;
@ -2479,6 +2519,93 @@ static const MemoryRegionOps nvic_systick_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
static MemTxResult ras_read(void *opaque, hwaddr addr,
uint64_t *data, unsigned size,
MemTxAttrs attrs)
{
if (attrs.user) {
return MEMTX_ERROR;
}
switch (addr) {
case 0xe10: /* ERRIIDR */
/* architect field = Arm; product/variant/revision 0 */
*data = 0x43b;
break;
case 0xfc8: /* ERRDEVID */
/* Minimal RAS: we implement 0 error record indexes */
*data = 0;
break;
default:
qemu_log_mask(LOG_UNIMP, "Read RAS register offset 0x%x\n",
(uint32_t)addr);
*data = 0;
break;
}
return MEMTX_OK;
}
static MemTxResult ras_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size,
MemTxAttrs attrs)
{
if (attrs.user) {
return MEMTX_ERROR;
}
switch (addr) {
default:
qemu_log_mask(LOG_UNIMP, "Write to RAS register offset 0x%x\n",
(uint32_t)addr);
break;
}
return MEMTX_OK;
}
static const MemoryRegionOps ras_ops = {
.read_with_attrs = ras_read,
.write_with_attrs = ras_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
/*
* Unassigned portions of the PPB space are RAZ/WI for privileged
* accesses, and fault for non-privileged accesses.
*/
static MemTxResult ppb_default_read(void *opaque, hwaddr addr,
uint64_t *data, unsigned size,
MemTxAttrs attrs)
{
qemu_log_mask(LOG_UNIMP, "Read of unassigned area of PPB: offset 0x%x\n",
(uint32_t)addr);
if (attrs.user) {
return MEMTX_ERROR;
}
*data = 0;
return MEMTX_OK;
}
static MemTxResult ppb_default_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size,
MemTxAttrs attrs)
{
qemu_log_mask(LOG_UNIMP, "Write of unassigned area of PPB: offset 0x%x\n",
(uint32_t)addr);
if (attrs.user) {
return MEMTX_ERROR;
}
return MEMTX_OK;
}
static const MemoryRegionOps ppb_default_ops = {
.read_with_attrs = ppb_default_read,
.write_with_attrs = ppb_default_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 1,
.valid.max_access_size = 8,
};
static int nvic_post_load(void *opaque, int version_id)
{
NVICState *s = opaque;
@ -2675,7 +2802,6 @@ static void nvic_systick_trigger(void *opaque, int n, int level)
static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
{
NVICState *s = NVIC(dev);
int regionlen;
/* The armv7m container object will have set our CPU pointer */
if (!s->cpu || !arm_feature(&s->cpu->env, ARM_FEATURE_M)) {
@ -2718,7 +2844,20 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
M_REG_S));
}
/* The NVIC and System Control Space (SCS) starts at 0xe000e000
/*
* This device provides a single sysbus memory region which
* represents the whole of the "System PPB" space. This is the
* range from 0xe0000000 to 0xe00fffff and includes the NVIC,
* the System Control Space (system registers), the systick timer,
* and for CPUs with the Security extension an NS banked version
* of all of these.
*
* The default behaviour for unimplemented registers/ranges
* (for instance the Data Watchpoint and Trace unit at 0xe0001000)
* is to RAZ/WI for privileged access and BusFault for non-privileged
* access.
*
* The NVIC and System Control Space (SCS) starts at 0xe000e000
* and looks like this:
* 0x004 - ICTR
* 0x010 - 0xff - systick
@ -2741,35 +2880,48 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
* generally code determining which banked register to use should
* use attrs.secure; code determining actual behaviour of the system
* should use env->v7m.secure.
*
* The container covers the whole PPB space. Within it the priority
* of overlapping regions is:
* - default region (for RAZ/WI and BusFault) : -1
* - system register regions : 0
* - systick : 1
* This is because the systick device is a small block of registers
* in the middle of the other system control registers.
*/
regionlen = arm_feature(&s->cpu->env, ARM_FEATURE_V8) ? 0x21000 : 0x1000;
memory_region_init(&s->container, OBJECT(s), "nvic", regionlen);
/* The system register region goes at the bottom of the priority
* stack as it covers the whole page.
*/
memory_region_init(&s->container, OBJECT(s), "nvic", 0x100000);
memory_region_init_io(&s->defaultmem, OBJECT(s), &ppb_default_ops, s,
"nvic-default", 0x100000);
memory_region_add_subregion_overlap(&s->container, 0, &s->defaultmem, -1);
memory_region_init_io(&s->sysregmem, OBJECT(s), &nvic_sysreg_ops, s,
"nvic_sysregs", 0x1000);
memory_region_add_subregion(&s->container, 0, &s->sysregmem);
memory_region_add_subregion(&s->container, 0xe000, &s->sysregmem);
memory_region_init_io(&s->systickmem, OBJECT(s),
&nvic_systick_ops, s,
"nvic_systick", 0xe0);
memory_region_add_subregion_overlap(&s->container, 0x10,
memory_region_add_subregion_overlap(&s->container, 0xe010,
&s->systickmem, 1);
if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) {
memory_region_init_io(&s->sysreg_ns_mem, OBJECT(s),
&nvic_sysreg_ns_ops, &s->sysregmem,
"nvic_sysregs_ns", 0x1000);
memory_region_add_subregion(&s->container, 0x20000, &s->sysreg_ns_mem);
memory_region_add_subregion(&s->container, 0x2e000, &s->sysreg_ns_mem);
memory_region_init_io(&s->systick_ns_mem, OBJECT(s),
&nvic_sysreg_ns_ops, &s->systickmem,
"nvic_systick_ns", 0xe0);
memory_region_add_subregion_overlap(&s->container, 0x20010,
memory_region_add_subregion_overlap(&s->container, 0x2e010,
&s->systick_ns_mem, 1);
}
if (cpu_isar_feature(aa32_ras, s->cpu)) {
memory_region_init_io(&s->ras_mem, OBJECT(s),
&ras_ops, s, "nvic_ras", 0x1000);
memory_region_add_subregion(&s->container, 0x5000, &s->ras_mem);
}
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->container);
}

View File

@ -91,7 +91,7 @@ static const char *imx25_ccm_reg_name(uint32_t reg)
case IMX25_CCM_LPIMR1_REG:
return "lpimr1";
default:
sprintf(unknown, "[%d ?]", reg);
sprintf(unknown, "[%u ?]", reg);
return unknown;
}
}
@ -118,7 +118,7 @@ static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_MPCTL_REG], CKIH_FREQ);
}
DPRINTF("freq = %d\n", freq);
DPRINTF("freq = %u\n", freq);
return freq;
}
@ -136,7 +136,7 @@ static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
freq = freq / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_CLK_DIV));
DPRINTF("freq = %d\n", freq);
DPRINTF("freq = %u\n", freq);
return freq;
}
@ -149,7 +149,7 @@ static uint32_t imx25_ccm_get_ahb_clk(IMXCCMState *dev)
freq = imx25_ccm_get_mcu_clk(dev)
/ (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], AHB_CLK_DIV));
DPRINTF("freq = %d\n", freq);
DPRINTF("freq = %u\n", freq);
return freq;
}
@ -160,7 +160,7 @@ static uint32_t imx25_ccm_get_ipg_clk(IMXCCMState *dev)
freq = imx25_ccm_get_ahb_clk(dev) / 2;
DPRINTF("freq = %d\n", freq);
DPRINTF("freq = %u\n", freq);
return freq;
}
@ -186,7 +186,7 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
break;
}
DPRINTF("Clock = %d) = %d\n", clock, freq);
DPRINTF("Clock = %d) = %u\n", clock, freq);
return freq;
}

View File

@ -89,7 +89,7 @@ static const char *imx31_ccm_reg_name(uint32_t reg)
case IMX31_CCM_PDR2_REG:
return "PDR2";
default:
sprintf(unknown, "[%d ?]", reg);
sprintf(unknown, "[%u ?]", reg);
return unknown;
}
}
@ -120,7 +120,7 @@ static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev)
freq = CKIH_FREQ;
}
DPRINTF("freq = %d\n", freq);
DPRINTF("freq = %u\n", freq);
return freq;
}
@ -133,7 +133,7 @@ static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev)
freq = imx_ccm_calc_pll(s->reg[IMX31_CCM_MPCTL_REG],
imx31_ccm_get_pll_ref_clk(dev));
DPRINTF("freq = %d\n", freq);
DPRINTF("freq = %u\n", freq);
return freq;
}
@ -150,7 +150,7 @@ static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
freq = imx31_ccm_get_mpll_clk(dev);
}
DPRINTF("freq = %d\n", freq);
DPRINTF("freq = %u\n", freq);
return freq;
}
@ -163,7 +163,7 @@ static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
freq = imx31_ccm_get_mcu_main_clk(dev)
/ (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MAX));
DPRINTF("freq = %d\n", freq);
DPRINTF("freq = %u\n", freq);
return freq;
}
@ -176,7 +176,7 @@ static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev)
freq = imx31_ccm_get_hclk_clk(dev)
/ (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], IPG));
DPRINTF("freq = %d\n", freq);
DPRINTF("freq = %u\n", freq);
return freq;
}
@ -201,7 +201,7 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
break;
}
DPRINTF("Clock = %d) = %d\n", clock, freq);
DPRINTF("Clock = %d) = %u\n", clock, freq);
return freq;
}

View File

@ -96,7 +96,7 @@ static const char *imx6_ccm_reg_name(uint32_t reg)
case CCM_CMEOR:
return "CMEOR";
default:
sprintf(unknown, "%d ?", reg);
sprintf(unknown, "%u ?", reg);
return unknown;
}
}
@ -235,7 +235,7 @@ static const char *imx6_analog_reg_name(uint32_t reg)
case USB_ANALOG_DIGPROG:
return "USB_ANALOG_DIGPROG";
default:
sprintf(unknown, "%d ?", reg);
sprintf(unknown, "%u ?", reg);
return unknown;
}
}
@ -263,7 +263,7 @@ static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev)
freq *= 20;
}
DPRINTF("freq = %d\n", (uint32_t)freq);
DPRINTF("freq = %u\n", (uint32_t)freq);
return freq;
}
@ -275,7 +275,7 @@ static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev)
freq = imx6_analog_get_pll2_clk(dev) * 18
/ EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC);
DPRINTF("freq = %d\n", (uint32_t)freq);
DPRINTF("freq = %u\n", (uint32_t)freq);
return freq;
}
@ -287,7 +287,7 @@ static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev)
freq = imx6_analog_get_pll2_clk(dev) * 18
/ EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC);
DPRINTF("freq = %d\n", (uint32_t)freq);
DPRINTF("freq = %u\n", (uint32_t)freq);
return freq;
}
@ -315,7 +315,7 @@ static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev)
break;
}
DPRINTF("freq = %d\n", (uint32_t)freq);
DPRINTF("freq = %u\n", (uint32_t)freq);
return freq;
}
@ -327,7 +327,7 @@ static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev)
freq = imx6_analog_get_periph_clk(dev)
/ (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF));
DPRINTF("freq = %d\n", (uint32_t)freq);
DPRINTF("freq = %u\n", (uint32_t)freq);
return freq;
}
@ -339,7 +339,7 @@ static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev)
freq = imx6_ccm_get_ahb_clk(dev)
/ (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF));
DPRINTF("freq = %d\n", (uint32_t)freq);
DPRINTF("freq = %u\n", (uint32_t)freq);
return freq;
}
@ -351,7 +351,7 @@ static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev)
freq = imx6_ccm_get_ipg_clk(dev)
/ (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF));
DPRINTF("freq = %d\n", (uint32_t)freq);
DPRINTF("freq = %u\n", (uint32_t)freq);
return freq;
}
@ -385,7 +385,7 @@ static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
break;
}
DPRINTF("Clock = %d) = %d\n", clock, freq);
DPRINTF("Clock = %d) = %u\n", clock, freq);
return freq;
}

View File

@ -68,7 +68,7 @@ static const char *imx6_src_reg_name(uint32_t reg)
case SRC_GPR10:
return "SRC_GPR10";
default:
sprintf(unknown, "%d ?", reg);
sprintf(unknown, "%u ?", reg);
return unknown;
}
}

View File

@ -143,7 +143,7 @@ static const char *imx6ul_ccm_reg_name(uint32_t reg)
case CCM_CMEOR:
return "CMEOR";
default:
sprintf(unknown, "%d ?", reg);
sprintf(unknown, "%u ?", reg);
return unknown;
}
}
@ -274,7 +274,7 @@ static const char *imx6ul_analog_reg_name(uint32_t reg)
case USB_ANALOG_DIGPROG:
return "USB_ANALOG_DIGPROG";
default:
sprintf(unknown, "%d ?", reg);
sprintf(unknown, "%u ?", reg);
return unknown;
}
}

View File

@ -38,7 +38,7 @@ uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
freq = klass->get_clock_frequency(dev, clock);
}
DPRINTF("(clock = %d) = %d\n", clock, freq);
DPRINTF("(clock = %d) = %u\n", clock, freq);
return freq;
}
@ -65,7 +65,7 @@ uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq)
freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
(mfd * pd)) << 10;
DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq,
DPRINTF("(pllreg = 0x%08x, base_freq = %u) = %d\n", pllreg, base_freq,
freq);
return freq;

View File

@ -4,3 +4,4 @@ softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_pcm3680_pci.c'))
softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_mioe3680_pci.c'))
softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c'))
softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c'))
softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c'))

9
hw/net/can/trace-events Normal file
View File

@ -0,0 +1,9 @@
# xlnx-zynqmp-can.c
xlnx_can_update_irq(uint32_t isr, uint32_t ier, uint32_t irq) "ISR: 0x%08x IER: 0x%08x IRQ: 0x%08x"
xlnx_can_reset(uint32_t val) "Resetting controller with value = 0x%08x"
xlnx_can_rx_fifo_filter_reject(uint32_t id, uint8_t dlc) "Frame: ID: 0x%08x DLC: 0x%02x"
xlnx_can_filter_id_pre_write(uint8_t filter_num, uint32_t value) "Filter%d ID: 0x%08x"
xlnx_can_filter_mask_pre_write(uint8_t filter_num, uint32_t value) "Filter%d MASK: 0x%08x"
xlnx_can_tx_data(uint32_t id, uint8_t dlc, uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "Frame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"
xlnx_can_rx_data(uint32_t id, uint32_t dlc, uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "Frame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"
xlnx_can_rx_discard(uint32_t status) "Controller is not enabled for bus communication. Status Register: 0x%08x"

1
hw/net/can/trace.h Normal file
View File

@ -0,0 +1 @@
#include "trace/trace-hw_net_can.h"

1161
hw/net/can/xlnx-zynqmp-can.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
#include "hw/intc/arm_gic.h"
#include "hw/net/cadence_gem.h"
#include "hw/char/cadence_uart.h"
#include "hw/net/xlnx-zynqmp-can.h"
#include "hw/ide/ahci.h"
#include "hw/sd/sdhci.h"
#include "hw/ssi/xilinx_spips.h"
@ -33,6 +34,7 @@
#include "hw/cpu/cluster.h"
#include "target/arm/cpu.h"
#include "qom/object.h"
#include "net/can_emu.h"
#define TYPE_XLNX_ZYNQMP "xlnx,zynqmp"
OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP)
@ -41,6 +43,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP)
#define XLNX_ZYNQMP_NUM_RPU_CPUS 2
#define XLNX_ZYNQMP_NUM_GEMS 4
#define XLNX_ZYNQMP_NUM_UARTS 2
#define XLNX_ZYNQMP_NUM_CAN 2
#define XLNX_ZYNQMP_CAN_REF_CLK (24 * 1000 * 1000)
#define XLNX_ZYNQMP_NUM_SDHCI 2
#define XLNX_ZYNQMP_NUM_SPIS 2
#define XLNX_ZYNQMP_NUM_GDMA_CH 8
@ -92,6 +96,7 @@ struct XlnxZynqMPState {
CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS];
CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS];
XlnxZynqMPCANState can[XLNX_ZYNQMP_NUM_CAN];
SysbusAHCIState sata;
SDHCIState sdhci[XLNX_ZYNQMP_NUM_SDHCI];
XilinxSPIPS spi[XLNX_ZYNQMP_NUM_SPIS];
@ -112,6 +117,9 @@ struct XlnxZynqMPState {
bool virt;
/* Has the RPU subsystem? */
bool has_rpu;
/* CAN bus. */
CanBusState *canbus[XLNX_ZYNQMP_NUM_CAN];
};
#endif

View File

@ -83,7 +83,9 @@ struct NVICState {
MemoryRegion sysreg_ns_mem;
MemoryRegion systickmem;
MemoryRegion systick_ns_mem;
MemoryRegion ras_mem;
MemoryRegion container;
MemoryRegion defaultmem;
uint32_t num_irq;
qemu_irq excpout;

View File

@ -0,0 +1,78 @@
/*
* QEMU model of the Xilinx ZynqMP CAN controller.
*
* Copyright (c) 2020 Xilinx Inc.
*
* Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
*
* Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and
* Pavel Pisa.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef XLNX_ZYNQMP_CAN_H
#define XLNX_ZYNQMP_CAN_H
#include "hw/register.h"
#include "net/can_emu.h"
#include "net/can_host.h"
#include "qemu/fifo32.h"
#include "hw/ptimer.h"
#include "hw/qdev-clock.h"
#define TYPE_XLNX_ZYNQMP_CAN "xlnx.zynqmp-can"
#define XLNX_ZYNQMP_CAN(obj) \
OBJECT_CHECK(XlnxZynqMPCANState, (obj), TYPE_XLNX_ZYNQMP_CAN)
#define MAX_CAN_CTRLS 2
#define XLNX_ZYNQMP_CAN_R_MAX (0x84 / 4)
#define MAILBOX_CAPACITY 64
#define CAN_TIMER_MAX 0XFFFFUL
#define CAN_DEFAULT_CLOCK (24 * 1000 * 1000)
/* Each CAN_FRAME will have 4 * 32bit size. */
#define CAN_FRAME_SIZE 4
#define RXFIFO_SIZE (MAILBOX_CAPACITY * CAN_FRAME_SIZE)
typedef struct XlnxZynqMPCANState {
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
CanBusClientState bus_client;
CanBusState *canbus;
struct {
uint32_t ext_clk_freq;
} cfg;
RegisterInfo reg_info[XLNX_ZYNQMP_CAN_R_MAX];
uint32_t regs[XLNX_ZYNQMP_CAN_R_MAX];
Fifo32 rx_fifo;
Fifo32 tx_fifo;
Fifo32 txhpb_fifo;
ptimer_state *can_timer;
} XlnxZynqMPCANState;
#endif

View File

@ -1433,6 +1433,7 @@ if have_system
'hw/misc',
'hw/misc/macio',
'hw/net',
'hw/net/can',
'hw/nvram',
'hw/pci',
'hw/pci-host',

View File

@ -262,6 +262,9 @@ static void arm_cpu_reset(DeviceState *dev)
* always reset to 4.
*/
env->v7m.ltpsize = 4;
/* The LTPSIZE field in FPDSCR is constant and reads as 4. */
env->v7m.fpdscr[M_REG_NS] = 4 << FPCR_LTPSIZE_SHIFT;
env->v7m.fpdscr[M_REG_S] = 4 << FPCR_LTPSIZE_SHIFT;
}
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
@ -1674,7 +1677,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
}
}
if (!cpu->has_el3) {
if (!arm_feature(env, ARM_FEATURE_M) && !cpu->has_el3) {
/* If the has_el3 CPU property is disabled then we need to disable the
* feature.
*/

View File

@ -1521,9 +1521,21 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val);
#define FPCR_IXE (1 << 12) /* Inexact exception trap enable */
#define FPCR_IDE (1 << 15) /* Input Denormal exception trap enable */
#define FPCR_FZ16 (1 << 19) /* ARMv8.2+, FP16 flush-to-zero */
#define FPCR_RMODE_MASK (3 << 22) /* Rounding mode */
#define FPCR_FZ (1 << 24) /* Flush-to-zero enable bit */
#define FPCR_DN (1 << 25) /* Default NaN enable bit */
#define FPCR_AHP (1 << 26) /* Alternative half-precision */
#define FPCR_QC (1 << 27) /* Cumulative saturation bit */
#define FPCR_V (1 << 28) /* FP overflow flag */
#define FPCR_C (1 << 29) /* FP carry flag */
#define FPCR_Z (1 << 30) /* FP zero flag */
#define FPCR_N (1 << 31) /* FP negative flag */
#define FPCR_LTPSIZE_SHIFT 16 /* LTPSIZE, M-profile only */
#define FPCR_LTPSIZE_MASK (7 << FPCR_LTPSIZE_SHIFT)
#define FPCR_NZCV_MASK (FPCR_N | FPCR_Z | FPCR_C | FPCR_V)
#define FPCR_NZCVQC_MASK (FPCR_NZCV_MASK | FPCR_QC)
static inline uint32_t vfp_get_fpsr(CPUARMState *env)
{
@ -1568,6 +1580,15 @@ enum arm_cpu_mode {
#define ARM_VFP_FPEXC 8
#define ARM_VFP_FPINST 9
#define ARM_VFP_FPINST2 10
/* These ones are M-profile only */
#define ARM_VFP_FPSCR_NZCVQC 2
#define ARM_VFP_VPR 12
#define ARM_VFP_P0 13
#define ARM_VFP_FPCXT_NS 14
#define ARM_VFP_FPCXT_S 15
/* QEMU-internal value meaning "FPSCR, but we care only about NZCV" */
#define QEMU_VFP_FPSCR_NZCV 0xffff
/* iwMMXt coprocessor control registers. */
#define ARM_IWMMXT_wCID 0
@ -1590,6 +1611,8 @@ FIELD(V7M_CCR, STKOFHFNMIGN, 10, 1)
FIELD(V7M_CCR, DC, 16, 1)
FIELD(V7M_CCR, IC, 17, 1)
FIELD(V7M_CCR, BP, 18, 1)
FIELD(V7M_CCR, LOB, 19, 1)
FIELD(V7M_CCR, TRD, 20, 1)
/* V7M SCR bits */
FIELD(V7M_SCR, SLEEPONEXIT, 1, 1)
@ -1804,6 +1827,15 @@ FIELD(ID_MMFR4, LSM, 20, 4)
FIELD(ID_MMFR4, CCIDX, 24, 4)
FIELD(ID_MMFR4, EVT, 28, 4)
FIELD(ID_PFR0, STATE0, 0, 4)
FIELD(ID_PFR0, STATE1, 4, 4)
FIELD(ID_PFR0, STATE2, 8, 4)
FIELD(ID_PFR0, STATE3, 12, 4)
FIELD(ID_PFR0, CSV2, 16, 4)
FIELD(ID_PFR0, AMU, 20, 4)
FIELD(ID_PFR0, DIT, 24, 4)
FIELD(ID_PFR0, RAS, 28, 4)
FIELD(ID_PFR1, PROGMOD, 0, 4)
FIELD(ID_PFR1, SECURITY, 4, 4)
FIELD(ID_PFR1, MPROGMOD, 8, 4)
@ -3550,11 +3582,25 @@ static inline bool isar_feature_aa32_predinv(const ARMISARegisters *id)
return FIELD_EX32(id->id_isar6, ID_ISAR6, SPECRES) != 0;
}
static inline bool isar_feature_aa32_ras(const ARMISARegisters *id)
{
return FIELD_EX32(id->id_pfr0, ID_PFR0, RAS) != 0;
}
static inline bool isar_feature_aa32_mprofile(const ARMISARegisters *id)
{
return FIELD_EX32(id->id_pfr1, ID_PFR1, MPROGMOD) != 0;
}
static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id)
{
/*
* Return true if M-profile state handling insns
* (VSCCLRM, CLRM, FPCTX access insns) are implemented
*/
return FIELD_EX32(id->id_pfr1, ID_PFR1, SECURITY) >= 3;
}
static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id)
{
/* Sadly this is encoded differently for A-profile and M-profile */

View File

@ -11754,6 +11754,11 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
} else {
uint32_t ap = extract32(env->pmsav8.rbar[secure][matchregion], 1, 2);
uint32_t xn = extract32(env->pmsav8.rbar[secure][matchregion], 0, 1);
bool pxn = false;
if (arm_feature(env, ARM_FEATURE_V8_1M)) {
pxn = extract32(env->pmsav8.rlar[secure][matchregion], 4, 1);
}
if (m_is_system_region(env, address)) {
/* System space is always execute never */
@ -11761,7 +11766,7 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
}
*prot = simple_ap_to_rw_prot(env, mmu_idx, ap);
if (*prot && !xn) {
if (*prot && !xn && !(pxn && !is_user)) {
*prot |= PAGE_EXEC;
}
/* We don't need to look the attribute up in the MAIR0/MAIR1

View File

@ -29,13 +29,17 @@
# If the coprocessor is not present or disabled then we will generate
# the NOCP exception; otherwise we let the insn through to the main decode.
%vd_dp 22:1 12:4
%vd_sp 12:4 22:1
&nocp cp
{
# Special cases which do not take an early NOCP: VLLDM and VLSTM
VLLDM_VLSTM 1110 1100 001 l:1 rn:4 0000 1010 0000 0000
# TODO: VSCCLRM (new in v8.1M) is similar:
#VSCCLRM 1110 1100 1-01 1111 ---- 1011 ---- ---0
VLLDM_VLSTM 1110 1100 001 l:1 rn:4 0000 1010 op:1 000 0000
# VSCCLRM (new in v8.1M) is similar:
VSCCLRM 1110 1100 1.01 1111 .... 1011 imm:7 0 vd=%vd_dp size=3
VSCCLRM 1110 1100 1.01 1111 .... 1010 imm:8 vd=%vd_sp size=2
NOCP 111- 1110 ---- ---- ---- cp:4 ---- ---- &nocp
NOCP 111- 110- ---- ---- ---- cp:4 ---- ---- &nocp

View File

@ -722,11 +722,15 @@ load_fail:
* The HardFault is Secure if BFHFNMINS is 0 (meaning that all HFs are
* secure); otherwise it targets the same security state as the
* underlying exception.
* In v8.1M HardFaults from vector table fetch fails don't set FORCED.
*/
if (!(cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) {
exc_secure = true;
}
env->v7m.hfsr |= R_V7M_HFSR_VECTTBL_MASK | R_V7M_HFSR_FORCED_MASK;
env->v7m.hfsr |= R_V7M_HFSR_VECTTBL_MASK;
if (!arm_feature(env, ARM_FEATURE_V8_1M)) {
env->v7m.hfsr |= R_V7M_HFSR_FORCED_MASK;
}
armv7m_nvic_set_pending_derived(env->nvic, ARMV7M_EXCP_HARD, exc_secure);
return false;
}
@ -897,10 +901,12 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain,
* Clear registers if necessary to prevent non-secure exception
* code being able to see register values from secure code.
* Where register values become architecturally UNKNOWN we leave
* them with their previous values.
* them with their previous values. v8.1M is tighter than v8.0M
* here and always zeroes the caller-saved registers regardless
* of the security state the exception is targeting.
*/
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
if (!targets_secure) {
if (!targets_secure || arm_feature(env, ARM_FEATURE_V8_1M)) {
/*
* Always clear the caller-saved registers (they have been
* pushed to the stack earlier in v7m_push_stack()).
@ -909,10 +915,16 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain,
* v7m_push_callee_stack()).
*/
int i;
/*
* r4..r11 are callee-saves, zero only if background
* state was Secure (EXCRET.S == 1) and exception
* targets Non-secure state
*/
bool zero_callee_saves = !targets_secure &&
(lr & R_V7M_EXCRET_S_MASK);
for (i = 0; i < 13; i++) {
/* r4..r11 are callee-saves, zero only if EXCRET.S == 1 */
if (i < 4 || i > 11 || (lr & R_V7M_EXCRET_S_MASK)) {
if (i < 4 || i > 11 || zero_callee_saves) {
env->regs[i] = 0;
}
}
@ -1503,7 +1515,27 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
v7m_exception_taken(cpu, excret, true, false);
return;
} else {
/* Clear s0..s15 and FPSCR */
if (arm_feature(env, ARM_FEATURE_V8_1M)) {
/* v8.1M adds this NOCP check */
bool nsacr_pass = exc_secure ||
extract32(env->v7m.nsacr, 10, 1);
bool cpacr_pass = v7m_cpacr_pass(env, exc_secure, true);
if (!nsacr_pass) {
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, true);
env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_NOCP_MASK;
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: NSACR prevents clearing FPU registers\n");
v7m_exception_taken(cpu, excret, true, false);
} else if (!cpacr_pass) {
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
exc_secure);
env->v7m.cfsr[exc_secure] |= R_V7M_CFSR_NOCP_MASK;
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: CPACR prevents clearing FPU registers\n");
v7m_exception_taken(cpu, excret, true, false);
}
}
/* Clear s0..s15 and FPSCR; TODO also VPR when MVE is implemented */
int i;
for (i = 0; i < 16; i += 2) {
@ -1967,6 +1999,64 @@ static bool v7m_read_half_insn(ARMCPU *cpu, ARMMMUIdx mmu_idx,
return true;
}
static bool v7m_read_sg_stack_word(ARMCPU *cpu, ARMMMUIdx mmu_idx,
uint32_t addr, uint32_t *spdata)
{
/*
* Read a word of data from the stack for the SG instruction,
* writing the value into *spdata. If the load succeeds, return
* true; otherwise pend an appropriate exception and return false.
* (We can't use data load helpers here that throw an exception
* because of the context we're called in, which is halfway through
* arm_v7m_cpu_do_interrupt().)
*/
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
MemTxAttrs attrs = {};
MemTxResult txres;
target_ulong page_size;
hwaddr physaddr;
int prot;
ARMMMUFaultInfo fi = {};
ARMCacheAttrs cacheattrs = {};
uint32_t value;
if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &physaddr,
&attrs, &prot, &page_size, &fi, &cacheattrs)) {
/* MPU/SAU lookup failed */
if (fi.type == ARMFault_QEMU_SFault) {
qemu_log_mask(CPU_LOG_INT,
"...SecureFault during stack word read\n");
env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK | R_V7M_SFSR_SFARVALID_MASK;
env->v7m.sfar = addr;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
} else {
qemu_log_mask(CPU_LOG_INT,
"...MemManageFault during stack word read\n");
env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_DACCVIOL_MASK |
R_V7M_CFSR_MMARVALID_MASK;
env->v7m.mmfar[M_REG_S] = addr;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM, false);
}
return false;
}
value = address_space_ldl(arm_addressspace(cs, attrs), physaddr,
attrs, &txres);
if (txres != MEMTX_OK) {
/* BusFault trying to read the data */
qemu_log_mask(CPU_LOG_INT,
"...BusFault during stack word read\n");
env->v7m.cfsr[M_REG_NS] |=
(R_V7M_CFSR_PRECISERR_MASK | R_V7M_CFSR_BFARVALID_MASK);
env->v7m.bfar = addr;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false);
return false;
}
*spdata = value;
return true;
}
static bool v7m_handle_execute_nsc(ARMCPU *cpu)
{
/*
@ -2023,6 +2113,34 @@ static bool v7m_handle_execute_nsc(ARMCPU *cpu)
*/
qemu_log_mask(CPU_LOG_INT, "...really an SG instruction at 0x%08" PRIx32
", executing it\n", env->regs[15]);
if (cpu_isar_feature(aa32_m_sec_state, cpu) &&
!arm_v7m_is_handler_mode(env)) {
/*
* v8.1M exception stack frame integrity check. Note that we
* must perform the memory access even if CCR_S.TRD is zero
* and we aren't going to check what the data loaded is.
*/
uint32_t spdata, sp;
/*
* We know we are currently NS, so the S stack pointers must be
* in other_ss_{psp,msp}, not in regs[13]/other_sp.
*/
sp = v7m_using_psp(env) ? env->v7m.other_ss_psp : env->v7m.other_ss_msp;
if (!v7m_read_sg_stack_word(cpu, mmu_idx, sp, &spdata)) {
/* Stack access failed and an exception has been pended */
return false;
}
if (env->v7m.ccr[M_REG_S] & R_V7M_CCR_TRD_MASK) {
if (((spdata & ~1) == 0xfefa125a) ||
!(env->v7m.control[M_REG_S] & 1)) {
goto gen_invep;
}
}
}
env->regs[14] &= ~1;
env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK;
switch_v7m_security_state(env, true);

View File

@ -307,6 +307,10 @@ CLZ 1111 1010 1011 ---- 1111 .... 1000 .... @rdm
# SEV 1111 0011 1010 1111 1000 0000 0000 0100
# SEVL 1111 0011 1010 1111 1000 0000 0000 0101
# For M-profile minimal-RAS ESB can be a NOP, which is the
# default behaviour since it is in the hint space.
# ESB 1111 0011 1010 1111 1000 0000 0001 0000
# The canonical nop ends in 0000 0000, but the whole rest
# of the space is "reserved hint, behaves as nop".
NOP 1111 0011 1010 1111 1000 0000 ---- ----
@ -609,7 +613,11 @@ UXTAB 1111 1010 0101 .... 1111 .... 10.. .... @rrr_rot
STM_t32 1110 1000 10.0 .... ................ @ldstm i=1 b=0
STM_t32 1110 1001 00.0 .... ................ @ldstm i=0 b=1
LDM_t32 1110 1000 10.1 .... ................ @ldstm i=1 b=0
{
# Rn=15 UNDEFs for LDM; M-profile CLRM uses that encoding
CLRM 1110 1000 1001 1111 list:16
LDM_t32 1110 1000 10.1 .... ................ @ldstm i=1 b=0
}
LDM_t32 1110 1001 00.1 .... ................ @ldstm i=0 b=1
&rfe !extern rn w pu

View File

@ -83,6 +83,32 @@ static inline long vfp_f16_offset(unsigned reg, bool top)
return offs;
}
/*
* Generate code for M-profile lazy FP state preservation if needed;
* this corresponds to the pseudocode PreserveFPState() function.
*/
static void gen_preserve_fp_state(DisasContext *s)
{
if (s->v7m_lspact) {
/*
* Lazy state saving affects external memory and also the NVIC,
* so we must mark it as an IO operation for icount (and cause
* this to be the last insn in the TB).
*/
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
s->base.is_jmp = DISAS_UPDATE_EXIT;
gen_io_start();
}
gen_helper_v7m_preserve_fp_state(cpu_env);
/*
* If the preserve_fp_state helper doesn't throw an exception
* then it will clear LSPACT; we don't need to repeat this for
* any further FP insns in this TB.
*/
s->v7m_lspact = false;
}
}
/*
* Check that VFP access is enabled. If it is, do the necessary
* M-profile lazy-FP handling and then return true.
@ -113,24 +139,7 @@ static bool full_vfp_access_check(DisasContext *s, bool ignore_vfp_enabled)
/* Handle M-profile lazy FP state mechanics */
/* Trigger lazy-state preservation if necessary */
if (s->v7m_lspact) {
/*
* Lazy state saving affects external memory and also the NVIC,
* so we must mark it as an IO operation for icount (and cause
* this to be the last insn in the TB).
*/
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
s->base.is_jmp = DISAS_UPDATE_EXIT;
gen_io_start();
}
gen_helper_v7m_preserve_fp_state(cpu_env);
/*
* If the preserve_fp_state helper doesn't throw an exception
* then it will clear LSPACT; we don't need to repeat this for
* any further FP insns in this TB.
*/
s->v7m_lspact = false;
}
gen_preserve_fp_state(s);
/* Update ownership of FP context: set FPCCR.S to match current state */
if (s->v8m_fpccr_s_wrong) {
@ -607,24 +616,266 @@ static bool trans_VDUP(DisasContext *s, arg_VDUP *a)
return true;
}
/*
* M-profile provides two different sets of instructions that can
* access floating point system registers: VMSR/VMRS (which move
* to/from a general purpose register) and VLDR/VSTR sysreg (which
* move directly to/from memory). In some cases there are also side
* effects which must happen after any write to memory (which could
* cause an exception). So we implement the common logic for the
* sysreg access in gen_M_fp_sysreg_write() and gen_M_fp_sysreg_read(),
* which take pointers to callback functions which will perform the
* actual "read/write general purpose register" and "read/write
* memory" operations.
*/
/*
* Emit code to store the sysreg to its final destination; frees the
* TCG temp 'value' it is passed.
*/
typedef void fp_sysreg_storefn(DisasContext *s, void *opaque, TCGv_i32 value);
/*
* Emit code to load the value to be copied to the sysreg; returns
* a new TCG temporary
*/
typedef TCGv_i32 fp_sysreg_loadfn(DisasContext *s, void *opaque);
/* Common decode/access checks for fp sysreg read/write */
typedef enum FPSysRegCheckResult {
FPSysRegCheckFailed, /* caller should return false */
FPSysRegCheckDone, /* caller should return true */
FPSysRegCheckContinue, /* caller should continue generating code */
} FPSysRegCheckResult;
static FPSysRegCheckResult fp_sysreg_checks(DisasContext *s, int regno)
{
if (!dc_isar_feature(aa32_fpsp_v2, s)) {
return FPSysRegCheckFailed;
}
switch (regno) {
case ARM_VFP_FPSCR:
case QEMU_VFP_FPSCR_NZCV:
break;
case ARM_VFP_FPSCR_NZCVQC:
if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
return false;
}
break;
case ARM_VFP_FPCXT_S:
if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
return false;
}
if (!s->v8m_secure) {
return false;
}
break;
default:
return FPSysRegCheckFailed;
}
if (!vfp_access_check(s)) {
return FPSysRegCheckDone;
}
return FPSysRegCheckContinue;
}
static bool gen_M_fp_sysreg_write(DisasContext *s, int regno,
fp_sysreg_loadfn *loadfn,
void *opaque)
{
/* Do a write to an M-profile floating point system register */
TCGv_i32 tmp;
switch (fp_sysreg_checks(s, regno)) {
case FPSysRegCheckFailed:
return false;
case FPSysRegCheckDone:
return true;
case FPSysRegCheckContinue:
break;
}
switch (regno) {
case ARM_VFP_FPSCR:
tmp = loadfn(s, opaque);
gen_helper_vfp_set_fpscr(cpu_env, tmp);
tcg_temp_free_i32(tmp);
gen_lookup_tb(s);
break;
case ARM_VFP_FPSCR_NZCVQC:
{
TCGv_i32 fpscr;
tmp = loadfn(s, opaque);
/*
* TODO: when we implement MVE, write the QC bit.
* For non-MVE, QC is RES0.
*/
tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK);
fpscr = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]);
tcg_gen_andi_i32(fpscr, fpscr, ~FPCR_NZCV_MASK);
tcg_gen_or_i32(fpscr, fpscr, tmp);
store_cpu_field(fpscr, vfp.xregs[ARM_VFP_FPSCR]);
tcg_temp_free_i32(tmp);
break;
}
case ARM_VFP_FPCXT_S:
{
TCGv_i32 sfpa, control, fpscr;
/* Set FPSCR[27:0] and CONTROL.SFPA from value */
tmp = loadfn(s, opaque);
sfpa = tcg_temp_new_i32();
tcg_gen_shri_i32(sfpa, tmp, 31);
control = load_cpu_field(v7m.control[M_REG_S]);
tcg_gen_deposit_i32(control, control, sfpa,
R_V7M_CONTROL_SFPA_SHIFT, 1);
store_cpu_field(control, v7m.control[M_REG_S]);
fpscr = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]);
tcg_gen_andi_i32(fpscr, fpscr, FPCR_NZCV_MASK);
tcg_gen_andi_i32(tmp, tmp, ~FPCR_NZCV_MASK);
tcg_gen_or_i32(fpscr, fpscr, tmp);
store_cpu_field(fpscr, vfp.xregs[ARM_VFP_FPSCR]);
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(sfpa);
break;
}
default:
g_assert_not_reached();
}
return true;
}
static bool gen_M_fp_sysreg_read(DisasContext *s, int regno,
fp_sysreg_storefn *storefn,
void *opaque)
{
/* Do a read from an M-profile floating point system register */
TCGv_i32 tmp;
switch (fp_sysreg_checks(s, regno)) {
case FPSysRegCheckFailed:
return false;
case FPSysRegCheckDone:
return true;
case FPSysRegCheckContinue:
break;
}
switch (regno) {
case ARM_VFP_FPSCR:
tmp = tcg_temp_new_i32();
gen_helper_vfp_get_fpscr(tmp, cpu_env);
storefn(s, opaque, tmp);
break;
case ARM_VFP_FPSCR_NZCVQC:
/*
* TODO: MVE has a QC bit, which we probably won't store
* in the xregs[] field. For non-MVE, where QC is RES0,
* we can just fall through to the FPSCR_NZCV case.
*/
case QEMU_VFP_FPSCR_NZCV:
/*
* Read just NZCV; this is a special case to avoid the
* helper call for the "VMRS to CPSR.NZCV" insn.
*/
tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]);
tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK);
storefn(s, opaque, tmp);
break;
case ARM_VFP_FPCXT_S:
{
TCGv_i32 control, sfpa, fpscr;
/* Bits [27:0] from FPSCR, bit [31] from CONTROL.SFPA */
tmp = tcg_temp_new_i32();
sfpa = tcg_temp_new_i32();
gen_helper_vfp_get_fpscr(tmp, cpu_env);
tcg_gen_andi_i32(tmp, tmp, ~FPCR_NZCV_MASK);
control = load_cpu_field(v7m.control[M_REG_S]);
tcg_gen_andi_i32(sfpa, control, R_V7M_CONTROL_SFPA_MASK);
tcg_gen_shli_i32(sfpa, sfpa, 31 - R_V7M_CONTROL_SFPA_SHIFT);
tcg_gen_or_i32(tmp, tmp, sfpa);
tcg_temp_free_i32(sfpa);
/*
* Store result before updating FPSCR etc, in case
* it is a memory write which causes an exception.
*/
storefn(s, opaque, tmp);
/*
* Now we must reset FPSCR from FPDSCR_NS, and clear
* CONTROL.SFPA; so we'll end the TB here.
*/
tcg_gen_andi_i32(control, control, ~R_V7M_CONTROL_SFPA_MASK);
store_cpu_field(control, v7m.control[M_REG_S]);
fpscr = load_cpu_field(v7m.fpdscr[M_REG_NS]);
gen_helper_vfp_set_fpscr(cpu_env, fpscr);
tcg_temp_free_i32(fpscr);
gen_lookup_tb(s);
break;
}
default:
g_assert_not_reached();
}
return true;
}
static void fp_sysreg_to_gpr(DisasContext *s, void *opaque, TCGv_i32 value)
{
arg_VMSR_VMRS *a = opaque;
if (a->rt == 15) {
/* Set the 4 flag bits in the CPSR */
gen_set_nzcv(value);
tcg_temp_free_i32(value);
} else {
store_reg(s, a->rt, value);
}
}
static TCGv_i32 gpr_to_fp_sysreg(DisasContext *s, void *opaque)
{
arg_VMSR_VMRS *a = opaque;
return load_reg(s, a->rt);
}
static bool gen_M_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a)
{
/*
* Accesses to R15 are UNPREDICTABLE; we choose to undef.
* FPSCR -> r15 is a special case which writes to the PSR flags;
* set a->reg to a special value to tell gen_M_fp_sysreg_read()
* we only care about the top 4 bits of FPSCR there.
*/
if (a->rt == 15) {
if (a->l && a->reg == ARM_VFP_FPSCR) {
a->reg = QEMU_VFP_FPSCR_NZCV;
} else {
return false;
}
}
if (a->l) {
/* VMRS, move FP system register to gp register */
return gen_M_fp_sysreg_read(s, a->reg, fp_sysreg_to_gpr, a);
} else {
/* VMSR, move gp register to FP system register */
return gen_M_fp_sysreg_write(s, a->reg, gpr_to_fp_sysreg, a);
}
}
static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a)
{
TCGv_i32 tmp;
bool ignore_vfp_enabled = false;
if (!dc_isar_feature(aa32_fpsp_v2, s)) {
return false;
if (arm_dc_feature(s, ARM_FEATURE_M)) {
return gen_M_VMSR_VMRS(s, a);
}
if (arm_dc_feature(s, ARM_FEATURE_M)) {
/*
* The only M-profile VFP vmrs/vmsr sysreg is FPSCR.
* Accesses to R15 are UNPREDICTABLE; we choose to undef.
* (FPSCR -> r15 is a special case which writes to the PSR flags.)
*/
if (a->rt == 15 && (!a->l || a->reg != ARM_VFP_FPSCR)) {
return false;
}
if (!dc_isar_feature(aa32_fpsp_v2, s)) {
return false;
}
switch (a->reg) {
@ -701,7 +952,7 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a)
case ARM_VFP_FPSCR:
if (a->rt == 15) {
tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]);
tcg_gen_andi_i32(tmp, tmp, 0xf0000000);
tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK);
} else {
tmp = tcg_temp_new_i32();
gen_helper_vfp_get_fpscr(tmp, cpu_env);
@ -756,6 +1007,97 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a)
return true;
}
static void fp_sysreg_to_memory(DisasContext *s, void *opaque, TCGv_i32 value)
{
arg_vldr_sysreg *a = opaque;
uint32_t offset = a->imm;
TCGv_i32 addr;
if (!a->a) {
offset = - offset;
}
addr = load_reg(s, a->rn);
if (a->p) {
tcg_gen_addi_i32(addr, addr, offset);
}
if (s->v8m_stackcheck && a->rn == 13 && a->w) {
gen_helper_v8m_stackcheck(cpu_env, addr);
}
gen_aa32_st_i32(s, value, addr, get_mem_index(s),
MO_UL | MO_ALIGN | s->be_data);
tcg_temp_free_i32(value);
if (a->w) {
/* writeback */
if (!a->p) {
tcg_gen_addi_i32(addr, addr, offset);
}
store_reg(s, a->rn, addr);
} else {
tcg_temp_free_i32(addr);
}
}
static TCGv_i32 memory_to_fp_sysreg(DisasContext *s, void *opaque)
{
arg_vldr_sysreg *a = opaque;
uint32_t offset = a->imm;
TCGv_i32 addr;
TCGv_i32 value = tcg_temp_new_i32();
if (!a->a) {
offset = - offset;
}
addr = load_reg(s, a->rn);
if (a->p) {
tcg_gen_addi_i32(addr, addr, offset);
}
if (s->v8m_stackcheck && a->rn == 13 && a->w) {
gen_helper_v8m_stackcheck(cpu_env, addr);
}
gen_aa32_ld_i32(s, value, addr, get_mem_index(s),
MO_UL | MO_ALIGN | s->be_data);
if (a->w) {
/* writeback */
if (!a->p) {
tcg_gen_addi_i32(addr, addr, offset);
}
store_reg(s, a->rn, addr);
} else {
tcg_temp_free_i32(addr);
}
return value;
}
static bool trans_VLDR_sysreg(DisasContext *s, arg_vldr_sysreg *a)
{
if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
return false;
}
if (a->rn == 15) {
return false;
}
return gen_M_fp_sysreg_write(s, a->reg, memory_to_fp_sysreg, a);
}
static bool trans_VSTR_sysreg(DisasContext *s, arg_vldr_sysreg *a)
{
if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
return false;
}
if (a->rn == 15) {
return false;
}
return gen_M_fp_sysreg_read(s, a->reg, fp_sysreg_to_memory, a);
}
static bool trans_VMOV_half(DisasContext *s, arg_VMOV_single *a)
{
TCGv_i32 tmp;
@ -3379,6 +3721,31 @@ static bool trans_VLLDM_VLSTM(DisasContext *s, arg_VLLDM_VLSTM *a)
!arm_dc_feature(s, ARM_FEATURE_V8)) {
return false;
}
if (a->op) {
/*
* T2 encoding ({D0-D31} reglist): v8.1M and up. We choose not
* to take the IMPDEF option to make memory accesses to the stack
* slots that correspond to the D16-D31 registers (discarding
* read data and writing UNKNOWN values), so for us the T2
* encoding behaves identically to the T1 encoding.
*/
if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
return false;
}
} else {
/*
* T1 encoding ({D0-D15} reglist); undef if we have 32 Dregs.
* This is currently architecturally impossible, but we add the
* check to stay in line with the pseudocode. Note that we must
* emit code for the UNDEF so it takes precedence over the NOCP.
*/
if (dc_isar_feature(aa32_simd_r32, s)) {
unallocated_encoding(s);
return true;
}
}
/*
* If not secure, UNDEF. We must emit code for this
* rather than returning false so that this takes
@ -3406,6 +3773,90 @@ static bool trans_VLLDM_VLSTM(DisasContext *s, arg_VLLDM_VLSTM *a)
return true;
}
static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a)
{
int btmreg, topreg;
TCGv_i64 zero;
TCGv_i32 aspen, sfpa;
if (!dc_isar_feature(aa32_m_sec_state, s)) {
/* Before v8.1M, fall through in decode to NOCP check */
return false;
}
/* Explicitly UNDEF because this takes precedence over NOCP */
if (!arm_dc_feature(s, ARM_FEATURE_M_MAIN) || !s->v8m_secure) {
unallocated_encoding(s);
return true;
}
if (!dc_isar_feature(aa32_vfp_simd, s)) {
/* NOP if we have neither FP nor MVE */
return true;
}
/*
* If FPCCR.ASPEN != 0 && CONTROL_S.SFPA == 0 then there is no
* active floating point context so we must NOP (without doing
* any lazy state preservation or the NOCP check).
*/
aspen = load_cpu_field(v7m.fpccr[M_REG_S]);
sfpa = load_cpu_field(v7m.control[M_REG_S]);
tcg_gen_andi_i32(aspen, aspen, R_V7M_FPCCR_ASPEN_MASK);
tcg_gen_xori_i32(aspen, aspen, R_V7M_FPCCR_ASPEN_MASK);
tcg_gen_andi_i32(sfpa, sfpa, R_V7M_CONTROL_SFPA_MASK);
tcg_gen_or_i32(sfpa, sfpa, aspen);
arm_gen_condlabel(s);
tcg_gen_brcondi_i32(TCG_COND_EQ, sfpa, 0, s->condlabel);
if (s->fp_excp_el != 0) {
gen_exception_insn(s, s->pc_curr, EXCP_NOCP,
syn_uncategorized(), s->fp_excp_el);
return true;
}
topreg = a->vd + a->imm - 1;
btmreg = a->vd;
/* Convert to Sreg numbers if the insn specified in Dregs */
if (a->size == 3) {
topreg = topreg * 2 + 1;
btmreg *= 2;
}
if (topreg > 63 || (topreg > 31 && !(topreg & 1))) {
/* UNPREDICTABLE: we choose to undef */
unallocated_encoding(s);
return true;
}
/* Silently ignore requests to clear D16-D31 if they don't exist */
if (topreg > 31 && !dc_isar_feature(aa32_simd_r32, s)) {
topreg = 31;
}
if (!vfp_access_check(s)) {
return true;
}
/* Zero the Sregs from btmreg to topreg inclusive. */
zero = tcg_const_i64(0);
if (btmreg & 1) {
write_neon_element64(zero, btmreg >> 1, 1, MO_32);
btmreg++;
}
for (; btmreg + 1 <= topreg; btmreg += 2) {
write_neon_element64(zero, btmreg >> 1, 0, MO_64);
}
if (btmreg == topreg) {
write_neon_element64(zero, btmreg >> 1, 0, MO_32);
btmreg++;
}
assert(btmreg == topreg + 1);
/* TODO: when MVE is implemented, zero VPR here */
return true;
}
static bool trans_NOCP(DisasContext *s, arg_nocp *a)
{
/*

View File

@ -100,6 +100,39 @@ void arm_translate_init(void)
a64_translate_init();
}
/* Generate a label used for skipping this instruction */
static void arm_gen_condlabel(DisasContext *s)
{
if (!s->condjmp) {
s->condlabel = gen_new_label();
s->condjmp = 1;
}
}
/*
* Constant expanders for the decoders.
*/
static int negate(DisasContext *s, int x)
{
return -x;
}
static int plus_2(DisasContext *s, int x)
{
return x + 2;
}
static int times_2(DisasContext *s, int x)
{
return x * 2;
}
static int times_4(DisasContext *s, int x)
{
return x * 4;
}
/* Flags for the disas_set_da_iss info argument:
* lower bits hold the Rt register number, higher bits are flags.
*/
@ -1221,6 +1254,9 @@ static void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop)
long off = neon_element_offset(reg, ele, memop);
switch (memop) {
case MO_32:
tcg_gen_st32_i64(src, cpu_env, off);
break;
case MO_64:
tcg_gen_st_i64(src, cpu_env, off);
break;
@ -5156,15 +5192,6 @@ static void gen_srs(DisasContext *s,
s->base.is_jmp = DISAS_UPDATE_EXIT;
}
/* Generate a label used for skipping this instruction */
static void arm_gen_condlabel(DisasContext *s)
{
if (!s->condjmp) {
s->condlabel = gen_new_label();
s->condjmp = 1;
}
}
/* Skip this instruction if the ARM condition is false */
static void arm_skip_unless(DisasContext *s, uint32_t cond)
{
@ -5174,29 +5201,9 @@ static void arm_skip_unless(DisasContext *s, uint32_t cond)
/*
* Constant expanders for the decoders.
* Constant expanders used by T16/T32 decode
*/
static int negate(DisasContext *s, int x)
{
return -x;
}
static int plus_2(DisasContext *s, int x)
{
return x + 2;
}
static int times_2(DisasContext *s, int x)
{
return x * 2;
}
static int times_4(DisasContext *s, int x)
{
return x * 4;
}
/* Return only the rotation part of T32ExpandImm. */
static int t32_expandimm_rot(DisasContext *s, int x)
{
@ -7965,6 +7972,44 @@ static bool trans_LDM_t16(DisasContext *s, arg_ldst_block *a)
return do_ldm(s, a, 1);
}
static bool trans_CLRM(DisasContext *s, arg_CLRM *a)
{
int i;
TCGv_i32 zero;
if (!dc_isar_feature(aa32_m_sec_state, s)) {
return false;
}
if (extract32(a->list, 13, 1)) {
return false;
}
if (!a->list) {
/* UNPREDICTABLE; we choose to UNDEF */
return false;
}
zero = tcg_const_i32(0);
for (i = 0; i < 15; i++) {
if (extract32(a->list, i, 1)) {
/* Clear R[i] */
tcg_gen_mov_i32(cpu_R[i], zero);
}
}
if (extract32(a->list, 15, 1)) {
/*
* Clear APSR (by calling the MSR helper with the same argument
* as for "MSR APSR_nzcvqg, Rn": mask = 0b1100, SYSM=0)
*/
TCGv_i32 maskreg = tcg_const_i32(0xc << 8);
gen_helper_v7m_msr(cpu_env, maskreg, zero);
tcg_temp_free_i32(maskreg);
}
tcg_temp_free_i32(zero);
return true;
}
/*
* Branch, branch with link
*/

View File

@ -84,6 +84,20 @@ VLDR_VSTR_hp ---- 1101 u:1 .0 l:1 rn:4 .... 1001 imm:8 vd=%vd_sp
VLDR_VSTR_sp ---- 1101 u:1 .0 l:1 rn:4 .... 1010 imm:8 vd=%vd_sp
VLDR_VSTR_dp ---- 1101 u:1 .0 l:1 rn:4 .... 1011 imm:8 vd=%vd_dp
# M-profile VLDR/VSTR to sysreg
%vldr_sysreg 22:1 13:3
%imm7_0x4 0:7 !function=times_4
&vldr_sysreg rn reg imm a w p
@vldr_sysreg .... ... . a:1 . . . rn:4 ... . ... .. ....... \
reg=%vldr_sysreg imm=%imm7_0x4 &vldr_sysreg
# P=0 W=0 is SEE "Related encodings", so split into two patterns
VLDR_sysreg ---- 110 1 . . w:1 1 .... ... 0 111 11 ....... @vldr_sysreg p=1
VLDR_sysreg ---- 110 0 . . 1 1 .... ... 0 111 11 ....... @vldr_sysreg p=0 w=1
VSTR_sysreg ---- 110 1 . . w:1 0 .... ... 0 111 11 ....... @vldr_sysreg p=1
VSTR_sysreg ---- 110 0 . . 1 0 .... ... 0 111 11 ....... @vldr_sysreg p=0 w=1
# We split the load/store multiple up into two patterns to avoid
# overlap with other insns in the "Advanced SIMD load/store and 64-bit move"
# grouping:

View File

@ -156,6 +156,7 @@ qtests_aarch64 = \
['arm-cpu-features',
'numa-test',
'boot-serial-test',
'xlnx-can-test',
'migration-test']
qtests_s390x = \

View File

@ -20,6 +20,7 @@
#include "libqtest-single.h"
#include "qemu/bitops.h"
#include "qemu-common.h"
#define RNG_BASE_ADDR 0xf000b000
@ -36,6 +37,13 @@
/* Number of bits to collect for randomness tests. */
#define TEST_INPUT_BITS (128)
static void dump_buf_if_failed(const uint8_t *buf, size_t size)
{
if (g_test_failed()) {
qemu_hexdump(stderr, "", buf, size);
}
}
static void rng_writeb(unsigned int offset, uint8_t value)
{
writeb(RNG_BASE_ADDR + offset, value);
@ -188,6 +196,7 @@ static void test_continuous_monobit(void)
}
g_assert_cmpfloat(calc_monobit_p(buf, sizeof(buf)), >, 0.01);
dump_buf_if_failed(buf, sizeof(buf));
}
/*
@ -209,6 +218,7 @@ static void test_continuous_runs(void)
}
g_assert_cmpfloat(calc_runs_p(buf.l, sizeof(buf) * BITS_PER_BYTE), >, 0.01);
dump_buf_if_failed(buf.c, sizeof(buf));
}
/*
@ -230,6 +240,7 @@ static void test_first_byte_monobit(void)
}
g_assert_cmpfloat(calc_monobit_p(buf, sizeof(buf)), >, 0.01);
dump_buf_if_failed(buf, sizeof(buf));
}
/*
@ -254,6 +265,7 @@ static void test_first_byte_runs(void)
}
g_assert_cmpfloat(calc_runs_p(buf.l, sizeof(buf) * BITS_PER_BYTE), >, 0.01);
dump_buf_if_failed(buf.c, sizeof(buf));
}
int main(int argc, char **argv)

360
tests/qtest/xlnx-can-test.c Normal file
View File

@ -0,0 +1,360 @@
/*
* QTests for the Xilinx ZynqMP CAN controller.
*
* Copyright (c) 2020 Xilinx Inc.
*
* Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "libqos/libqtest.h"
/* Base address. */
#define CAN0_BASE_ADDR 0xFF060000
#define CAN1_BASE_ADDR 0xFF070000
/* Register addresses. */
#define R_SRR_OFFSET 0x00
#define R_MSR_OFFSET 0x04
#define R_SR_OFFSET 0x18
#define R_ISR_OFFSET 0x1C
#define R_ICR_OFFSET 0x24
#define R_TXID_OFFSET 0x30
#define R_TXDLC_OFFSET 0x34
#define R_TXDATA1_OFFSET 0x38
#define R_TXDATA2_OFFSET 0x3C
#define R_RXID_OFFSET 0x50
#define R_RXDLC_OFFSET 0x54
#define R_RXDATA1_OFFSET 0x58
#define R_RXDATA2_OFFSET 0x5C
#define R_AFR 0x60
#define R_AFMR1 0x64
#define R_AFIR1 0x68
#define R_AFMR2 0x6C
#define R_AFIR2 0x70
#define R_AFMR3 0x74
#define R_AFIR3 0x78
#define R_AFMR4 0x7C
#define R_AFIR4 0x80
/* CAN modes. */
#define CONFIG_MODE 0x00
#define NORMAL_MODE 0x00
#define LOOPBACK_MODE 0x02
#define SNOOP_MODE 0x04
#define SLEEP_MODE 0x01
#define ENABLE_CAN (1 << 1)
#define STATUS_NORMAL_MODE (1 << 3)
#define STATUS_LOOPBACK_MODE (1 << 1)
#define STATUS_SNOOP_MODE (1 << 12)
#define STATUS_SLEEP_MODE (1 << 2)
#define ISR_TXOK (1 << 1)
#define ISR_RXOK (1 << 4)
static void match_rx_tx_data(const uint32_t *buf_tx, const uint32_t *buf_rx,
uint8_t can_timestamp)
{
uint16_t size = 0;
uint8_t len = 4;
while (size < len) {
if (R_RXID_OFFSET + 4 * size == R_RXDLC_OFFSET) {
g_assert_cmpint(buf_rx[size], ==, buf_tx[size] + can_timestamp);
} else {
g_assert_cmpint(buf_rx[size], ==, buf_tx[size]);
}
size++;
}
}
static void read_data(QTestState *qts, uint64_t can_base_addr, uint32_t *buf_rx)
{
uint32_t int_status;
/* Read the interrupt on CAN rx. */
int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_RXOK;
g_assert_cmpint(int_status, ==, ISR_RXOK);
/* Read the RX register data for CAN. */
buf_rx[0] = qtest_readl(qts, can_base_addr + R_RXID_OFFSET);
buf_rx[1] = qtest_readl(qts, can_base_addr + R_RXDLC_OFFSET);
buf_rx[2] = qtest_readl(qts, can_base_addr + R_RXDATA1_OFFSET);
buf_rx[3] = qtest_readl(qts, can_base_addr + R_RXDATA2_OFFSET);
/* Clear the RX interrupt. */
qtest_writel(qts, CAN1_BASE_ADDR + R_ICR_OFFSET, ISR_RXOK);
}
static void send_data(QTestState *qts, uint64_t can_base_addr,
const uint32_t *buf_tx)
{
uint32_t int_status;
/* Write the TX register data for CAN. */
qtest_writel(qts, can_base_addr + R_TXID_OFFSET, buf_tx[0]);
qtest_writel(qts, can_base_addr + R_TXDLC_OFFSET, buf_tx[1]);
qtest_writel(qts, can_base_addr + R_TXDATA1_OFFSET, buf_tx[2]);
qtest_writel(qts, can_base_addr + R_TXDATA2_OFFSET, buf_tx[3]);
/* Read the interrupt on CAN for tx. */
int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_TXOK;
g_assert_cmpint(int_status, ==, ISR_TXOK);
/* Clear the interrupt for tx. */
qtest_writel(qts, CAN0_BASE_ADDR + R_ICR_OFFSET, ISR_TXOK);
}
/*
* This test will be transferring data from CAN0 and CAN1 through canbus. CAN0
* initiate the data transfer to can-bus, CAN1 receives the data. Test compares
* the data sent from CAN0 with received on CAN1.
*/
static void test_can_bus(void)
{
const uint32_t buf_tx[4] = { 0xFF, 0x80000000, 0x12345678, 0x87654321 };
uint32_t buf_rx[4] = { 0x00, 0x00, 0x00, 0x00 };
uint32_t status = 0;
uint8_t can_timestamp = 1;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
" -object can-bus,id=canbus0"
" -machine xlnx-zcu102.canbus0=canbus0"
" -machine xlnx-zcu102.canbus1=canbus0"
);
/* Configure the CAN0 and CAN1. */
qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
/* Check here if CAN0 and CAN1 are in normal mode. */
status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
send_data(qts, CAN0_BASE_ADDR, buf_tx);
read_data(qts, CAN1_BASE_ADDR, buf_rx);
match_rx_tx_data(buf_tx, buf_rx, can_timestamp);
qtest_quit(qts);
}
/*
* This test is performing loopback mode on CAN0 and CAN1. Data sent from TX of
* each CAN0 and CAN1 are compared with RX register data for respective CAN.
*/
static void test_can_loopback(void)
{
uint32_t buf_tx[4] = { 0xFF, 0x80000000, 0x12345678, 0x87654321 };
uint32_t buf_rx[4] = { 0x00, 0x00, 0x00, 0x00 };
uint32_t status = 0;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
" -object can-bus,id=canbus0"
" -machine xlnx-zcu102.canbus0=canbus0"
" -machine xlnx-zcu102.canbus1=canbus0"
);
/* Configure the CAN0 in loopback mode. */
qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, CONFIG_MODE);
qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, LOOPBACK_MODE);
qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
/* Check here if CAN0 is set in loopback mode. */
status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE);
send_data(qts, CAN0_BASE_ADDR, buf_tx);
read_data(qts, CAN0_BASE_ADDR, buf_rx);
match_rx_tx_data(buf_tx, buf_rx, 0);
/* Configure the CAN1 in loopback mode. */
qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, CONFIG_MODE);
qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, LOOPBACK_MODE);
qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
/* Check here if CAN1 is set in loopback mode. */
status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE);
send_data(qts, CAN1_BASE_ADDR, buf_tx);
read_data(qts, CAN1_BASE_ADDR, buf_rx);
match_rx_tx_data(buf_tx, buf_rx, 0);
qtest_quit(qts);
}
/*
* Enable filters for CAN1. This will filter incoming messages with ID. In this
* test message will pass through filter 2.
*/
static void test_can_filter(void)
{
uint32_t buf_tx[4] = { 0x14, 0x80000000, 0x12345678, 0x87654321 };
uint32_t buf_rx[4] = { 0x00, 0x00, 0x00, 0x00 };
uint32_t status = 0;
uint8_t can_timestamp = 1;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
" -object can-bus,id=canbus0"
" -machine xlnx-zcu102.canbus0=canbus0"
" -machine xlnx-zcu102.canbus1=canbus0"
);
/* Configure the CAN0 and CAN1. */
qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
/* Check here if CAN0 and CAN1 are in normal mode. */
status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
/* Set filter for CAN1 for incoming messages. */
qtest_writel(qts, CAN1_BASE_ADDR + R_AFR, 0x0);
qtest_writel(qts, CAN1_BASE_ADDR + R_AFMR1, 0xF7);
qtest_writel(qts, CAN1_BASE_ADDR + R_AFIR1, 0x121F);
qtest_writel(qts, CAN1_BASE_ADDR + R_AFMR2, 0x5431);
qtest_writel(qts, CAN1_BASE_ADDR + R_AFIR2, 0x14);
qtest_writel(qts, CAN1_BASE_ADDR + R_AFMR3, 0x1234);
qtest_writel(qts, CAN1_BASE_ADDR + R_AFIR3, 0x5431);
qtest_writel(qts, CAN1_BASE_ADDR + R_AFMR4, 0xFFF);
qtest_writel(qts, CAN1_BASE_ADDR + R_AFIR4, 0x1234);
qtest_writel(qts, CAN1_BASE_ADDR + R_AFR, 0xF);
send_data(qts, CAN0_BASE_ADDR, buf_tx);
read_data(qts, CAN1_BASE_ADDR, buf_rx);
match_rx_tx_data(buf_tx, buf_rx, can_timestamp);
qtest_quit(qts);
}
/* Testing sleep mode on CAN0 while CAN1 is in normal mode. */
static void test_can_sleepmode(void)
{
uint32_t buf_tx[4] = { 0x14, 0x80000000, 0x12345678, 0x87654321 };
uint32_t buf_rx[4] = { 0x00, 0x00, 0x00, 0x00 };
uint32_t status = 0;
uint8_t can_timestamp = 1;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
" -object can-bus,id=canbus0"
" -machine xlnx-zcu102.canbus0=canbus0"
" -machine xlnx-zcu102.canbus1=canbus0"
);
/* Configure the CAN0. */
qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, CONFIG_MODE);
qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, SLEEP_MODE);
qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
/* Check here if CAN0 is in SLEEP mode and CAN1 in normal mode. */
status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_SLEEP_MODE);
status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
send_data(qts, CAN1_BASE_ADDR, buf_tx);
/*
* Once CAN1 sends data on can-bus. CAN0 should exit sleep mode.
* Check the CAN0 status now. It should exit the sleep mode and receive the
* incoming data.
*/
status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
read_data(qts, CAN0_BASE_ADDR, buf_rx);
match_rx_tx_data(buf_tx, buf_rx, can_timestamp);
qtest_quit(qts);
}
/* Testing Snoop mode on CAN0 while CAN1 is in normal mode. */
static void test_can_snoopmode(void)
{
uint32_t buf_tx[4] = { 0x14, 0x80000000, 0x12345678, 0x87654321 };
uint32_t buf_rx[4] = { 0x00, 0x00, 0x00, 0x00 };
uint32_t status = 0;
uint8_t can_timestamp = 1;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
" -object can-bus,id=canbus0"
" -machine xlnx-zcu102.canbus0=canbus0"
" -machine xlnx-zcu102.canbus1=canbus0"
);
/* Configure the CAN0. */
qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, CONFIG_MODE);
qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, SNOOP_MODE);
qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
/* Check here if CAN0 is in SNOOP mode and CAN1 in normal mode. */
status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_SNOOP_MODE);
status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
send_data(qts, CAN1_BASE_ADDR, buf_tx);
read_data(qts, CAN0_BASE_ADDR, buf_rx);
match_rx_tx_data(buf_tx, buf_rx, can_timestamp);
qtest_quit(qts);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("/net/can/can_bus", test_can_bus);
qtest_add_func("/net/can/can_loopback", test_can_loopback);
qtest_add_func("/net/can/can_filter", test_can_filter);
qtest_add_func("/net/can/can_test_snoopmode", test_can_snoopmode);
qtest_add_func("/net/can/can_test_sleepmode", test_can_sleepmode);
return g_test_run();
}