diff options
Diffstat (limited to 'target-arm')
-rw-r--r-- | target-arm/cpu.c | 6 | ||||
-rw-r--r-- | target-arm/cpu.h | 15 | ||||
-rw-r--r-- | target-arm/helper.c | 441 | ||||
-rw-r--r-- | target-arm/machine.c | 10 | ||||
-rw-r--r-- | target-arm/translate.c | 4 |
5 files changed, 426 insertions, 50 deletions
diff --git a/target-arm/cpu.c b/target-arm/cpu.c index ae5795337f..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) @@ -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 33afa185e9..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 */ @@ -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 diff --git a/target-arm/helper.c b/target-arm/helper.c index 23099236ad..5727da296c 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -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; diff --git a/target-arm/machine.c b/target-arm/machine.c index a2a75fbd19..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); @@ -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++) { diff --git a/target-arm/translate.c b/target-arm/translate.c index a2a0ecddad..29008a4b34 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; } @@ -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; |