Patchwork [3/3] Enable kvm emulated watchdog

login
register
mail settings
Submitter Bharat Bhushan
Date July 10, 2012, 7:03 a.m.
Message ID <1341903808-2120-3-git-send-email-Bharat.Bhushan@freescale.com>
Download mbox | patch
Permalink /patch/170066/
State New
Headers show

Comments

Bharat Bhushan - July 10, 2012, 7:03 a.m.
This patch adds the support to enable KVM emulated watchdog
if KVM supports (use the capability enablement in watchdog handler).
Also added the support to handle the exit caused by watchdog
(KVM_EXIT_WDT). In the handling we clear the TSR register.
Watchdog state machine is cleared whenever VM state changes to running.
This is to handle the cases like return from debug halt etc.

Signed-off-by: Bharat Bhushan <bharat.bhushan@freescale.com>
---
v2:
 - Merged ([PATCH 3/4] Watchdog exit handling support) and ([PATCH 4/4] Enable to use kvm emulated watchdog)
 - Clear watchdog state machine when VM state changes to running.

 hw/ppc_booke.c            |    5 ++
 linux-headers/linux/kvm.h |    2 +
 target-ppc/cpu.h          |    1 +
 target-ppc/kvm.c          |   93 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 101 insertions(+), 0 deletions(-)

Patch

diff --git a/hw/ppc_booke.c b/hw/ppc_booke.c
index 837a5b6..a9fba15 100644
--- a/hw/ppc_booke.c
+++ b/hw/ppc_booke.c
@@ -203,6 +203,11 @@  static void booke_wdt_cb(void *opaque)
                              booke_timer->wdt_timer);
 }
 
+void ppc_booke_wdt_clear_tsr(CPUPPCState *env, target_ulong tsr)
+{
+    env->spr[SPR_BOOKE_TSR] = tsr & ~(TSR_ENW | TSR_WIS | TSR_WRS_MASK);
+}
+
 void store_booke_tsr(CPUPPCState *env, target_ulong val)
 {
     env->spr[SPR_BOOKE_TSR] &= ~val;
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 4b9e575..8a149db 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -163,6 +163,7 @@  struct kvm_pit_config {
 #define KVM_EXIT_OSI              18
 #define KVM_EXIT_PAPR_HCALL	  19
 #define KVM_EXIT_S390_UCONTROL	  20
+#define KVM_EXIT_WDT              21
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 #define KVM_INTERNAL_ERROR_EMULATION 1
@@ -618,6 +619,7 @@  struct kvm_ppc_smmu_info {
 #define KVM_CAP_PPC_GET_SMMU_INFO 78
 #define KVM_CAP_S390_COW 79
 #define KVM_CAP_PPC_ALLOC_HTAB 80
+#define KVM_CAP_PPC_WDT 81
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index ca2fc21..78212b4 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -1191,6 +1191,7 @@  void store_40x_dbcr0 (CPUPPCState *env, uint32_t val);
 void store_40x_sler (CPUPPCState *env, uint32_t val);
 void store_booke_tcr (CPUPPCState *env, target_ulong val);
 void store_booke_tsr (CPUPPCState *env, target_ulong val);
+void ppc_booke_wdt_clear_tsr(CPUPPCState *env, target_ulong tsr);
 void booke206_flush_tlb(CPUPPCState *env, int flags, const int check_iprot);
 target_phys_addr_t booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb);
 int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb,
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index b6ef72d..e6a581a 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -32,6 +32,7 @@ 
 #include "device_tree.h"
 #include "hw/sysbus.h"
 #include "hw/spapr.h"
+#include "hw/watchdog.h"
 
 #include "hw/sysbus.h"
 #include "hw/spapr.h"
@@ -60,6 +61,7 @@  static int cap_booke_sregs;
 static int cap_ppc_smt;
 static int cap_ppc_rma;
 static int cap_spapr_tce;
+static int cap_ppc_wdt;
 
 /* XXX We have a race condition where we actually have a level triggered
  *     interrupt, but the infrastructure can't expose that yet, so the guest
@@ -86,6 +88,7 @@  int kvm_arch_init(KVMState *s)
     cap_ppc_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT);
     cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA);
     cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE);
+    cap_ppc_wdt = kvm_check_extension(s, KVM_CAP_PPC_WDT);
 
     if (!cap_interrupt_level) {
         fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the "
@@ -168,6 +171,25 @@  static int kvm_booke206_tlb_init(CPUPPCState *env)
     return 0;
 }
 
+static int kvm_wdt_enable(CPUPPCState *env)
+{
+    int ret;
+    struct kvm_enable_cap encap = {};
+
+    if (!kvm_enabled() || !cap_ppc_wdt) {
+        return 0;
+    }
+
+    encap.cap = KVM_CAP_PPC_WDT;
+    ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &encap);
+    if (ret < 0) {
+        fprintf(stderr, "%s: couldn't enable KVM_CAP_PPC_WDT: %s\n",
+                __func__, strerror(-ret));
+        return ret;
+    }
+
+    return ret;
+}
 
 #if defined(TARGET_PPC64)
 static void kvm_get_fallback_smmu_info(CPUPPCState *env,
@@ -371,6 +393,33 @@  static inline void kvm_fixup_page_sizes(CPUPPCState *env)
 
 #endif /* !defined (TARGET_PPC64) */
 
+static void cpu_state_change_handler(void *opaque, int running, RunState state)
+{
+    CPUPPCState *env = opaque;
+
+    struct kvm_sregs sregs;
+
+    printf("running = %d, state = %d \n", running, state);
+    if (!running)
+        return;
+
+    /*
+     * Clear watchdog interrupt condition by clearing TSR.
+     * Similar logic needed to be implemented for watchdog
+     * emulation in qemu.
+     */
+    if (cap_booke_sregs && cap_ppc_wdt) {
+        kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
+
+        /* Clear TSR.ENW, TSR.WIS and TSR.WRS */
+        ppc_booke_wdt_clear_tsr(env, sregs.u.e.tsr);
+        sregs.u.e.tsr = env->spr[SPR_BOOKE_TSR];
+        sregs.u.e.update_special = KVM_SREGS_E_BASE | KVM_SREGS_E_UPDATE_TSR;
+
+        kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs);
+    }
+}
+
 int kvm_arch_init_vcpu(CPUPPCState *cenv)
 {
     int ret;
@@ -384,6 +433,13 @@  int kvm_arch_init_vcpu(CPUPPCState *cenv)
         return ret;
     }
 
+    ret = kvm_wdt_enable(cenv);
+    if (ret) {
+        return ret;
+    }
+
+    qemu_add_vm_change_state_handler(cpu_state_change_handler, cenv);
+
     idle_timer = qemu_new_timer_ns(vm_clock, kvm_kick_env, cenv);
 
     /* Some targets support access to KVM's guest TLB. */
@@ -743,6 +799,37 @@  static int kvmppc_handle_dcr_write(CPUPPCState *env, uint32_t dcrn, uint32_t dat
     return 0;
 }
 
+static int kvm_arch_handle_watchdog(CPUPPCState *env)
+{
+    int ret;
+    struct kvm_sregs sregs;
+
+    watchdog_perform_action();
+
+    /*
+     * Clear watchdog interrupt condition by clearing TSR.
+     * Similar logic needed to be implemented for watchdog emulation in qemu
+     */
+    if (cap_booke_sregs && cap_ppc_wdt) {
+        ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
+        if (ret < 0) {
+            return ret;
+        }
+
+        /* Clear TSR.ENW, TSR.WIS and TSR.WRS */
+        ppc_booke_wdt_clear_tsr(env, sregs.u.e.tsr);
+        sregs.u.e.tsr = env->spr[SPR_BOOKE_TSR];
+
+        sregs.u.e.update_special = KVM_SREGS_E_BASE | KVM_SREGS_E_UPDATE_TSR;
+        ret = kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
 int kvm_arch_handle_exit(CPUPPCState *env, struct kvm_run *run)
 {
     int ret;
@@ -769,6 +856,12 @@  int kvm_arch_handle_exit(CPUPPCState *env, struct kvm_run *run)
         ret = 1;
         break;
 #endif
+#ifdef KVM_EXIT_WDT
+    case KVM_EXIT_WDT:
+        dprintf("booke watchdog action\n");
+        ret = kvm_arch_handle_watchdog(env);
+        break;
+#endif
     default:
         fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
         ret = -1;