target/arm: Move get_phys_addr_pmsav7 to ptw.c
Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220604040607.269301-8-richard.henderson@linaro.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
7d2e08c960
commit
1f2e87e5ab
@ -11678,8 +11678,7 @@ do_fault:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool pmsav7_use_background_region(ARMCPU *cpu,
|
bool pmsav7_use_background_region(ARMCPU *cpu, ARMMMUIdx mmu_idx, bool is_user)
|
||||||
ARMMMUIdx mmu_idx, bool is_user)
|
|
||||||
{
|
{
|
||||||
/* Return true if we should use the default memory map as a
|
/* Return true if we should use the default memory map as a
|
||||||
* "background" region if there are no hits against any MPU regions.
|
* "background" region if there are no hits against any MPU regions.
|
||||||
@ -11698,14 +11697,14 @@ static bool pmsav7_use_background_region(ARMCPU *cpu,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool m_is_ppb_region(CPUARMState *env, uint32_t address)
|
bool m_is_ppb_region(CPUARMState *env, uint32_t address)
|
||||||
{
|
{
|
||||||
/* True if address is in the M profile PPB region 0xe0000000 - 0xe00fffff */
|
/* True if address is in the M profile PPB region 0xe0000000 - 0xe00fffff */
|
||||||
return arm_feature(env, ARM_FEATURE_M) &&
|
return arm_feature(env, ARM_FEATURE_M) &&
|
||||||
extract32(address, 20, 12) == 0xe00;
|
extract32(address, 20, 12) == 0xe00;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool m_is_system_region(CPUARMState *env, uint32_t address)
|
bool m_is_system_region(CPUARMState *env, uint32_t address)
|
||||||
{
|
{
|
||||||
/* True if address is in the M profile system region
|
/* True if address is in the M profile system region
|
||||||
* 0xe0000000 - 0xffffffff
|
* 0xe0000000 - 0xffffffff
|
||||||
@ -11713,193 +11712,6 @@ static inline bool m_is_system_region(CPUARMState *env, uint32_t address)
|
|||||||
return arm_feature(env, ARM_FEATURE_M) && extract32(address, 29, 3) == 0x7;
|
return arm_feature(env, ARM_FEATURE_M) && extract32(address, 29, 3) == 0x7;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
|
||||||
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
|
||||||
hwaddr *phys_ptr, int *prot,
|
|
||||||
target_ulong *page_size,
|
|
||||||
ARMMMUFaultInfo *fi)
|
|
||||||
{
|
|
||||||
ARMCPU *cpu = env_archcpu(env);
|
|
||||||
int n;
|
|
||||||
bool is_user = regime_is_user(env, mmu_idx);
|
|
||||||
|
|
||||||
*phys_ptr = address;
|
|
||||||
*page_size = TARGET_PAGE_SIZE;
|
|
||||||
*prot = 0;
|
|
||||||
|
|
||||||
if (regime_translation_disabled(env, mmu_idx) ||
|
|
||||||
m_is_ppb_region(env, address)) {
|
|
||||||
/* MPU disabled or M profile PPB access: use default memory map.
|
|
||||||
* The other case which uses the default memory map in the
|
|
||||||
* v7M ARM ARM pseudocode is exception vector reads from the vector
|
|
||||||
* table. In QEMU those accesses are done in arm_v7m_load_vector(),
|
|
||||||
* which always does a direct read using address_space_ldl(), rather
|
|
||||||
* than going via this function, so we don't need to check that here.
|
|
||||||
*/
|
|
||||||
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[%d]: Rsize field cannot be 0\n", n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
rsize++;
|
|
||||||
rmask = (1ull << rsize) - 1;
|
|
||||||
|
|
||||||
if (base & rmask) {
|
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
|
||||||
"DRBAR[%d]: 0x%" PRIx32 " misaligned "
|
|
||||||
"to DRSR region size, mask = 0x%" PRIx32 "\n",
|
|
||||||
n, base, rmask);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address < base || address > base + rmask) {
|
|
||||||
/*
|
|
||||||
* Address not in this region. We must check whether the
|
|
||||||
* region covers addresses in the same page as our address.
|
|
||||||
* In that case we must not report a size that covers the
|
|
||||||
* whole page for a subsequent hit against a different MPU
|
|
||||||
* region or the background region, because it would result in
|
|
||||||
* incorrect TLB hits for subsequent accesses to addresses that
|
|
||||||
* are in this MPU region.
|
|
||||||
*/
|
|
||||||
if (ranges_overlap(base, rmask,
|
|
||||||
address & TARGET_PAGE_MASK,
|
|
||||||
TARGET_PAGE_SIZE)) {
|
|
||||||
*page_size = 1;
|
|
||||||
}
|
|
||||||
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 (srdis) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (rsize < TARGET_PAGE_BITS) {
|
|
||||||
*page_size = 1 << rsize;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == -1) { /* no hits */
|
|
||||||
if (!pmsav7_use_background_region(cpu, mmu_idx, is_user)) {
|
|
||||||
/* background fault */
|
|
||||||
fi->type = ARMFault_Background;
|
|
||||||
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);
|
|
||||||
uint32_t xn = extract32(env->pmsav7.dracr[n], 12, 1);
|
|
||||||
|
|
||||||
if (m_is_system_region(env, address)) {
|
|
||||||
/* System space is always execute never */
|
|
||||||
xn = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
case 7:
|
|
||||||
/* for v7M, same as 6; for R profile a reserved value */
|
|
||||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
|
||||||
*prot |= PAGE_READ | PAGE_EXEC;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* fall through */
|
|
||||||
default:
|
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
|
||||||
"DRACR[%d]: Bad value for AP bits: 0x%"
|
|
||||||
PRIx32 "\n", 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;
|
|
||||||
case 7:
|
|
||||||
/* for v7M, same as 6; for R profile a reserved value */
|
|
||||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
|
||||||
*prot |= PAGE_READ | PAGE_EXEC;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* fall through */
|
|
||||||
default:
|
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
|
||||||
"DRACR[%d]: Bad value for AP bits: 0x%"
|
|
||||||
PRIx32 "\n", n, ap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* execute never */
|
|
||||||
if (xn) {
|
|
||||||
*prot &= ~PAGE_EXEC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fi->type = ARMFault_Permission;
|
|
||||||
fi->level = 1;
|
|
||||||
return !(*prot & (1 << access_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool v8m_is_sau_exempt(CPUARMState *env,
|
static bool v8m_is_sau_exempt(CPUARMState *env,
|
||||||
uint32_t address, MMUAccessType access_type)
|
uint32_t address, MMUAccessType access_type)
|
||||||
{
|
{
|
||||||
|
190
target/arm/ptw.c
190
target/arm/ptw.c
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/range.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "internals.h"
|
#include "internals.h"
|
||||||
#include "ptw.h"
|
#include "ptw.h"
|
||||||
@ -415,6 +416,195 @@ void get_phys_addr_pmsav7_default(CPUARMState *env,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
||||||
|
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
||||||
|
hwaddr *phys_ptr, int *prot,
|
||||||
|
target_ulong *page_size,
|
||||||
|
ARMMMUFaultInfo *fi)
|
||||||
|
{
|
||||||
|
ARMCPU *cpu = env_archcpu(env);
|
||||||
|
int n;
|
||||||
|
bool is_user = regime_is_user(env, mmu_idx);
|
||||||
|
|
||||||
|
*phys_ptr = address;
|
||||||
|
*page_size = TARGET_PAGE_SIZE;
|
||||||
|
*prot = 0;
|
||||||
|
|
||||||
|
if (regime_translation_disabled(env, mmu_idx) ||
|
||||||
|
m_is_ppb_region(env, address)) {
|
||||||
|
/*
|
||||||
|
* MPU disabled or M profile PPB access: use default memory map.
|
||||||
|
* The other case which uses the default memory map in the
|
||||||
|
* v7M ARM ARM pseudocode is exception vector reads from the vector
|
||||||
|
* table. In QEMU those accesses are done in arm_v7m_load_vector(),
|
||||||
|
* which always does a direct read using address_space_ldl(), rather
|
||||||
|
* than going via this function, so we don't need to check that here.
|
||||||
|
*/
|
||||||
|
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[%d]: Rsize field cannot be 0\n", n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rsize++;
|
||||||
|
rmask = (1ull << rsize) - 1;
|
||||||
|
|
||||||
|
if (base & rmask) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"DRBAR[%d]: 0x%" PRIx32 " misaligned "
|
||||||
|
"to DRSR region size, mask = 0x%" PRIx32 "\n",
|
||||||
|
n, base, rmask);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address < base || address > base + rmask) {
|
||||||
|
/*
|
||||||
|
* Address not in this region. We must check whether the
|
||||||
|
* region covers addresses in the same page as our address.
|
||||||
|
* In that case we must not report a size that covers the
|
||||||
|
* whole page for a subsequent hit against a different MPU
|
||||||
|
* region or the background region, because it would result in
|
||||||
|
* incorrect TLB hits for subsequent accesses to addresses that
|
||||||
|
* are in this MPU region.
|
||||||
|
*/
|
||||||
|
if (ranges_overlap(base, rmask,
|
||||||
|
address & TARGET_PAGE_MASK,
|
||||||
|
TARGET_PAGE_SIZE)) {
|
||||||
|
*page_size = 1;
|
||||||
|
}
|
||||||
|
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 (srdis) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (rsize < TARGET_PAGE_BITS) {
|
||||||
|
*page_size = 1 << rsize;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == -1) { /* no hits */
|
||||||
|
if (!pmsav7_use_background_region(cpu, mmu_idx, is_user)) {
|
||||||
|
/* background fault */
|
||||||
|
fi->type = ARMFault_Background;
|
||||||
|
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);
|
||||||
|
uint32_t xn = extract32(env->pmsav7.dracr[n], 12, 1);
|
||||||
|
|
||||||
|
if (m_is_system_region(env, address)) {
|
||||||
|
/* System space is always execute never */
|
||||||
|
xn = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
case 7:
|
||||||
|
/* for v7M, same as 6; for R profile a reserved value */
|
||||||
|
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||||
|
*prot |= PAGE_READ | PAGE_EXEC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"DRACR[%d]: Bad value for AP bits: 0x%"
|
||||||
|
PRIx32 "\n", 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;
|
||||||
|
case 7:
|
||||||
|
/* for v7M, same as 6; for R profile a reserved value */
|
||||||
|
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||||
|
*prot |= PAGE_READ | PAGE_EXEC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"DRACR[%d]: Bad value for AP bits: 0x%"
|
||||||
|
PRIx32 "\n", n, ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* execute never */
|
||||||
|
if (xn) {
|
||||||
|
*prot &= ~PAGE_EXEC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fi->type = ARMFault_Permission;
|
||||||
|
fi->level = 1;
|
||||||
|
return !(*prot & (1 << access_type));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get_phys_addr - get the physical address for this virtual address
|
* get_phys_addr - get the physical address for this virtual address
|
||||||
*
|
*
|
||||||
|
@ -33,14 +33,14 @@ simple_ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, int ap)
|
|||||||
return simple_ap_to_rw_prot_is_user(ap, regime_is_user(env, mmu_idx));
|
return simple_ap_to_rw_prot_is_user(ap, regime_is_user(env, mmu_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool m_is_ppb_region(CPUARMState *env, uint32_t address);
|
||||||
|
bool m_is_system_region(CPUARMState *env, uint32_t address);
|
||||||
|
|
||||||
void get_phys_addr_pmsav7_default(CPUARMState *env,
|
void get_phys_addr_pmsav7_default(CPUARMState *env,
|
||||||
ARMMMUIdx mmu_idx,
|
ARMMMUIdx mmu_idx,
|
||||||
int32_t address, int *prot);
|
int32_t address, int *prot);
|
||||||
bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
bool pmsav7_use_background_region(ARMCPU *cpu, ARMMMUIdx mmu_idx, bool is_user);
|
||||||
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
|
||||||
hwaddr *phys_ptr, int *prot,
|
|
||||||
target_ulong *page_size,
|
|
||||||
ARMMMUFaultInfo *fi);
|
|
||||||
bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address,
|
bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address,
|
||||||
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
||||||
hwaddr *phys_ptr, MemTxAttrs *txattrs,
|
hwaddr *phys_ptr, MemTxAttrs *txattrs,
|
||||||
|
Loading…
Reference in New Issue
Block a user