aboutsummaryrefslogtreecommitdiff
path: root/accel/tcg/user-exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'accel/tcg/user-exec.c')
-rw-r--r--accel/tcg/user-exec.c66
1 files changed, 47 insertions, 19 deletions
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
index 4384b59a4d..897d1571c4 100644
--- a/accel/tcg/user-exec.c
+++ b/accel/tcg/user-exec.c
@@ -64,27 +64,56 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
CPUState *cpu = current_cpu;
CPUClass *cc;
unsigned long address = (unsigned long)info->si_addr;
- MMUAccessType access_type;
+ MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
- /* We must handle PC addresses from two different sources:
- * a call return address and a signal frame address.
- *
- * Within cpu_restore_state_from_tb we assume the former and adjust
- * the address by -GETPC_ADJ so that the address is within the call
- * insn so that addr does not accidentally match the beginning of the
- * next guest insn.
- *
- * However, when the PC comes from the signal frame, it points to
- * the actual faulting host insn and not a call insn. Subtracting
- * GETPC_ADJ in that case may accidentally match the previous guest insn.
- *
- * So for the later case, adjust forward to compensate for what
- * will be done later by cpu_restore_state_from_tb.
- */
- if (helper_retaddr) {
+ switch (helper_retaddr) {
+ default:
+ /*
+ * Fault during host memory operation within a helper function.
+ * The helper's host return address, saved here, gives us a
+ * pointer into the generated code that will unwind to the
+ * correct guest pc.
+ */
pc = helper_retaddr;
- } else {
+ break;
+
+ case 0:
+ /*
+ * Fault during host memory operation within generated code.
+ * (Or, a unrelated bug within qemu, but we can't tell from here).
+ *
+ * We take the host pc from the signal frame. However, we cannot
+ * use that value directly. Within cpu_restore_state_from_tb, we
+ * assume PC comes from GETPC(), as used by the helper functions,
+ * so we adjust the address by -GETPC_ADJ to form an address that
+ * is within the call insn, so that the address does not accidentially
+ * match the beginning of the next guest insn. However, when the
+ * pc comes from the signal frame it points to the actual faulting
+ * host memory insn and not the return from a call insn.
+ *
+ * Therefore, adjust to compensate for what will be done later
+ * by cpu_restore_state_from_tb.
+ */
pc += GETPC_ADJ;
+ break;
+
+ case 1:
+ /*
+ * Fault during host read for translation, or loosely, "execution".
+ *
+ * The guest pc is already pointing to the start of the TB for which
+ * code is being generated. If the guest translator manages the
+ * page crossings correctly, this is exactly the correct address
+ * (and if the translator doesn't handle page boundaries correctly
+ * there's little we can do about that here). Therefore, do not
+ * trigger the unwinder.
+ *
+ * Like tb_gen_code, release the memory lock before cpu_loop_exit.
+ */
+ pc = 0;
+ access_type = MMU_INST_FETCH;
+ mmap_unlock();
+ break;
}
/* For synchronous signals we expect to be coming from the vCPU
@@ -155,7 +184,6 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
clear_helper_retaddr();
cc = CPU_GET_CLASS(cpu);
- access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc);
g_assert_not_reached();
}