mirror of https://github.com/xemu-project/xemu.git
Merge branch 'target-arm.for-upstream' of git://git.linaro.org/people/pmaydell/qemu-arm
* 'target-arm.for-upstream' of git://git.linaro.org/people/pmaydell/qemu-arm: target-arm: Add support for long format translation table walks target-arm: Implement TTBCR changes for LPAE target-arm: Implement long-descriptor PAR format target-arm: Use target_phys_addr_t in get_phys_addr() target-arm: Add 64 bit PAR, TTBR0, TTBR1 for LPAE target-arm: Add 64 bit variants of DBGDRAR and DBGDSAR for LPAE target-arm: Add AMAIR0, AMAIR1 LPAE cp15 registers target-arm: Extend feature flags to 64 bits target-arm: Implement privileged-execute-never (PXN) ARM: Make target_phys_addr_t 64 bits and physaddrs 40 bits hw/imx_avic.c: Avoid format error when target_phys_addr_t is 64 bits target-arm: Fix TCG temp handling in 64 bit cp writes target-arm: Fix some copy-and-paste errors in cp register names target-arm: Fix typo that meant TTBR1 accesses went to TTBR0 target-arm: Fix CP15 based WFI
This commit is contained in:
commit
6e2fe79da8
|
@ -3571,7 +3571,7 @@ case "$target_arch2" in
|
|||
bflt="yes"
|
||||
target_nptl="yes"
|
||||
gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
|
||||
target_phys_bits=32
|
||||
target_phys_bits=64
|
||||
target_llong_alignment=4
|
||||
target_libs_softmmu="$fdt_libs"
|
||||
;;
|
||||
|
|
|
@ -267,7 +267,7 @@ static void imx_avic_write(void *opaque, target_phys_addr_t offset,
|
|||
/* Vector Registers not yet supported */
|
||||
if (offset >= 0x100 && offset <= 0x2fc) {
|
||||
IPRINTF("imx_avic_write to vector register %d ignored\n",
|
||||
(offset - 0x100) >> 2);
|
||||
(unsigned int)((offset - 0x100) >> 2));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ static void arm_cpu_reset(CPUState *s)
|
|||
|
||||
static inline void set_feature(CPUARMState *env, int feature)
|
||||
{
|
||||
env->features |= 1u << feature;
|
||||
env->features |= 1ULL << feature;
|
||||
}
|
||||
|
||||
static void arm_cpu_initfn(Object *obj)
|
||||
|
@ -192,6 +192,9 @@ void arm_cpu_realize(ARMCPU *cpu)
|
|||
if (arm_feature(env, ARM_FEATURE_VFP3)) {
|
||||
set_feature(env, ARM_FEATURE_VFP);
|
||||
}
|
||||
if (arm_feature(env, ARM_FEATURE_LPAE)) {
|
||||
set_feature(env, ARM_FEATURE_PXN);
|
||||
}
|
||||
|
||||
register_cp_regs_for_features(cpu);
|
||||
}
|
||||
|
@ -532,6 +535,7 @@ static void cortex_a15_initfn(Object *obj)
|
|||
set_feature(&cpu->env, ARM_FEATURE_V7MP);
|
||||
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
|
||||
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
|
||||
set_feature(&cpu->env, ARM_FEATURE_LPAE);
|
||||
cpu->midr = 0x412fc0f1;
|
||||
cpu->reset_fpsid = 0x410430f0;
|
||||
cpu->mvfr0 = 0x10110222;
|
||||
|
|
|
@ -113,7 +113,9 @@ typedef struct CPUARMState {
|
|||
uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */
|
||||
uint32_t c1_scr; /* secure config register. */
|
||||
uint32_t c2_base0; /* MMU translation table base 0. */
|
||||
uint32_t c2_base1; /* MMU translation table base 1. */
|
||||
uint32_t c2_base0_hi; /* MMU translation table base 0, high 32 bits */
|
||||
uint32_t c2_base1; /* MMU translation table base 0. */
|
||||
uint32_t c2_base1_hi; /* MMU translation table base 1, high 32 bits */
|
||||
uint32_t c2_control; /* MMU translation table base control. */
|
||||
uint32_t c2_mask; /* MMU translation table base selection mask. */
|
||||
uint32_t c2_base_mask; /* MMU translation table base 0 mask. */
|
||||
|
@ -127,6 +129,7 @@ typedef struct CPUARMState {
|
|||
uint32_t c6_insn; /* Fault address registers. */
|
||||
uint32_t c6_data;
|
||||
uint32_t c7_par; /* Translation result. */
|
||||
uint32_t c7_par_hi; /* Translation result, high 32 bits */
|
||||
uint32_t c9_insn; /* Cache lockdown registers. */
|
||||
uint32_t c9_data;
|
||||
uint32_t c9_pmcr; /* performance monitor control register */
|
||||
|
@ -221,7 +224,7 @@ typedef struct CPUARMState {
|
|||
/* These fields after the common ones so they are preserved on reset. */
|
||||
|
||||
/* Internal CPU feature flags. */
|
||||
uint32_t features;
|
||||
uint64_t features;
|
||||
|
||||
void *nvic;
|
||||
const struct arm_boot_info *boot_info;
|
||||
|
@ -386,11 +389,13 @@ enum arm_features {
|
|||
ARM_FEATURE_CACHE_DIRTY_REG, /* 1136/1176 cache dirty status register */
|
||||
ARM_FEATURE_CACHE_BLOCK_OPS, /* v6 optional cache block operations */
|
||||
ARM_FEATURE_MPIDR, /* has cp15 MPIDR */
|
||||
ARM_FEATURE_PXN, /* has Privileged Execute Never bit */
|
||||
ARM_FEATURE_LPAE, /* has Large Physical Address Extension */
|
||||
};
|
||||
|
||||
static inline int arm_feature(CPUARMState *env, int feature)
|
||||
{
|
||||
return (env->features & (1u << feature)) != 0;
|
||||
return (env->features & (1ULL << feature)) != 0;
|
||||
}
|
||||
|
||||
void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf);
|
||||
|
@ -619,7 +624,7 @@ static inline bool cp_access_ok(CPUARMState *env,
|
|||
#define TARGET_PAGE_BITS 10
|
||||
#endif
|
||||
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 32
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 40
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 32
|
||||
|
||||
static inline CPUARMState *cpu_init(const char *cpu_model)
|
||||
|
@ -636,7 +641,7 @@ static inline CPUARMState *cpu_init(const char *cpu_model)
|
|||
#define cpu_signal_handler cpu_arm_signal_handler
|
||||
#define cpu_list arm_cpu_list
|
||||
|
||||
#define CPU_SAVE_VERSION 7
|
||||
#define CPU_SAVE_VERSION 9
|
||||
|
||||
/* MMU modes definitions */
|
||||
#define MMU_MODE0_SUFFIX _kernel
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
#include "helper.h"
|
||||
#include "host-utils.h"
|
||||
#include "sysemu.h"
|
||||
#include "bitops.h"
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static inline int get_phys_addr(CPUARMState *env, uint32_t address,
|
||||
int access_type, int is_user,
|
||||
uint32_t *phys_ptr, int *prot,
|
||||
target_phys_addr_t *phys_ptr, int *prot,
|
||||
target_ulong *page_size);
|
||||
#endif
|
||||
|
||||
|
@ -216,9 +217,9 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
|
|||
.access = PL1_W, .type = ARM_CP_NOP },
|
||||
{ .name = "ISB", .cp = 15, .crn = 7, .crm = 5, .opc1 = 0, .opc2 = 4,
|
||||
.access = PL0_W, .type = ARM_CP_NOP },
|
||||
{ .name = "ISB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 4,
|
||||
{ .name = "DSB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 4,
|
||||
.access = PL0_W, .type = ARM_CP_NOP },
|
||||
{ .name = "ISB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 5,
|
||||
{ .name = "DMB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 5,
|
||||
.access = PL0_W, .type = ARM_CP_NOP },
|
||||
{ .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 2,
|
||||
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_insn),
|
||||
|
@ -346,7 +347,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
|
|||
*/
|
||||
{ .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
{ .name = "DBGDRAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||
{ .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
/* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */
|
||||
{ .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4,
|
||||
|
@ -491,7 +492,9 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
|||
|
||||
static int par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||
{
|
||||
if (arm_feature(env, ARM_FEATURE_V7)) {
|
||||
if (arm_feature(env, ARM_FEATURE_LPAE)) {
|
||||
env->cp15.c7_par = value;
|
||||
} else if (arm_feature(env, ARM_FEATURE_V7)) {
|
||||
env->cp15.c7_par = value & 0xfffff6ff;
|
||||
} else {
|
||||
env->cp15.c7_par = value & 0xfffff1ff;
|
||||
|
@ -501,9 +504,20 @@ static int par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
|||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* get_phys_addr() isn't present for user-mode-only targets */
|
||||
|
||||
/* Return true if extended addresses are enabled, ie this is an
|
||||
* LPAE implementation and we are using the long-descriptor translation
|
||||
* table format because the TTBCR EAE bit is set.
|
||||
*/
|
||||
static inline bool extended_addresses_enabled(CPUARMState *env)
|
||||
{
|
||||
return arm_feature(env, ARM_FEATURE_LPAE)
|
||||
&& (env->cp15.c2_control & (1 << 31));
|
||||
}
|
||||
|
||||
static int ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||
{
|
||||
uint32_t phys_addr;
|
||||
target_phys_addr_t phys_addr;
|
||||
target_ulong page_size;
|
||||
int prot;
|
||||
int ret, is_user = ri->opc2 & 2;
|
||||
|
@ -515,18 +529,44 @@ static int ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
|||
}
|
||||
ret = get_phys_addr(env, value, access_type, is_user,
|
||||
&phys_addr, &prot, &page_size);
|
||||
if (ret == 0) {
|
||||
/* We do not set any attribute bits in the PAR */
|
||||
if (page_size == (1 << 24)
|
||||
&& arm_feature(env, ARM_FEATURE_V7)) {
|
||||
env->cp15.c7_par = (phys_addr & 0xff000000) | 1 << 1;
|
||||
if (extended_addresses_enabled(env)) {
|
||||
/* ret is a DFSR/IFSR value for the long descriptor
|
||||
* translation table format, but with WnR always clear.
|
||||
* Convert it to a 64-bit PAR.
|
||||
*/
|
||||
uint64_t par64 = (1 << 11); /* LPAE bit always set */
|
||||
if (ret == 0) {
|
||||
par64 |= phys_addr & ~0xfffULL;
|
||||
/* We don't set the ATTR or SH fields in the PAR. */
|
||||
} else {
|
||||
env->cp15.c7_par = phys_addr & 0xfffff000;
|
||||
par64 |= 1; /* F */
|
||||
par64 |= (ret & 0x3f) << 1; /* FS */
|
||||
/* Note that S2WLK and FSTAGE are always zero, because we don't
|
||||
* implement virtualization and therefore there can't be a stage 2
|
||||
* fault.
|
||||
*/
|
||||
}
|
||||
env->cp15.c7_par = par64;
|
||||
env->cp15.c7_par_hi = par64 >> 32;
|
||||
} else {
|
||||
env->cp15.c7_par = ((ret & (10 << 1)) >> 5) |
|
||||
((ret & (12 << 1)) >> 6) |
|
||||
((ret & 0xf) << 1) | 1;
|
||||
/* ret is a DFSR/IFSR value for the short descriptor
|
||||
* translation table format (with WnR always clear).
|
||||
* Convert it to a 32-bit PAR.
|
||||
*/
|
||||
if (ret == 0) {
|
||||
/* We do not set any attribute bits in the PAR */
|
||||
if (page_size == (1 << 24)
|
||||
&& arm_feature(env, ARM_FEATURE_V7)) {
|
||||
env->cp15.c7_par = (phys_addr & 0xff000000) | 1 << 1;
|
||||
} else {
|
||||
env->cp15.c7_par = phys_addr & 0xfffff000;
|
||||
}
|
||||
} else {
|
||||
env->cp15.c7_par = ((ret & (10 << 1)) >> 5) |
|
||||
((ret & (12 << 1)) >> 6) |
|
||||
((ret & 0xf) << 1) | 1;
|
||||
}
|
||||
env->cp15.c7_par_hi = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -653,7 +693,20 @@ static const ARMCPRegInfo pmsav5_cp_reginfo[] = {
|
|||
static int vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
value &= 7;
|
||||
if (arm_feature(env, ARM_FEATURE_LPAE)) {
|
||||
value &= ~((7 << 19) | (3 << 14) | (0xf << 3));
|
||||
/* With LPAE the TTBCR could result in a change of ASID
|
||||
* via the TTBCR.A1 bit, so do a TLB flush.
|
||||
*/
|
||||
tlb_flush(env, 1);
|
||||
} else {
|
||||
value &= 7;
|
||||
}
|
||||
/* Note that we always calculate c2_mask and c2_base_mask, but
|
||||
* they are only used for short-descriptor tables (ie if EAE is 0);
|
||||
* for long-descriptor tables the TTBCR fields are used differently
|
||||
* and the c2_mask and c2_base_mask values are meaningless.
|
||||
*/
|
||||
env->cp15.c2_control = value;
|
||||
env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> value);
|
||||
env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> value);
|
||||
|
@ -679,7 +732,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
|
|||
.fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, },
|
||||
{ .name = "TTBR1", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1,
|
||||
.access = PL1_RW,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, },
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c2_base1), .resetvalue = 0, },
|
||||
{ .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
|
||||
.access = PL1_RW, .writefn = vmsa_ttbcr_write,
|
||||
.resetfn = vmsa_ttbcr_reset,
|
||||
|
@ -871,6 +924,96 @@ static const ARMCPRegInfo mpidr_cp_reginfo[] = {
|
|||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
static int par64_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value)
|
||||
{
|
||||
*value = ((uint64_t)env->cp15.c7_par_hi << 32) | env->cp15.c7_par;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int par64_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||
{
|
||||
env->cp15.c7_par_hi = value >> 32;
|
||||
env->cp15.c7_par = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void par64_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
env->cp15.c7_par_hi = 0;
|
||||
env->cp15.c7_par = 0;
|
||||
}
|
||||
|
||||
static int ttbr064_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t *value)
|
||||
{
|
||||
*value = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ttbr064_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
env->cp15.c2_base0_hi = value >> 32;
|
||||
env->cp15.c2_base0 = value;
|
||||
/* Writes to the 64 bit format TTBRs may change the ASID */
|
||||
tlb_flush(env, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ttbr064_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
env->cp15.c2_base0_hi = 0;
|
||||
env->cp15.c2_base0 = 0;
|
||||
}
|
||||
|
||||
static int ttbr164_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t *value)
|
||||
{
|
||||
*value = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ttbr164_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
env->cp15.c2_base1_hi = value >> 32;
|
||||
env->cp15.c2_base1 = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ttbr164_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
env->cp15.c2_base1_hi = 0;
|
||||
env->cp15.c2_base1 = 0;
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo lpae_cp_reginfo[] = {
|
||||
/* NOP AMAIR0/1: the override is because these clash with tha rather
|
||||
* broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo.
|
||||
*/
|
||||
{ .name = "AMAIR0", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
|
||||
.resetvalue = 0 },
|
||||
{ .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
|
||||
.resetvalue = 0 },
|
||||
/* 64 bit access versions of the (dummy) debug registers */
|
||||
{ .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0,
|
||||
.access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 },
|
||||
{ .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0,
|
||||
.access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 },
|
||||
{ .name = "PAR", .cp = 15, .crm = 7, .opc1 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_64BIT,
|
||||
.readfn = par64_read, .writefn = par64_write, .resetfn = par64_reset },
|
||||
{ .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr064_read,
|
||||
.writefn = ttbr064_write, .resetfn = ttbr064_reset },
|
||||
{ .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1,
|
||||
.access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr164_read,
|
||||
.writefn = ttbr164_write, .resetfn = ttbr164_reset },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
static int sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||
{
|
||||
env->cp15.c1_sys = value;
|
||||
|
@ -1016,6 +1159,9 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
|||
if (arm_feature(env, ARM_FEATURE_MPIDR)) {
|
||||
define_arm_cp_regs(cpu, mpidr_cp_reginfo);
|
||||
}
|
||||
if (arm_feature(env, ARM_FEATURE_LPAE)) {
|
||||
define_arm_cp_regs(cpu, lpae_cp_reginfo);
|
||||
}
|
||||
/* Slightly awkwardly, the OMAP and StrongARM cores need all of
|
||||
* cp15 crn=0 to be writes-ignored, whereas for other cores they should
|
||||
* be read-only (ie write causes UNDEF exception).
|
||||
|
@ -1833,8 +1979,8 @@ static uint32_t get_level1_table_address(CPUARMState *env, uint32_t address)
|
|||
}
|
||||
|
||||
static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
|
||||
int is_user, uint32_t *phys_ptr, int *prot,
|
||||
target_ulong *page_size)
|
||||
int is_user, target_phys_addr_t *phys_ptr,
|
||||
int *prot, target_ulong *page_size)
|
||||
{
|
||||
int code;
|
||||
uint32_t table;
|
||||
|
@ -1843,7 +1989,7 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
|
|||
int ap;
|
||||
int domain;
|
||||
int domain_prot;
|
||||
uint32_t phys_addr;
|
||||
target_phys_addr_t phys_addr;
|
||||
|
||||
/* Pagetable walk. */
|
||||
/* Lookup l1 descriptor. */
|
||||
|
@ -1928,45 +2074,46 @@ do_fault:
|
|||
}
|
||||
|
||||
static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
|
||||
int is_user, uint32_t *phys_ptr, int *prot,
|
||||
target_ulong *page_size)
|
||||
int is_user, target_phys_addr_t *phys_ptr,
|
||||
int *prot, target_ulong *page_size)
|
||||
{
|
||||
int code;
|
||||
uint32_t table;
|
||||
uint32_t desc;
|
||||
uint32_t xn;
|
||||
uint32_t pxn = 0;
|
||||
int type;
|
||||
int ap;
|
||||
int domain;
|
||||
int domain = 0;
|
||||
int domain_prot;
|
||||
uint32_t phys_addr;
|
||||
target_phys_addr_t phys_addr;
|
||||
|
||||
/* Pagetable walk. */
|
||||
/* Lookup l1 descriptor. */
|
||||
table = get_level1_table_address(env, address);
|
||||
desc = ldl_phys(table);
|
||||
type = (desc & 3);
|
||||
if (type == 0) {
|
||||
/* Section translation fault. */
|
||||
if (type == 0 || (type == 3 && !arm_feature(env, ARM_FEATURE_PXN))) {
|
||||
/* Section translation fault, or attempt to use the encoding
|
||||
* which is Reserved on implementations without PXN.
|
||||
*/
|
||||
code = 5;
|
||||
domain = 0;
|
||||
goto do_fault;
|
||||
} else if (type == 2 && (desc & (1 << 18))) {
|
||||
/* Supersection. */
|
||||
domain = 0;
|
||||
} else {
|
||||
/* Section or page. */
|
||||
}
|
||||
if ((type == 1) || !(desc & (1 << 18))) {
|
||||
/* Page or Section. */
|
||||
domain = (desc >> 5) & 0x0f;
|
||||
}
|
||||
domain_prot = (env->cp15.c3 >> (domain * 2)) & 3;
|
||||
if (domain_prot == 0 || domain_prot == 2) {
|
||||
if (type == 2)
|
||||
if (type != 1) {
|
||||
code = 9; /* Section domain fault. */
|
||||
else
|
||||
} else {
|
||||
code = 11; /* Page domain fault. */
|
||||
}
|
||||
goto do_fault;
|
||||
}
|
||||
if (type == 2) {
|
||||
if (type != 1) {
|
||||
if (desc & (1 << 18)) {
|
||||
/* Supersection. */
|
||||
phys_addr = (desc & 0xff000000) | (address & 0x00ffffff);
|
||||
|
@ -1978,8 +2125,12 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
|
|||
}
|
||||
ap = ((desc >> 10) & 3) | ((desc >> 13) & 4);
|
||||
xn = desc & (1 << 4);
|
||||
pxn = desc & 1;
|
||||
code = 13;
|
||||
} else {
|
||||
if (arm_feature(env, ARM_FEATURE_PXN)) {
|
||||
pxn = (desc >> 2) & 1;
|
||||
}
|
||||
/* Lookup l2 entry. */
|
||||
table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
|
||||
desc = ldl_phys(table);
|
||||
|
@ -2007,6 +2158,9 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
|
|||
if (domain_prot == 3) {
|
||||
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
} else {
|
||||
if (pxn && !is_user) {
|
||||
xn = 1;
|
||||
}
|
||||
if (xn && access_type == 2)
|
||||
goto do_fault;
|
||||
|
||||
|
@ -2031,8 +2185,187 @@ do_fault:
|
|||
return code | (domain << 4);
|
||||
}
|
||||
|
||||
static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, int access_type,
|
||||
int is_user, uint32_t *phys_ptr, int *prot)
|
||||
/* Fault type for long-descriptor MMU fault reporting; this corresponds
|
||||
* to bits [5..2] in the STATUS field in long-format DFSR/IFSR.
|
||||
*/
|
||||
typedef enum {
|
||||
translation_fault = 1,
|
||||
access_fault = 2,
|
||||
permission_fault = 3,
|
||||
} MMUFaultType;
|
||||
|
||||
static int get_phys_addr_lpae(CPUARMState *env, uint32_t address,
|
||||
int access_type, int is_user,
|
||||
target_phys_addr_t *phys_ptr, int *prot,
|
||||
target_ulong *page_size_ptr)
|
||||
{
|
||||
/* Read an LPAE long-descriptor translation table. */
|
||||
MMUFaultType fault_type = translation_fault;
|
||||
uint32_t level = 1;
|
||||
uint32_t epd;
|
||||
uint32_t tsz;
|
||||
uint64_t ttbr;
|
||||
int ttbr_select;
|
||||
int n;
|
||||
target_phys_addr_t descaddr;
|
||||
uint32_t tableattrs;
|
||||
target_ulong page_size;
|
||||
uint32_t attrs;
|
||||
|
||||
/* Determine whether this address is in the region controlled by
|
||||
* TTBR0 or TTBR1 (or if it is in neither region and should fault).
|
||||
* This is a Non-secure PL0/1 stage 1 translation, so controlled by
|
||||
* TTBCR/TTBR0/TTBR1 in accordance with ARM ARM DDI0406C table B-32:
|
||||
*/
|
||||
uint32_t t0sz = extract32(env->cp15.c2_control, 0, 3);
|
||||
uint32_t t1sz = extract32(env->cp15.c2_control, 16, 3);
|
||||
if (t0sz && !extract32(address, 32 - t0sz, t0sz)) {
|
||||
/* there is a ttbr0 region and we are in it (high bits all zero) */
|
||||
ttbr_select = 0;
|
||||
} else if (t1sz && !extract32(~address, 32 - t1sz, t1sz)) {
|
||||
/* there is a ttbr1 region and we are in it (high bits all one) */
|
||||
ttbr_select = 1;
|
||||
} else if (!t0sz) {
|
||||
/* ttbr0 region is "everything not in the ttbr1 region" */
|
||||
ttbr_select = 0;
|
||||
} else if (!t1sz) {
|
||||
/* ttbr1 region is "everything not in the ttbr0 region" */
|
||||
ttbr_select = 1;
|
||||
} else {
|
||||
/* in the gap between the two regions, this is a Translation fault */
|
||||
fault_type = translation_fault;
|
||||
goto do_fault;
|
||||
}
|
||||
|
||||
/* Note that QEMU ignores shareability and cacheability attributes,
|
||||
* so we don't need to do anything with the SH, ORGN, IRGN fields
|
||||
* in the TTBCR. Similarly, TTBCR:A1 selects whether we get the
|
||||
* ASID from TTBR0 or TTBR1, but QEMU's TLB doesn't currently
|
||||
* implement any ASID-like capability so we can ignore it (instead
|
||||
* we will always flush the TLB any time the ASID is changed).
|
||||
*/
|
||||
if (ttbr_select == 0) {
|
||||
ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
|
||||
epd = extract32(env->cp15.c2_control, 7, 1);
|
||||
tsz = t0sz;
|
||||
} else {
|
||||
ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
|
||||
epd = extract32(env->cp15.c2_control, 23, 1);
|
||||
tsz = t1sz;
|
||||
}
|
||||
|
||||
if (epd) {
|
||||
/* Translation table walk disabled => Translation fault on TLB miss */
|
||||
goto do_fault;
|
||||
}
|
||||
|
||||
/* If the region is small enough we will skip straight to a 2nd level
|
||||
* lookup. This affects the number of bits of the address used in
|
||||
* combination with the TTBR to find the first descriptor. ('n' here
|
||||
* matches the usage in the ARM ARM sB3.6.6, where bits [39..n] are
|
||||
* from the TTBR, [n-1..3] from the vaddr, and [2..0] always zero).
|
||||
*/
|
||||
if (tsz > 1) {
|
||||
level = 2;
|
||||
n = 14 - tsz;
|
||||
} else {
|
||||
n = 5 - tsz;
|
||||
}
|
||||
|
||||
/* Clear the vaddr bits which aren't part of the within-region address,
|
||||
* so that we don't have to special case things when calculating the
|
||||
* first descriptor address.
|
||||
*/
|
||||
address &= (0xffffffffU >> tsz);
|
||||
|
||||
/* Now we can extract the actual base address from the TTBR */
|
||||
descaddr = extract64(ttbr, 0, 40);
|
||||
descaddr &= ~((1ULL << n) - 1);
|
||||
|
||||
tableattrs = 0;
|
||||
for (;;) {
|
||||
uint64_t descriptor;
|
||||
|
||||
descaddr |= ((address >> (9 * (4 - level))) & 0xff8);
|
||||
descriptor = ldq_phys(descaddr);
|
||||
if (!(descriptor & 1) ||
|
||||
(!(descriptor & 2) && (level == 3))) {
|
||||
/* Invalid, or the Reserved level 3 encoding */
|
||||
goto do_fault;
|
||||
}
|
||||
descaddr = descriptor & 0xfffffff000ULL;
|
||||
|
||||
if ((descriptor & 2) && (level < 3)) {
|
||||
/* Table entry. The top five bits are attributes which may
|
||||
* propagate down through lower levels of the table (and
|
||||
* which are all arranged so that 0 means "no effect", so
|
||||
* we can gather them up by ORing in the bits at each level).
|
||||
*/
|
||||
tableattrs |= extract64(descriptor, 59, 5);
|
||||
level++;
|
||||
continue;
|
||||
}
|
||||
/* Block entry at level 1 or 2, or page entry at level 3.
|
||||
* These are basically the same thing, although the number
|
||||
* of bits we pull in from the vaddr varies.
|
||||
*/
|
||||
page_size = (1 << (39 - (9 * level)));
|
||||
descaddr |= (address & (page_size - 1));
|
||||
/* Extract attributes from the descriptor and merge with table attrs */
|
||||
attrs = extract64(descriptor, 2, 10)
|
||||
| (extract64(descriptor, 52, 12) << 10);
|
||||
attrs |= extract32(tableattrs, 0, 2) << 11; /* XN, PXN */
|
||||
attrs |= extract32(tableattrs, 3, 1) << 5; /* APTable[1] => AP[2] */
|
||||
/* The sense of AP[1] vs APTable[0] is reversed, as APTable[0] == 1
|
||||
* means "force PL1 access only", which means forcing AP[1] to 0.
|
||||
*/
|
||||
if (extract32(tableattrs, 2, 1)) {
|
||||
attrs &= ~(1 << 4);
|
||||
}
|
||||
/* Since we're always in the Non-secure state, NSTable is ignored. */
|
||||
break;
|
||||
}
|
||||
/* Here descaddr is the final physical address, and attributes
|
||||
* are all in attrs.
|
||||
*/
|
||||
fault_type = access_fault;
|
||||
if ((attrs & (1 << 8)) == 0) {
|
||||
/* Access flag */
|
||||
goto do_fault;
|
||||
}
|
||||
fault_type = permission_fault;
|
||||
if (is_user && !(attrs & (1 << 4))) {
|
||||
/* Unprivileged access not enabled */
|
||||
goto do_fault;
|
||||
}
|
||||
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
if (attrs & (1 << 12) || (!is_user && (attrs & (1 << 11)))) {
|
||||
/* XN or PXN */
|
||||
if (access_type == 2) {
|
||||
goto do_fault;
|
||||
}
|
||||
*prot &= ~PAGE_EXEC;
|
||||
}
|
||||
if (attrs & (1 << 5)) {
|
||||
/* Write access forbidden */
|
||||
if (access_type == 1) {
|
||||
goto do_fault;
|
||||
}
|
||||
*prot &= ~PAGE_WRITE;
|
||||
}
|
||||
|
||||
*phys_ptr = descaddr;
|
||||
*page_size_ptr = page_size;
|
||||
return 0;
|
||||
|
||||
do_fault:
|
||||
/* Long-descriptor format IFSR/DFSR value */
|
||||
return (1 << 9) | (fault_type << 2) | level;
|
||||
}
|
||||
|
||||
static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
|
||||
int access_type, int is_user,
|
||||
target_phys_addr_t *phys_ptr, int *prot)
|
||||
{
|
||||
int n;
|
||||
uint32_t mask;
|
||||
|
@ -2091,9 +2424,32 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, int access_type
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* get_phys_addr - get the physical address for this virtual address
|
||||
*
|
||||
* Find the physical address corresponding to the given virtual address,
|
||||
* by doing a translation table walk on MMU based systems or using the
|
||||
* MPU state on MPU based systems.
|
||||
*
|
||||
* Returns 0 if the translation was successful. Otherwise, phys_ptr,
|
||||
* prot and page_size are not filled in, and the return value provides
|
||||
* information on why the translation aborted, in the format of a
|
||||
* 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
|
||||
* value.
|
||||
*
|
||||
* @env: CPUARMState
|
||||
* @address: virtual address to get physical address for
|
||||
* @access_type: 0 for read, 1 for write, 2 for execute
|
||||
* @is_user: 0 for privileged access, 1 for user
|
||||
* @phys_ptr: set to the physical address corresponding to the virtual address
|
||||
* @prot: set to the permissions for the page containing phys_ptr
|
||||
* @page_size: set to the size of the page containing phys_ptr
|
||||
*/
|
||||
static inline int get_phys_addr(CPUARMState *env, uint32_t address,
|
||||
int access_type, int is_user,
|
||||
uint32_t *phys_ptr, int *prot,
|
||||
target_phys_addr_t *phys_ptr, int *prot,
|
||||
target_ulong *page_size)
|
||||
{
|
||||
/* Fast Context Switch Extension. */
|
||||
|
@ -2110,6 +2466,9 @@ static inline int get_phys_addr(CPUARMState *env, uint32_t address,
|
|||
*page_size = TARGET_PAGE_SIZE;
|
||||
return get_phys_addr_mpu(env, address, access_type, is_user, phys_ptr,
|
||||
prot);
|
||||
} else if (extended_addresses_enabled(env)) {
|
||||
return get_phys_addr_lpae(env, address, access_type, is_user, phys_ptr,
|
||||
prot, page_size);
|
||||
} else if (env->cp15.c1_sys & (1 << 23)) {
|
||||
return get_phys_addr_v6(env, address, access_type, is_user, phys_ptr,
|
||||
prot, page_size);
|
||||
|
@ -2122,7 +2481,7 @@ static inline int get_phys_addr(CPUARMState *env, uint32_t address,
|
|||
int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address,
|
||||
int access_type, int mmu_idx)
|
||||
{
|
||||
uint32_t phys_addr;
|
||||
target_phys_addr_t phys_addr;
|
||||
target_ulong page_size;
|
||||
int prot;
|
||||
int ret, is_user;
|
||||
|
@ -2132,7 +2491,7 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address,
|
|||
&page_size);
|
||||
if (ret == 0) {
|
||||
/* Map a single [sub]page. */
|
||||
phys_addr &= ~(uint32_t)0x3ff;
|
||||
phys_addr &= ~(target_phys_addr_t)0x3ff;
|
||||
address &= ~(uint32_t)0x3ff;
|
||||
tlb_set_page (env, address, phys_addr, prot, mmu_idx, page_size);
|
||||
return 0;
|
||||
|
@ -2154,7 +2513,7 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address,
|
|||
|
||||
target_phys_addr_t cpu_get_phys_page_debug(CPUARMState *env, target_ulong addr)
|
||||
{
|
||||
uint32_t phys_addr;
|
||||
target_phys_addr_t phys_addr;
|
||||
target_ulong page_size;
|
||||
int prot;
|
||||
int ret;
|
||||
|
|
|
@ -27,7 +27,9 @@ void cpu_save(QEMUFile *f, void *opaque)
|
|||
qemu_put_be32(f, env->cp15.c1_xscaleauxcr);
|
||||
qemu_put_be32(f, env->cp15.c1_scr);
|
||||
qemu_put_be32(f, env->cp15.c2_base0);
|
||||
qemu_put_be32(f, env->cp15.c2_base0_hi);
|
||||
qemu_put_be32(f, env->cp15.c2_base1);
|
||||
qemu_put_be32(f, env->cp15.c2_base1_hi);
|
||||
qemu_put_be32(f, env->cp15.c2_control);
|
||||
qemu_put_be32(f, env->cp15.c2_mask);
|
||||
qemu_put_be32(f, env->cp15.c2_base_mask);
|
||||
|
@ -42,6 +44,7 @@ void cpu_save(QEMUFile *f, void *opaque)
|
|||
qemu_put_be32(f, env->cp15.c6_insn);
|
||||
qemu_put_be32(f, env->cp15.c6_data);
|
||||
qemu_put_be32(f, env->cp15.c7_par);
|
||||
qemu_put_be32(f, env->cp15.c7_par_hi);
|
||||
qemu_put_be32(f, env->cp15.c9_insn);
|
||||
qemu_put_be32(f, env->cp15.c9_data);
|
||||
qemu_put_be32(f, env->cp15.c9_pmcr);
|
||||
|
@ -60,7 +63,7 @@ void cpu_save(QEMUFile *f, void *opaque)
|
|||
qemu_put_be32(f, env->cp15.c15_diagnostic);
|
||||
qemu_put_be32(f, env->cp15.c15_power_diagnostic);
|
||||
|
||||
qemu_put_be32(f, env->features);
|
||||
qemu_put_be64(f, env->features);
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_VFP)) {
|
||||
for (i = 0; i < 16; i++) {
|
||||
|
@ -144,7 +147,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
|||
env->cp15.c1_xscaleauxcr = qemu_get_be32(f);
|
||||
env->cp15.c1_scr = qemu_get_be32(f);
|
||||
env->cp15.c2_base0 = qemu_get_be32(f);
|
||||
env->cp15.c2_base0_hi = qemu_get_be32(f);
|
||||
env->cp15.c2_base1 = qemu_get_be32(f);
|
||||
env->cp15.c2_base1_hi = qemu_get_be32(f);
|
||||
env->cp15.c2_control = qemu_get_be32(f);
|
||||
env->cp15.c2_mask = qemu_get_be32(f);
|
||||
env->cp15.c2_base_mask = qemu_get_be32(f);
|
||||
|
@ -159,6 +164,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
|||
env->cp15.c6_insn = qemu_get_be32(f);
|
||||
env->cp15.c6_data = qemu_get_be32(f);
|
||||
env->cp15.c7_par = qemu_get_be32(f);
|
||||
env->cp15.c7_par_hi = qemu_get_be32(f);
|
||||
env->cp15.c9_insn = qemu_get_be32(f);
|
||||
env->cp15.c9_data = qemu_get_be32(f);
|
||||
env->cp15.c9_pmcr = qemu_get_be32(f);
|
||||
|
@ -177,7 +183,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
|||
env->cp15.c15_diagnostic = qemu_get_be32(f);
|
||||
env->cp15.c15_power_diagnostic = qemu_get_be32(f);
|
||||
|
||||
env->features = qemu_get_be32(f);
|
||||
env->features = qemu_get_be64(f);
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_VFP)) {
|
||||
for (i = 0; i < 16; i++) {
|
||||
|
|
|
@ -6236,7 +6236,7 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
|
|||
}
|
||||
gen_set_pc_im(s->pc);
|
||||
s->is_jmp = DISAS_WFI;
|
||||
break;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -6263,7 +6263,9 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
|
|||
tcg_gen_trunc_i64_i32(tmp, tmp64);
|
||||
store_reg(s, rt, tmp);
|
||||
tcg_gen_shri_i64(tmp64, tmp64, 32);
|
||||
tmp = tcg_temp_new_i32();
|
||||
tcg_gen_trunc_i64_i32(tmp, tmp64);
|
||||
tcg_temp_free_i64(tmp64);
|
||||
store_reg(s, rt2, tmp);
|
||||
} else {
|
||||
TCGv tmp;
|
||||
|
|
Loading…
Reference in New Issue