From 2bee5105481637f9603bc71ef7cbb81e8da9cd68 Mon Sep 17 00:00:00 2001 From: Paul Brook Date: Thu, 12 Jul 2012 10:58:35 +0000 Subject: [PATCH 01/15] target-arm: Fix CP15 based WFI The coprocessor register rework broke cp15 based WFI instructions. We incorrectly fall through the normal register write case, which incorrectly adds a forced block termination. We've already done a special version of this (DISAS_WFI), so return immediately. Signed-off-by: Paul Brook Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index a2a0ecddad..f39b9caa28 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -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; } From 81a60ada7e85c334c9516cad89d25a7dd889f399 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:58:36 +0000 Subject: [PATCH 02/15] target-arm: Fix typo that meant TTBR1 accesses went to TTBR0 Fix a copy-and-paste error in the register description for TTBR1 that meant it was a duplicate of TTBR0 rather than affecting the correct bit of CPU state. Signed-off-by: Peter Maydell --- target-arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 23099236ad..ca5d8e9e28 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -679,7 +679,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, From 091fd17ce8ebdc2922d536e13d2bb96f8acc83f0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:58:36 +0000 Subject: [PATCH 03/15] target-arm: Fix some copy-and-paste errors in cp register names Fix a couple of cases where cp register names were copy-and-pasted. These are harmless since we don't use the name for anything (except debugging convenience) but could be confusing. Signed-off-by: Peter Maydell Reviewed-by: Igor Mitsyanko --- target-arm/helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index ca5d8e9e28..0015bee977 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -216,9 +216,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 +346,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, From ed336850e830711a3b940a1d311b5958f439f342 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:04 +0000 Subject: [PATCH 04/15] target-arm: Fix TCG temp handling in 64 bit cp writes Fix errors in the TCG temp handling in the 64 bit coprocessor write path: we were reusing a 32 bit temp after it had been freed by store_reg(), and failing to free a 64 bit temp. This bug has no visible effect at this point because there aren't any non-NOP 64 bit registers yet; it needs to be fixed as a prerequisite for the 64 bit registers in LPAE support. Signed-off-by: Peter Maydell --- target-arm/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target-arm/translate.c b/target-arm/translate.c index f39b9caa28..29008a4b34 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -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; From 40291d61461c4619928a83a640205d71c72b155a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:02 +0000 Subject: [PATCH 05/15] hw/imx_avic.c: Avoid format error when target_phys_addr_t is 64 bits Add a missing cast to avoid gcc complaining about format string errors when printing an expression based on a target_phys_addr_t. Signed-off-by: Peter Maydell Reviewed-by: Peter Chubb --- hw/imx_avic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/imx_avic.c b/hw/imx_avic.c index 25f47f331b..4f010e8ee2 100644 --- a/hw/imx_avic.c +++ b/hw/imx_avic.c @@ -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; } From 3cc0cd61f4adfecf2c0de392fe61a7e323e496ba Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:03 +0000 Subject: [PATCH 06/15] ARM: Make target_phys_addr_t 64 bits and physaddrs 40 bits Make target_phys_addr_t 64 bits for ARM targets, and set TARGET_PHYS_ADDR_SPACE_BITS to 40. This should have no effect for ARM boards where physical addresses really are 32 bits (except perhaps a slight performance hit on 32 bit hosts for system emulation) but allows us to implement the Large Physical Address Extensions for Cortex-A15, which mean 40 bit physical addresses. Signed-off-by: Peter Maydell --- configure | 2 +- target-arm/cpu.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 500fe24cba..32a8290667 100755 --- a/configure +++ b/configure @@ -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" ;; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 33afa185e9..aadfca0a26 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -619,7 +619,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) From de9b05b807918d40db9e26ddd6a54ad2978ac5b7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:05 +0000 Subject: [PATCH 07/15] target-arm: Implement privileged-execute-never (PXN) Implement the privileged-execute-never (PXN) translation table bit. It is implementation-defined whether this is implemented, so we give it its own ARM_FEATURE_ flag. LPAE requires PXN, so add also an LPAE feature flag and the implication logic, as a placeholder for actually implementing LPAE at a later date. Signed-off-by: Peter Maydell --- target-arm/cpu.c | 4 ++++ target-arm/cpu.h | 2 ++ target-arm/helper.c | 32 ++++++++++++++++++++------------ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index ae5795337f..526e7254f2 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -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; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index aadfca0a26..82cad4b975 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -386,6 +386,8 @@ 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) diff --git a/target-arm/helper.c b/target-arm/helper.c index 0015bee977..e51b038944 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1935,9 +1935,10 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, 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; @@ -1946,27 +1947,27 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, 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 +1979,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 +2012,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; From 918f5dca18d62d014bc84fde726a88bd7e8d3615 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:06 +0000 Subject: [PATCH 08/15] target-arm: Extend feature flags to 64 bits Extend feature flags to 64 bits, as we've just run out of space in the 32 bit integer we were using for them. Signed-off-by: Peter Maydell --- target-arm/cpu.c | 2 +- target-arm/cpu.h | 6 +++--- target-arm/machine.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 526e7254f2..b00f5fa547 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -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) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 82cad4b975..3c5d2be1c1 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -221,7 +221,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; @@ -392,7 +392,7 @@ enum arm_features { 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); @@ -638,7 +638,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 8 /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel diff --git a/target-arm/machine.c b/target-arm/machine.c index a2a75fbd19..429cbc8bcb 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -60,7 +60,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++) { @@ -177,7 +177,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++) { From 7ac681cf2aafcef4b17f0f6007e1f4a2520528db Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:07 +0000 Subject: [PATCH 09/15] target-arm: Add AMAIR0, AMAIR1 LPAE cp15 registers Add implementations of the AMAIR0 and AMAIR1 LPAE Auxiliary Memory Attribute Indirection Registers. These are implementation defined and we choose to implement them as RAZ/WI, matching the Cortex-A7 and Cortex-A15. Signed-off-by: Peter Maydell --- target-arm/helper.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/target-arm/helper.c b/target-arm/helper.c index e51b038944..e96404aa5c 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -871,6 +871,19 @@ static const ARMCPRegInfo mpidr_cp_reginfo[] = { REGINFO_SENTINEL }; +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 }, + REGINFO_SENTINEL +}; + static int sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { env->cp15.c1_sys = value; @@ -1016,6 +1029,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). From f9fc619a0deaf2b61fe6f48034c94ca7e38801bb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:08 +0000 Subject: [PATCH 10/15] target-arm: Add 64 bit variants of DBGDRAR and DBGDSAR for LPAE LPAE extends the DBGDRAR and DBGDSAR debug registers to 64 bits; we only implement these as dummy RAZ versions; provide dummies for the 64 bit accesses as well. Signed-off-by: Peter Maydell --- target-arm/helper.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target-arm/helper.c b/target-arm/helper.c index e96404aa5c..0cf7b8dd77 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -881,6 +881,11 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { { .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 }, REGINFO_SENTINEL }; From 891a2fe72031b7f9d4d2f5f8539206dbd1d6c3ec Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:09 +0000 Subject: [PATCH 11/15] target-arm: Add 64 bit PAR, TTBR0, TTBR1 for LPAE Under LPAE, the cp15 registers PAR, TTBR0 and TTBR1 are extended to 64 bits, with a 64 bit (MRRC/MCRR) access path to read the full width of the register. Add the state fields for the top half and the 64 bit access path. Actual use of the top half of the register will come with the addition of the long-descriptor translation table format support. For the PAR we also need to correct the masking applied for 32 bit writes (there are no bits reserved if LPAE is implemented) and clear the high half when doing a 32 bit result VA-to-PA lookup. Signed-off-by: Peter Maydell --- target-arm/cpu.h | 7 ++-- target-arm/helper.c | 77 +++++++++++++++++++++++++++++++++++++++++++- target-arm/machine.c | 6 ++++ 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 3c5d2be1c1..191895cca8 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -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 */ @@ -638,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 8 +#define CPU_SAVE_VERSION 9 /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel diff --git a/target-arm/helper.c b/target-arm/helper.c index 0cf7b8dd77..6bb6767bc2 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -491,7 +491,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; @@ -528,6 +530,7 @@ static int ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) ((ret & (12 << 1)) >> 6) | ((ret & 0xf) << 1) | 1; } + env->cp15.c7_par_hi = 0; return 0; } #endif @@ -871,6 +874,69 @@ 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. @@ -886,6 +952,15 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .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 }; diff --git a/target-arm/machine.c b/target-arm/machine.c index 429cbc8bcb..68dca7ffb2 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -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); @@ -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); From 77a71dd1cb561a920fba63d734cbacdc1357c490 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:09 +0000 Subject: [PATCH 12/15] target-arm: Use target_phys_addr_t in get_phys_addr() In the implementation of get_phys_addr(), consistently use target_phys_addr_t to hold the physical address rather than uint32_t. Signed-off-by: Peter Maydell --- target-arm/helper.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 6bb6767bc2..d66bc3e832 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -7,7 +7,7 @@ #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 @@ -505,7 +505,7 @@ static int par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) /* get_phys_addr() isn't present for user-mode-only targets */ 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; @@ -1929,8 +1929,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; @@ -1939,7 +1939,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. */ @@ -2024,8 +2024,8 @@ 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; @@ -2036,7 +2036,7 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, int ap; int domain = 0; int domain_prot; - uint32_t phys_addr; + target_phys_addr_t phys_addr; /* Pagetable walk. */ /* Lookup l1 descriptor. */ @@ -2135,8 +2135,9 @@ 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) +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; @@ -2197,7 +2198,7 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, int access_type 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. */ @@ -2226,7 +2227,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; @@ -2236,7 +2237,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; @@ -2258,7 +2259,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; From 702a9357897293e804d2ac7550227d0d07ed9fc9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:10 +0000 Subject: [PATCH 13/15] target-arm: Implement long-descriptor PAR format Implement the different format of the PAR when long descriptor translation tables are in use. Note that we assume that get_phys_addr() returns a long-descriptor format DFSR value on failure if long descriptors are in use; this added subtlety tips the balance and makes it worth adding a comment documenting the API to get_phys_addr(). Signed-off-by: Peter Maydell --- target-arm/helper.c | 79 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index d66bc3e832..110858dde1 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -503,6 +503,17 @@ 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) { target_phys_addr_t phys_addr; @@ -517,20 +528,45 @@ 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; } - env->cp15.c7_par_hi = 0; return 0; } #endif @@ -2196,6 +2232,29 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, 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, target_phys_addr_t *phys_ptr, int *prot, From e42c4db3a3de213f6a374dbdc7e75767dc3bfac2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:11 +0000 Subject: [PATCH 14/15] target-arm: Implement TTBCR changes for LPAE Implement the changes to the TTBCR register required for LPAE: * many fewer bits should be RAZ/WI * since TTBCR changes can result in a change of ASID, we must flush the TLB on writes to it Signed-off-by: Peter Maydell --- target-arm/helper.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 110858dde1..72b44b03aa 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -692,7 +692,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); From 3dde962f39717737b1031de129754f21eb77979f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 Jul 2012 10:59:12 +0000 Subject: [PATCH 15/15] target-arm: Add support for long format translation table walks Implement the actual table walk code for LPAE's long format translation tables. Signed-off-by: Peter Maydell --- target-arm/helper.c | 182 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/target-arm/helper.c b/target-arm/helper.c index 72b44b03aa..5727da296c 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -3,6 +3,7 @@ #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, @@ -2184,6 +2185,184 @@ do_fault: return code | (domain << 4); } +/* 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) @@ -2287,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);