[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PULL 06/12] target-arm: Implement PMSAv7 MPU
From: |
Peter Maydell |
Subject: |
[Qemu-devel] [PULL 06/12] target-arm: Implement PMSAv7 MPU |
Date: |
Fri, 19 Jun 2015 14:47:10 +0100 |
From: Peter Crosthwaite <address@hidden>
Unified MPU only. Uses ARM architecture major revision to switch
between PMSAv5 and v7 when ARM_FEATURE_MPU is set. PMSA v6 remains
unsupported and is asserted against.
Reviewed-by: Peter Maydell <address@hidden>
Signed-off-by: Peter Crosthwaite <address@hidden>
Message-id: address@hidden
Signed-off-by: Peter Maydell <address@hidden>
---
target-arm/cpu.h | 1 +
target-arm/helper.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 174 insertions(+), 1 deletion(-)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 0c320ea..80297b3 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -560,6 +560,7 @@ void pmccntr_sync(CPUARMState *env);
#define SCTLR_DT (1U << 16) /* up to ??, RAO in v6 and v7 */
#define SCTLR_nTWI (1U << 16) /* v8 onward */
#define SCTLR_HA (1U << 17)
+#define SCTLR_BR (1U << 17) /* PMSA only */
#define SCTLR_IT (1U << 18) /* up to ??, RAO in v6 and v7 */
#define SCTLR_nTWE (1U << 18) /* v8 onward */
#define SCTLR_WXN (1U << 19)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index b6442a6..efce6cd 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -5835,6 +5835,167 @@ do_fault:
return true;
}
+static inline void get_phys_addr_pmsav7_default(CPUARMState *env,
+ ARMMMUIdx mmu_idx,
+ int32_t address, int *prot)
+{
+ *prot = PAGE_READ | PAGE_WRITE;
+ switch (address) {
+ case 0xF0000000 ... 0xFFFFFFFF:
+ if (regime_sctlr(env, mmu_idx) & SCTLR_V) { /* hivecs execing is ok */
+ *prot |= PAGE_EXEC;
+ }
+ break;
+ case 0x00000000 ... 0x7FFFFFFF:
+ *prot |= PAGE_EXEC;
+ break;
+ }
+
+}
+
+static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
+ int access_type, ARMMMUIdx mmu_idx,
+ hwaddr *phys_ptr, int *prot, uint32_t *fsr)
+{
+ ARMCPU *cpu = arm_env_get_cpu(env);
+ int n;
+ bool is_user = regime_is_user(env, mmu_idx);
+
+ *phys_ptr = address;
+ *prot = 0;
+
+ if (regime_translation_disabled(env, mmu_idx)) { /* MPU disabled */
+ get_phys_addr_pmsav7_default(env, mmu_idx, address, prot);
+ } else { /* MPU enabled */
+ for (n = (int)cpu->pmsav7_dregion - 1; n >= 0; n--) {
+ /* region search */
+ uint32_t base = env->pmsav7.drbar[n];
+ uint32_t rsize = extract32(env->pmsav7.drsr[n], 1, 5);
+ uint32_t rmask;
+ bool srdis = false;
+
+ if (!(env->pmsav7.drsr[n] & 0x1)) {
+ continue;
+ }
+
+ if (!rsize) {
+ qemu_log_mask(LOG_GUEST_ERROR, "DRSR.Rsize field can not be
0");
+ continue;
+ }
+ rsize++;
+ rmask = (1ull << rsize) - 1;
+
+ if (base & rmask) {
+ qemu_log_mask(LOG_GUEST_ERROR, "DRBAR %" PRIx32 " misaligned "
+ "to DRSR region size, mask = %" PRIx32,
+ base, rmask);
+ continue;
+ }
+
+ if (address < base || address > base + rmask) {
+ continue;
+ }
+
+ /* Region matched */
+
+ if (rsize >= 8) { /* no subregions for regions < 256 bytes */
+ int i, snd;
+ uint32_t srdis_mask;
+
+ rsize -= 3; /* sub region size (power of 2) */
+ snd = ((address - base) >> rsize) & 0x7;
+ srdis = extract32(env->pmsav7.drsr[n], snd + 8, 1);
+
+ srdis_mask = srdis ? 0x3 : 0x0;
+ for (i = 2; i <= 8 && rsize < TARGET_PAGE_BITS; i *= 2) {
+ /* This will check in groups of 2, 4 and then 8, whether
+ * the subregion bits are consistent. rsize is incremented
+ * back up to give the region size, considering consistent
+ * adjacent subregions as one region. Stop testing if rsize
+ * is already big enough for an entire QEMU page.
+ */
+ int snd_rounded = snd & ~(i - 1);
+ uint32_t srdis_multi = extract32(env->pmsav7.drsr[n],
+ snd_rounded + 8, i);
+ if (srdis_mask ^ srdis_multi) {
+ break;
+ }
+ srdis_mask = (srdis_mask << i) | srdis_mask;
+ rsize++;
+ }
+ }
+ if (rsize < TARGET_PAGE_BITS) {
+ qemu_log_mask(LOG_UNIMP, "No support for MPU (sub)region"
+ "alignment of %" PRIu32 " bits. Minimum is %d\n",
+ rsize, TARGET_PAGE_BITS);
+ continue;
+ }
+ if (srdis) {
+ continue;
+ }
+ break;
+ }
+
+ if (n == -1) { /* no hits */
+ if (cpu->pmsav7_dregion &&
+ (is_user || !(regime_sctlr(env, mmu_idx) & SCTLR_BR))) {
+ /* background fault */
+ *fsr = 0;
+ return true;
+ }
+ get_phys_addr_pmsav7_default(env, mmu_idx, address, prot);
+ } else { /* a MPU hit! */
+ uint32_t ap = extract32(env->pmsav7.dracr[n], 8, 3);
+
+ if (is_user) { /* User mode AP bit decoding */
+ switch (ap) {
+ case 0:
+ case 1:
+ case 5:
+ break; /* no access */
+ case 3:
+ *prot |= PAGE_WRITE;
+ /* fall through */
+ case 2:
+ case 6:
+ *prot |= PAGE_READ | PAGE_EXEC;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Bad value for AP bits in DRACR %"
+ PRIx32 "\n", ap);
+ }
+ } else { /* Priv. mode AP bits decoding */
+ switch (ap) {
+ case 0:
+ break; /* no access */
+ case 1:
+ case 2:
+ case 3:
+ *prot |= PAGE_WRITE;
+ /* fall through */
+ case 5:
+ case 6:
+ *prot |= PAGE_READ | PAGE_EXEC;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Bad value for AP bits in DRACR %"
+ PRIx32 "\n", ap);
+ }
+ }
+
+ /* execute never */
+ if (env->pmsav7.dracr[n] & (1 << 12)) {
+ *prot &= ~PAGE_EXEC;
+ }
+ }
+ }
+
+ *fsr = 0x00d; /* Permission fault */
+ return !(*prot & (1 << access_type));
+}
+
static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address,
int access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, int *prot, uint32_t *fsr)
@@ -5920,7 +6081,7 @@ static bool get_phys_addr_pmsav5(CPUARMState *env,
uint32_t address,
* DFSR/IFSR fault register, with the following caveats:
* * we honour the short vs long DFSR format differences.
* * the WnR bit is never set (the caller must do this).
- * * for MPU based systems we don't bother to return a full FSR format
+ * * for PSMAv5 based systems we don't bother to return a full FSR format
* value.
*
* @env: CPUARMState
@@ -5968,6 +6129,16 @@ static inline bool get_phys_addr(CPUARMState *env,
target_ulong address,
}
}
+ /* pmsav7 has special handling for when MPU is disabled so call it before
+ * the common MMU/MPU disabled check below.
+ */
+ if (arm_feature(env, ARM_FEATURE_MPU) &&
+ arm_feature(env, ARM_FEATURE_V7)) {
+ *page_size = TARGET_PAGE_SIZE;
+ return get_phys_addr_pmsav7(env, address, access_type, mmu_idx,
+ phys_ptr, prot, fsr);
+ }
+
if (regime_translation_disabled(env, mmu_idx)) {
/* MMU/MPU disabled. */
*phys_ptr = address;
@@ -5977,6 +6148,7 @@ static inline bool get_phys_addr(CPUARMState *env,
target_ulong address,
}
if (arm_feature(env, ARM_FEATURE_MPU)) {
+ /* Pre-v7 MPU */
*page_size = TARGET_PAGE_SIZE;
return get_phys_addr_pmsav5(env, address, access_type, mmu_idx,
phys_ptr, prot, fsr);
--
1.9.1
- [Qemu-devel] [PULL 00/12] target-arm queue, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 10/12] arm: xlnx-zynqmp: Add 2xCortexR5 CPUs, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 01/12] target-arm: Add the Cortex-M4 CPU, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 09/12] arm: xlnx-zynqmp: Add boot-cpu property, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 07/12] target-arm: Add support for Cortex-R5, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 08/12] arm: xlnx-zynqmp: Preface CPU variables with "apu", Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 12/12] semihosting: add --semihosting-config arg sub-argument, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 06/12] target-arm: Implement PMSAv7 MPU,
Peter Maydell <=
- [Qemu-devel] [PULL 11/12] semihosting: create SemihostingConfig structure and semihost.h, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 04/12] target-arm/helper.c: define MPUIR register, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 05/12] target-arm: Add registers for PMSAv7, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 02/12] hw/arm/sysbus-fdt: enable vfio-calxeda-xgmac dynamic instantiation, Peter Maydell, 2015/06/19
- [Qemu-devel] [PULL 03/12] target-arm: Do not reset sysregs marked as ALIAS, Peter Maydell, 2015/06/19
- Re: [Qemu-devel] [PULL 00/12] target-arm queue, Peter Maydell, 2015/06/19