| 1 | /* |
| 2 | * QEMU ARM CP Register GCS regiters and instructions |
| 3 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | */ |
| 5 | |
| 6 | #include "qemu/osdep.h" |
| 7 | #include "qemu/timer.h" |
| 8 | #include "exec/icount.h" |
| 9 | #include "hw/core/irq.h" |
| 10 | #include "cpu.h" |
| 11 | #include "cpu-features.h" |
| 12 | #include "cpregs.h" |
| 13 | #include "internals.h" |
| 14 | |
| 15 | |
| 16 | static CPAccessResult access_gcs(CPUARMState *env, const ARMCPRegInfo *ri, |
| 17 | bool isread) |
| 18 | { |
| 19 | if (arm_current_el(env) < 3 |
| 20 | && arm_feature(env, ARM_FEATURE_EL3) |
| 21 | && !(env->cp15.scr_el3 & SCR_GCSEN)) { |
| 22 | return CP_ACCESS_TRAP_EL3; |
| 23 | } |
| 24 | return CP_ACCESS_OK; |
| 25 | } |
| 26 | |
| 27 | static CPAccessResult access_gcs_el0(CPUARMState *env, const ARMCPRegInfo *ri, |
| 28 | bool isread) |
| 29 | { |
| 30 | if (arm_current_el(env) == 0 && !(env->cp15.gcscr_el[0] & GCSCRE0_NTR)) { |
| 31 | return CP_ACCESS_TRAP_EL1; |
| 32 | } |
| 33 | return access_gcs(env, ri, isread); |
| 34 | } |
| 35 | |
| 36 | static void gcspr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| 37 | uint64_t value) |
| 38 | { |
| 39 | /* |
| 40 | * Bits [2:0] are RES0, so we might as well clear them now, |
| 41 | * rather than upon each usage a-la GetCurrentGCSPointer. |
| 42 | */ |
| 43 | raw_write(env, ri, value & ~7); |
| 44 | } |
| 45 | |
| 46 | static CPAccessResult access_gcspushm(CPUARMState *env, const ARMCPRegInfo *ri, |
| 47 | bool isread) |
| 48 | { |
| 49 | int el = arm_current_el(env); |
| 50 | if (!(env->cp15.gcscr_el[el] & GCSCR_PUSHMEN)) { |
| 51 | return CP_ACCESS_TRAP_BIT | (el ? el : 1); |
| 52 | } |
| 53 | return CP_ACCESS_OK; |
| 54 | } |
| 55 | |
| 56 | static CPAccessResult access_gcspushx(CPUARMState *env, const ARMCPRegInfo *ri, |
| 57 | bool isread) |
| 58 | { |
| 59 | /* Trap if lock taken, and enabled. */ |
| 60 | if (!(env->pstate & PSTATE_EXLOCK)) { |
| 61 | int el = arm_current_el(env); |
| 62 | if (env->cp15.gcscr_el[el] & GCSCR_EXLOCKEN) { |
| 63 | return CP_ACCESS_EXLOCK; |
| 64 | } |
| 65 | } |
| 66 | return CP_ACCESS_OK; |
| 67 | } |
| 68 | |
| 69 | static CPAccessResult access_gcspopcx(CPUARMState *env, const ARMCPRegInfo *ri, |
| 70 | bool isread) |
| 71 | { |
| 72 | /* Trap if lock not taken, and enabled. */ |
| 73 | if (env->pstate & PSTATE_EXLOCK) { |
| 74 | int el = arm_current_el(env); |
| 75 | if (env->cp15.gcscr_el[el] & GCSCR_EXLOCKEN) { |
| 76 | return CP_ACCESS_EXLOCK; |
| 77 | } |
| 78 | } |
| 79 | return CP_ACCESS_OK; |
| 80 | } |
| 81 | |
| 82 | static const ARMCPRegInfo gcs_reginfo[] = { |
| 83 | { .name = "GCSCRE0_EL1", .state = ARM_CP_STATE_AA64, |
| 84 | .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 2, |
| 85 | .access = PL1_RW, .accessfn = access_gcs, .fgt = FGT_NGCS_EL0, |
| 86 | .fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[0]) }, |
| 87 | { .name = "GCSCR_EL1", .state = ARM_CP_STATE_AA64, |
| 88 | .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 0, |
| 89 | .access = PL1_RW, .accessfn = access_gcs, .fgt = FGT_NGCS_EL1, |
| 90 | .nv2_redirect_offset = 0x8d0 | NV2_REDIR_NV1, |
| 91 | .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 5, 0), |
| 92 | .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 5, 0), |
| 93 | .fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[1]) }, |
| 94 | { .name = "GCSCR_EL2", .state = ARM_CP_STATE_AA64, |
| 95 | .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 5, .opc2 = 0, |
| 96 | .access = PL2_RW, .accessfn = access_gcs, |
| 97 | .fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[2]) }, |
| 98 | { .name = "GCSCR_EL3", .state = ARM_CP_STATE_AA64, |
| 99 | .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 5, .opc2 = 0, |
| 100 | .access = PL3_RW, .fgt = FGT_GCSCR_EL3, |
| 101 | .fieldoffset = offsetof(CPUARMState, cp15.gcscr_el[3]) }, |
| 102 | |
| 103 | { .name = "GCSPR_EL0", .state = ARM_CP_STATE_AA64, |
| 104 | .opc0 = 3, .opc1 = 3, .crn = 2, .crm = 5, .opc2 = 1, |
| 105 | .access = PL0_R | PL1_W, .accessfn = access_gcs_el0, |
| 106 | .fgt = FGT_NGCS_EL0, .writefn = gcspr_write, |
| 107 | .fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[0]) }, |
| 108 | { .name = "GCSPR_EL1", .state = ARM_CP_STATE_AA64, |
| 109 | .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 5, .opc2 = 1, |
| 110 | .access = PL1_RW, .accessfn = access_gcs, |
| 111 | .fgt = FGT_NGCS_EL1, .writefn = gcspr_write, |
| 112 | .nv2_redirect_offset = 0x8c0 | NV2_REDIR_NV1, |
| 113 | .vhe_redir_to_el2 = ENCODE_AA64_CP_REG(3, 4, 2, 5, 1), |
| 114 | .vhe_redir_to_el01 = ENCODE_AA64_CP_REG(3, 5, 2, 5, 1), |
| 115 | .fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[1]) }, |
| 116 | { .name = "GCSPR_EL2", .state = ARM_CP_STATE_AA64, |
| 117 | .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 5, .opc2 = 1, |
| 118 | .access = PL2_RW, .accessfn = access_gcs, .writefn = gcspr_write, |
| 119 | .fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[2]) }, |
| 120 | { .name = "GCSPR_EL3", .state = ARM_CP_STATE_AA64, |
| 121 | .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 5, .opc2 = 1, |
| 122 | .access = PL3_RW, .fgt = FGT_GCSPR_EL3, |
| 123 | .writefn = gcspr_write, |
| 124 | .fieldoffset = offsetof(CPUARMState, cp15.gcspr_el[2]) }, |
| 125 | |
| 126 | { .name = "GCSPUSHM", .state = ARM_CP_STATE_AA64, |
| 127 | .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 0, |
| 128 | .access = PL0_W, .accessfn = access_gcspushm, |
| 129 | .fgt = FGT_NGCSPUSHM_EL1, .type = ARM_CP_GCSPUSHM }, |
| 130 | { .name = "GCSPOPM", .state = ARM_CP_STATE_AA64, |
| 131 | .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 1, |
| 132 | .access = PL0_R, .type = ARM_CP_GCSPOPM }, |
| 133 | { .name = "GCSSS1", .state = ARM_CP_STATE_AA64, |
| 134 | .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 2, |
| 135 | .access = PL0_W, .type = ARM_CP_GCSSS1 }, |
| 136 | { .name = "GCSSS2", .state = ARM_CP_STATE_AA64, |
| 137 | .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 7, .opc2 = 3, |
| 138 | .access = PL0_R, .type = ARM_CP_GCSSS2 }, |
| 139 | { .name = "GCSPUSHX", .state = ARM_CP_STATE_AA64, |
| 140 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 4, |
| 141 | .access = PL1_W, .accessfn = access_gcspushx, .fgt = FGT_NGCSEPP, |
| 142 | .type = ARM_CP_GCSPUSHX }, |
| 143 | { .name = "GCSPOPCX", .state = ARM_CP_STATE_AA64, |
| 144 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 5, |
| 145 | .access = PL1_W, .accessfn = access_gcspopcx, .fgt = FGT_NGCSEPP, |
| 146 | .type = ARM_CP_GCSPOPCX }, |
| 147 | { .name = "GCSPOPX", .state = ARM_CP_STATE_AA64, |
| 148 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 7, .opc2 = 6, |
| 149 | .access = PL1_W, .type = ARM_CP_GCSPOPX }, |
| 150 | }; |
| 151 | |
| 152 | void define_gcs_cpregs(ARMCPU *cpu) |
| 153 | { |
| 154 | if (cpu_isar_feature(aa64_gcs, cpu)) { |
| 155 | define_arm_cp_regs(cpu, gcs_reginfo); |
| 156 | } |
| 157 | } |