Compare commits
	
		
			5 Commits
		
	
	
		
			v4.0.0-rc1
			...
			singlestep
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 34370ee6ff | ||
|  | 9953b32e77 | ||
|  | 0da2fd84e1 | ||
|  | b82417e8bf | ||
|  | 0b72719af8 | 
| @@ -2267,6 +2267,13 @@ bool kvm_arm_supports_user_irq(void) | |||||||
|     return kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ); |     return kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Whether the KVM_SET_GUEST_DEBUG ioctl supports single stepping */ | ||||||
|  | int kvm_has_guestdbg_singlestep(void) | ||||||
|  | { | ||||||
|  |     /* return kvm_check_extension(kvm_state, KVM_CAP_GUEST_DEBUG_SSTEP); */ | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| #ifdef KVM_CAP_SET_GUEST_DEBUG | #ifdef KVM_CAP_SET_GUEST_DEBUG | ||||||
| struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, | struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, | ||||||
|                                                  target_ulong pc) |                                                  target_ulong pc) | ||||||
| @@ -2316,6 +2323,15 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) | |||||||
|     return data.err; |     return data.err; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void kvm_set_singlestep(CPUState *cs, int enabled) | ||||||
|  | { | ||||||
|  |     if (kvm_has_guestdbg_singlestep()) { | ||||||
|  |         kvm_update_guest_debug(cs, 0); | ||||||
|  |     } else { | ||||||
|  |         kvm_arch_set_singlestep(cs, enabled); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, | int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, | ||||||
|                           target_ulong len, int type) |                           target_ulong len, int type) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -79,6 +79,10 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) | |||||||
|     return -ENOSYS; |     return -ENOSYS; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void kvm_set_singlestep(CPUState *cs, int enabled) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
| int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, | int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, | ||||||
|                           target_ulong len, int type) |                           target_ulong len, int type) | ||||||
| { | { | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								exec.c
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								exec.c
									
									
									
									
									
								
							| @@ -1236,7 +1236,7 @@ void cpu_single_step(CPUState *cpu, int enabled) | |||||||
|     if (cpu->singlestep_enabled != enabled) { |     if (cpu->singlestep_enabled != enabled) { | ||||||
|         cpu->singlestep_enabled = enabled; |         cpu->singlestep_enabled = enabled; | ||||||
|         if (kvm_enabled()) { |         if (kvm_enabled()) { | ||||||
|             kvm_update_guest_debug(cpu, 0); |             kvm_set_singlestep(cpu, enabled); | ||||||
|         } else { |         } else { | ||||||
|             /* must flush all the translated code to avoid inconsistencies */ |             /* must flush all the translated code to avoid inconsistencies */ | ||||||
|             /* XXX: only flush what is necessary */ |             /* XXX: only flush what is necessary */ | ||||||
|   | |||||||
| @@ -214,6 +214,7 @@ int kvm_has_pit_state2(void); | |||||||
| int kvm_has_many_ioeventfds(void); | int kvm_has_many_ioeventfds(void); | ||||||
| int kvm_has_gsi_routing(void); | int kvm_has_gsi_routing(void); | ||||||
| int kvm_has_intx_set_mask(void); | int kvm_has_intx_set_mask(void); | ||||||
|  | int kvm_has_guestdbg_singlestep(void); | ||||||
|  |  | ||||||
| int kvm_init_vcpu(CPUState *cpu); | int kvm_init_vcpu(CPUState *cpu); | ||||||
| int kvm_cpu_exec(CPUState *cpu); | int kvm_cpu_exec(CPUState *cpu); | ||||||
| @@ -246,6 +247,7 @@ bool kvm_memcrypt_enabled(void); | |||||||
|  */ |  */ | ||||||
| int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len); | int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len); | ||||||
|  |  | ||||||
|  | void kvm_arch_set_singlestep(CPUState *cpu, int enabled); | ||||||
|  |  | ||||||
| #ifdef NEED_CPU_H | #ifdef NEED_CPU_H | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| @@ -258,6 +260,7 @@ int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr, | |||||||
|                           target_ulong len, int type); |                           target_ulong len, int type); | ||||||
| void kvm_remove_all_breakpoints(CPUState *cpu); | void kvm_remove_all_breakpoints(CPUState *cpu); | ||||||
| int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap); | int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap); | ||||||
|  | void kvm_set_singlestep(CPUState *cs, int enabled); | ||||||
|  |  | ||||||
| int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); | int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); | ||||||
| int kvm_on_sigbus(int code, void *addr); | int kvm_on_sigbus(int code, void *addr); | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ stub-obj-y += get-vm-name.o | |||||||
| stub-obj-y += iothread.o | stub-obj-y += iothread.o | ||||||
| stub-obj-y += iothread-lock.o | stub-obj-y += iothread-lock.o | ||||||
| stub-obj-y += is-daemonized.o | stub-obj-y += is-daemonized.o | ||||||
|  | stub-obj-y += kvm-arch-set-singlestep.o | ||||||
| stub-obj-$(CONFIG_LINUX_AIO) += linux-aio.o | stub-obj-$(CONFIG_LINUX_AIO) += linux-aio.o | ||||||
| stub-obj-y += machine-init-done.o | stub-obj-y += machine-init-done.o | ||||||
| stub-obj-y += migr-blocker.o | stub-obj-y += migr-blocker.o | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								stubs/kvm-arch-set-singlestep.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								stubs/kvm-arch-set-singlestep.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | #include "qemu/osdep.h" | ||||||
|  | #include "qemu/error-report.h" | ||||||
|  | #include "sysemu/kvm.h" | ||||||
|  |  | ||||||
|  | void kvm_arch_set_singlestep(CPUState *cpu, int enabled) | ||||||
|  | { | ||||||
|  |     warn_report("KVM does not support single stepping"); | ||||||
|  | } | ||||||
| @@ -1171,6 +1171,11 @@ struct CPUPPCState { | |||||||
|     uint32_t tm_vscr; |     uint32_t tm_vscr; | ||||||
|     uint64_t tm_dscr; |     uint64_t tm_dscr; | ||||||
|     uint64_t tm_tar; |     uint64_t tm_tar; | ||||||
|  |  | ||||||
|  |     /* Used for software single step */ | ||||||
|  |     target_ulong sstep_msr; | ||||||
|  |     target_ulong sstep_srr0; | ||||||
|  |     target_ulong sstep_srr1; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #define SET_FIT_PERIOD(a_, b_, c_, d_)          \ | #define SET_FIT_PERIOD(a_, b_, c_, d_)          \ | ||||||
| @@ -1266,6 +1271,7 @@ struct PPCVirtualHypervisorClass { | |||||||
|     OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \ |     OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \ | ||||||
|                      TYPE_PPC_VIRTUAL_HYPERVISOR) |                      TYPE_PPC_VIRTUAL_HYPERVISOR) | ||||||
|  |  | ||||||
|  | target_ulong ppc_get_trace_int_handler_addr(CPUState *cs); | ||||||
| void ppc_cpu_do_interrupt(CPUState *cpu); | void ppc_cpu_do_interrupt(CPUState *cpu); | ||||||
| bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req); | bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req); | ||||||
| void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, | void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, | ||||||
| @@ -1281,6 +1287,12 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg); | |||||||
| void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu); | void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu); | ||||||
| const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name); | const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name); | ||||||
| #endif | #endif | ||||||
|  | uint32_t ppc_gdb_read_insn(CPUState *cs, target_ulong addr); | ||||||
|  | uint32_t ppc_gdb_get_op(uint32_t insn); | ||||||
|  | uint32_t ppc_gdb_get_xop(uint32_t insn); | ||||||
|  | uint32_t ppc_gdb_get_spr(uint32_t insn); | ||||||
|  | uint32_t ppc_gdb_get_rt(uint32_t insn); | ||||||
|  |  | ||||||
| int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, | int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, | ||||||
|                                int cpuid, void *opaque); |                                int cpuid, void *opaque); | ||||||
| int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, | int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, | ||||||
| @@ -2227,6 +2239,10 @@ enum { | |||||||
|                         PPC2_ISA300) |                         PPC2_ISA300) | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | #define XOP_RFID 18 | ||||||
|  | #define XOP_MFMSR 83 | ||||||
|  | #define XOP_MTSPR 467 | ||||||
|  |  | ||||||
| /*****************************************************************************/ | /*****************************************************************************/ | ||||||
| /* Memory access type : | /* Memory access type : | ||||||
|  * may be needed for precise access rights control and precise exceptions. |  * may be needed for precise access rights control and precise exceptions. | ||||||
|   | |||||||
| @@ -107,6 +107,26 @@ static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp, | |||||||
|     return POWERPC_EXCP_RESET; |     return POWERPC_EXCP_RESET; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail) | ||||||
|  | { | ||||||
|  |     uint64_t offset = 0; | ||||||
|  |  | ||||||
|  |     switch (ail) { | ||||||
|  |     case AIL_NONE: | ||||||
|  |         break; | ||||||
|  |     case AIL_0001_8000: | ||||||
|  |         offset = 0x18000; | ||||||
|  |         break; | ||||||
|  |     case AIL_C000_0000_0000_4000: | ||||||
|  |         offset = 0xc000000000004000ull; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         cpu_abort(cs, "Invalid AIL combination %d\n", ail); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Note that this function should be greatly optimized | /* Note that this function should be greatly optimized | ||||||
|  * when called with a constant excp, from ppc_hw_interrupt |  * when called with a constant excp, from ppc_hw_interrupt | ||||||
| @@ -708,17 +728,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) | |||||||
|     /* Handle AIL */ |     /* Handle AIL */ | ||||||
|     if (ail) { |     if (ail) { | ||||||
|         new_msr |= (1 << MSR_IR) | (1 << MSR_DR); |         new_msr |= (1 << MSR_IR) | (1 << MSR_DR); | ||||||
|         switch(ail) { |         vector |= ppc_excp_vector_offset(cs, ail); | ||||||
|         case AIL_0001_8000: |  | ||||||
|             vector |= 0x18000; |  | ||||||
|             break; |  | ||||||
|         case AIL_C000_0000_0000_4000: |  | ||||||
|             vector |= 0xc000000000004000ull; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             cpu_abort(cs, "Invalid AIL combination %d\n", ail); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #if defined(TARGET_PPC64) | #if defined(TARGET_PPC64) | ||||||
| @@ -760,6 +770,17 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) | |||||||
|     check_tlb_flush(env, false); |     check_tlb_flush(env, false); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | target_ulong ppc_get_trace_int_handler_addr(CPUState *cs) | ||||||
|  | { | ||||||
|  |     PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||||
|  |     CPUPPCState *env = &cpu->env; | ||||||
|  |     int ail; | ||||||
|  |  | ||||||
|  |     ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT; | ||||||
|  |     return env->excp_vectors[POWERPC_EXCP_TRACE] | | ||||||
|  |         ppc_excp_vector_offset(cs, ail); | ||||||
|  | } | ||||||
|  |  | ||||||
| void ppc_cpu_do_interrupt(CPUState *cs) | void ppc_cpu_do_interrupt(CPUState *cs) | ||||||
| { | { | ||||||
|     PowerPCCPU *cpu = POWERPC_CPU(cs); |     PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||||
|   | |||||||
| @@ -380,3 +380,38 @@ const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name) | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | uint32_t ppc_gdb_read_insn(CPUState *cs, target_ulong addr) | ||||||
|  | { | ||||||
|  |     PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||||
|  |     CPUPPCState *env = &cpu->env; | ||||||
|  |     uint32_t insn; | ||||||
|  |  | ||||||
|  |     cpu_memory_rw_debug(cs, addr, (uint8_t *)&insn, sizeof(insn), 0); | ||||||
|  |  | ||||||
|  |     if (msr_le) { | ||||||
|  |         return ldl_le_p(&insn); | ||||||
|  |     } else { | ||||||
|  |         return ldl_be_p(&insn); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t ppc_gdb_get_op(uint32_t insn) | ||||||
|  | { | ||||||
|  |     return extract32(insn, 26, 6); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t ppc_gdb_get_xop(uint32_t insn) | ||||||
|  | { | ||||||
|  |     return extract32(insn, 1, 10); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t ppc_gdb_get_spr(uint32_t insn) | ||||||
|  | { | ||||||
|  |     return extract32(insn, 11, 5) << 5 | extract32(insn, 16, 5); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t ppc_gdb_get_rt(uint32_t insn) | ||||||
|  | { | ||||||
|  |     return extract32(insn, 21, 5); | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										314
									
								
								target/ppc/kvm.c
									
									
									
									
									
								
							
							
						
						
									
										314
									
								
								target/ppc/kvm.c
									
									
									
									
									
								
							| @@ -1554,6 +1554,86 @@ void kvm_arch_remove_all_hw_breakpoints(void) | |||||||
|     nb_hw_breakpoint = nb_hw_watchpoint = 0; |     nb_hw_breakpoint = nb_hw_watchpoint = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void kvm_arch_set_singlestep(CPUState *cs, int enabled) | ||||||
|  | { | ||||||
|  |     PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||||
|  |     CPUPPCState *env = &cpu->env; | ||||||
|  |     target_ulong trace_handler_addr; | ||||||
|  |     uint32_t insn; | ||||||
|  |     bool rfid; | ||||||
|  |  | ||||||
|  |     if (!enabled) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     cpu_synchronize_state(cs); | ||||||
|  |     insn = ppc_gdb_read_insn(cs, env->nip); | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |      * rfid needs special handling because it: | ||||||
|  |      *   - overwrites NIP with SRR0; | ||||||
|  |      *   - overwrites MSR with SRR1; | ||||||
|  |      *   - cannot be single stepped. | ||||||
|  |      */ | ||||||
|  |     rfid = ppc_gdb_get_op(insn) == 19 && ppc_gdb_get_xop(insn) == XOP_RFID; | ||||||
|  |  | ||||||
|  |     if (rfid && kvm_find_sw_breakpoint(cs, env->spr[SPR_SRR0])) { | ||||||
|  |         /* | ||||||
|  |          * There is a breakpoint at the next instruction address. It | ||||||
|  |          * will already cause the vm exit we need for the single step, | ||||||
|  |          * so there's nothing to be done. | ||||||
|  |          */ | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |      * Save the registers that will be affected by the single step | ||||||
|  |      * mechanism. These will be restored after the step at | ||||||
|  |      * kvm_handle_singlestep. | ||||||
|  |      */ | ||||||
|  |     env->sstep_msr = env->msr; | ||||||
|  |     env->sstep_srr0 = env->spr[SPR_SRR0]; | ||||||
|  |     env->sstep_srr1 = env->spr[SPR_SRR1]; | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |      * MSR_SE = 1 will cause a Trace Interrupt in the guest after the | ||||||
|  |      * next instruction executes. If this is a rfid, use SRR1 instead | ||||||
|  |      * of MSR. | ||||||
|  |      */ | ||||||
|  |     if (rfid) { | ||||||
|  |         if ((env->spr[SPR_SRR1] >> MSR_SE) & 1) { | ||||||
|  |             /* | ||||||
|  |              * The guest is doing a single step itself. Make sure we | ||||||
|  |              * restore it later. | ||||||
|  |              */ | ||||||
|  |             env->sstep_msr |= (1ULL << MSR_SE); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         env->spr[SPR_SRR1] |= (1ULL << MSR_SE); | ||||||
|  |     } else { | ||||||
|  |         env->msr |= (1ULL << MSR_SE); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |      * We set a breakpoint at the interrupt handler address so | ||||||
|  |      * that the singlestep will be seen by KVM (this is treated by | ||||||
|  |      * KVM like an ordinary breakpoint) and control is returned to | ||||||
|  |      * QEMU. | ||||||
|  |      */ | ||||||
|  |     trace_handler_addr = ppc_get_trace_int_handler_addr(cs); | ||||||
|  |  | ||||||
|  |     if (env->nip == trace_handler_addr) { | ||||||
|  |         /* | ||||||
|  |          * We are trying to step over the interrupt handler | ||||||
|  |          * address itself; move the breakpoint to the next | ||||||
|  |          * instruction. | ||||||
|  |          */ | ||||||
|  |         trace_handler_addr += 4; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     kvm_insert_breakpoint(cs, trace_handler_addr, 4, GDB_BREAKPOINT_SW); | ||||||
|  | } | ||||||
|  |  | ||||||
| void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) | void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) | ||||||
| { | { | ||||||
|     int n; |     int n; | ||||||
| @@ -1593,70 +1673,194 @@ void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Revert any side-effects caused during single step */ | ||||||
|  | static void restore_singlestep_env(CPUState *cs) | ||||||
|  | { | ||||||
|  |     PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||||
|  |     CPUPPCState *env = &cpu->env; | ||||||
|  |     uint32_t insn; | ||||||
|  |     int reg; | ||||||
|  |     int spr; | ||||||
|  |  | ||||||
|  |     insn = ppc_gdb_read_insn(cs, env->spr[SPR_SRR0] - 4); | ||||||
|  |  | ||||||
|  |     env->spr[SPR_SRR0] = env->sstep_srr0; | ||||||
|  |     env->spr[SPR_SRR1] = env->sstep_srr1; | ||||||
|  |  | ||||||
|  |     if (ppc_gdb_get_op(insn) != 31) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     reg = ppc_gdb_get_rt(insn); | ||||||
|  |  | ||||||
|  |     switch (ppc_gdb_get_xop(insn)) { | ||||||
|  |     case XOP_MTSPR: | ||||||
|  |         /* | ||||||
|  |          * mtspr: the guest altered the SRR, so do not use the | ||||||
|  |          *        pre-step value. | ||||||
|  |          */ | ||||||
|  |         spr = ppc_gdb_get_spr(insn); | ||||||
|  |         if (spr == SPR_SRR0 || spr == SPR_SRR1) { | ||||||
|  |             env->spr[spr] = env->gpr[reg]; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case XOP_MFMSR: | ||||||
|  |         /* | ||||||
|  |          * mfmsr: clear MSR_SE bit to avoid the guest knowing | ||||||
|  |          *         that it is being single-stepped. | ||||||
|  |          */ | ||||||
|  |         env->gpr[reg] &= ~(1ULL << MSR_SE); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int kvm_handle_singlestep(CPUState *cs, target_ulong address) | ||||||
|  | { | ||||||
|  |     PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||||
|  |     CPUPPCState *env = &cpu->env; | ||||||
|  |     target_ulong trace_handler_addr; | ||||||
|  |  | ||||||
|  |     if (kvm_has_guestdbg_singlestep()) { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     cpu_synchronize_state(cs); | ||||||
|  |     trace_handler_addr = ppc_get_trace_int_handler_addr(cs); | ||||||
|  |  | ||||||
|  |     if (address == trace_handler_addr) { | ||||||
|  |         kvm_remove_breakpoint(cs, trace_handler_addr, 4, GDB_BREAKPOINT_SW); | ||||||
|  |  | ||||||
|  |         if (env->sstep_msr & (1ULL << MSR_SE)) { | ||||||
|  |             /* | ||||||
|  |              * The guest expects the last instruction to have caused a | ||||||
|  |              * single step, go back into the interrupt handler. | ||||||
|  |              */ | ||||||
|  |             return 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         env->nip = env->spr[SPR_SRR0]; | ||||||
|  |         /* Bits 33-36, 43-47 are set by the interrupt */ | ||||||
|  |         env->msr = env->spr[SPR_SRR1] & ~(1ULL << MSR_SE | | ||||||
|  |                                           PPC_BITMASK(33, 36) | | ||||||
|  |                                           PPC_BITMASK(43, 47)); | ||||||
|  |         restore_singlestep_env(cs); | ||||||
|  |  | ||||||
|  |     } else if (address == trace_handler_addr + 4) { | ||||||
|  |         /* | ||||||
|  |          * A step at trace_handler_addr would interfere with the | ||||||
|  |          * singlestep mechanism itself, so we have previously | ||||||
|  |          * displaced the breakpoint to the next instruction. | ||||||
|  |          */ | ||||||
|  |         kvm_remove_breakpoint(cs, trace_handler_addr + 4, 4, GDB_BREAKPOINT_SW); | ||||||
|  |         restore_singlestep_env(cs); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int kvm_handle_hw_breakpoint(CPUState *cs, | ||||||
|  |                                     struct kvm_debug_exit_arch *arch_info) | ||||||
|  | { | ||||||
|  |     int handle = 0; | ||||||
|  |     int n; | ||||||
|  |     int flag = 0; | ||||||
|  |  | ||||||
|  |     if (nb_hw_breakpoint + nb_hw_watchpoint > 0) { | ||||||
|  |         if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) { | ||||||
|  |             n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW); | ||||||
|  |             if (n >= 0) { | ||||||
|  |                 handle = 1; | ||||||
|  |             } | ||||||
|  |         } else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ | | ||||||
|  |                                         KVMPPC_DEBUG_WATCH_WRITE)) { | ||||||
|  |             n = find_hw_watchpoint(arch_info->address,  &flag); | ||||||
|  |             if (n >= 0) { | ||||||
|  |                 handle = 1; | ||||||
|  |                 cs->watchpoint_hit = &hw_watchpoint; | ||||||
|  |                 hw_watchpoint.vaddr = hw_debug_points[n].addr; | ||||||
|  |                 hw_watchpoint.flags = flag; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return handle; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int kvm_handle_sw_breakpoint(CPUState *cs, target_ulong address) | ||||||
|  | { | ||||||
|  |     target_ulong trace_handler_addr; | ||||||
|  |  | ||||||
|  |     if (kvm_has_guestdbg_singlestep()) { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     cpu_synchronize_state(cs); | ||||||
|  |     trace_handler_addr = ppc_get_trace_int_handler_addr(cs); | ||||||
|  |  | ||||||
|  |     if (address == trace_handler_addr) { | ||||||
|  |         CPU_FOREACH(cs) { | ||||||
|  |             if (cs->singlestep_enabled) { | ||||||
|  |                 /* | ||||||
|  |                  * We hit this breakpoint while another cpu is doing a | ||||||
|  |                  * software single step. Go back into the guest to | ||||||
|  |                  * give chance for the single step to finish. | ||||||
|  |                  */ | ||||||
|  |                 return 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run) | static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run) | ||||||
| { | { | ||||||
|     CPUState *cs = CPU(cpu); |     CPUState *cs = CPU(cpu); | ||||||
|     CPUPPCState *env = &cpu->env; |     CPUPPCState *env = &cpu->env; | ||||||
|     struct kvm_debug_exit_arch *arch_info = &run->debug.arch; |     struct kvm_debug_exit_arch *arch_info = &run->debug.arch; | ||||||
|     int handle = 0; |  | ||||||
|     int n; |  | ||||||
|     int flag = 0; |  | ||||||
|  |  | ||||||
|     if (cs->singlestep_enabled) { |     if (cs->singlestep_enabled) { | ||||||
|         handle = 1; |         return kvm_handle_singlestep(cs, arch_info->address); | ||||||
|     } else if (arch_info->status) { |  | ||||||
|         if (nb_hw_breakpoint + nb_hw_watchpoint > 0) { |  | ||||||
|             if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) { |  | ||||||
|                 n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW); |  | ||||||
|                 if (n >= 0) { |  | ||||||
|                     handle = 1; |  | ||||||
|                 } |  | ||||||
|             } else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ | |  | ||||||
|                                             KVMPPC_DEBUG_WATCH_WRITE)) { |  | ||||||
|                 n = find_hw_watchpoint(arch_info->address,  &flag); |  | ||||||
|                 if (n >= 0) { |  | ||||||
|                     handle = 1; |  | ||||||
|                     cs->watchpoint_hit = &hw_watchpoint; |  | ||||||
|                     hw_watchpoint.vaddr = hw_debug_points[n].addr; |  | ||||||
|                     hw_watchpoint.flags = flag; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else if (kvm_find_sw_breakpoint(cs, arch_info->address)) { |  | ||||||
|         handle = 1; |  | ||||||
|     } else { |  | ||||||
|         /* QEMU is not able to handle debug exception, so inject |  | ||||||
|          * program exception to guest; |  | ||||||
|          * Yes program exception NOT debug exception !! |  | ||||||
|          * When QEMU is using debug resources then debug exception must |  | ||||||
|          * be always set. To achieve this we set MSR_DE and also set |  | ||||||
|          * MSRP_DEP so guest cannot change MSR_DE. |  | ||||||
|          * When emulating debug resource for guest we want guest |  | ||||||
|          * to control MSR_DE (enable/disable debug interrupt on need). |  | ||||||
|          * Supporting both configurations are NOT possible. |  | ||||||
|          * So the result is that we cannot share debug resources |  | ||||||
|          * between QEMU and Guest on BOOKE architecture. |  | ||||||
|          * In the current design QEMU gets the priority over guest, |  | ||||||
|          * this means that if QEMU is using debug resources then guest |  | ||||||
|          * cannot use them; |  | ||||||
|          * For software breakpoint QEMU uses a privileged instruction; |  | ||||||
|          * So there cannot be any reason that we are here for guest |  | ||||||
|          * set debug exception, only possibility is guest executed a |  | ||||||
|          * privileged / illegal instruction and that's why we are |  | ||||||
|          * injecting a program interrupt. |  | ||||||
|          */ |  | ||||||
|  |  | ||||||
|         cpu_synchronize_state(cs); |  | ||||||
|         /* env->nip is PC, so increment this by 4 to use |  | ||||||
|          * ppc_cpu_do_interrupt(), which set srr0 = env->nip - 4. |  | ||||||
|          */ |  | ||||||
|         env->nip += 4; |  | ||||||
|         cs->exception_index = POWERPC_EXCP_PROGRAM; |  | ||||||
|         env->error_code = POWERPC_EXCP_INVAL; |  | ||||||
|         ppc_cpu_do_interrupt(cs); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return handle; |     if (arch_info->status) { | ||||||
|  |         return kvm_handle_hw_breakpoint(cs, arch_info); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (kvm_find_sw_breakpoint(cs, arch_info->address)) { | ||||||
|  |         return kvm_handle_sw_breakpoint(cs, arch_info->address); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |      * QEMU is not able to handle debug exception, so inject | ||||||
|  |      * program exception to guest; | ||||||
|  |      * Yes program exception NOT debug exception !! | ||||||
|  |      * When QEMU is using debug resources then debug exception must | ||||||
|  |      * be always set. To achieve this we set MSR_DE and also set | ||||||
|  |      * MSRP_DEP so guest cannot change MSR_DE. | ||||||
|  |      * When emulating debug resource for guest we want guest | ||||||
|  |      * to control MSR_DE (enable/disable debug interrupt on need). | ||||||
|  |      * Supporting both configurations are NOT possible. | ||||||
|  |      * So the result is that we cannot share debug resources | ||||||
|  |      * between QEMU and Guest on BOOKE architecture. | ||||||
|  |      * In the current design QEMU gets the priority over guest, | ||||||
|  |      * this means that if QEMU is using debug resources then guest | ||||||
|  |      * cannot use them; | ||||||
|  |      * For software breakpoint QEMU uses a privileged instruction; | ||||||
|  |      * So there cannot be any reason that we are here for guest | ||||||
|  |      * set debug exception, only possibility is guest executed a | ||||||
|  |      * privileged / illegal instruction and that's why we are | ||||||
|  |      * injecting a program interrupt. | ||||||
|  |      */ | ||||||
|  |     cpu_synchronize_state(cs); | ||||||
|  |     /* | ||||||
|  |      * env->nip is PC, so increment this by 4 to use | ||||||
|  |      * ppc_cpu_do_interrupt(), which set srr0 = env->nip - 4. | ||||||
|  |      */ | ||||||
|  |     env->nip += 4; | ||||||
|  |     cs->exception_index = POWERPC_EXCP_PROGRAM; | ||||||
|  |     env->error_code = POWERPC_EXCP_INVAL; | ||||||
|  |     ppc_cpu_do_interrupt(cs); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) | int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user