diff options
author | Bharat Bhushan <r65777@freescale.com> | 2012-08-08 16:38:19 -0400 |
---|---|---|
committer | Alexander Graf <agraf@suse.de> | 2012-10-05 17:38:47 -0400 |
commit | f61c94bb99ca4253ac5dd57750e1af209a4beb7a (patch) | |
tree | 348d15c584a683cd805ed71ed5c8a81848d24e70 /arch/powerpc/kvm/booke.c | |
parent | 7c973a2ebb8fb9c8ee2ae9647f9ad7b0ad58a3e6 (diff) |
KVM: PPC: booke: Add watchdog emulation
This patch adds the watchdog emulation in KVM. The watchdog
emulation is enabled by KVM_ENABLE_CAP(KVM_CAP_PPC_BOOKE_WATCHDOG) ioctl.
The kernel timer are used for watchdog emulation and emulates
h/w watchdog state machine. On watchdog timer expiry, it exit to QEMU
if TCR.WRC is non ZERO. QEMU can reset/shutdown etc depending upon how
it is configured.
Signed-off-by: Liu Yu <yu.liu@freescale.com>
Signed-off-by: Scott Wood <scottwood@freescale.com>
[bharat.bhushan@freescale.com: reworked patch]
Signed-off-by: Bharat Bhushan <bharat.bhushan@freescale.com>
[agraf: adjust to new request framework]
Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'arch/powerpc/kvm/booke.c')
-rw-r--r-- | arch/powerpc/kvm/booke.c | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index c36493087dbf..09e8bf33a8c9 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c | |||
@@ -209,6 +209,16 @@ void kvmppc_core_dequeue_external(struct kvm_vcpu *vcpu, | |||
209 | clear_bit(BOOKE_IRQPRIO_EXTERNAL_LEVEL, &vcpu->arch.pending_exceptions); | 209 | clear_bit(BOOKE_IRQPRIO_EXTERNAL_LEVEL, &vcpu->arch.pending_exceptions); |
210 | } | 210 | } |
211 | 211 | ||
212 | static void kvmppc_core_queue_watchdog(struct kvm_vcpu *vcpu) | ||
213 | { | ||
214 | kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_WATCHDOG); | ||
215 | } | ||
216 | |||
217 | static void kvmppc_core_dequeue_watchdog(struct kvm_vcpu *vcpu) | ||
218 | { | ||
219 | clear_bit(BOOKE_IRQPRIO_WATCHDOG, &vcpu->arch.pending_exceptions); | ||
220 | } | ||
221 | |||
212 | static void set_guest_srr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) | 222 | static void set_guest_srr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) |
213 | { | 223 | { |
214 | #ifdef CONFIG_KVM_BOOKE_HV | 224 | #ifdef CONFIG_KVM_BOOKE_HV |
@@ -328,6 +338,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, | |||
328 | msr_mask = MSR_CE | MSR_ME | MSR_DE; | 338 | msr_mask = MSR_CE | MSR_ME | MSR_DE; |
329 | int_class = INT_CLASS_NONCRIT; | 339 | int_class = INT_CLASS_NONCRIT; |
330 | break; | 340 | break; |
341 | case BOOKE_IRQPRIO_WATCHDOG: | ||
331 | case BOOKE_IRQPRIO_CRITICAL: | 342 | case BOOKE_IRQPRIO_CRITICAL: |
332 | case BOOKE_IRQPRIO_DBELL_CRIT: | 343 | case BOOKE_IRQPRIO_DBELL_CRIT: |
333 | allowed = vcpu->arch.shared->msr & MSR_CE; | 344 | allowed = vcpu->arch.shared->msr & MSR_CE; |
@@ -407,12 +418,121 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, | |||
407 | return allowed; | 418 | return allowed; |
408 | } | 419 | } |
409 | 420 | ||
421 | /* | ||
422 | * Return the number of jiffies until the next timeout. If the timeout is | ||
423 | * longer than the NEXT_TIMER_MAX_DELTA, then return NEXT_TIMER_MAX_DELTA | ||
424 | * because the larger value can break the timer APIs. | ||
425 | */ | ||
426 | static unsigned long watchdog_next_timeout(struct kvm_vcpu *vcpu) | ||
427 | { | ||
428 | u64 tb, wdt_tb, wdt_ticks = 0; | ||
429 | u64 nr_jiffies = 0; | ||
430 | u32 period = TCR_GET_WP(vcpu->arch.tcr); | ||
431 | |||
432 | wdt_tb = 1ULL << (63 - period); | ||
433 | tb = get_tb(); | ||
434 | /* | ||
435 | * The watchdog timeout will hapeen when TB bit corresponding | ||
436 | * to watchdog will toggle from 0 to 1. | ||
437 | */ | ||
438 | if (tb & wdt_tb) | ||
439 | wdt_ticks = wdt_tb; | ||
440 | |||
441 | wdt_ticks += wdt_tb - (tb & (wdt_tb - 1)); | ||
442 | |||
443 | /* Convert timebase ticks to jiffies */ | ||
444 | nr_jiffies = wdt_ticks; | ||
445 | |||
446 | if (do_div(nr_jiffies, tb_ticks_per_jiffy)) | ||
447 | nr_jiffies++; | ||
448 | |||
449 | return min_t(unsigned long long, nr_jiffies, NEXT_TIMER_MAX_DELTA); | ||
450 | } | ||
451 | |||
452 | static void arm_next_watchdog(struct kvm_vcpu *vcpu) | ||
453 | { | ||
454 | unsigned long nr_jiffies; | ||
455 | unsigned long flags; | ||
456 | |||
457 | /* | ||
458 | * If TSR_ENW and TSR_WIS are not set then no need to exit to | ||
459 | * userspace, so clear the KVM_REQ_WATCHDOG request. | ||
460 | */ | ||
461 | if ((vcpu->arch.tsr & (TSR_ENW | TSR_WIS)) != (TSR_ENW | TSR_WIS)) | ||
462 | clear_bit(KVM_REQ_WATCHDOG, &vcpu->requests); | ||
463 | |||
464 | spin_lock_irqsave(&vcpu->arch.wdt_lock, flags); | ||
465 | nr_jiffies = watchdog_next_timeout(vcpu); | ||
466 | /* | ||
467 | * If the number of jiffies of watchdog timer >= NEXT_TIMER_MAX_DELTA | ||
468 | * then do not run the watchdog timer as this can break timer APIs. | ||
469 | */ | ||
470 | if (nr_jiffies < NEXT_TIMER_MAX_DELTA) | ||
471 | mod_timer(&vcpu->arch.wdt_timer, jiffies + nr_jiffies); | ||
472 | else | ||
473 | del_timer(&vcpu->arch.wdt_timer); | ||
474 | spin_unlock_irqrestore(&vcpu->arch.wdt_lock, flags); | ||
475 | } | ||
476 | |||
477 | void kvmppc_watchdog_func(unsigned long data) | ||
478 | { | ||
479 | struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data; | ||
480 | u32 tsr, new_tsr; | ||
481 | int final; | ||
482 | |||
483 | do { | ||
484 | new_tsr = tsr = vcpu->arch.tsr; | ||
485 | final = 0; | ||
486 | |||
487 | /* Time out event */ | ||
488 | if (tsr & TSR_ENW) { | ||
489 | if (tsr & TSR_WIS) | ||
490 | final = 1; | ||
491 | else | ||
492 | new_tsr = tsr | TSR_WIS; | ||
493 | } else { | ||
494 | new_tsr = tsr | TSR_ENW; | ||
495 | } | ||
496 | } while (cmpxchg(&vcpu->arch.tsr, tsr, new_tsr) != tsr); | ||
497 | |||
498 | if (new_tsr & TSR_WIS) { | ||
499 | smp_wmb(); | ||
500 | kvm_make_request(KVM_REQ_PENDING_TIMER, vcpu); | ||
501 | kvm_vcpu_kick(vcpu); | ||
502 | } | ||
503 | |||
504 | /* | ||
505 | * If this is final watchdog expiry and some action is required | ||
506 | * then exit to userspace. | ||
507 | */ | ||
508 | if (final && (vcpu->arch.tcr & TCR_WRC_MASK) && | ||
509 | vcpu->arch.watchdog_enabled) { | ||
510 | smp_wmb(); | ||
511 | kvm_make_request(KVM_REQ_WATCHDOG, vcpu); | ||
512 | kvm_vcpu_kick(vcpu); | ||
513 | } | ||
514 | |||
515 | /* | ||
516 | * Stop running the watchdog timer after final expiration to | ||
517 | * prevent the host from being flooded with timers if the | ||
518 | * guest sets a short period. | ||
519 | * Timers will resume when TSR/TCR is updated next time. | ||
520 | */ | ||
521 | if (!final) | ||
522 | arm_next_watchdog(vcpu); | ||
523 | } | ||
524 | |||
410 | static void update_timer_ints(struct kvm_vcpu *vcpu) | 525 | static void update_timer_ints(struct kvm_vcpu *vcpu) |
411 | { | 526 | { |
412 | if ((vcpu->arch.tcr & TCR_DIE) && (vcpu->arch.tsr & TSR_DIS)) | 527 | if ((vcpu->arch.tcr & TCR_DIE) && (vcpu->arch.tsr & TSR_DIS)) |
413 | kvmppc_core_queue_dec(vcpu); | 528 | kvmppc_core_queue_dec(vcpu); |
414 | else | 529 | else |
415 | kvmppc_core_dequeue_dec(vcpu); | 530 | kvmppc_core_dequeue_dec(vcpu); |
531 | |||
532 | if ((vcpu->arch.tcr & TCR_WIE) && (vcpu->arch.tsr & TSR_WIS)) | ||
533 | kvmppc_core_queue_watchdog(vcpu); | ||
534 | else | ||
535 | kvmppc_core_dequeue_watchdog(vcpu); | ||
416 | } | 536 | } |
417 | 537 | ||
418 | static void kvmppc_core_check_exceptions(struct kvm_vcpu *vcpu) | 538 | static void kvmppc_core_check_exceptions(struct kvm_vcpu *vcpu) |
@@ -466,6 +586,11 @@ int kvmppc_core_check_requests(struct kvm_vcpu *vcpu) | |||
466 | kvmppc_core_flush_tlb(vcpu); | 586 | kvmppc_core_flush_tlb(vcpu); |
467 | #endif | 587 | #endif |
468 | 588 | ||
589 | if (kvm_check_request(KVM_REQ_WATCHDOG, vcpu)) { | ||
590 | vcpu->run->exit_reason = KVM_EXIT_WATCHDOG; | ||
591 | r = 0; | ||
592 | } | ||
593 | |||
469 | return r; | 594 | return r; |
470 | } | 595 | } |
471 | 596 | ||
@@ -995,6 +1120,21 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) | |||
995 | return r; | 1120 | return r; |
996 | } | 1121 | } |
997 | 1122 | ||
1123 | int kvmppc_subarch_vcpu_init(struct kvm_vcpu *vcpu) | ||
1124 | { | ||
1125 | /* setup watchdog timer once */ | ||
1126 | spin_lock_init(&vcpu->arch.wdt_lock); | ||
1127 | setup_timer(&vcpu->arch.wdt_timer, kvmppc_watchdog_func, | ||
1128 | (unsigned long)vcpu); | ||
1129 | |||
1130 | return 0; | ||
1131 | } | ||
1132 | |||
1133 | void kvmppc_subarch_vcpu_uninit(struct kvm_vcpu *vcpu) | ||
1134 | { | ||
1135 | del_timer_sync(&vcpu->arch.wdt_timer); | ||
1136 | } | ||
1137 | |||
998 | int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) | 1138 | int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) |
999 | { | 1139 | { |
1000 | int i; | 1140 | int i; |
@@ -1090,7 +1230,13 @@ static int set_sregs_base(struct kvm_vcpu *vcpu, | |||
1090 | } | 1230 | } |
1091 | 1231 | ||
1092 | if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_TSR) { | 1232 | if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_TSR) { |
1233 | u32 old_tsr = vcpu->arch.tsr; | ||
1234 | |||
1093 | vcpu->arch.tsr = sregs->u.e.tsr; | 1235 | vcpu->arch.tsr = sregs->u.e.tsr; |
1236 | |||
1237 | if ((old_tsr ^ vcpu->arch.tsr) & (TSR_ENW | TSR_WIS)) | ||
1238 | arm_next_watchdog(vcpu); | ||
1239 | |||
1094 | update_timer_ints(vcpu); | 1240 | update_timer_ints(vcpu); |
1095 | } | 1241 | } |
1096 | 1242 | ||
@@ -1251,6 +1397,7 @@ void kvmppc_core_commit_memory_region(struct kvm *kvm, | |||
1251 | void kvmppc_set_tcr(struct kvm_vcpu *vcpu, u32 new_tcr) | 1397 | void kvmppc_set_tcr(struct kvm_vcpu *vcpu, u32 new_tcr) |
1252 | { | 1398 | { |
1253 | vcpu->arch.tcr = new_tcr; | 1399 | vcpu->arch.tcr = new_tcr; |
1400 | arm_next_watchdog(vcpu); | ||
1254 | update_timer_ints(vcpu); | 1401 | update_timer_ints(vcpu); |
1255 | } | 1402 | } |
1256 | 1403 | ||
@@ -1265,6 +1412,14 @@ void kvmppc_set_tsr_bits(struct kvm_vcpu *vcpu, u32 tsr_bits) | |||
1265 | void kvmppc_clr_tsr_bits(struct kvm_vcpu *vcpu, u32 tsr_bits) | 1412 | void kvmppc_clr_tsr_bits(struct kvm_vcpu *vcpu, u32 tsr_bits) |
1266 | { | 1413 | { |
1267 | clear_bits(tsr_bits, &vcpu->arch.tsr); | 1414 | clear_bits(tsr_bits, &vcpu->arch.tsr); |
1415 | |||
1416 | /* | ||
1417 | * We may have stopped the watchdog due to | ||
1418 | * being stuck on final expiration. | ||
1419 | */ | ||
1420 | if (tsr_bits & (TSR_ENW | TSR_WIS)) | ||
1421 | arm_next_watchdog(vcpu); | ||
1422 | |||
1268 | update_timer_ints(vcpu); | 1423 | update_timer_ints(vcpu); |
1269 | } | 1424 | } |
1270 | 1425 | ||