|  | /* | 
|  | * QEMU ARM CPU | 
|  | * | 
|  | * Copyright (c) 2012 SUSE LINUX Products GmbH | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public License | 
|  | * as published by the Free Software Foundation; either version 2 | 
|  | * of the License, or (at your option) any later version. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License | 
|  | * along with this program; if not, see | 
|  | * <http://www.gnu.org/licenses/gpl-2.0.html> | 
|  | */ | 
|  |  | 
|  | #include "cpu.h" | 
|  | #include "qemu-common.h" | 
|  | #if !defined(CONFIG_USER_ONLY) | 
|  | #include "hw/loader.h" | 
|  | #endif | 
|  | #include "sysemu.h" | 
|  |  | 
|  | static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) | 
|  | { | 
|  | /* Reset a single ARMCPRegInfo register */ | 
|  | ARMCPRegInfo *ri = value; | 
|  | ARMCPU *cpu = opaque; | 
|  |  | 
|  | if (ri->type & ARM_CP_SPECIAL) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (ri->resetfn) { | 
|  | ri->resetfn(&cpu->env, ri); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* A zero offset is never possible as it would be regs[0] | 
|  | * so we use it to indicate that reset is being handled elsewhere. | 
|  | * This is basically only used for fields in non-core coprocessors | 
|  | * (like the pxa2xx ones). | 
|  | */ | 
|  | if (!ri->fieldoffset) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (ri->type & ARM_CP_64BIT) { | 
|  | CPREG_FIELD64(&cpu->env, ri) = ri->resetvalue; | 
|  | } else { | 
|  | CPREG_FIELD32(&cpu->env, ri) = ri->resetvalue; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* CPUClass::reset() */ | 
|  | static void arm_cpu_reset(CPUState *s) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(s); | 
|  | ARMCPUClass *acc = ARM_CPU_GET_CLASS(cpu); | 
|  | CPUARMState *env = &cpu->env; | 
|  |  | 
|  | if (qemu_loglevel_mask(CPU_LOG_RESET)) { | 
|  | qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); | 
|  | log_cpu_state(env, 0); | 
|  | } | 
|  |  | 
|  | acc->parent_reset(s); | 
|  |  | 
|  | memset(env, 0, offsetof(CPUARMState, breakpoints)); | 
|  | g_hash_table_foreach(cpu->cp_regs, cp_reg_reset, cpu); | 
|  | env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid; | 
|  | env->vfp.xregs[ARM_VFP_MVFR0] = cpu->mvfr0; | 
|  | env->vfp.xregs[ARM_VFP_MVFR1] = cpu->mvfr1; | 
|  |  | 
|  | if (arm_feature(env, ARM_FEATURE_IWMMXT)) { | 
|  | env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q'; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_USER_ONLY) | 
|  | env->uncached_cpsr = ARM_CPU_MODE_USR; | 
|  | /* For user mode we must enable access to coprocessors */ | 
|  | env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30; | 
|  | if (arm_feature(env, ARM_FEATURE_IWMMXT)) { | 
|  | env->cp15.c15_cpar = 3; | 
|  | } else if (arm_feature(env, ARM_FEATURE_XSCALE)) { | 
|  | env->cp15.c15_cpar = 1; | 
|  | } | 
|  | #else | 
|  | /* SVC mode with interrupts disabled.  */ | 
|  | env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; | 
|  | /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is | 
|  | clear at reset.  Initial SP and PC are loaded from ROM.  */ | 
|  | if (IS_M(env)) { | 
|  | uint32_t pc; | 
|  | uint8_t *rom; | 
|  | env->uncached_cpsr &= ~CPSR_I; | 
|  | rom = rom_ptr(0); | 
|  | if (rom) { | 
|  | /* We should really use ldl_phys here, in case the guest | 
|  | modified flash and reset itself.  However images | 
|  | loaded via -kernel have not been copied yet, so load the | 
|  | values directly from there.  */ | 
|  | env->regs[13] = ldl_p(rom); | 
|  | pc = ldl_p(rom + 4); | 
|  | env->thumb = pc & 1; | 
|  | env->regs[15] = pc & ~1; | 
|  | } | 
|  | } | 
|  | env->vfp.xregs[ARM_VFP_FPEXC] = 0; | 
|  | #endif | 
|  | set_flush_to_zero(1, &env->vfp.standard_fp_status); | 
|  | set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status); | 
|  | set_default_nan_mode(1, &env->vfp.standard_fp_status); | 
|  | set_float_detect_tininess(float_tininess_before_rounding, | 
|  | &env->vfp.fp_status); | 
|  | set_float_detect_tininess(float_tininess_before_rounding, | 
|  | &env->vfp.standard_fp_status); | 
|  | tlb_flush(env, 1); | 
|  | /* Reset is a state change for some CPUARMState fields which we | 
|  | * bake assumptions about into translated code, so we need to | 
|  | * tb_flush(). | 
|  | */ | 
|  | tb_flush(env); | 
|  | } | 
|  |  | 
|  | static inline void set_feature(CPUARMState *env, int feature) | 
|  | { | 
|  | env->features |= 1ULL << feature; | 
|  | } | 
|  |  | 
|  | static void arm_cpu_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  |  | 
|  | cpu_exec_init(&cpu->env); | 
|  | cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, | 
|  | g_free, g_free); | 
|  | } | 
|  |  | 
|  | static void arm_cpu_finalizefn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | g_hash_table_destroy(cpu->cp_regs); | 
|  | } | 
|  |  | 
|  | void arm_cpu_realize(ARMCPU *cpu) | 
|  | { | 
|  | /* This function is called by cpu_arm_init() because it | 
|  | * needs to do common actions based on feature bits, etc | 
|  | * that have been set by the subclass init functions. | 
|  | * When we have QOM realize support it should become | 
|  | * a true realize function instead. | 
|  | */ | 
|  | CPUARMState *env = &cpu->env; | 
|  | /* Some features automatically imply others: */ | 
|  | if (arm_feature(env, ARM_FEATURE_V7)) { | 
|  | set_feature(env, ARM_FEATURE_VAPA); | 
|  | set_feature(env, ARM_FEATURE_THUMB2); | 
|  | set_feature(env, ARM_FEATURE_MPIDR); | 
|  | if (!arm_feature(env, ARM_FEATURE_M)) { | 
|  | set_feature(env, ARM_FEATURE_V6K); | 
|  | } else { | 
|  | set_feature(env, ARM_FEATURE_V6); | 
|  | } | 
|  | } | 
|  | if (arm_feature(env, ARM_FEATURE_V6K)) { | 
|  | set_feature(env, ARM_FEATURE_V6); | 
|  | set_feature(env, ARM_FEATURE_MVFR); | 
|  | } | 
|  | if (arm_feature(env, ARM_FEATURE_V6)) { | 
|  | set_feature(env, ARM_FEATURE_V5); | 
|  | if (!arm_feature(env, ARM_FEATURE_M)) { | 
|  | set_feature(env, ARM_FEATURE_AUXCR); | 
|  | } | 
|  | } | 
|  | if (arm_feature(env, ARM_FEATURE_V5)) { | 
|  | set_feature(env, ARM_FEATURE_V4T); | 
|  | } | 
|  | if (arm_feature(env, ARM_FEATURE_M)) { | 
|  | set_feature(env, ARM_FEATURE_THUMB_DIV); | 
|  | } | 
|  | if (arm_feature(env, ARM_FEATURE_ARM_DIV)) { | 
|  | set_feature(env, ARM_FEATURE_THUMB_DIV); | 
|  | } | 
|  | if (arm_feature(env, ARM_FEATURE_VFP4)) { | 
|  | set_feature(env, ARM_FEATURE_VFP3); | 
|  | } | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* CPU models */ | 
|  |  | 
|  | static void arm926_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); | 
|  | cpu->midr = 0x41069265; | 
|  | cpu->reset_fpsid = 0x41011090; | 
|  | cpu->ctr = 0x1dd20d2; | 
|  | cpu->reset_sctlr = 0x00090078; | 
|  | } | 
|  |  | 
|  | static void arm946_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_MPU); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | cpu->midr = 0x41059461; | 
|  | cpu->ctr = 0x0f004006; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void arm1026_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP); | 
|  | set_feature(&cpu->env, ARM_FEATURE_AUXCR); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); | 
|  | cpu->midr = 0x4106a262; | 
|  | cpu->reset_fpsid = 0x410110a0; | 
|  | cpu->ctr = 0x1dd20d2; | 
|  | cpu->reset_sctlr = 0x00090078; | 
|  | cpu->reset_auxcr = 1; | 
|  | { | 
|  | /* The 1026 had an IFAR at c6,c0,0,1 rather than the ARMv6 c6,c0,0,2 */ | 
|  | ARMCPRegInfo ifar = { | 
|  | .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, | 
|  | .access = PL1_RW, | 
|  | .fieldoffset = offsetof(CPUARMState, cp15.c6_insn), | 
|  | .resetvalue = 0 | 
|  | }; | 
|  | define_one_arm_cp_reg(cpu, &ifar); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void arm1136_r2_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | /* What qemu calls "arm1136_r2" is actually the 1136 r0p2, ie an | 
|  | * older core than plain "arm1136". In particular this does not | 
|  | * have the v6K features. | 
|  | * These ID register values are correct for 1136 but may be wrong | 
|  | * for 1136_r2 (in particular r0p2 does not actually implement most | 
|  | * of the ID registers). | 
|  | */ | 
|  | set_feature(&cpu->env, ARM_FEATURE_V6); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); | 
|  | set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); | 
|  | cpu->midr = 0x4107b362; | 
|  | cpu->reset_fpsid = 0x410120b4; | 
|  | cpu->mvfr0 = 0x11111111; | 
|  | cpu->mvfr1 = 0x00000000; | 
|  | cpu->ctr = 0x1dd20d2; | 
|  | cpu->reset_sctlr = 0x00050078; | 
|  | cpu->id_pfr0 = 0x111; | 
|  | cpu->id_pfr1 = 0x1; | 
|  | cpu->id_dfr0 = 0x2; | 
|  | cpu->id_afr0 = 0x3; | 
|  | cpu->id_mmfr0 = 0x01130003; | 
|  | cpu->id_mmfr1 = 0x10030302; | 
|  | cpu->id_mmfr2 = 0x01222110; | 
|  | cpu->id_isar0 = 0x00140011; | 
|  | cpu->id_isar1 = 0x12002111; | 
|  | cpu->id_isar2 = 0x11231111; | 
|  | cpu->id_isar3 = 0x01102131; | 
|  | cpu->id_isar4 = 0x141; | 
|  | cpu->reset_auxcr = 7; | 
|  | } | 
|  |  | 
|  | static void arm1136_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V6K); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V6); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); | 
|  | set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); | 
|  | cpu->midr = 0x4117b363; | 
|  | cpu->reset_fpsid = 0x410120b4; | 
|  | cpu->mvfr0 = 0x11111111; | 
|  | cpu->mvfr1 = 0x00000000; | 
|  | cpu->ctr = 0x1dd20d2; | 
|  | cpu->reset_sctlr = 0x00050078; | 
|  | cpu->id_pfr0 = 0x111; | 
|  | cpu->id_pfr1 = 0x1; | 
|  | cpu->id_dfr0 = 0x2; | 
|  | cpu->id_afr0 = 0x3; | 
|  | cpu->id_mmfr0 = 0x01130003; | 
|  | cpu->id_mmfr1 = 0x10030302; | 
|  | cpu->id_mmfr2 = 0x01222110; | 
|  | cpu->id_isar0 = 0x00140011; | 
|  | cpu->id_isar1 = 0x12002111; | 
|  | cpu->id_isar2 = 0x11231111; | 
|  | cpu->id_isar3 = 0x01102131; | 
|  | cpu->id_isar4 = 0x141; | 
|  | cpu->reset_auxcr = 7; | 
|  | } | 
|  |  | 
|  | static void arm1176_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V6K); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VAPA); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); | 
|  | set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); | 
|  | cpu->midr = 0x410fb767; | 
|  | cpu->reset_fpsid = 0x410120b5; | 
|  | cpu->mvfr0 = 0x11111111; | 
|  | cpu->mvfr1 = 0x00000000; | 
|  | cpu->ctr = 0x1dd20d2; | 
|  | cpu->reset_sctlr = 0x00050078; | 
|  | cpu->id_pfr0 = 0x111; | 
|  | cpu->id_pfr1 = 0x11; | 
|  | cpu->id_dfr0 = 0x33; | 
|  | cpu->id_afr0 = 0; | 
|  | cpu->id_mmfr0 = 0x01130003; | 
|  | cpu->id_mmfr1 = 0x10030302; | 
|  | cpu->id_mmfr2 = 0x01222100; | 
|  | cpu->id_isar0 = 0x0140011; | 
|  | cpu->id_isar1 = 0x12002111; | 
|  | cpu->id_isar2 = 0x11231121; | 
|  | cpu->id_isar3 = 0x01102131; | 
|  | cpu->id_isar4 = 0x01141; | 
|  | cpu->reset_auxcr = 7; | 
|  | } | 
|  |  | 
|  | static void arm11mpcore_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V6K); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VAPA); | 
|  | set_feature(&cpu->env, ARM_FEATURE_MPIDR); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | cpu->midr = 0x410fb022; | 
|  | cpu->reset_fpsid = 0x410120b4; | 
|  | cpu->mvfr0 = 0x11111111; | 
|  | cpu->mvfr1 = 0x00000000; | 
|  | cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ | 
|  | cpu->id_pfr0 = 0x111; | 
|  | cpu->id_pfr1 = 0x1; | 
|  | cpu->id_dfr0 = 0; | 
|  | cpu->id_afr0 = 0x2; | 
|  | cpu->id_mmfr0 = 0x01100103; | 
|  | cpu->id_mmfr1 = 0x10020302; | 
|  | cpu->id_mmfr2 = 0x01222000; | 
|  | cpu->id_isar0 = 0x00100011; | 
|  | cpu->id_isar1 = 0x12002111; | 
|  | cpu->id_isar2 = 0x11221011; | 
|  | cpu->id_isar3 = 0x01102131; | 
|  | cpu->id_isar4 = 0x141; | 
|  | cpu->reset_auxcr = 1; | 
|  | } | 
|  |  | 
|  | static void cortex_m3_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V7); | 
|  | set_feature(&cpu->env, ARM_FEATURE_M); | 
|  | cpu->midr = 0x410fc231; | 
|  | } | 
|  |  | 
|  | static const ARMCPRegInfo cortexa8_cp_reginfo[] = { | 
|  | { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, | 
|  | .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, | 
|  | { .name = "L2AUXCR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2, | 
|  | .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, | 
|  | REGINFO_SENTINEL | 
|  | }; | 
|  |  | 
|  | static void cortex_a8_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V7); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP3); | 
|  | set_feature(&cpu->env, ARM_FEATURE_NEON); | 
|  | set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | cpu->midr = 0x410fc080; | 
|  | cpu->reset_fpsid = 0x410330c0; | 
|  | cpu->mvfr0 = 0x11110222; | 
|  | cpu->mvfr1 = 0x00011100; | 
|  | cpu->ctr = 0x82048004; | 
|  | cpu->reset_sctlr = 0x00c50078; | 
|  | cpu->id_pfr0 = 0x1031; | 
|  | cpu->id_pfr1 = 0x11; | 
|  | cpu->id_dfr0 = 0x400; | 
|  | cpu->id_afr0 = 0; | 
|  | cpu->id_mmfr0 = 0x31100003; | 
|  | cpu->id_mmfr1 = 0x20000000; | 
|  | cpu->id_mmfr2 = 0x01202000; | 
|  | cpu->id_mmfr3 = 0x11; | 
|  | cpu->id_isar0 = 0x00101111; | 
|  | cpu->id_isar1 = 0x12112111; | 
|  | cpu->id_isar2 = 0x21232031; | 
|  | cpu->id_isar3 = 0x11112131; | 
|  | cpu->id_isar4 = 0x00111142; | 
|  | cpu->clidr = (1 << 27) | (2 << 24) | 3; | 
|  | cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */ | 
|  | cpu->ccsidr[1] = 0x2007e01a; /* 16k L1 icache. */ | 
|  | cpu->ccsidr[2] = 0xf0000000; /* No L2 icache. */ | 
|  | cpu->reset_auxcr = 2; | 
|  | define_arm_cp_regs(cpu, cortexa8_cp_reginfo); | 
|  | } | 
|  |  | 
|  | static const ARMCPRegInfo cortexa9_cp_reginfo[] = { | 
|  | /* power_control should be set to maximum latency. Again, | 
|  | * default to 0 and set by private hook | 
|  | */ | 
|  | { .name = "A9_PWRCTL", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 0, | 
|  | .access = PL1_RW, .resetvalue = 0, | 
|  | .fieldoffset = offsetof(CPUARMState, cp15.c15_power_control) }, | 
|  | { .name = "A9_DIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 1, | 
|  | .access = PL1_RW, .resetvalue = 0, | 
|  | .fieldoffset = offsetof(CPUARMState, cp15.c15_diagnostic) }, | 
|  | { .name = "A9_PWRDIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 2, | 
|  | .access = PL1_RW, .resetvalue = 0, | 
|  | .fieldoffset = offsetof(CPUARMState, cp15.c15_power_diagnostic) }, | 
|  | { .name = "NEONBUSY", .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, | 
|  | .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, | 
|  | /* TLB lockdown control */ | 
|  | { .name = "TLB_LOCKR", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 2, | 
|  | .access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP }, | 
|  | { .name = "TLB_LOCKW", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 4, | 
|  | .access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP }, | 
|  | { .name = "TLB_VA", .cp = 15, .crn = 15, .crm = 5, .opc1 = 5, .opc2 = 2, | 
|  | .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, | 
|  | { .name = "TLB_PA", .cp = 15, .crn = 15, .crm = 6, .opc1 = 5, .opc2 = 2, | 
|  | .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, | 
|  | { .name = "TLB_ATTR", .cp = 15, .crn = 15, .crm = 7, .opc1 = 5, .opc2 = 2, | 
|  | .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, | 
|  | REGINFO_SENTINEL | 
|  | }; | 
|  |  | 
|  | static void cortex_a9_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V7); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP3); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); | 
|  | set_feature(&cpu->env, ARM_FEATURE_NEON); | 
|  | set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); | 
|  | /* Note that A9 supports the MP extensions even for | 
|  | * A9UP and single-core A9MP (which are both different | 
|  | * and valid configurations; we don't model A9UP). | 
|  | */ | 
|  | set_feature(&cpu->env, ARM_FEATURE_V7MP); | 
|  | cpu->midr = 0x410fc090; | 
|  | cpu->reset_fpsid = 0x41033090; | 
|  | cpu->mvfr0 = 0x11110222; | 
|  | cpu->mvfr1 = 0x01111111; | 
|  | cpu->ctr = 0x80038003; | 
|  | cpu->reset_sctlr = 0x00c50078; | 
|  | cpu->id_pfr0 = 0x1031; | 
|  | cpu->id_pfr1 = 0x11; | 
|  | cpu->id_dfr0 = 0x000; | 
|  | cpu->id_afr0 = 0; | 
|  | cpu->id_mmfr0 = 0x00100103; | 
|  | cpu->id_mmfr1 = 0x20000000; | 
|  | cpu->id_mmfr2 = 0x01230000; | 
|  | cpu->id_mmfr3 = 0x00002111; | 
|  | cpu->id_isar0 = 0x00101111; | 
|  | cpu->id_isar1 = 0x13112111; | 
|  | cpu->id_isar2 = 0x21232041; | 
|  | cpu->id_isar3 = 0x11112131; | 
|  | cpu->id_isar4 = 0x00111142; | 
|  | cpu->clidr = (1 << 27) | (1 << 24) | 3; | 
|  | cpu->ccsidr[0] = 0xe00fe015; /* 16k L1 dcache. */ | 
|  | cpu->ccsidr[1] = 0x200fe015; /* 16k L1 icache. */ | 
|  | { | 
|  | ARMCPRegInfo cbar = { | 
|  | .name = "CBAR", .cp = 15, .crn = 15,  .crm = 0, .opc1 = 4, | 
|  | .opc2 = 0, .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar, | 
|  | .fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address) | 
|  | }; | 
|  | define_one_arm_cp_reg(cpu, &cbar); | 
|  | define_arm_cp_regs(cpu, cortexa9_cp_reginfo); | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifndef CONFIG_USER_ONLY | 
|  | static int a15_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri, | 
|  | uint64_t *value) | 
|  | { | 
|  | /* Linux wants the number of processors from here. | 
|  | * Might as well set the interrupt-controller bit too. | 
|  | */ | 
|  | *value = ((smp_cpus - 1) << 24) | (1 << 23); | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static const ARMCPRegInfo cortexa15_cp_reginfo[] = { | 
|  | #ifndef CONFIG_USER_ONLY | 
|  | { .name = "L2CTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2, | 
|  | .access = PL1_RW, .resetvalue = 0, .readfn = a15_l2ctlr_read, | 
|  | .writefn = arm_cp_write_ignore, }, | 
|  | #endif | 
|  | { .name = "L2ECTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 3, | 
|  | .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, | 
|  | REGINFO_SENTINEL | 
|  | }; | 
|  |  | 
|  | static void cortex_a15_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V7); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP4); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); | 
|  | set_feature(&cpu->env, ARM_FEATURE_NEON); | 
|  | set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); | 
|  | set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); | 
|  | 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; | 
|  | cpu->mvfr1 = 0x11111111; | 
|  | cpu->ctr = 0x8444c004; | 
|  | cpu->reset_sctlr = 0x00c50078; | 
|  | cpu->id_pfr0 = 0x00001131; | 
|  | cpu->id_pfr1 = 0x00011011; | 
|  | cpu->id_dfr0 = 0x02010555; | 
|  | cpu->id_afr0 = 0x00000000; | 
|  | cpu->id_mmfr0 = 0x10201105; | 
|  | cpu->id_mmfr1 = 0x20000000; | 
|  | cpu->id_mmfr2 = 0x01240000; | 
|  | cpu->id_mmfr3 = 0x02102211; | 
|  | cpu->id_isar0 = 0x02101110; | 
|  | cpu->id_isar1 = 0x13112111; | 
|  | cpu->id_isar2 = 0x21232041; | 
|  | cpu->id_isar3 = 0x11112131; | 
|  | cpu->id_isar4 = 0x10011142; | 
|  | cpu->clidr = 0x0a200023; | 
|  | cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ | 
|  | cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ | 
|  | cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */ | 
|  | define_arm_cp_regs(cpu, cortexa15_cp_reginfo); | 
|  | } | 
|  |  | 
|  | static void ti925t_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V4T); | 
|  | set_feature(&cpu->env, ARM_FEATURE_OMAPCP); | 
|  | cpu->midr = ARM_CPUID_TI925T; | 
|  | cpu->ctr = 0x5109149; | 
|  | cpu->reset_sctlr = 0x00000070; | 
|  | } | 
|  |  | 
|  | static void sa1100_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_STRONGARM); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | cpu->midr = 0x4401A11B; | 
|  | cpu->reset_sctlr = 0x00000070; | 
|  | } | 
|  |  | 
|  | static void sa1110_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_STRONGARM); | 
|  | set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); | 
|  | cpu->midr = 0x6901B119; | 
|  | cpu->reset_sctlr = 0x00000070; | 
|  | } | 
|  |  | 
|  | static void pxa250_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | cpu->midr = 0x69052100; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa255_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | cpu->midr = 0x69052d00; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa260_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | cpu->midr = 0x69052903; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa261_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | cpu->midr = 0x69052d05; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa262_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | cpu->midr = 0x69052d06; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa270a0_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | set_feature(&cpu->env, ARM_FEATURE_IWMMXT); | 
|  | cpu->midr = 0x69054110; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa270a1_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | set_feature(&cpu->env, ARM_FEATURE_IWMMXT); | 
|  | cpu->midr = 0x69054111; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa270b0_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | set_feature(&cpu->env, ARM_FEATURE_IWMMXT); | 
|  | cpu->midr = 0x69054112; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa270b1_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | set_feature(&cpu->env, ARM_FEATURE_IWMMXT); | 
|  | cpu->midr = 0x69054113; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa270c0_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | set_feature(&cpu->env, ARM_FEATURE_IWMMXT); | 
|  | cpu->midr = 0x69054114; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void pxa270c5_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V5); | 
|  | set_feature(&cpu->env, ARM_FEATURE_XSCALE); | 
|  | set_feature(&cpu->env, ARM_FEATURE_IWMMXT); | 
|  | cpu->midr = 0x69054117; | 
|  | cpu->ctr = 0xd172172; | 
|  | cpu->reset_sctlr = 0x00000078; | 
|  | } | 
|  |  | 
|  | static void arm_any_initfn(Object *obj) | 
|  | { | 
|  | ARMCPU *cpu = ARM_CPU(obj); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V7); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP4); | 
|  | set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); | 
|  | set_feature(&cpu->env, ARM_FEATURE_NEON); | 
|  | set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); | 
|  | set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); | 
|  | set_feature(&cpu->env, ARM_FEATURE_V7MP); | 
|  | cpu->midr = 0xffffffff; | 
|  | } | 
|  |  | 
|  | typedef struct ARMCPUInfo { | 
|  | const char *name; | 
|  | void (*initfn)(Object *obj); | 
|  | } ARMCPUInfo; | 
|  |  | 
|  | static const ARMCPUInfo arm_cpus[] = { | 
|  | { .name = "arm926",      .initfn = arm926_initfn }, | 
|  | { .name = "arm946",      .initfn = arm946_initfn }, | 
|  | { .name = "arm1026",     .initfn = arm1026_initfn }, | 
|  | /* What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an | 
|  | * older core than plain "arm1136". In particular this does not | 
|  | * have the v6K features. | 
|  | */ | 
|  | { .name = "arm1136-r2",  .initfn = arm1136_r2_initfn }, | 
|  | { .name = "arm1136",     .initfn = arm1136_initfn }, | 
|  | { .name = "arm1176",     .initfn = arm1176_initfn }, | 
|  | { .name = "arm11mpcore", .initfn = arm11mpcore_initfn }, | 
|  | { .name = "cortex-m3",   .initfn = cortex_m3_initfn }, | 
|  | { .name = "cortex-a8",   .initfn = cortex_a8_initfn }, | 
|  | { .name = "cortex-a9",   .initfn = cortex_a9_initfn }, | 
|  | { .name = "cortex-a15",  .initfn = cortex_a15_initfn }, | 
|  | { .name = "ti925t",      .initfn = ti925t_initfn }, | 
|  | { .name = "sa1100",      .initfn = sa1100_initfn }, | 
|  | { .name = "sa1110",      .initfn = sa1110_initfn }, | 
|  | { .name = "pxa250",      .initfn = pxa250_initfn }, | 
|  | { .name = "pxa255",      .initfn = pxa255_initfn }, | 
|  | { .name = "pxa260",      .initfn = pxa260_initfn }, | 
|  | { .name = "pxa261",      .initfn = pxa261_initfn }, | 
|  | { .name = "pxa262",      .initfn = pxa262_initfn }, | 
|  | /* "pxa270" is an alias for "pxa270-a0" */ | 
|  | { .name = "pxa270",      .initfn = pxa270a0_initfn }, | 
|  | { .name = "pxa270-a0",   .initfn = pxa270a0_initfn }, | 
|  | { .name = "pxa270-a1",   .initfn = pxa270a1_initfn }, | 
|  | { .name = "pxa270-b0",   .initfn = pxa270b0_initfn }, | 
|  | { .name = "pxa270-b1",   .initfn = pxa270b1_initfn }, | 
|  | { .name = "pxa270-c0",   .initfn = pxa270c0_initfn }, | 
|  | { .name = "pxa270-c5",   .initfn = pxa270c5_initfn }, | 
|  | { .name = "any",         .initfn = arm_any_initfn }, | 
|  | }; | 
|  |  | 
|  | static void arm_cpu_class_init(ObjectClass *oc, void *data) | 
|  | { | 
|  | ARMCPUClass *acc = ARM_CPU_CLASS(oc); | 
|  | CPUClass *cc = CPU_CLASS(acc); | 
|  |  | 
|  | acc->parent_reset = cc->reset; | 
|  | cc->reset = arm_cpu_reset; | 
|  | } | 
|  |  | 
|  | static void cpu_register(const ARMCPUInfo *info) | 
|  | { | 
|  | TypeInfo type_info = { | 
|  | .name = info->name, | 
|  | .parent = TYPE_ARM_CPU, | 
|  | .instance_size = sizeof(ARMCPU), | 
|  | .instance_init = info->initfn, | 
|  | .class_size = sizeof(ARMCPUClass), | 
|  | }; | 
|  |  | 
|  | type_register_static(&type_info); | 
|  | } | 
|  |  | 
|  | static const TypeInfo arm_cpu_type_info = { | 
|  | .name = TYPE_ARM_CPU, | 
|  | .parent = TYPE_CPU, | 
|  | .instance_size = sizeof(ARMCPU), | 
|  | .instance_init = arm_cpu_initfn, | 
|  | .instance_finalize = arm_cpu_finalizefn, | 
|  | .abstract = true, | 
|  | .class_size = sizeof(ARMCPUClass), | 
|  | .class_init = arm_cpu_class_init, | 
|  | }; | 
|  |  | 
|  | static void arm_cpu_register_types(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | type_register_static(&arm_cpu_type_info); | 
|  | for (i = 0; i < ARRAY_SIZE(arm_cpus); i++) { | 
|  | cpu_register(&arm_cpus[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | type_init(arm_cpu_register_types) |