diff options
author | Hollis Blanchard <hollisb@us.ibm.com> | 2008-07-25 14:54:49 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2008-10-15 04:15:16 -0400 |
commit | 6a0ab738ef42d87951b3980f61b1f4cbb14d4171 (patch) | |
tree | 8650adf6b8c2df9817c7d6dff2a2f8a4b4904abc /arch/powerpc/kvm | |
parent | b5e2fec0ebc3fcaff954092bb69444a67a904c0a (diff) |
KVM: ppc: guest breakpoint support
Allow host userspace to program hardware debug registers to set breakpoints
inside guests.
Signed-off-by: Jerone Young <jyoung5@us.ibm.com>
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'arch/powerpc/kvm')
-rw-r--r-- | arch/powerpc/kvm/booke_guest.c | 15 | ||||
-rw-r--r-- | arch/powerpc/kvm/booke_interrupts.S | 11 | ||||
-rw-r--r-- | arch/powerpc/kvm/powerpc.c | 84 |
3 files changed, 108 insertions, 2 deletions
diff --git a/arch/powerpc/kvm/booke_guest.c b/arch/powerpc/kvm/booke_guest.c index 9c8ad850c6e3..3cca079975e1 100644 --- a/arch/powerpc/kvm/booke_guest.c +++ b/arch/powerpc/kvm/booke_guest.c | |||
@@ -410,6 +410,21 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
410 | break; | 410 | break; |
411 | } | 411 | } |
412 | 412 | ||
413 | case BOOKE_INTERRUPT_DEBUG: { | ||
414 | u32 dbsr; | ||
415 | |||
416 | vcpu->arch.pc = mfspr(SPRN_CSRR0); | ||
417 | |||
418 | /* clear IAC events in DBSR register */ | ||
419 | dbsr = mfspr(SPRN_DBSR); | ||
420 | dbsr &= DBSR_IAC1 | DBSR_IAC2 | DBSR_IAC3 | DBSR_IAC4; | ||
421 | mtspr(SPRN_DBSR, dbsr); | ||
422 | |||
423 | run->exit_reason = KVM_EXIT_DEBUG; | ||
424 | r = RESUME_HOST; | ||
425 | break; | ||
426 | } | ||
427 | |||
413 | default: | 428 | default: |
414 | printk(KERN_EMERG "exit_nr %d\n", exit_nr); | 429 | printk(KERN_EMERG "exit_nr %d\n", exit_nr); |
415 | BUG(); | 430 | BUG(); |
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S index 3b653b5309b8..8eaba2613ffd 100644 --- a/arch/powerpc/kvm/booke_interrupts.S +++ b/arch/powerpc/kvm/booke_interrupts.S | |||
@@ -42,7 +42,8 @@ | |||
42 | #define HOST_STACK_LR (HOST_STACK_SIZE + 4) /* In caller stack frame. */ | 42 | #define HOST_STACK_LR (HOST_STACK_SIZE + 4) /* In caller stack frame. */ |
43 | 43 | ||
44 | #define NEED_INST_MASK ((1<<BOOKE_INTERRUPT_PROGRAM) | \ | 44 | #define NEED_INST_MASK ((1<<BOOKE_INTERRUPT_PROGRAM) | \ |
45 | (1<<BOOKE_INTERRUPT_DTLB_MISS)) | 45 | (1<<BOOKE_INTERRUPT_DTLB_MISS) | \ |
46 | (1<<BOOKE_INTERRUPT_DEBUG)) | ||
46 | 47 | ||
47 | #define NEED_DEAR_MASK ((1<<BOOKE_INTERRUPT_DATA_STORAGE) | \ | 48 | #define NEED_DEAR_MASK ((1<<BOOKE_INTERRUPT_DATA_STORAGE) | \ |
48 | (1<<BOOKE_INTERRUPT_DTLB_MISS)) | 49 | (1<<BOOKE_INTERRUPT_DTLB_MISS)) |
@@ -431,6 +432,14 @@ lightweight_exit: | |||
431 | oris r3, r3, KVMPPC_MSR_MASK@h | 432 | oris r3, r3, KVMPPC_MSR_MASK@h |
432 | ori r3, r3, KVMPPC_MSR_MASK@l | 433 | ori r3, r3, KVMPPC_MSR_MASK@l |
433 | mtsrr1 r3 | 434 | mtsrr1 r3 |
435 | |||
436 | /* Clear any debug events which occurred since we disabled MSR[DE]. | ||
437 | * XXX This gives us a 3-instruction window in which a breakpoint | ||
438 | * intended for guest context could fire in the host instead. */ | ||
439 | lis r3, 0xffff | ||
440 | ori r3, r3, 0xffff | ||
441 | mtspr SPRN_DBSR, r3 | ||
442 | |||
434 | lwz r3, VCPU_GPR(r3)(r4) | 443 | lwz r3, VCPU_GPR(r3)(r4) |
435 | lwz r4, VCPU_GPR(r4)(r4) | 444 | lwz r4, VCPU_GPR(r4)(r4) |
436 | rfi | 445 | rfi |
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 53826a5f6c06..b75607180ddb 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c | |||
@@ -239,18 +239,100 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) | |||
239 | { | 239 | { |
240 | } | 240 | } |
241 | 241 | ||
242 | /* Note: clearing MSR[DE] just means that the debug interrupt will not be | ||
243 | * delivered *immediately*. Instead, it simply sets the appropriate DBSR bits. | ||
244 | * If those DBSR bits are still set when MSR[DE] is re-enabled, the interrupt | ||
245 | * will be delivered as an "imprecise debug event" (which is indicated by | ||
246 | * DBSR[IDE]. | ||
247 | */ | ||
248 | static void kvmppc_disable_debug_interrupts(void) | ||
249 | { | ||
250 | mtmsr(mfmsr() & ~MSR_DE); | ||
251 | } | ||
252 | |||
253 | static void kvmppc_restore_host_debug_state(struct kvm_vcpu *vcpu) | ||
254 | { | ||
255 | kvmppc_disable_debug_interrupts(); | ||
256 | |||
257 | mtspr(SPRN_IAC1, vcpu->arch.host_iac[0]); | ||
258 | mtspr(SPRN_IAC2, vcpu->arch.host_iac[1]); | ||
259 | mtspr(SPRN_IAC3, vcpu->arch.host_iac[2]); | ||
260 | mtspr(SPRN_IAC4, vcpu->arch.host_iac[3]); | ||
261 | mtspr(SPRN_DBCR1, vcpu->arch.host_dbcr1); | ||
262 | mtspr(SPRN_DBCR2, vcpu->arch.host_dbcr2); | ||
263 | mtspr(SPRN_DBCR0, vcpu->arch.host_dbcr0); | ||
264 | mtmsr(vcpu->arch.host_msr); | ||
265 | } | ||
266 | |||
267 | static void kvmppc_load_guest_debug_registers(struct kvm_vcpu *vcpu) | ||
268 | { | ||
269 | struct kvm_guest_debug *dbg = &vcpu->guest_debug; | ||
270 | u32 dbcr0 = 0; | ||
271 | |||
272 | vcpu->arch.host_msr = mfmsr(); | ||
273 | kvmppc_disable_debug_interrupts(); | ||
274 | |||
275 | /* Save host debug register state. */ | ||
276 | vcpu->arch.host_iac[0] = mfspr(SPRN_IAC1); | ||
277 | vcpu->arch.host_iac[1] = mfspr(SPRN_IAC2); | ||
278 | vcpu->arch.host_iac[2] = mfspr(SPRN_IAC3); | ||
279 | vcpu->arch.host_iac[3] = mfspr(SPRN_IAC4); | ||
280 | vcpu->arch.host_dbcr0 = mfspr(SPRN_DBCR0); | ||
281 | vcpu->arch.host_dbcr1 = mfspr(SPRN_DBCR1); | ||
282 | vcpu->arch.host_dbcr2 = mfspr(SPRN_DBCR2); | ||
283 | |||
284 | /* set registers up for guest */ | ||
285 | |||
286 | if (dbg->bp[0]) { | ||
287 | mtspr(SPRN_IAC1, dbg->bp[0]); | ||
288 | dbcr0 |= DBCR0_IAC1 | DBCR0_IDM; | ||
289 | } | ||
290 | if (dbg->bp[1]) { | ||
291 | mtspr(SPRN_IAC2, dbg->bp[1]); | ||
292 | dbcr0 |= DBCR0_IAC2 | DBCR0_IDM; | ||
293 | } | ||
294 | if (dbg->bp[2]) { | ||
295 | mtspr(SPRN_IAC3, dbg->bp[2]); | ||
296 | dbcr0 |= DBCR0_IAC3 | DBCR0_IDM; | ||
297 | } | ||
298 | if (dbg->bp[3]) { | ||
299 | mtspr(SPRN_IAC4, dbg->bp[3]); | ||
300 | dbcr0 |= DBCR0_IAC4 | DBCR0_IDM; | ||
301 | } | ||
302 | |||
303 | mtspr(SPRN_DBCR0, dbcr0); | ||
304 | mtspr(SPRN_DBCR1, 0); | ||
305 | mtspr(SPRN_DBCR2, 0); | ||
306 | } | ||
307 | |||
242 | void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) | 308 | void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) |
243 | { | 309 | { |
310 | if (vcpu->guest_debug.enabled) | ||
311 | kvmppc_load_guest_debug_registers(vcpu); | ||
244 | } | 312 | } |
245 | 313 | ||
246 | void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) | 314 | void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) |
247 | { | 315 | { |
316 | if (vcpu->guest_debug.enabled) | ||
317 | kvmppc_restore_host_debug_state(vcpu); | ||
248 | } | 318 | } |
249 | 319 | ||
250 | int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu, | 320 | int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu, |
251 | struct kvm_debug_guest *dbg) | 321 | struct kvm_debug_guest *dbg) |
252 | { | 322 | { |
253 | return -ENOTSUPP; | 323 | int i; |
324 | |||
325 | vcpu->guest_debug.enabled = dbg->enabled; | ||
326 | if (vcpu->guest_debug.enabled) { | ||
327 | for (i=0; i < ARRAY_SIZE(vcpu->guest_debug.bp); i++) { | ||
328 | if (dbg->breakpoints[i].enabled) | ||
329 | vcpu->guest_debug.bp[i] = dbg->breakpoints[i].address; | ||
330 | else | ||
331 | vcpu->guest_debug.bp[i] = 0; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | return 0; | ||
254 | } | 336 | } |
255 | 337 | ||
256 | static void kvmppc_complete_dcr_load(struct kvm_vcpu *vcpu, | 338 | static void kvmppc_complete_dcr_load(struct kvm_vcpu *vcpu, |