target-arm queue:
* various minor M profile bugfixes * aspeed/smc: handle dummy bytes when doing fast reads in command mode * pflash_cfi01: fix per-device sector length in CFI table * arm: stellaris: make MII accesses complete immediately * hw/char/exynos4210_uart: Drop unused local variable frame_size * arm_gicv3: Fix broken logic in ELRSR calculation * dma: omap: check dma channel data_type -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJYi2djAAoJEDwlJe0UNgzey7gQAJiOTv78+bIKRlrHtY0Iklq4 LaubD7ySIhy0/loN4p4adkIrPvGCozRLJ90TALULEWIY6zaefRwyH1NUkr+Zsqgk FZuDjopE2oQ7uZ4Xms4DjwsSfYbQuOReLynqJaaCI+qcatIcQI2Pff53SM7OHkKc dpw2suGrB2PKB9MZNslmclWvIwxCvLek78q7yc0kYMf3XXLHrXU/NyDnMSNKSRak JlsLvZb2ma5itSSgSRXafoLIX5Tr9R837P5pTs8asEhVBXkRUX31UCsub18A/X11 UwtnLejosK9ohQB7PX9zTRPAzPk7OABvb2HW6+wax2UJg63lgYHcm0Trp6l3U1+J p4k5eJrrk5fdg+WR1vDWUezKMgqV/CN7libYc5rH7d9AXzzDLh+EuEhrH2LsS73M uXGaMPhORFPravvCq95BNmEnkl6w7CgyyHLMf31cvmDJAbQKdAW+LL8aGL5G5Fm0 QbZOihw6ycfHPgmdytEX57aLxJ8y6Hr0AQJ3DO1NS0+y/mipTmdbvF/OH6NS1z8v /IIXZpV+8BKfupxlVEnaaGxJ+QnH8JP4CrrjKcHDhq6CffdYzIF/Vnmq0Mc6Qx97 qT/cvWJ2cR/GgSDohdhmDvElHxo5q0DnjOU+fORxaSvf1wY9NdaAp8DDdDxy8pOH DzaMGajgtb2l3FfX21zp =0R2j -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170127' into staging target-arm queue: * various minor M profile bugfixes * aspeed/smc: handle dummy bytes when doing fast reads in command mode * pflash_cfi01: fix per-device sector length in CFI table * arm: stellaris: make MII accesses complete immediately * hw/char/exynos4210_uart: Drop unused local variable frame_size * arm_gicv3: Fix broken logic in ELRSR calculation * dma: omap: check dma channel data_type # gpg: Signature made Fri 27 Jan 2017 15:29:39 GMT # gpg: using RSA key 0x3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20170127: (22 commits) dma: omap: check dma channel data_type arm_gicv3: Fix broken logic in ELRSR calculation hw/char/exynos4210_uart: Drop unused local variable frame_size arm: stellaris: make MII accesses complete immediately armv7m: R14 should reset to 0xffffffff armv7m: FAULTMASK should be 0 on reset armv7m: Honour CCR.USERSETMPEND armv7m: Report no-coprocessor faults correctly armv7m: set CFSR.UNDEFINSTR on undefined instructions armv7m: honour CCR.STACKALIGN on exception entry armv7m: implement CCR, CFSR, HFSR, DFSR, BFAR, and MMFAR armv7m: add state for v7M CCR, CFSR, HFSR, DFSR, MMFAR, BFAR armv7m_nvic: keep a pointer to the CPU target/arm: Drop IS_M() macro pflash_cfi01: fix per-device sector length in CFI table armv7m: Clear FAULTMASK on return from non-NMI exceptions armv7m: Fix reads of CONTROL register bit 1 hw/registerfields.h: Pull FIELD etc macros out of hw/register.h armv7m: Explicit error for bad vector table armv7m: Replace armv7m.hack with unassigned_access handler ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
3aca12f841
@ -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;
|
||||
}
|
||||
|
@ -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(),
|
||||
};
|
||||
|
||||
|
@ -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) +
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
60
include/hw/registerfields.h
Normal file
60
include/hw/registerfields.h
Normal file
@ -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
|
@ -573,6 +573,7 @@ void cpu_loop(CPUARMState *env)
|
||||
|
||||
switch(trapnr) {
|
||||
case EXCP_UDEF:
|
||||
case EXCP_NOCP:
|
||||
{
|
||||
TaskState *ts = cs->opaque;
|
||||
uint32_t opcode;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user