arm: add MPU support to M profile CPUs
The M series MPU is almost the same as the already implemented R profile MPU (v7 PMSA). So all we need to implement here is the MPU register interface in the system register space. This implementation has the same restriction as the R profile MPU that it doesn't permit regions to be sized down smaller than 1K. We also do not yet implement support for MPU_CTRL.HFNMIENA; this bit should if zero disable use of the MPU when running HardFault, NMI or with FAULTMASK set to 1 (ie at an execution priority of less than zero) -- if the MPU is enabled we don't treat these cases any differently. Signed-off-by: Michael Davidsaver <mdavidsaver@gmail.com> Message-id: 1493122030-32191-13-git-send-email-peter.maydell@linaro.org [PMM: Keep all the bits in mpu_ctrl field, rather than using SCTLR bits for them; drop broken HFNMIENA support; various cleanup] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
5dd0641d23
commit
29c483a506
@ -19,6 +19,7 @@
|
|||||||
#include "hw/arm/arm.h"
|
#include "hw/arm/arm.h"
|
||||||
#include "hw/arm/armv7m_nvic.h"
|
#include "hw/arm/armv7m_nvic.h"
|
||||||
#include "target/arm/cpu.h"
|
#include "target/arm/cpu.h"
|
||||||
|
#include "exec/exec-all.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
@ -528,6 +529,39 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset)
|
|||||||
case 0xd70: /* ISAR4. */
|
case 0xd70: /* ISAR4. */
|
||||||
return 0x01310102;
|
return 0x01310102;
|
||||||
/* TODO: Implement debug registers. */
|
/* TODO: Implement debug registers. */
|
||||||
|
case 0xd90: /* MPU_TYPE */
|
||||||
|
/* Unified MPU; if the MPU is not present this value is zero */
|
||||||
|
return cpu->pmsav7_dregion << 8;
|
||||||
|
break;
|
||||||
|
case 0xd94: /* MPU_CTRL */
|
||||||
|
return cpu->env.v7m.mpu_ctrl;
|
||||||
|
case 0xd98: /* MPU_RNR */
|
||||||
|
return cpu->env.cp15.c6_rgnr;
|
||||||
|
case 0xd9c: /* MPU_RBAR */
|
||||||
|
case 0xda4: /* MPU_RBAR_A1 */
|
||||||
|
case 0xdac: /* MPU_RBAR_A2 */
|
||||||
|
case 0xdb4: /* MPU_RBAR_A3 */
|
||||||
|
{
|
||||||
|
int region = cpu->env.cp15.c6_rgnr;
|
||||||
|
|
||||||
|
if (region >= cpu->pmsav7_dregion) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (cpu->env.pmsav7.drbar[region] & 0x1f) | (region & 0xf);
|
||||||
|
}
|
||||||
|
case 0xda0: /* MPU_RASR */
|
||||||
|
case 0xda8: /* MPU_RASR_A1 */
|
||||||
|
case 0xdb0: /* MPU_RASR_A2 */
|
||||||
|
case 0xdb8: /* MPU_RASR_A3 */
|
||||||
|
{
|
||||||
|
int region = cpu->env.cp15.c6_rgnr;
|
||||||
|
|
||||||
|
if (region >= cpu->pmsav7_dregion) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ((cpu->env.pmsav7.dracr[region] & 0xffff) << 16) |
|
||||||
|
(cpu->env.pmsav7.drsr[region] & 0xffff);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
|
qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
|
||||||
return 0;
|
return 0;
|
||||||
@ -627,6 +661,76 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value)
|
|||||||
qemu_log_mask(LOG_UNIMP,
|
qemu_log_mask(LOG_UNIMP,
|
||||||
"NVIC: Aux fault status registers unimplemented\n");
|
"NVIC: Aux fault status registers unimplemented\n");
|
||||||
break;
|
break;
|
||||||
|
case 0xd90: /* MPU_TYPE */
|
||||||
|
return; /* RO */
|
||||||
|
case 0xd94: /* MPU_CTRL */
|
||||||
|
if ((value &
|
||||||
|
(R_V7M_MPU_CTRL_HFNMIENA_MASK | R_V7M_MPU_CTRL_ENABLE_MASK))
|
||||||
|
== R_V7M_MPU_CTRL_HFNMIENA_MASK) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "MPU_CTRL: HFNMIENA and !ENABLE is "
|
||||||
|
"UNPREDICTABLE\n");
|
||||||
|
}
|
||||||
|
cpu->env.v7m.mpu_ctrl = value & (R_V7M_MPU_CTRL_ENABLE_MASK |
|
||||||
|
R_V7M_MPU_CTRL_HFNMIENA_MASK |
|
||||||
|
R_V7M_MPU_CTRL_PRIVDEFENA_MASK);
|
||||||
|
tlb_flush(CPU(cpu));
|
||||||
|
break;
|
||||||
|
case 0xd98: /* MPU_RNR */
|
||||||
|
if (value >= cpu->pmsav7_dregion) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "MPU region out of range %"
|
||||||
|
PRIu32 "/%" PRIu32 "\n",
|
||||||
|
value, cpu->pmsav7_dregion);
|
||||||
|
} else {
|
||||||
|
cpu->env.cp15.c6_rgnr = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xd9c: /* MPU_RBAR */
|
||||||
|
case 0xda4: /* MPU_RBAR_A1 */
|
||||||
|
case 0xdac: /* MPU_RBAR_A2 */
|
||||||
|
case 0xdb4: /* MPU_RBAR_A3 */
|
||||||
|
{
|
||||||
|
int region;
|
||||||
|
|
||||||
|
if (value & (1 << 4)) {
|
||||||
|
/* VALID bit means use the region number specified in this
|
||||||
|
* value and also update MPU_RNR.REGION with that value.
|
||||||
|
*/
|
||||||
|
region = extract32(value, 0, 4);
|
||||||
|
if (region >= cpu->pmsav7_dregion) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"MPU region out of range %u/%" PRIu32 "\n",
|
||||||
|
region, cpu->pmsav7_dregion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cpu->env.cp15.c6_rgnr = region;
|
||||||
|
} else {
|
||||||
|
region = cpu->env.cp15.c6_rgnr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region >= cpu->pmsav7_dregion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu->env.pmsav7.drbar[region] = value & ~0x1f;
|
||||||
|
tlb_flush(CPU(cpu));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xda0: /* MPU_RASR */
|
||||||
|
case 0xda8: /* MPU_RASR_A1 */
|
||||||
|
case 0xdb0: /* MPU_RASR_A2 */
|
||||||
|
case 0xdb8: /* MPU_RASR_A3 */
|
||||||
|
{
|
||||||
|
int region = cpu->env.cp15.c6_rgnr;
|
||||||
|
|
||||||
|
if (region >= cpu->pmsav7_dregion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu->env.pmsav7.drsr[region] = value & 0xff3f;
|
||||||
|
cpu->env.pmsav7.dracr[region] = (value >> 16) & 0x173f;
|
||||||
|
tlb_flush(CPU(cpu));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0xf00: /* Software Triggered Interrupt Register */
|
case 0xf00: /* Software Triggered Interrupt Register */
|
||||||
{
|
{
|
||||||
/* user mode can only write to STIR if CCR.USERSETMPEND permits it */
|
/* user mode can only write to STIR if CCR.USERSETMPEND permits it */
|
||||||
|
@ -418,6 +418,7 @@ typedef struct CPUARMState {
|
|||||||
uint32_t dfsr; /* Debug Fault Status Register */
|
uint32_t dfsr; /* Debug Fault Status Register */
|
||||||
uint32_t mmfar; /* MemManage Fault Address */
|
uint32_t mmfar; /* MemManage Fault Address */
|
||||||
uint32_t bfar; /* BusFault Address */
|
uint32_t bfar; /* BusFault Address */
|
||||||
|
unsigned mpu_ctrl; /* MPU_CTRL (some bits kept in sctlr_el[1]) */
|
||||||
int exception;
|
int exception;
|
||||||
} v7m;
|
} v7m;
|
||||||
|
|
||||||
@ -1168,6 +1169,11 @@ FIELD(V7M_DFSR, DWTTRAP, 2, 1)
|
|||||||
FIELD(V7M_DFSR, VCATCH, 3, 1)
|
FIELD(V7M_DFSR, VCATCH, 3, 1)
|
||||||
FIELD(V7M_DFSR, EXTERNAL, 4, 1)
|
FIELD(V7M_DFSR, EXTERNAL, 4, 1)
|
||||||
|
|
||||||
|
/* v7M MPU_CTRL bits */
|
||||||
|
FIELD(V7M_MPU_CTRL, ENABLE, 0, 1)
|
||||||
|
FIELD(V7M_MPU_CTRL, HFNMIENA, 1, 1)
|
||||||
|
FIELD(V7M_MPU_CTRL, PRIVDEFENA, 2, 1)
|
||||||
|
|
||||||
/* If adding a feature bit which corresponds to a Linux ELF
|
/* If adding a feature bit which corresponds to a Linux ELF
|
||||||
* HWCAP bit, remember to update the feature-bit-to-hwcap
|
* HWCAP bit, remember to update the feature-bit-to-hwcap
|
||||||
* mapping in linux-user/elfload.c:get_elf_hwcap().
|
* mapping in linux-user/elfload.c:get_elf_hwcap().
|
||||||
|
@ -7076,6 +7076,10 @@ static inline uint32_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx)
|
|||||||
static inline bool regime_translation_disabled(CPUARMState *env,
|
static inline bool regime_translation_disabled(CPUARMState *env,
|
||||||
ARMMMUIdx mmu_idx)
|
ARMMMUIdx mmu_idx)
|
||||||
{
|
{
|
||||||
|
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||||
|
return !(env->v7m.mpu_ctrl & R_V7M_MPU_CTRL_ENABLE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
if (mmu_idx == ARMMMUIdx_S2NS) {
|
if (mmu_idx == ARMMMUIdx_S2NS) {
|
||||||
return (env->cp15.hcr_el2 & HCR_VM) == 0;
|
return (env->cp15.hcr_el2 & HCR_VM) == 0;
|
||||||
}
|
}
|
||||||
@ -8205,6 +8209,25 @@ static inline void get_phys_addr_pmsav7_default(CPUARMState *env,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool pmsav7_use_background_region(ARMCPU *cpu,
|
||||||
|
ARMMMUIdx mmu_idx, bool is_user)
|
||||||
|
{
|
||||||
|
/* Return true if we should use the default memory map as a
|
||||||
|
* "background" region if there are no hits against any MPU regions.
|
||||||
|
*/
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
|
|
||||||
|
if (is_user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||||
|
return env->v7m.mpu_ctrl & R_V7M_MPU_CTRL_PRIVDEFENA_MASK;
|
||||||
|
} else {
|
||||||
|
return regime_sctlr(env, mmu_idx) & SCTLR_BR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
||||||
int access_type, ARMMMUIdx mmu_idx,
|
int access_type, ARMMMUIdx mmu_idx,
|
||||||
hwaddr *phys_ptr, int *prot, uint32_t *fsr)
|
hwaddr *phys_ptr, int *prot, uint32_t *fsr)
|
||||||
@ -8292,7 +8315,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (n == -1) { /* no hits */
|
if (n == -1) { /* no hits */
|
||||||
if (is_user || !(regime_sctlr(env, mmu_idx) & SCTLR_BR)) {
|
if (!pmsav7_use_background_region(cpu, mmu_idx, is_user)) {
|
||||||
/* background fault */
|
/* background fault */
|
||||||
*fsr = 0;
|
*fsr = 0;
|
||||||
return true;
|
return true;
|
||||||
|
@ -99,8 +99,8 @@ static bool m_needed(void *opaque)
|
|||||||
|
|
||||||
static const VMStateDescription vmstate_m = {
|
static const VMStateDescription vmstate_m = {
|
||||||
.name = "cpu/m",
|
.name = "cpu/m",
|
||||||
.version_id = 3,
|
.version_id = 4,
|
||||||
.minimum_version_id = 3,
|
.minimum_version_id = 4,
|
||||||
.needed = m_needed,
|
.needed = m_needed,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32(env.v7m.vecbase, ARMCPU),
|
VMSTATE_UINT32(env.v7m.vecbase, ARMCPU),
|
||||||
@ -112,6 +112,7 @@ static const VMStateDescription vmstate_m = {
|
|||||||
VMSTATE_UINT32(env.v7m.dfsr, ARMCPU),
|
VMSTATE_UINT32(env.v7m.dfsr, ARMCPU),
|
||||||
VMSTATE_UINT32(env.v7m.mmfar, ARMCPU),
|
VMSTATE_UINT32(env.v7m.mmfar, ARMCPU),
|
||||||
VMSTATE_UINT32(env.v7m.bfar, ARMCPU),
|
VMSTATE_UINT32(env.v7m.bfar, ARMCPU),
|
||||||
|
VMSTATE_UINT32(env.v7m.mpu_ctrl, ARMCPU),
|
||||||
VMSTATE_INT32(env.v7m.exception, ARMCPU),
|
VMSTATE_INT32(env.v7m.exception, ARMCPU),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user