From patchwork Fri Jun 19 13:47:10 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 486722 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id B43BF1401AF for ; Fri, 19 Jun 2015 23:53:35 +1000 (AEST) Received: from localhost ([::1]:58206 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z5wjd-0002ct-U7 for incoming@patchwork.ozlabs.org; Fri, 19 Jun 2015 09:53:33 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:35787) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z5wde-0006IG-FV for qemu-devel@nongnu.org; Fri, 19 Jun 2015 09:47:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Z5wdc-0001Kg-NL for qemu-devel@nongnu.org; Fri, 19 Jun 2015 09:47:22 -0400 Received: from mnementh.archaic.org.uk ([2001:8b0:1d0::1]:34490) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z5wdc-0001Hr-Dc for qemu-devel@nongnu.org; Fri, 19 Jun 2015 09:47:20 -0400 Received: from pm215 by mnementh.archaic.org.uk with local (Exim 4.80) (envelope-from ) id 1Z5wdY-0006bv-Op for qemu-devel@nongnu.org; Fri, 19 Jun 2015 14:47:16 +0100 From: Peter Maydell To: qemu-devel@nongnu.org Date: Fri, 19 Jun 2015 14:47:10 +0100 Message-Id: <1434721636-25357-7-git-send-email-peter.maydell@linaro.org> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1434721636-25357-1-git-send-email-peter.maydell@linaro.org> References: <1434721636-25357-1-git-send-email-peter.maydell@linaro.org> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2001:8b0:1d0::1 Subject: [Qemu-devel] [PULL 06/12] target-arm: Implement PMSAv7 MPU X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Peter Crosthwaite 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 Signed-off-by: Peter Crosthwaite Message-id: dcb03cda6dd754c5cc6a962fa11f25089811e954.1434501320.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- 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);