KVM: x86: Prevent starting PIT timers in the absence of irqchip support
authorJan Kiszka <jan.kiszka@siemens.com>
Wed, 14 Dec 2011 18:25:13 +0000 (19:25 +0100)
committerAvi Kivity <avi@redhat.com>
Sun, 25 Dec 2011 15:13:18 +0000 (17:13 +0200)
User space may create the PIT and forgets about setting up the irqchips.
In that case, firing PIT IRQs will crash the host:

BUG: unable to handle kernel NULL pointer dereference at 0000000000000128
IP: [<ffffffffa10f6280>] kvm_set_irq+0x30/0x170 [kvm]
...
Call Trace:
 [<ffffffffa11228c1>] pit_do_work+0x51/0xd0 [kvm]
 [<ffffffff81071431>] process_one_work+0x111/0x4d0
 [<ffffffff81071bb2>] worker_thread+0x152/0x340
 [<ffffffff81075c8e>] kthread+0x7e/0x90
 [<ffffffff815a4474>] kernel_thread_helper+0x4/0x10

Prevent this by checking the irqchip mode before starting a timer. We
can't deny creating the PIT if the irqchips aren't set up yet as
current user land expects this order to work.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
arch/x86/kvm/i8254.c

index 76e3f1cd03696997964db7814de98a578a9eb4a7..405f2620392f5e32d393166520e707b2d039b3bb 100644 (file)
@@ -338,11 +338,15 @@ static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
                return HRTIMER_NORESTART;
 }
 
-static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period)
+static void create_pit_timer(struct kvm *kvm, u32 val, int is_period)
 {
+       struct kvm_kpit_state *ps = &kvm->arch.vpit->pit_state;
        struct kvm_timer *pt = &ps->pit_timer;
        s64 interval;
 
+       if (!irqchip_in_kernel(kvm))
+               return;
+
        interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
 
        pr_debug("create pit timer, interval is %llu nsec\n", interval);
@@ -394,13 +398,13 @@ static void pit_load_count(struct kvm *kvm, int channel, u32 val)
         /* FIXME: enhance mode 4 precision */
        case 4:
                if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)) {
-                       create_pit_timer(ps, val, 0);
+                       create_pit_timer(kvm, val, 0);
                }
                break;
        case 2:
        case 3:
                if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)){
-                       create_pit_timer(ps, val, 1);
+                       create_pit_timer(kvm, val, 1);
                }
                break;
        default: