[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 3/3] armv7-m: add MPU to cortex-m3 and cortex-m4
From: |
Michael Davidsaver |
Subject: |
[Qemu-devel] [PATCH 3/3] armv7-m: add MPU to cortex-m3 and cortex-m4 |
Date: |
Fri, 9 Oct 2015 09:28:31 -0400 |
The M series MPU is almost the same as the already
implemented R series MPU. So use the M series
and translate as best we can.
The HFNMIENA bit in MPU_CTRL is not implemented.
Implement CFSR and MMFAR to report fault address
to MemManage handler.
Add MPU feature flag to cortex-m3 and -m4.
---
hw/intc/armv7m_nvic.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++--
target-arm/cpu-qom.h | 4 ++
target-arm/cpu.c | 14 +++++
target-arm/helper.c | 7 +++
4 files changed, 174 insertions(+), 5 deletions(-)
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index a671d84..94011cf 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -245,12 +245,11 @@ 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 ARM_CPU(current_cpu)->pmsav7_cfsr;
+ case 0xd34: /* MMFAR MemManage Fault Address */
+ return ARM_CPU(current_cpu)->pmsav7_mmfar;
case 0xd2c: /* Hard Fault Status. */
case 0xd30: /* Debug Fault Status. */
- case 0xd34: /* Mem Manage Address. */
case 0xd38: /* Bus Fault Address. */
case 0xd3c: /* Aux Fault Status. */
/* TODO: Implement fault status registers. */
@@ -283,6 +282,55 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
case 0xd70: /* ISAR4. */
return 0x01310102;
/* TODO: Implement debug registers. */
+ case 0xd90: /* MPU_TYPE */
+ cpu = ARM_CPU(current_cpu);
+ return cpu->has_mpu ? (cpu->pmsav7_dregion<<8) : 0;
+ break;
+ case 0xd94: /* MPU_CTRL */
+ val = 0;
+ cpu = ARM_CPU(current_cpu);
+ if(cpu->env.cp15.sctlr_el[0] & SCTLR_M)
+ val |= 1; /* ENABLE */
+ /* HFNMIENA not implemented, see nvic_writel() */
+ if(cpu->env.cp15.sctlr_el[0] & SCTLR_BR)
+ val |= 4; /* PRIVDEFENA */
+ return val;
+ case 0xd98: /* MPU_RNR */
+ return ARM_CPU(current_cpu)->env.cp15.c6_rgnr;
+ case 0xd9c: /* MPU_RBAR */
+ case 0xda4: /* MPU_RBAR_A1 */
+ case 0xdaC: /* MPU_RBAR_A2 */
+ case 0xdb4: /* MPU_RBAR_A3 */
+ {
+ uint32_t range;
+ cpu = ARM_CPU(current_cpu);
+ if(offset==0xd9c)
+ range = cpu->env.cp15.c6_rgnr;
+ else
+ range = (offset-0xda4)/8;
+
+ if(range>=cpu->pmsav7_dregion) return 0;
+
+ return (cpu->env.pmsav7.drbar[range]&(0x1f)) | (range&0xf);
+ }
+ case 0xda0: /* MPU_RASR */
+ case 0xda8: /* MPU_RASR_A1 */
+ case 0xdb0: /* MPU_RASR_A2 */
+ case 0xdb8: /* MPU_RASR_A3 */
+ {
+ uint32_t range;
+ cpu = ARM_CPU(current_cpu);
+
+ if(offset==0xda0)
+ range = cpu->env.cp15.c6_rgnr;
+ else
+ range = (offset-0xda8)/8;
+
+ if(range>=cpu->pmsav7_dregion) return 0;
+
+ return ((cpu->env.pmsav7.dracr[range]&0xffff)<<16)
+ | (cpu->env.pmsav7.drsr[range]&0xffff);
+ }
default:
qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
return 0;
@@ -376,14 +424,110 @@ 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. */
+ case 0xd34: /* Mem Manage Address. */
+ return;
case 0xd2c: /* Hard Fault Status. */
case 0xd30: /* Debug Fault Status. */
- case 0xd34: /* Mem Manage Address. */
case 0xd38: /* Bus Fault Address. */
case 0xd3c: /* Aux Fault Status. */
qemu_log_mask(LOG_UNIMP,
"NVIC: fault status registers unimplemented\n");
break;
+ case 0xd90: /* MPU_TYPE (0xe000ed90) */
+ return; /* RO */
+ case 0xd94: /* MPU_CTRL */
+ {
+ unsigned q;
+ uint32_t tset=0, tclear=0;
+ if(value&1) {
+ tset |= SCTLR_M;
+ } else {
+ tclear |= SCTLR_M;
+ }
+ if(value&2) {
+ /* The M series MPU is almost functionally the same
+ * as the R series MPU, so we translate to the R series
+ * register format.
+ *
+ * One difference is that the R series MPU doesn't implement
+ * HFNMIENA (bypass MPU in some exception handlers).
+ */
+ qemu_log_mask(LOG_UNIMP, "M profile does not implement HFNMIENA
bit in MPU_CTRL\n");
+ }
+ if(value&4) {
+ tset |= SCTLR_BR;
+ } else {
+ tclear |= SCTLR_BR;
+ }
+ cpu = ARM_CPU(current_cpu);
+ /* TODO, which sctlr(s) really need to be set? */
+ for(q=0; q<4; q++) {
+ cpu->env.cp15.sctlr_el[q] |= tset;
+ cpu->env.cp15.sctlr_el[q] &= ~tclear;
+ }
+ tlb_flush(CPU(cpu), 1);
+ }
+ break;
+ case 0xd98: /* MPU_RNR */
+ cpu = ARM_CPU(current_cpu);
+ if(value>=cpu->pmsav7_dregion) {
+ qemu_log_mask(LOG_GUEST_ERROR, "MPU region out of range %u/%u\n",
+ (unsigned)value, (unsigned)cpu->pmsav7_dregion);
+ } else {
+ cpu->env.cp15.c6_rgnr = value;
+ }
+ tlb_flush(CPU(cpu), 1); /* necessary? */
+ break;
+ case 0xd9c: /* MPU_RBAR */
+ case 0xda4: /* MPU_RBAR_A1 */
+ case 0xdac: /* MPU_RBAR_A2 */
+ case 0xdb4: /* MPU_RBAR_A3 */
+ {
+ uint32_t range;
+ uint32_t base = value;
+ cpu = ARM_CPU(current_cpu);
+
+ if(offset==0xd9c)
+ range = cpu->env.cp15.c6_rgnr;
+ else
+ range = (offset-0xda4)/8;
+
+ if(value&(1<<4)) {
+ range = value&0xf;
+
+ if(range>=cpu->pmsav7_dregion) {
+ qemu_log_mask(LOG_GUEST_ERROR, "MPU region out of range
%u/%u\n",
+ (unsigned)range, (unsigned)cpu->pmsav7_dregion);
+ return;
+ }
+ cpu->env.cp15.c6_rgnr = range;
+ base &= ~0x1f;
+
+ } else if(range>=cpu->pmsav7_dregion)
+ return;
+
+ cpu->env.pmsav7.drbar[range] = base&~0x3;
+ }
+ tlb_flush(CPU(cpu), 1);
+ break;
+ case 0xda0: /* MPU_RASR */
+ case 0xda8: /* MPU_RASR_A1 */
+ case 0xdb0: /* MPU_RASR_A2 */
+ case 0xdb8: /* MPU_RASR_A3 */
+ {
+ uint32_t range;
+ cpu = ARM_CPU(current_cpu);
+
+ if(offset==0xda0)
+ range = cpu->env.cp15.c6_rgnr;
+ else
+ range = (offset-0xda8)/8;
+
+ cpu->env.pmsav7.drsr[range] = value&0xff3f;
+ cpu->env.pmsav7.dracr[range] = (value>>16)&0x173f;
+ }
+ tlb_flush(CPU(cpu), 1);
+ break;
case 0xf00: /* Software Triggered Interrupt Register */
if ((value & 0x1ff) < s->num_irq) {
gic_set_pending_private(&s->gic, 0, value & 0x1ff);
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index 25fb1ce..d75a496 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -107,6 +107,10 @@ typedef struct ARMCPU {
bool has_mpu;
/* PMSAv7 MPU number of supported regions */
uint32_t pmsav7_dregion;
+ /* PMASv7 Fault status */
+ uint32_t pmsav7_cfsr;
+ /* PMASv7 Address of fault */
+ uint32_t pmsav7_mmfar;
/* PSCI conduit used to invoke PSCI methods
* 0 - disabled, 1 - smc, 2 - hvc
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index d7b4445..deb5878 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -516,6 +516,12 @@ static Property arm_cpu_has_mpu_property =
static Property arm_cpu_pmsav7_dregion_property =
DEFINE_PROP_UINT32("pmsav7-dregion", ARMCPU, pmsav7_dregion, 16);
+static Property arm_cpu_pmsav7_cfsr_property =
+ DEFINE_PROP_UINT32("pmsav7-cfsr", ARMCPU, pmsav7_cfsr, 0);
+
+static Property arm_cpu_pmsav7_mmfar_property =
+ DEFINE_PROP_UINT32("pmsav7-mmfar", ARMCPU, pmsav7_mmfar, 0);
+
static void arm_cpu_post_init(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
@@ -551,6 +557,12 @@ static void arm_cpu_post_init(Object *obj)
qdev_property_add_static(DEVICE(obj),
&arm_cpu_pmsav7_dregion_property,
&error_abort);
+ qdev_property_add_static(DEVICE(obj),
+ &arm_cpu_pmsav7_cfsr_property,
+ &error_abort);
+ qdev_property_add_static(DEVICE(obj),
+ &arm_cpu_pmsav7_mmfar_property,
+ &error_abort);
}
}
@@ -889,6 +901,7 @@ static void cortex_m3_initfn(Object *obj)
ARMCPU *cpu = ARM_CPU(obj);
set_feature(&cpu->env, ARM_FEATURE_V7);
set_feature(&cpu->env, ARM_FEATURE_M);
+ set_feature(&cpu->env, ARM_FEATURE_MPU);
cpu->midr = 0x410fc231;
}
@@ -898,6 +911,7 @@ static void cortex_m4_initfn(Object *obj)
set_feature(&cpu->env, ARM_FEATURE_V7);
set_feature(&cpu->env, ARM_FEATURE_M);
+ set_feature(&cpu->env, ARM_FEATURE_MPU);
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
cpu->midr = 0x410fc240; /* r0p0 */
}
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 56b238f..368cbc6 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -5357,11 +5357,15 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
case EXCP_DATA_ABORT:
if(env->v7m.exception!=0 && env->exception.vaddress>=0xfffffff0) {
/* this isn't a real fault, but rather a result of return from
interrupt */
+ cpu->pmsav7_mmfar = 0;
+ cpu->pmsav7_cfsr = 0;
do_v7m_exception_exit(env);
return;
}
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
env->v7m.exception = ARMV7M_EXCP_MEM;
+ cpu->pmsav7_mmfar = env->exception.vaddress;
+ cpu->pmsav7_cfsr = (1<<1)|(1<<7);
break;
case EXCP_BKPT:
if (semihosting_enabled()) {
@@ -5382,6 +5386,9 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
env->v7m.exception = armv7m_nvic_acknowledge_irq(env->nvic);
break;
case EXCP_EXCEPTION_EXIT:
+
+ cpu->pmsav7_mmfar = 0;
+ cpu->pmsav7_cfsr = 0;
do_v7m_exception_exit(env);
return;
default:
--
2.1.4