From 4b6a83fb0c34a6fcc7bb1058284e3c3674e54421 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 20 Jun 2012 11:57:06 +0000 Subject: [PATCH] target-arm: initial coprocessor register framework Initial infrastructure for data-driven registration of coprocessor register implementations. We still fall back to the old-style switch statements pending complete conversion of all existing registers. Signed-off-by: Peter Maydell --- target-arm/cpu-qom.h | 3 + target-arm/cpu.c | 41 +++++++++ target-arm/cpu.h | 201 +++++++++++++++++++++++++++++++++++++++++ target-arm/helper.c | 101 +++++++++++++++++++++ target-arm/helper.h | 5 + target-arm/op_helper.c | 42 ++++++++- target-arm/translate.c | 156 +++++++++++++++++++++++++++++++- 7 files changed, 546 insertions(+), 3 deletions(-) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index a61c68d21..848ac2ffd 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -58,6 +58,9 @@ typedef struct ARMCPU { CPUARMState env; + /* Coprocessor information */ + GHashTable *cp_regs; + /* The instance init functions for implementation-specific subclasses * set these fields to specify the implementation-dependent values of * various constant registers and reset values of non-constant diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 934894bf9..6456a3dfa 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -24,6 +24,37 @@ #include "hw/loader.h" #endif +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) { @@ -39,6 +70,7 @@ static void arm_cpu_reset(CPUState *s) acc->parent_reset(s); memset(env, 0, offsetof(CPUARMState, breakpoints)); + g_hash_table_foreach(cpu->cp_regs, cp_reg_reset, cpu); env->cp15.c15_config_base_address = cpu->reset_cbar; env->cp15.c0_cpuid = cpu->midr; env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid; @@ -130,6 +162,14 @@ 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) @@ -657,6 +697,7 @@ static const TypeInfo arm_cpu_type_info = { .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, diff --git a/target-arm/cpu.h b/target-arm/cpu.h index d01285fd5..6c7d3e72e 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -410,6 +410,207 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, void *opaque); +/* Interface for defining coprocessor registers. + * Registers are defined in tables of arm_cp_reginfo structs + * which are passed to define_arm_cp_regs(). + */ + +/* When looking up a coprocessor register we look for it + * via an integer which encodes all of: + * coprocessor number + * Crn, Crm, opc1, opc2 fields + * 32 or 64 bit register (ie is it accessed via MRC/MCR + * or via MRRC/MCRR?) + * We allow 4 bits for opc1 because MRRC/MCRR have a 4 bit field. + * (In this case crn and opc2 should be zero.) + */ +#define ENCODE_CP_REG(cp, is64, crn, crm, opc1, opc2) \ + (((cp) << 16) | ((is64) << 15) | ((crn) << 11) | \ + ((crm) << 7) | ((opc1) << 3) | (opc2)) + +#define DECODE_CPREG_CRN(enc) (((enc) >> 7) & 0xf) + +/* ARMCPRegInfo type field bits. If the SPECIAL bit is set this is a + * special-behaviour cp reg and bits [15..8] indicate what behaviour + * it has. Otherwise it is a simple cp reg, where CONST indicates that + * TCG can assume the value to be constant (ie load at translate time) + * and 64BIT indicates a 64 bit wide coprocessor register. SUPPRESS_TB_END + * indicates that the TB should not be ended after a write to this register + * (the default is that the TB ends after cp writes). OVERRIDE permits + * a register definition to override a previous definition for the + * same (cp, is64, crn, crm, opc1, opc2) tuple: either the new or the + * old must have the OVERRIDE bit set. + */ +#define ARM_CP_SPECIAL 1 +#define ARM_CP_CONST 2 +#define ARM_CP_64BIT 4 +#define ARM_CP_SUPPRESS_TB_END 8 +#define ARM_CP_OVERRIDE 16 +#define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8)) +#define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8)) +#define ARM_LAST_SPECIAL ARM_CP_WFI +/* Used only as a terminator for ARMCPRegInfo lists */ +#define ARM_CP_SENTINEL 0xffff +/* Mask of only the flag bits in a type field */ +#define ARM_CP_FLAG_MASK 0x1f + +/* Return true if cptype is a valid type field. This is used to try to + * catch errors where the sentinel has been accidentally left off the end + * of a list of registers. + */ +static inline bool cptype_valid(int cptype) +{ + return ((cptype & ~ARM_CP_FLAG_MASK) == 0) + || ((cptype & ARM_CP_SPECIAL) && + (cptype <= ARM_LAST_SPECIAL)); +} + +/* Access rights: + * We define bits for Read and Write access for what rev C of the v7-AR ARM ARM + * defines as PL0 (user), PL1 (fiq/irq/svc/abt/und/sys, ie privileged), and + * PL2 (hyp). The other level which has Read and Write bits is Secure PL1 + * (ie any of the privileged modes in Secure state, or Monitor mode). + * If a register is accessible in one privilege level it's always accessible + * in higher privilege levels too. Since "Secure PL1" also follows this rule + * (ie anything visible in PL2 is visible in S-PL1, some things are only + * visible in S-PL1) but "Secure PL1" is a bit of a mouthful, we bend the + * terminology a little and call this PL3. + * + * If access permissions for a register are more complex than can be + * described with these bits, then use a laxer set of restrictions, and + * do the more restrictive/complex check inside a helper function. + */ +#define PL3_R 0x80 +#define PL3_W 0x40 +#define PL2_R (0x20 | PL3_R) +#define PL2_W (0x10 | PL3_W) +#define PL1_R (0x08 | PL2_R) +#define PL1_W (0x04 | PL2_W) +#define PL0_R (0x02 | PL1_R) +#define PL0_W (0x01 | PL1_W) + +#define PL3_RW (PL3_R | PL3_W) +#define PL2_RW (PL2_R | PL2_W) +#define PL1_RW (PL1_R | PL1_W) +#define PL0_RW (PL0_R | PL0_W) + +static inline int arm_current_pl(CPUARMState *env) +{ + if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR) { + return 0; + } + /* We don't currently implement the Virtualization or TrustZone + * extensions, so PL2 and PL3 don't exist for us. + */ + return 1; +} + +typedef struct ARMCPRegInfo ARMCPRegInfo; + +/* Access functions for coprocessor registers. These should return + * 0 on success, or one of the EXCP_* constants if access should cause + * an exception (in which case *value is not written). + */ +typedef int CPReadFn(CPUARMState *env, const ARMCPRegInfo *opaque, + uint64_t *value); +typedef int CPWriteFn(CPUARMState *env, const ARMCPRegInfo *opaque, + uint64_t value); +/* Hook function for register reset */ +typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque); + +#define CP_ANY 0xff + +/* Definition of an ARM coprocessor register */ +struct ARMCPRegInfo { + /* Name of register (useful mainly for debugging, need not be unique) */ + const char *name; + /* Location of register: coprocessor number and (crn,crm,opc1,opc2) + * tuple. Any of crm, opc1 and opc2 may be CP_ANY to indicate a + * 'wildcard' field -- any value of that field in the MRC/MCR insn + * will be decoded to this register. The register read and write + * callbacks will be passed an ARMCPRegInfo with the crn/crm/opc1/opc2 + * used by the program, so it is possible to register a wildcard and + * then behave differently on read/write if necessary. + * For 64 bit registers, only crm and opc1 are relevant; crn and opc2 + * must both be zero. + */ + uint8_t cp; + uint8_t crn; + uint8_t crm; + uint8_t opc1; + uint8_t opc2; + /* Register type: ARM_CP_* bits/values */ + int type; + /* Access rights: PL*_[RW] */ + int access; + /* The opaque pointer passed to define_arm_cp_regs_with_opaque() when + * this register was defined: can be used to hand data through to the + * register read/write functions, since they are passed the ARMCPRegInfo*. + */ + void *opaque; + /* Value of this register, if it is ARM_CP_CONST. Otherwise, if + * fieldoffset is non-zero, the reset value of the register. + */ + uint64_t resetvalue; + /* Offset of the field in CPUARMState for this register. This is not + * needed if either: + * 1. type is ARM_CP_CONST or one of the ARM_CP_SPECIALs + * 2. both readfn and writefn are specified + */ + ptrdiff_t fieldoffset; /* offsetof(CPUARMState, field) */ + /* Function for handling reads of this register. If NULL, then reads + * will be done by loading from the offset into CPUARMState specified + * by fieldoffset. + */ + CPReadFn *readfn; + /* Function for handling writes of this register. If NULL, then writes + * will be done by writing to the offset into CPUARMState specified + * by fieldoffset. + */ + CPWriteFn *writefn; + /* Function for resetting the register. If NULL, then reset will be done + * by writing resetvalue to the field specified in fieldoffset. If + * fieldoffset is 0 then no reset will be done. + */ + CPResetFn *resetfn; +}; + +/* Macros which are lvalues for the field in CPUARMState for the + * ARMCPRegInfo *ri. + */ +#define CPREG_FIELD32(env, ri) \ + (*(uint32_t *)((char *)(env) + (ri)->fieldoffset)) +#define CPREG_FIELD64(env, ri) \ + (*(uint64_t *)((char *)(env) + (ri)->fieldoffset)) + +#define REGINFO_SENTINEL { .type = ARM_CP_SENTINEL } + +void define_arm_cp_regs_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *regs, void *opaque); +void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *regs, void *opaque); +static inline void define_arm_cp_regs(ARMCPU *cpu, const ARMCPRegInfo *regs) +{ + define_arm_cp_regs_with_opaque(cpu, regs, 0); +} +static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs) +{ + define_one_arm_cp_reg_with_opaque(cpu, regs, 0); +} +const ARMCPRegInfo *get_arm_cp_reginfo(ARMCPU *cpu, uint32_t encoded_cp); + +/* CPWriteFn that can be used to implement writes-ignored behaviour */ +int arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); +/* CPReadFn that can be used for read-as-zero behaviour */ +int arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value); + +static inline bool cp_access_ok(CPUARMState *env, + const ARMCPRegInfo *ri, int isread) +{ + return (ri->access >> ((arm_current_pl(env) * 2) + isread)) & 1; +} + /* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3. Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are conventional cores (ie. Application or Realtime profile). */ diff --git a/target-arm/helper.c b/target-arm/helper.c index bbb1d05d1..70324234e 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -137,6 +137,107 @@ void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf) g_slist_free(list); } +void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *r, void *opaque) +{ + /* Define implementations of coprocessor registers. + * We store these in a hashtable because typically + * there are less than 150 registers in a space which + * is 16*16*16*8*8 = 262144 in size. + * Wildcarding is supported for the crm, opc1 and opc2 fields. + * If a register is defined twice then the second definition is + * used, so this can be used to define some generic registers and + * then override them with implementation specific variations. + * At least one of the original and the second definition should + * include ARM_CP_OVERRIDE in its type bits -- this is just a guard + * against accidental use. + */ + int crm, opc1, opc2; + int crmmin = (r->crm == CP_ANY) ? 0 : r->crm; + int crmmax = (r->crm == CP_ANY) ? 15 : r->crm; + int opc1min = (r->opc1 == CP_ANY) ? 0 : r->opc1; + int opc1max = (r->opc1 == CP_ANY) ? 7 : r->opc1; + int opc2min = (r->opc2 == CP_ANY) ? 0 : r->opc2; + int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2; + /* 64 bit registers have only CRm and Opc1 fields */ + assert(!((r->type & ARM_CP_64BIT) && (r->opc2 || r->crn))); + /* Check that the register definition has enough info to handle + * reads and writes if they are permitted. + */ + if (!(r->type & (ARM_CP_SPECIAL|ARM_CP_CONST))) { + if (r->access & PL3_R) { + assert(r->fieldoffset || r->readfn); + } + if (r->access & PL3_W) { + assert(r->fieldoffset || r->writefn); + } + } + /* Bad type field probably means missing sentinel at end of reg list */ + assert(cptype_valid(r->type)); + for (crm = crmmin; crm <= crmmax; crm++) { + for (opc1 = opc1min; opc1 <= opc1max; opc1++) { + for (opc2 = opc2min; opc2 <= opc2max; opc2++) { + uint32_t *key = g_new(uint32_t, 1); + ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo)); + int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; + *key = ENCODE_CP_REG(r->cp, is64, r->crn, crm, opc1, opc2); + r2->opaque = opaque; + /* Make sure reginfo passed to helpers for wildcarded regs + * has the correct crm/opc1/opc2 for this reg, not CP_ANY: + */ + r2->crm = crm; + r2->opc1 = opc1; + r2->opc2 = opc2; + /* Overriding of an existing definition must be explicitly + * requested. + */ + if (!(r->type & ARM_CP_OVERRIDE)) { + ARMCPRegInfo *oldreg; + oldreg = g_hash_table_lookup(cpu->cp_regs, key); + if (oldreg && !(oldreg->type & ARM_CP_OVERRIDE)) { + fprintf(stderr, "Register redefined: cp=%d %d bit " + "crn=%d crm=%d opc1=%d opc2=%d, " + "was %s, now %s\n", r2->cp, 32 + 32 * is64, + r2->crn, r2->crm, r2->opc1, r2->opc2, + oldreg->name, r2->name); + assert(0); + } + } + g_hash_table_insert(cpu->cp_regs, key, r2); + } + } + } +} + +void define_arm_cp_regs_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *regs, void *opaque) +{ + /* Define a whole list of registers */ + const ARMCPRegInfo *r; + for (r = regs; r->type != ARM_CP_SENTINEL; r++) { + define_one_arm_cp_reg_with_opaque(cpu, r, opaque); + } +} + +const ARMCPRegInfo *get_arm_cp_reginfo(ARMCPU *cpu, uint32_t encoded_cp) +{ + return g_hash_table_lookup(cpu->cp_regs, &encoded_cp); +} + +int arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Helper coprocessor write function for write-ignore registers */ + return 0; +} + +int arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) +{ + /* Helper coprocessor write function for read-as-zero registers */ + *value = 0; + return 0; +} + static int bad_mode_switch(CPUARMState *env, int mode) { /* Return true if it is not valid for us to switch to diff --git a/target-arm/helper.h b/target-arm/helper.h index 16dd5fcc8..b6cefed20 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -65,6 +65,11 @@ DEF_HELPER_2(get_cp15, i32, env, i32) DEF_HELPER_3(set_cp, void, env, i32, i32) DEF_HELPER_2(get_cp, i32, env, i32) +DEF_HELPER_3(set_cp_reg, void, env, ptr, i32) +DEF_HELPER_2(get_cp_reg, i32, env, ptr) +DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64) +DEF_HELPER_2(get_cp_reg64, i64, env, ptr) + DEF_HELPER_2(get_r13_banked, i32, env, i32) DEF_HELPER_3(set_r13_banked, void, env, i32, i32) diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index b53369d7c..490111c22 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -23,13 +23,11 @@ #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) -#if !defined(CONFIG_USER_ONLY) static void raise_exception(int tt) { env->exception_index = tt; cpu_loop_exit(env); } -#endif uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, uint32_t rn, uint32_t maxindex) @@ -287,6 +285,46 @@ void HELPER(set_user_reg)(uint32_t regno, uint32_t val) } } +void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value) +{ + const ARMCPRegInfo *ri = rip; + int excp = ri->writefn(env, ri, value); + if (excp) { + raise_exception(excp); + } +} + +uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip) +{ + const ARMCPRegInfo *ri = rip; + uint64_t value; + int excp = ri->readfn(env, ri, &value); + if (excp) { + raise_exception(excp); + } + return value; +} + +void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value) +{ + const ARMCPRegInfo *ri = rip; + int excp = ri->writefn(env, ri, value); + if (excp) { + raise_exception(excp); + } +} + +uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip) +{ + const ARMCPRegInfo *ri = rip; + uint64_t value; + int excp = ri->readfn(env, ri, &value); + if (excp) { + raise_exception(excp); + } + return value; +} + /* ??? Flag setting arithmetic is awkward because we need to do comparisons. The only way to do that in TCG is a conditional branch, which clobbers all our temporaries. For now implement these as helper functions. */ diff --git a/target-arm/translate.c b/target-arm/translate.c index 437d9dbf0..d7edda758 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -6479,13 +6479,16 @@ static int disas_cp14_write(CPUARMState * env, DisasContext *s, uint32_t insn) static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) { - int cpnum; + int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2; + const ARMCPRegInfo *ri; + ARMCPU *cpu = arm_env_get_cpu(env); cpnum = (insn >> 8) & 0xf; if (arm_feature(env, ARM_FEATURE_XSCALE) && ((env->cp15.c15_cpar ^ 0x3fff) & (1 << cpnum))) return 1; + /* First check for coprocessor space used for actual instructions */ switch (cpnum) { case 0: case 1: @@ -6498,6 +6501,157 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) case 10: case 11: return disas_vfp_insn (env, s, insn); + default: + break; + } + + /* Otherwise treat as a generic register access */ + is64 = (insn & (1 << 25)) == 0; + if (!is64 && ((insn & (1 << 4)) == 0)) { + /* cdp */ + return 1; + } + + crm = insn & 0xf; + if (is64) { + crn = 0; + opc1 = (insn >> 4) & 0xf; + opc2 = 0; + rt2 = (insn >> 16) & 0xf; + } else { + crn = (insn >> 16) & 0xf; + opc1 = (insn >> 21) & 7; + opc2 = (insn >> 5) & 7; + rt2 = 0; + } + isread = (insn >> 20) & 1; + rt = (insn >> 12) & 0xf; + + ri = get_arm_cp_reginfo(cpu, + ENCODE_CP_REG(cpnum, is64, crn, crm, opc1, opc2)); + if (ri) { + /* Check access permissions */ + if (!cp_access_ok(env, ri, isread)) { + return 1; + } + + /* Handle special cases first */ + switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) { + case ARM_CP_NOP: + return 0; + case ARM_CP_WFI: + if (isread) { + return 1; + } + gen_set_pc_im(s->pc); + s->is_jmp = DISAS_WFI; + break; + default: + break; + } + + if (isread) { + /* Read */ + if (is64) { + TCGv_i64 tmp64; + TCGv_i32 tmp; + if (ri->type & ARM_CP_CONST) { + tmp64 = tcg_const_i64(ri->resetvalue); + } else if (ri->readfn) { + TCGv_ptr tmpptr; + gen_set_pc_im(s->pc); + tmp64 = tcg_temp_new_i64(); + tmpptr = tcg_const_ptr(ri); + gen_helper_get_cp_reg64(tmp64, cpu_env, tmpptr); + tcg_temp_free_ptr(tmpptr); + } else { + tmp64 = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp64, cpu_env, ri->fieldoffset); + } + tmp = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp, tmp64); + store_reg(s, rt, tmp); + tcg_gen_shri_i64(tmp64, tmp64, 32); + tcg_gen_trunc_i64_i32(tmp, tmp64); + store_reg(s, rt2, tmp); + } else { + TCGv tmp; + if (ri->type & ARM_CP_CONST) { + tmp = tcg_const_i32(ri->resetvalue); + } else if (ri->readfn) { + TCGv_ptr tmpptr; + gen_set_pc_im(s->pc); + tmp = tcg_temp_new_i32(); + tmpptr = tcg_const_ptr(ri); + gen_helper_get_cp_reg(tmp, cpu_env, tmpptr); + tcg_temp_free_ptr(tmpptr); + } else { + tmp = load_cpu_offset(ri->fieldoffset); + } + if (rt == 15) { + /* Destination register of r15 for 32 bit loads sets + * the condition codes from the high 4 bits of the value + */ + gen_set_nzcv(tmp); + tcg_temp_free_i32(tmp); + } else { + store_reg(s, rt, tmp); + } + } + } else { + /* Write */ + if (ri->type & ARM_CP_CONST) { + /* If not forbidden by access permissions, treat as WI */ + return 0; + } + + if (is64) { + TCGv tmplo, tmphi; + TCGv_i64 tmp64 = tcg_temp_new_i64(); + tmplo = load_reg(s, rt); + tmphi = load_reg(s, rt2); + tcg_gen_concat_i32_i64(tmp64, tmplo, tmphi); + tcg_temp_free_i32(tmplo); + tcg_temp_free_i32(tmphi); + if (ri->writefn) { + TCGv_ptr tmpptr = tcg_const_ptr(ri); + gen_set_pc_im(s->pc); + gen_helper_set_cp_reg64(cpu_env, tmpptr, tmp64); + tcg_temp_free_ptr(tmpptr); + } else { + tcg_gen_st_i64(tmp64, cpu_env, ri->fieldoffset); + } + tcg_temp_free_i64(tmp64); + } else { + if (ri->writefn) { + TCGv tmp; + TCGv_ptr tmpptr; + gen_set_pc_im(s->pc); + tmp = load_reg(s, rt); + tmpptr = tcg_const_ptr(ri); + gen_helper_set_cp_reg(cpu_env, tmpptr, tmp); + tcg_temp_free_ptr(tmpptr); + tcg_temp_free_i32(tmp); + } else { + TCGv tmp = load_reg(s, rt); + store_cpu_offset(tmp, ri->fieldoffset); + } + } + /* We default to ending the TB on a coprocessor register write, + * but allow this to be suppressed by the register definition + * (usually only necessary to work around guest bugs). + */ + if (!(ri->type & ARM_CP_SUPPRESS_TB_END)) { + gen_lookup_tb(s); + } + } + return 0; + } + + /* Fallback code: handle coprocessor registers not yet converted + * to ARMCPRegInfo. + */ + switch (cpnum) { case 14: /* Coprocessors 7-15 are architecturally reserved by ARM. Unfortunately Intel decided to ignore this. */ -- 2.39.2