@@ -582,6 +582,70 @@ int kvm_arm_cpreg_level(uint64_t regidx)
return KVM_PUT_RUNTIME_STATE;
}
+static int kvm_arm_cpreg_value(ARMCPU *cpu, ptrdiff_t fieldoffset)
+{
+ int i;
+
+ for (i = 0; i < cpu->cpreg_array_len; i++) {
+ uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]);
+ const ARMCPRegInfo *ri;
+ ri = get_arm_cp_reginfo(cpu->cp_regs, regidx);
+ if (!ri) {
+ continue;
+ }
+
+ if (ri->type & ARM_CP_NO_RAW) {
+ continue;
+ }
+
+ if (ri->fieldoffset == fieldoffset) {
+ cpu->cpreg_values[i] = read_raw_cp_reg(&cpu->env, ri);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/* Inject synchronous external abort */
+static void kvm_inject_arm_sea(CPUState *c)
+{
+ ARMCPU *cpu = ARM_CPU(c);
+ CPUARMState *env = &cpu->env;
+ unsigned long cpsr = pstate_read(env);
+ uint32_t esr, ret;
+
+ c->exception_index = EXCP_DATA_ABORT;
+ /* Inject the exception to El1 */
+ env->exception.target_el = 1;
+ CPUClass *cc = CPU_GET_CLASS(c);
+
+ /* Set the DFSC to Synchronous external abort and FnV to not valid,
+ * this will tell guest the FAR_EL1 is UNKNOWN.
+ */
+ esr = (0x10 | (1 << 10));
+
+ /* This exception is EL0 or EL1 fault. */
+ if ((cpsr & 0xf) == PSTATE_MODE_EL0t) {
+ esr |= (EC_DATAABORT << ARM_EL_EC_SHIFT);
+ } else {
+ esr |= (EC_DATAABORT_SAME_EL << ARM_EL_EC_SHIFT);
+ }
+
+ /* In the aarch64, there is only 32-bit instruction*/
+ esr |= ARM_EL_IL;
+ env->exception.syndrome = esr;
+
+ cc->do_interrupt(c);
+
+ /* set ESR_EL1 */
+ ret = kvm_arm_cpreg_value(cpu, offsetof(CPUARMState, cp15.esr_el[1]));
+
+ if (ret) {
+ fprintf(stderr, "<%s> failed to set esr_el1\n", __func__);
+ abort();
+ }
+}
+
#define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x))