summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/arm/armv7m.c8
-rw-r--r--hw/block/pflash_cfi01.c22
-rw-r--r--hw/char/exynos4210_uart.c6
-rw-r--r--hw/dma/omap_dma.c10
-rw-r--r--hw/intc/arm_gicv3_cpuif.c2
-rw-r--r--hw/intc/armv7m_nvic.c58
-rw-r--r--hw/net/stellaris_enet.c5
-rw-r--r--hw/ssi/aspeed_smc.c21
-rw-r--r--include/hw/compat.h4
-rw-r--r--include/hw/register.h47
-rw-r--r--include/hw/registerfields.h60
-rw-r--r--linux-user/main.c1
-rw-r--r--target/arm/cpu.c50
-rw-r--r--target/arm/cpu.h62
-rw-r--r--target/arm/helper.c160
-rw-r--r--target/arm/internals.h7
-rw-r--r--target/arm/machine.c12
-rw-r--r--target/arm/translate.c20
18 files changed, 386 insertions, 169 deletions
diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c
index 49d30782c8..0c9ca7bfa0 100644
--- a/hw/arm/armv7m.c
+++ b/hw/arm/armv7m.c
@@ -180,7 +180,6 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
uint64_t entry;
uint64_t lowaddr;
int big_endian;
- MemoryRegion *hack = g_new(MemoryRegion, 1);
if (cpu_model == NULL) {
cpu_model = "cortex-m3";
@@ -225,13 +224,6 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
}
}
- /* Hack to map an additional page of ram at the top of the address
- space. This stops qemu complaining about executing code outside RAM
- when returning from an exception. */
- memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_fatal);
- vmstate_register_ram_global(hack);
- memory_region_add_subregion(system_memory, 0xfffff000, hack);
-
qemu_register_reset(armv7m_reset, cpu);
return nvic;
}
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index 5f0ee9db00..71b98a3eef 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -99,6 +99,7 @@ struct pflash_t {
char *name;
void *storage;
VMChangeStateEntry *vmstate;
+ bool old_multiple_chip_handling;
};
static int pflash_post_load(void *opaque, int version_id);
@@ -703,7 +704,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
pflash_t *pfl = CFI_PFLASH01(dev);
uint64_t total_len;
int ret;
- uint64_t blocks_per_device, device_len;
+ uint64_t blocks_per_device, sector_len_per_device, device_len;
int num_devices;
Error *local_err = NULL;
@@ -726,8 +727,14 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
* in the cfi_table[].
*/
num_devices = pfl->device_width ? (pfl->bank_width / pfl->device_width) : 1;
- blocks_per_device = pfl->nb_blocs / num_devices;
- device_len = pfl->sector_len * blocks_per_device;
+ if (pfl->old_multiple_chip_handling) {
+ blocks_per_device = pfl->nb_blocs / num_devices;
+ sector_len_per_device = pfl->sector_len;
+ } else {
+ blocks_per_device = pfl->nb_blocs;
+ sector_len_per_device = pfl->sector_len / num_devices;
+ }
+ device_len = sector_len_per_device * blocks_per_device;
/* XXX: to be fixed */
#if 0
@@ -832,6 +839,9 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
pfl->cfi_table[0x2A] = 0x0B;
}
pfl->writeblock_size = 1 << pfl->cfi_table[0x2A];
+ if (!pfl->old_multiple_chip_handling && num_devices > 1) {
+ pfl->writeblock_size *= num_devices;
+ }
pfl->cfi_table[0x2B] = 0x00;
/* Number of erase block regions (uniform) */
@@ -839,8 +849,8 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
/* Erase block region 1 */
pfl->cfi_table[0x2D] = blocks_per_device - 1;
pfl->cfi_table[0x2E] = (blocks_per_device - 1) >> 8;
- pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
- pfl->cfi_table[0x30] = pfl->sector_len >> 16;
+ pfl->cfi_table[0x2F] = sector_len_per_device >> 8;
+ pfl->cfi_table[0x30] = sector_len_per_device >> 16;
/* Extended */
pfl->cfi_table[0x31] = 'P';
@@ -898,6 +908,8 @@ static Property pflash_cfi01_properties[] = {
DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0),
DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0),
DEFINE_PROP_STRING("name", struct pflash_t, name),
+ DEFINE_PROP_BOOL("old-multiple-chip-handling", struct pflash_t,
+ old_multiple_chip_handling, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c
index 820d1abeb9..0cd3dd3958 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -306,7 +306,7 @@ static void exynos4210_uart_update_irq(Exynos4210UartState *s)
static void exynos4210_uart_update_parameters(Exynos4210UartState *s)
{
- int speed, parity, data_bits, stop_bits, frame_size;
+ int speed, parity, data_bits, stop_bits;
QEMUSerialSetParams ssp;
uint64_t uclk_rate;
@@ -314,9 +314,7 @@ static void exynos4210_uart_update_parameters(Exynos4210UartState *s)
return;
}
- frame_size = 1; /* start bit */
if (s->reg[I_(ULCON)] & 0x20) {
- frame_size++; /* parity bit */
if (s->reg[I_(ULCON)] & 0x28) {
parity = 'E';
} else {
@@ -334,8 +332,6 @@ static void exynos4210_uart_update_parameters(Exynos4210UartState *s)
data_bits = (s->reg[I_(ULCON)] & 0x3) + 5;
- frame_size += data_bits + stop_bits;
-
uclk_rate = 24000000;
speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) +
diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c
index f6f86f9639..45dfe7aadd 100644
--- a/hw/dma/omap_dma.c
+++ b/hw/dma/omap_dma.c
@@ -878,15 +878,17 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s,
ch->burst[0] = (value & 0x0180) >> 7;
ch->pack[0] = (value & 0x0040) >> 6;
ch->port[0] = (enum omap_dma_port) ((value & 0x003c) >> 2);
- ch->data_type = 1 << (value & 3);
if (ch->port[0] >= __omap_dma_port_last)
printf("%s: invalid DMA port %i\n", __FUNCTION__,
ch->port[0]);
if (ch->port[1] >= __omap_dma_port_last)
printf("%s: invalid DMA port %i\n", __FUNCTION__,
ch->port[1]);
- if ((value & 3) == 3)
+ ch->data_type = 1 << (value & 3);
+ if ((value & 3) == 3) {
printf("%s: bad data_type for DMA channel\n", __FUNCTION__);
+ ch->data_type >>= 1;
+ }
break;
case 0x02: /* SYS_DMA_CCR_CH0 */
@@ -1988,8 +1990,10 @@ static void omap_dma4_write(void *opaque, hwaddr addr,
fprintf(stderr, "%s: bad MReqAddressTranslate sideband signal\n",
__FUNCTION__);
ch->data_type = 1 << (value & 3);
- if ((value & 3) == 3)
+ if ((value & 3) == 3) {
printf("%s: bad data_type for DMA channel\n", __FUNCTION__);
+ ch->data_type >>= 1;
+ }
break;
case 0x14: /* DMA4_CEN */
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index a9ee7fddf9..c25ee03556 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -2430,7 +2430,7 @@ static uint64_t ich_elrsr_read(CPUARMState *env, const ARMCPRegInfo *ri)
uint64_t lr = cs->ich_lr_el2[i];
if ((lr & ICH_LR_EL2_STATE_MASK) == 0 &&
- ((lr & ICH_LR_EL2_HW) == 1 || (lr & ICH_LR_EL2_EOI) == 0)) {
+ ((lr & ICH_LR_EL2_HW) != 0 || (lr & ICH_LR_EL2_EOI) == 0)) {
value |= (1 << i);
}
}
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 06d8db6bd6..fe5c303de9 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -23,6 +23,7 @@
typedef struct {
GICState gic;
+ ARMCPU *cpu;
struct {
uint32_t control;
uint32_t reload;
@@ -155,7 +156,7 @@ void armv7m_nvic_complete_irq(void *opaque, int irq)
static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
{
- ARMCPU *cpu;
+ ARMCPU *cpu = s->cpu;
uint32_t val;
int irq;
@@ -187,11 +188,9 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
case 0x1c: /* SysTick Calibration Value. */
return 10000;
case 0xd00: /* CPUID Base. */
- cpu = ARM_CPU(qemu_get_cpu(0));
return cpu->midr;
case 0xd04: /* Interrupt Control State. */
/* VECTACTIVE */
- cpu = ARM_CPU(qemu_get_cpu(0));
val = cpu->env.v7m.exception;
if (val == 1023) {
val = 0;
@@ -222,7 +221,6 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
val |= (1 << 31);
return val;
case 0xd08: /* Vector Table Offset. */
- cpu = ARM_CPU(qemu_get_cpu(0));
return cpu->env.v7m.vecbase;
case 0xd0c: /* Application Interrupt/Reset Control. */
return 0xfa050000;
@@ -230,8 +228,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
/* TODO: Implement SLEEPONEXIT. */
return 0;
case 0xd14: /* Configuration Control. */
- /* TODO: Implement Configuration Control bits. */
- return 0;
+ return cpu->env.v7m.ccr;
case 0xd24: /* System Handler Status. */
val = 0;
if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
@@ -250,16 +247,19 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
return val;
case 0xd28: /* Configurable Fault Status. */
- /* TODO: Implement Fault Status. */
- qemu_log_mask(LOG_UNIMP, "Configurable Fault Status unimplemented\n");
- return 0;
+ return cpu->env.v7m.cfsr;
case 0xd2c: /* Hard Fault Status. */
+ return cpu->env.v7m.hfsr;
case 0xd30: /* Debug Fault Status. */
- case 0xd34: /* Mem Manage Address. */
+ return cpu->env.v7m.dfsr;
+ case 0xd34: /* MMFAR MemManage Fault Address */
+ return cpu->env.v7m.mmfar;
case 0xd38: /* Bus Fault Address. */
+ return cpu->env.v7m.bfar;
case 0xd3c: /* Aux Fault Status. */
/* TODO: Implement fault status registers. */
- qemu_log_mask(LOG_UNIMP, "Fault status registers unimplemented\n");
+ qemu_log_mask(LOG_UNIMP,
+ "Aux Fault status registers unimplemented\n");
return 0;
case 0xd40: /* PFR0. */
return 0x00000030;
@@ -296,7 +296,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
{
- ARMCPU *cpu;
+ ARMCPU *cpu = s->cpu;
uint32_t oldval;
switch (offset) {
case 0x10: /* SysTick Control and Status. */
@@ -349,7 +349,6 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
}
break;
case 0xd08: /* Vector Table Offset. */
- cpu = ARM_CPU(qemu_get_cpu(0));
cpu->env.v7m.vecbase = value & 0xffffff80;
break;
case 0xd0c: /* Application Interrupt/Reset Control. */
@@ -369,9 +368,19 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
}
break;
case 0xd10: /* System Control. */
- case 0xd14: /* Configuration Control. */
/* TODO: Implement control registers. */
- qemu_log_mask(LOG_UNIMP, "NVIC: SCR and CCR unimplemented\n");
+ qemu_log_mask(LOG_UNIMP, "NVIC: SCR unimplemented\n");
+ break;
+ case 0xd14: /* Configuration Control. */
+ /* 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);
+
+ cpu->env.v7m.ccr = value;
break;
case 0xd24: /* System Handler Control. */
/* TODO: Real hardware allows you to set/clear the active bits
@@ -381,16 +390,29 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
break;
case 0xd28: /* Configurable Fault Status. */
+ cpu->env.v7m.cfsr &= ~value; /* W1C */
+ break;
case 0xd2c: /* Hard Fault Status. */
+ cpu->env.v7m.hfsr &= ~value; /* W1C */
+ break;
case 0xd30: /* Debug Fault Status. */
+ cpu->env.v7m.dfsr &= ~value; /* W1C */
+ break;
case 0xd34: /* Mem Manage Address. */
+ cpu->env.v7m.mmfar = value;
+ return;
case 0xd38: /* Bus Fault Address. */
+ cpu->env.v7m.bfar = value;
+ return;
case 0xd3c: /* Aux Fault Status. */
qemu_log_mask(LOG_UNIMP,
- "NVIC: fault status registers unimplemented\n");
+ "NVIC: Aux fault status registers unimplemented\n");
break;
case 0xf00: /* Software Triggered Interrupt Register */
- if ((value & 0x1ff) < s->num_irq) {
+ /* user mode can only write to STIR if CCR.USERSETMPEND permits it */
+ if ((value & 0x1ff) < s->num_irq &&
+ (arm_current_el(&cpu->env) ||
+ (cpu->env.v7m.ccr & R_V7M_CCR_USERSETMPEND_MASK))) {
gic_set_pending_private(&s->gic, 0, value & 0x1ff);
}
break;
@@ -495,6 +517,8 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
NVICClass *nc = NVIC_GET_CLASS(s);
Error *local_err = NULL;
+ s->cpu = ARM_CPU(qemu_get_cpu(0));
+ assert(s->cpu);
/* The NVIC always has only one CPU */
s->gic.num_cpu = 1;
/* Tell the common code we're an NVIC */
diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c
index 957730e023..04bd10ada3 100644
--- a/hw/net/stellaris_enet.c
+++ b/hw/net/stellaris_enet.c
@@ -416,7 +416,10 @@ static void stellaris_enet_write(void *opaque, hwaddr offset,
s->thr = value;
break;
case 0x20: /* MCTL */
- s->mctl = value;
+ /* TODO: MII registers aren't modelled.
+ * Clear START, indicating that the operation completes immediately.
+ */
+ s->mctl = value & ~1;
break;
case 0x24: /* MDV */
s->mdv = value;
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index ae1ad2dba6..087b29e8da 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -69,7 +69,9 @@
#define R_CTRL0 (0x10 / 4)
#define CTRL_CMD_SHIFT 16
#define CTRL_CMD_MASK 0xff
+#define CTRL_DUMMY_HIGH_SHIFT 14
#define CTRL_AST2400_SPI_4BYTE (1 << 13)
+#define CTRL_DUMMY_LOW_SHIFT 6 /* 2 bits [7:6] */
#define CTRL_CE_STOP_ACTIVE (1 << 2)
#define CTRL_CMD_MODE_MASK 0x3
#define CTRL_READMODE 0x0
@@ -485,6 +487,16 @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl,
return addr;
}
+static int aspeed_smc_flash_dummies(const AspeedSMCFlash *fl)
+{
+ const AspeedSMCState *s = fl->controller;
+ uint32_t r_ctrl0 = s->regs[s->r_ctrl0 + fl->id];
+ uint32_t dummy_high = (r_ctrl0 >> CTRL_DUMMY_HIGH_SHIFT) & 0x1;
+ uint32_t dummy_low = (r_ctrl0 >> CTRL_DUMMY_LOW_SHIFT) & 0x3;
+
+ return ((dummy_high << 2) | dummy_low) * 8;
+}
+
static void aspeed_smc_flash_send_addr(AspeedSMCFlash *fl, uint32_t addr)
{
const AspeedSMCState *s = fl->controller;
@@ -521,6 +533,15 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
aspeed_smc_flash_select(fl);
aspeed_smc_flash_send_addr(fl, addr);
+ /*
+ * Use fake transfers to model dummy bytes. The value should
+ * be configured to some non-zero value in fast read mode and
+ * zero in read mode.
+ */
+ for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) {
+ ssi_transfer(fl->controller->spi, 0xFF);
+ }
+
for (i = 0; i < size; i++) {
ret |= ssi_transfer(s->spi, 0x0) << (8 * i);
}
diff --git a/include/hw/compat.h b/include/hw/compat.h
index 34e9b4a660..ee0dd1b5df 100644
--- a/include/hw/compat.h
+++ b/include/hw/compat.h
@@ -10,6 +10,10 @@
.driver = "fw_cfg_io",\
.property = "x-file-slots",\
.value = stringify(0x10),\
+ },{\
+ .driver = "pflash_cfi01",\
+ .property = "old-multiple-chip-handling",\
+ .value = "on",\
},
#define HW_COMPAT_2_7 \
diff --git a/include/hw/register.h b/include/hw/register.h
index 5b6dc32091..de2414e6b4 100644
--- a/include/hw/register.h
+++ b/include/hw/register.h
@@ -13,6 +13,7 @@
#include "hw/qdev-core.h"
#include "exec/memory.h"
+#include "hw/registerfields.h"
typedef struct RegisterInfo RegisterInfo;
typedef struct RegisterAccessInfo RegisterAccessInfo;
@@ -206,50 +207,4 @@ RegisterInfoArray *register_init_block32(DeviceState *owner,
void register_finalize_block(RegisterInfoArray *r_array);
-/* Define constants for a 32 bit register */
-
-/* This macro will define A_FOO, for the byte address of a register
- * as well as R_FOO for the uint32_t[] register number (A_FOO / 4).
- */
-#define REG32(reg, addr) \
- enum { A_ ## reg = (addr) }; \
- enum { R_ ## reg = (addr) / 4 };
-
-/* Define SHIFT, LENGTH and MASK constants for a field within a register */
-
-/* This macro will define FOO_BAR_MASK, FOO_BAR_SHIFT and FOO_BAR_LENGTH
- * constants for field BAR in register FOO.
- */
-#define FIELD(reg, field, shift, length) \
- enum { R_ ## reg ## _ ## field ## _SHIFT = (shift)}; \
- enum { R_ ## reg ## _ ## field ## _LENGTH = (length)}; \
- enum { R_ ## reg ## _ ## field ## _MASK = \
- MAKE_64BIT_MASK(shift, length)};
-
-/* Extract a field from a register */
-#define FIELD_EX32(storage, reg, field) \
- extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \
- R_ ## reg ## _ ## field ## _LENGTH)
-
-/* Extract a field from an array of registers */
-#define ARRAY_FIELD_EX32(regs, reg, field) \
- FIELD_EX32((regs)[R_ ## reg], reg, field)
-
-/* Deposit a register field.
- * Assigning values larger then the target field will result in
- * compilation warnings.
- */
-#define FIELD_DP32(storage, reg, field, val) ({ \
- struct { \
- unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \
- } v = { .v = val }; \
- uint32_t d; \
- d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \
- R_ ## reg ## _ ## field ## _LENGTH, v.v); \
- d; })
-
-/* Deposit a field to array of registers. */
-#define ARRAY_FIELD_DP32(regs, reg, field, val) \
- (regs)[R_ ## reg] = FIELD_DP32((regs)[R_ ## reg], reg, field, val);
-
#endif
diff --git a/include/hw/registerfields.h b/include/hw/registerfields.h
new file mode 100644
index 0000000000..af101d5ae6
--- /dev/null
+++ b/include/hw/registerfields.h
@@ -0,0 +1,60 @@
+/*
+ * Register Definition API: field macros
+ *
+ * Copyright (c) 2016 Xilinx Inc.
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef REGISTERFIELDS_H
+#define REGISTERFIELDS_H
+
+/* Define constants for a 32 bit register */
+
+/* This macro will define A_FOO, for the byte address of a register
+ * as well as R_FOO for the uint32_t[] register number (A_FOO / 4).
+ */
+#define REG32(reg, addr) \
+ enum { A_ ## reg = (addr) }; \
+ enum { R_ ## reg = (addr) / 4 };
+
+/* Define SHIFT, LENGTH and MASK constants for a field within a register */
+
+/* This macro will define FOO_BAR_MASK, FOO_BAR_SHIFT and FOO_BAR_LENGTH
+ * constants for field BAR in register FOO.
+ */
+#define FIELD(reg, field, shift, length) \
+ enum { R_ ## reg ## _ ## field ## _SHIFT = (shift)}; \
+ enum { R_ ## reg ## _ ## field ## _LENGTH = (length)}; \
+ enum { R_ ## reg ## _ ## field ## _MASK = \
+ MAKE_64BIT_MASK(shift, length)};
+
+/* Extract a field from a register */
+#define FIELD_EX32(storage, reg, field) \
+ extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \
+ R_ ## reg ## _ ## field ## _LENGTH)
+
+/* Extract a field from an array of registers */
+#define ARRAY_FIELD_EX32(regs, reg, field) \
+ FIELD_EX32((regs)[R_ ## reg], reg, field)
+
+/* Deposit a register field.
+ * Assigning values larger then the target field will result in
+ * compilation warnings.
+ */
+#define FIELD_DP32(storage, reg, field, val) ({ \
+ struct { \
+ unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \
+ } v = { .v = val }; \
+ uint32_t d; \
+ d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \
+ R_ ## reg ## _ ## field ## _LENGTH, v.v); \
+ d; })
+
+/* Deposit a field to array of registers. */
+#define ARRAY_FIELD_DP32(regs, reg, field, val) \
+ (regs)[R_ ## reg] = FIELD_DP32((regs)[R_ ## reg], reg, field, val);
+
+#endif
diff --git a/linux-user/main.c b/linux-user/main.c
index f5c85574f9..30049581ef 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -573,6 +573,7 @@ void cpu_loop(CPUARMState *env)
switch(trapnr) {
case EXCP_UDEF:
+ case EXCP_NOCP:
{
TaskState *ts = cs->opaque;
uint32_t opcode;
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index a941f6611b..e9f10f7747 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -179,15 +179,27 @@ static void arm_cpu_reset(CPUState *s)
/* SVC mode with interrupts disabled. */
env->uncached_cpsr = ARM_CPU_MODE_SVC;
env->daif = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F;
- /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is
- * clear at reset. Initial SP and PC are loaded from ROM.
- */
- if (IS_M(env)) {
+
+ if (arm_feature(env, ARM_FEATURE_M)) {
uint32_t initial_msp; /* Loaded from 0x0 */
uint32_t initial_pc; /* Loaded from 0x4 */
uint8_t *rom;
- env->daif &= ~PSTATE_I;
+ /* For M profile we store FAULTMASK and PRIMASK in the
+ * PSTATE F and I bits; these are both clear at reset.
+ */
+ env->daif &= ~(PSTATE_I | PSTATE_F);
+
+ /* The reset value of this bit is IMPDEF, but ARM recommends
+ * that it resets to 1, so QEMU always does that rather than making
+ * it dependent on CPU model.
+ */
+ env->v7m.ccr = R_V7M_CCR_STKALIGN_MASK;
+
+ /* Unlike A/R profile, M profile defines the reset LR value */
+ env->regs[14] = 0xffffffff;
+
+ /* Load the initial SP and PC from the vector table at address 0 */
rom = rom_ptr(0);
if (rom) {
/* Address zero is covered by ROM which hasn't yet been
@@ -292,6 +304,33 @@ bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
}
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
+static void arm_v7m_unassigned_access(CPUState *cpu, hwaddr addr,
+ bool is_write, bool is_exec, int opaque,
+ unsigned size)
+{
+ ARMCPU *arm = ARM_CPU(cpu);
+ CPUARMState *env = &arm->env;
+
+ /* ARMv7-M interrupt return works by loading a magic value into the PC.
+ * On real hardware the load causes the return to occur. The qemu
+ * implementation performs the jump normally, then does the exception
+ * return by throwing a special exception when when the CPU tries to
+ * execute code at the magic address.
+ */
+ if (env->v7m.exception != 0 && addr >= 0xfffffff0 && is_exec) {
+ cpu->exception_index = EXCP_EXCEPTION_EXIT;
+ cpu_loop_exit(cpu);
+ }
+
+ /* In real hardware an attempt to access parts of the address space
+ * with nothing there will usually cause an external abort.
+ * However our QEMU board models are often missing device models where
+ * the guest can boot anyway with the default read-as-zero/writes-ignored
+ * behaviour that you get without a QEMU unassigned_access hook.
+ * So just return here to retain that default behaviour.
+ */
+}
+
static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
CPUClass *cc = CPU_GET_CLASS(cs);
@@ -1016,6 +1055,7 @@ static void arm_v7m_class_init(ObjectClass *oc, void *data)
cc->do_interrupt = arm_v7m_cpu_do_interrupt;
#endif
+ cc->do_unassigned_access = arm_v7m_unassigned_access;
cc->cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt;
}
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 151a5d754e..39bff86daf 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -21,6 +21,7 @@
#define ARM_CPU_H
#include "kvm-consts.h"
+#include "hw/registerfields.h"
#if defined(TARGET_AARCH64)
/* AArch64 definitions */
@@ -52,6 +53,7 @@
#define EXCP_VIRQ 14
#define EXCP_VFIQ 15
#define EXCP_SEMIHOST 16 /* semihosting call */
+#define EXCP_NOCP 17 /* v7M NOCP UsageFault */
#define ARMV7M_EXCP_RESET 1
#define ARMV7M_EXCP_NMI 2
@@ -405,7 +407,12 @@ typedef struct CPUARMState {
uint32_t vecbase;
uint32_t basepri;
uint32_t control;
- int current_sp;
+ uint32_t ccr; /* Configuration and Control */
+ uint32_t cfsr; /* Configurable Fault Status */
+ uint32_t hfsr; /* HardFault Status */
+ uint32_t dfsr; /* Debug Fault Status Register */
+ uint32_t mmfar; /* MemManage Fault Address */
+ uint32_t bfar; /* BusFault Address */
int exception;
} v7m;
@@ -1087,6 +1094,53 @@ enum arm_cpu_mode {
#define ARM_IWMMXT_wCGR2 10
#define ARM_IWMMXT_wCGR3 11
+/* V7M CCR bits */
+FIELD(V7M_CCR, NONBASETHRDENA, 0, 1)
+FIELD(V7M_CCR, USERSETMPEND, 1, 1)
+FIELD(V7M_CCR, UNALIGN_TRP, 3, 1)
+FIELD(V7M_CCR, DIV_0_TRP, 4, 1)
+FIELD(V7M_CCR, BFHFNMIGN, 8, 1)
+FIELD(V7M_CCR, STKALIGN, 9, 1)
+FIELD(V7M_CCR, DC, 16, 1)
+FIELD(V7M_CCR, IC, 17, 1)
+
+/* V7M CFSR bits for MMFSR */
+FIELD(V7M_CFSR, IACCVIOL, 0, 1)
+FIELD(V7M_CFSR, DACCVIOL, 1, 1)
+FIELD(V7M_CFSR, MUNSTKERR, 3, 1)
+FIELD(V7M_CFSR, MSTKERR, 4, 1)
+FIELD(V7M_CFSR, MLSPERR, 5, 1)
+FIELD(V7M_CFSR, MMARVALID, 7, 1)
+
+/* V7M CFSR bits for BFSR */
+FIELD(V7M_CFSR, IBUSERR, 8 + 0, 1)
+FIELD(V7M_CFSR, PRECISERR, 8 + 1, 1)
+FIELD(V7M_CFSR, IMPRECISERR, 8 + 2, 1)
+FIELD(V7M_CFSR, UNSTKERR, 8 + 3, 1)
+FIELD(V7M_CFSR, STKERR, 8 + 4, 1)
+FIELD(V7M_CFSR, LSPERR, 8 + 5, 1)
+FIELD(V7M_CFSR, BFARVALID, 8 + 7, 1)
+
+/* V7M CFSR bits for UFSR */
+FIELD(V7M_CFSR, UNDEFINSTR, 16 + 0, 1)
+FIELD(V7M_CFSR, INVSTATE, 16 + 1, 1)
+FIELD(V7M_CFSR, INVPC, 16 + 2, 1)
+FIELD(V7M_CFSR, NOCP, 16 + 3, 1)
+FIELD(V7M_CFSR, UNALIGNED, 16 + 8, 1)
+FIELD(V7M_CFSR, DIVBYZERO, 16 + 9, 1)
+
+/* V7M HFSR bits */
+FIELD(V7M_HFSR, VECTTBL, 1, 1)
+FIELD(V7M_HFSR, FORCED, 30, 1)
+FIELD(V7M_HFSR, DEBUGEVT, 31, 1)
+
+/* V7M DFSR bits */
+FIELD(V7M_DFSR, HALTED, 0, 1)
+FIELD(V7M_DFSR, BKPT, 1, 1)
+FIELD(V7M_DFSR, DWTTRAP, 2, 1)
+FIELD(V7M_DFSR, VCATCH, 3, 1)
+FIELD(V7M_DFSR, EXTERNAL, 4, 1)
+
/* If adding a feature bit which corresponds to a Linux ELF
* HWCAP bit, remember to update the feature-bit-to-hwcap
* mapping in linux-user/elfload.c:get_elf_hwcap().
@@ -1763,12 +1817,6 @@ bool write_list_to_cpustate(ARMCPU *cpu);
*/
bool write_cpustate_to_list(ARMCPU *cpu);
-/* Does the core conform to the "MicroController" profile. e.g. Cortex-M3.
- Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are
- conventional cores (ie. Application or Realtime profile). */
-
-#define IS_M(env) arm_feature(env, ARM_FEATURE_M)
-
#define ARM_CPUID_TI915T 0x54029152
#define ARM_CPUID_TI925T 0x54029252
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 7111c8cf18..c23df1b133 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -5947,14 +5947,19 @@ static uint32_t v7m_pop(CPUARMState *env)
}
/* Switch to V7M main or process stack pointer. */
-static void switch_v7m_sp(CPUARMState *env, int process)
+static void switch_v7m_sp(CPUARMState *env, bool new_spsel)
{
uint32_t tmp;
- if (env->v7m.current_sp != process) {
+ bool old_spsel = env->v7m.control & R_V7M_CONTROL_SPSEL_MASK;
+
+ if (old_spsel != new_spsel) {
tmp = env->v7m.other_sp;
env->v7m.other_sp = env->regs[13];
env->regs[13] = tmp;
- env->v7m.current_sp = process;
+
+ env->v7m.control = deposit32(env->v7m.control,
+ R_V7M_CONTROL_SPSEL_SHIFT,
+ R_V7M_CONTROL_SPSEL_LENGTH, new_spsel);
}
}
@@ -5964,8 +5969,13 @@ static void do_v7m_exception_exit(CPUARMState *env)
uint32_t xpsr;
type = env->regs[15];
- if (env->v7m.exception != 0)
+ if (env->v7m.exception != ARMV7M_EXCP_NMI) {
+ /* Auto-clear FAULTMASK on return from other than NMI */
+ env->daif &= ~PSTATE_F;
+ }
+ if (env->v7m.exception != 0) {
armv7m_nvic_complete_irq(env->nvic, env->v7m.exception);
+ }
/* Switch to the target stack. */
switch_v7m_sp(env, (type & 4) != 0);
@@ -6014,6 +6024,30 @@ static void arm_log_exception(int idx)
}
}
+static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
+
+{
+ CPUState *cs = CPU(cpu);
+ CPUARMState *env = &cpu->env;
+ MemTxResult result;
+ hwaddr vec = env->v7m.vecbase + env->v7m.exception * 4;
+ uint32_t addr;
+
+ addr = address_space_ldl(cs->as, vec,
+ MEMTXATTRS_UNSPECIFIED, &result);
+ if (result != MEMTX_OK) {
+ /* Architecturally this should cause a HardFault setting HSFR.VECTTBL,
+ * which would then be immediately followed by our failing to load
+ * the entry vector for that HardFault, which is a Lockup case.
+ * Since we don't model Lockup, we just report this guest error
+ * via cpu_abort().
+ */
+ cpu_abort(cs, "Failed to read from exception vector table "
+ "entry %08x\n", (unsigned)vec);
+ }
+ return addr;
+}
+
void arm_v7m_cpu_do_interrupt(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
@@ -6025,8 +6059,9 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
arm_log_exception(cs->exception_index);
lr = 0xfffffff1;
- if (env->v7m.current_sp)
+ if (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) {
lr |= 4;
+ }
if (env->v7m.exception == 0)
lr |= 8;
@@ -6037,6 +6072,11 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
switch (cs->exception_index) {
case EXCP_UDEF:
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ env->v7m.cfsr |= R_V7M_CFSR_UNDEFINSTR_MASK;
+ return;
+ case EXCP_NOCP:
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ env->v7m.cfsr |= R_V7M_CFSR_NOCP_MASK;
return;
case EXCP_SWI:
/* The PC already points to the next instruction. */
@@ -6075,10 +6115,8 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
return; /* Never happens. Keep compiler happy. */
}
- /* Align stack pointer. */
- /* ??? Should only do this if Configuration Control Register
- STACKALIGN bit is set. */
- if (env->regs[13] & 4) {
+ /* Align stack pointer if the guest wants that */
+ if ((env->regs[13] & 4) && (env->v7m.ccr & R_V7M_CCR_STKALIGN_MASK)) {
env->regs[13] -= 4;
xpsr |= 0x200;
}
@@ -6095,7 +6133,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
/* Clear IT bits */
env->condexec_bits = 0;
env->regs[14] = lr;
- addr = ldl_phys(cs->as, env->v7m.vecbase + env->v7m.exception * 4);
+ addr = arm_v7m_load_vector(cpu);
env->regs[15] = addr & 0xfffffffe;
env->thumb = addr & 1;
}
@@ -6660,7 +6698,7 @@ void arm_cpu_do_interrupt(CPUState *cs)
CPUARMState *env = &cpu->env;
unsigned int new_el = env->exception.target_el;
- assert(!IS_M(env));
+ assert(!arm_feature(env, ARM_FEATURE_M));
arm_log_exception(cs->exception_index);
qemu_log_mask(CPU_LOG_INT, "...from EL%d to EL%d\n", arm_current_el(env),
@@ -8243,27 +8281,38 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
+ uint32_t mask;
+ unsigned el = arm_current_el(env);
+
+ /* First handle registers which unprivileged can read */
+
+ switch (reg) {
+ case 0 ... 7: /* xPSR sub-fields */
+ mask = 0;
+ if ((reg & 1) && el) {
+ mask |= 0x000001ff; /* IPSR (unpriv. reads as zero) */
+ }
+ if (!(reg & 4)) {
+ mask |= 0xf8000000; /* APSR */
+ }
+ /* EPSR reads as zero */
+ return xpsr_read(env) & mask;
+ break;
+ case 20: /* CONTROL */
+ return env->v7m.control;
+ }
+
+ if (el == 0) {
+ return 0; /* unprivileged reads others as zero */
+ }
switch (reg) {
- case 0: /* APSR */
- return xpsr_read(env) & 0xf8000000;
- case 1: /* IAPSR */
- return xpsr_read(env) & 0xf80001ff;
- case 2: /* EAPSR */
- return xpsr_read(env) & 0xff00fc00;
- case 3: /* xPSR */
- return xpsr_read(env) & 0xff00fdff;
- case 5: /* IPSR */
- return xpsr_read(env) & 0x000001ff;
- case 6: /* EPSR */
- return xpsr_read(env) & 0x0700fc00;
- case 7: /* IEPSR */
- return xpsr_read(env) & 0x0700edff;
case 8: /* MSP */
- return env->v7m.current_sp ? env->v7m.other_sp : env->regs[13];
+ return (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) ?
+ env->v7m.other_sp : env->regs[13];
case 9: /* PSP */
- return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp;
+ return (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) ?
+ env->regs[13] : env->v7m.other_sp;
case 16: /* PRIMASK */
return (env->daif & PSTATE_I) != 0;
case 17: /* BASEPRI */
@@ -8271,52 +8320,40 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
return env->v7m.basepri;
case 19: /* FAULTMASK */
return (env->daif & PSTATE_F) != 0;
- case 20: /* CONTROL */
- return env->v7m.control;
default:
- /* ??? For debugging only. */
- cpu_abort(CPU(cpu), "Unimplemented system register read (%d)\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "Attempt to read unknown special"
+ " register %d\n", reg);
return 0;
}
}
void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
+ if (arm_current_el(env) == 0 && reg > 7) {
+ /* only xPSR sub-fields may be written by unprivileged */
+ return;
+ }
switch (reg) {
- case 0: /* APSR */
- xpsr_write(env, val, 0xf8000000);
- break;
- case 1: /* IAPSR */
- xpsr_write(env, val, 0xf8000000);
- break;
- case 2: /* EAPSR */
- xpsr_write(env, val, 0xfe00fc00);
- break;
- case 3: /* xPSR */
- xpsr_write(env, val, 0xfe00fc00);
- break;
- case 5: /* IPSR */
- /* IPSR bits are readonly. */
- break;
- case 6: /* EPSR */
- xpsr_write(env, val, 0x0600fc00);
- break;
- case 7: /* IEPSR */
- xpsr_write(env, val, 0x0600fc00);
+ case 0 ... 7: /* xPSR sub-fields */
+ /* only APSR is actually writable */
+ if (reg & 4) {
+ xpsr_write(env, val, 0xf8000000); /* APSR */
+ }
break;
case 8: /* MSP */
- if (env->v7m.current_sp)
+ if (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) {
env->v7m.other_sp = val;
- else
+ } else {
env->regs[13] = val;
+ }
break;
case 9: /* PSP */
- if (env->v7m.current_sp)
+ if (env->v7m.control & R_V7M_CONTROL_SPSEL_MASK) {
env->regs[13] = val;
- else
+ } else {
env->v7m.other_sp = val;
+ }
break;
case 16: /* PRIMASK */
if (val & 1) {
@@ -8341,12 +8378,13 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val)
}
break;
case 20: /* CONTROL */
- env->v7m.control = val & 3;
- switch_v7m_sp(env, (val & 2) != 0);
+ switch_v7m_sp(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0);
+ env->v7m.control = val & (R_V7M_CONTROL_SPSEL_MASK |
+ R_V7M_CONTROL_NPRIV_MASK);
break;
default:
- /* ??? For debugging only. */
- cpu_abort(CPU(cpu), "Unimplemented system register write (%d)\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "Attempt to write unknown special"
+ " register %d\n", reg);
return;
}
}
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 3cae5ff3b5..2e65bc12fa 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -25,6 +25,8 @@
#ifndef TARGET_ARM_INTERNALS_H
#define TARGET_ARM_INTERNALS_H
+#include "hw/registerfields.h"
+
/* register banks for CPU modes */
#define BANK_USRSYS 0
#define BANK_SVC 1
@@ -75,6 +77,11 @@ static const char * const excnames[] = {
*/
#define GTIMER_SCALE 16
+/* Bit definitions for the v7M CONTROL register */
+FIELD(V7M_CONTROL, NPRIV, 0, 1)
+FIELD(V7M_CONTROL, SPSEL, 1, 1)
+FIELD(V7M_CONTROL, FPCA, 2, 1)
+
/*
* For AArch64, map a given EL to an index in the banked_spsr array.
* Note that this mapping and the AArch32 mapping defined in bank_number()
diff --git a/target/arm/machine.c b/target/arm/machine.c
index 487320db1d..fa5ec76090 100644
--- a/target/arm/machine.c
+++ b/target/arm/machine.c
@@ -99,15 +99,19 @@ static bool m_needed(void *opaque)
static const VMStateDescription vmstate_m = {
.name = "cpu/m",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 3,
+ .minimum_version_id = 3,
.needed = m_needed,
.fields = (VMStateField[]) {
- VMSTATE_UINT32(env.v7m.other_sp, ARMCPU),
VMSTATE_UINT32(env.v7m.vecbase, ARMCPU),
VMSTATE_UINT32(env.v7m.basepri, ARMCPU),
VMSTATE_UINT32(env.v7m.control, ARMCPU),
- VMSTATE_INT32(env.v7m.current_sp, ARMCPU),
+ VMSTATE_UINT32(env.v7m.ccr, ARMCPU),
+ VMSTATE_UINT32(env.v7m.cfsr, ARMCPU),
+ VMSTATE_UINT32(env.v7m.hfsr, ARMCPU),
+ VMSTATE_UINT32(env.v7m.dfsr, ARMCPU),
+ VMSTATE_UINT32(env.v7m.mmfar, ARMCPU),
+ VMSTATE_UINT32(env.v7m.bfar, ARMCPU),
VMSTATE_INT32(env.v7m.exception, ARMCPU),
VMSTATE_END_OF_LIST()
}
diff --git a/target/arm/translate.c b/target/arm/translate.c
index c9186b6195..493c627bcf 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -10217,6 +10217,14 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
break;
case 6: case 7: case 14: case 15:
/* Coprocessor. */
+ if (arm_dc_feature(s, ARM_FEATURE_M)) {
+ /* We don't currently implement M profile FP support,
+ * so this entire space should give a NOCP fault.
+ */
+ gen_exception_insn(s, 4, EXCP_NOCP, syn_uncategorized(),
+ default_exception_el(s));
+ break;
+ }
if (((insn >> 24) & 3) == 3) {
/* Translate into the equivalent ARM encoding. */
insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28);
@@ -11719,12 +11727,12 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb)
break;
}
#else
- if (dc->pc >= 0xfffffff0 && arm_dc_feature(dc, ARM_FEATURE_M)) {
- /* We always get here via a jump, so know we are not in a
- conditional execution block. */
- gen_exception_internal(EXCP_EXCEPTION_EXIT);
- dc->is_jmp = DISAS_EXC;
- break;
+ if (arm_dc_feature(dc, ARM_FEATURE_M)) {
+ /* Branches to the magic exception-return addresses should
+ * already have been caught via the arm_v7m_unassigned_access hook,
+ * and never get here.
+ */
+ assert(dc->pc < 0xfffffff0);
}
#endif