diff options
author | Bryan Cantrill <bryan@joyent.com> | 2011-12-06 01:23:23 +0000 |
---|---|---|
committer | Bryan Cantrill <bryan@joyent.com> | 2011-12-06 01:23:23 +0000 |
commit | c1086e516e7350f245dab213303af08cd0e0e834 (patch) | |
tree | 74ab36eef6cb830d444e4129fe4351680240a721 | |
parent | 0b48fcf44fc479f9270a7f3eaab0fdbc587dd313 (diff) | |
download | illumos-kvm-c1086e516e7350f245dab213303af08cd0e0e834.tar.gz |
HVM-700 Panic in module "kvm" due to a NULL pointer dereference
HVM-708 record emulation failures via ring buffer, not cmn_err()
-rw-r--r-- | kvm.c | 20 | ||||
-rw-r--r-- | kvm_host.h | 7 | ||||
-rw-r--r-- | kvm_mdb.c | 6 | ||||
-rw-r--r-- | kvm_x86.c | 29 |
4 files changed, 52 insertions, 10 deletions
@@ -426,12 +426,23 @@ kvm_ringbuf_record(kvm_ringbuf_t *ringbuf, uint32_t tag, uint64_t payload) { kvm_ringbuf_entry_t *ent = &ringbuf->kvmr_buf[ringbuf->kvmr_ent++ & (KVM_RINGBUF_NENTRIES - 1)]; + int id = curthread->t_cpu->cpu_id; + hrtime_t tsc = gethrtime_unscaled(); ent->kvmre_tag = tag; - ent->kvmre_cpuid = curthread->t_cpu->cpu_id; + ent->kvmre_cpuid = id; ent->kvmre_thread = (uintptr_t)curthread; - ent->kvmre_tsc = gethrtime_unscaled(); + ent->kvmre_tsc = tsc; ent->kvmre_payload = payload; + + ent = &ringbuf->kvmr_taglast[tag]; + ent->kvmre_tag = tag; + ent->kvmre_cpuid = id; + ent->kvmre_thread = (uintptr_t)curthread; + ent->kvmre_tsc = tsc; + ent->kvmre_payload = payload; + + ringbuf->kvmr_tagcount[tag]++; } /* @@ -479,10 +490,11 @@ vcpu_load(struct kvm_vcpu *vcpu) int cpu; mutex_enter(&vcpu->mutex); - kpreempt_disable(); - cpu = CPU->cpu_seqid; installctx(curthread, vcpu, kvm_ctx_save, kvm_ctx_restore, NULL, NULL, NULL, NULL); + + kpreempt_disable(); + cpu = CPU->cpu_seqid; kvm_arch_vcpu_load(vcpu, cpu); kvm_ringbuf_record(&vcpu->kvcpu_ringbuf, KVM_RINGBUF_TAG_VCPULOAD, vcpu->cpu); @@ -109,6 +109,11 @@ extern int kvm_io_bus_unregister_dev(struct kvm *, enum kvm_bus, #define KVM_RINGBUF_TAG_VCPUCLEAR 5 #define KVM_RINGBUF_TAG_VCPULOAD 6 #define KVM_RINGBUF_TAG_VCPUPUT 7 +#define KVM_RINGBUF_TAG_RELOAD 8 +#define KVM_RINGBUF_TAG_EMUFAIL0 9 +#define KVM_RINGBUF_TAG_EMUFAIL1 10 +#define KVM_RINGBUF_TAG_EMUFAIL2 11 +#define KVM_RINGBUF_TAG_MAX 11 typedef struct kvm_ringbuf_entry { uint32_t kvmre_tag; /* tag for this entry */ @@ -120,6 +125,8 @@ typedef struct kvm_ringbuf_entry { typedef struct kvm_ringbuf { kvm_ringbuf_entry_t kvmr_buf[KVM_RINGBUF_NENTRIES]; /* ring buffer */ + kvm_ringbuf_entry_t kvmr_taglast[KVM_RINGBUF_TAG_MAX + 1]; + uint32_t kvmr_tagcount[KVM_RINGBUF_TAG_MAX + 1]; /* count of tags */ uint32_t kvmr_ent; /* current entry */ } kvm_ringbuf_t; @@ -346,7 +346,11 @@ kvm_mdb_ringbuf_entry(uintptr_t addr, uint_t flags, int argc, ent.kvmre_tag == KVM_RINGBUF_TAG_VCPUMIGRATE ? "migrate" : ent.kvmre_tag == KVM_RINGBUF_TAG_VCPUCLEAR ? "clear" : ent.kvmre_tag == KVM_RINGBUF_TAG_VCPULOAD ? "load" : - ent.kvmre_tag == KVM_RINGBUF_TAG_VCPUPUT ? "put" : "????", + ent.kvmre_tag == KVM_RINGBUF_TAG_VCPUPUT ? "put" : + ent.kvmre_tag == KVM_RINGBUF_TAG_RELOAD ? "reload" : + ent.kvmre_tag == KVM_RINGBUF_TAG_EMUFAIL0 ? "efail-0" : + ent.kvmre_tag == KVM_RINGBUF_TAG_EMUFAIL1 ? "efail-1" : + ent.kvmre_tag == KVM_RINGBUF_TAG_EMUFAIL2 ? "efail-2" : "????", ent.kvmre_payload); return (DCMD_OK); @@ -2577,16 +2577,17 @@ get_segment_base(struct kvm_vcpu *vcpu, int seg) void kvm_report_emulation_failure(struct kvm_vcpu *vcpu, const char *context) { - uint8_t opcodes[4]; + uint64_t ops, ctx = (uint64_t)context; unsigned long rip = kvm_rip_read(vcpu); unsigned long rip_linear; rip_linear = rip + get_segment_base(vcpu, VCPU_SREG_CS); - kvm_read_guest_virt(rip_linear, (void *)opcodes, 4, vcpu, NULL); + kvm_read_guest_virt(rip_linear, &ops, 8, vcpu, NULL); - cmn_err(CE_CONT, "!emulation failed (%s) rip %lx %02x %02x %02x %02x\n", - context, rip, opcodes[0], opcodes[1], opcodes[2], opcodes[3]); + kvm_ringbuf_record(&vcpu->kvcpu_ringbuf, KVM_RINGBUF_TAG_EMUFAIL0, ctx); + kvm_ringbuf_record(&vcpu->kvcpu_ringbuf, KVM_RINGBUF_TAG_EMUFAIL1, rip); + kvm_ringbuf_record(&vcpu->kvcpu_ringbuf, KVM_RINGBUF_TAG_EMUFAIL2, ops); } static struct x86_emulate_ops emulate_ops = { @@ -3364,7 +3365,7 @@ native_set_debugreg(int regno, unsigned long value) static int vcpu_enter_guest(struct kvm_vcpu *vcpu) { - int r; + int r, loaded; int req_int_win = !irqchip_in_kernel(vcpu->kvm) && vcpu->run->request_interrupt_window; @@ -3417,6 +3418,7 @@ vcpu_enter_guest(struct kvm_vcpu *vcpu) if (vcpu->fpu_active) kvm_load_guest_fpu(vcpu); + loaded = CPU->cpu_id; clear_bit(KVM_REQ_KICK, &vcpu->requests); if (vcpu->requests || issig(JUSTLOOKING)) { @@ -3428,6 +3430,23 @@ vcpu_enter_guest(struct kvm_vcpu *vcpu) inject_pending_event(vcpu); + if (CPU->cpu_id != loaded) { + /* + * The kpreempt_disable(), above, disables kernel migration -- + * but it doesn't disable migration when we block. The call + * to inject_pending_event() can, through a circuitous path, + * block, and we may therefore have moved to a different CPU. + * That's actually okay -- we just need to reload our state + * in this case. + */ + kvm_ringbuf_record(&vcpu->kvcpu_ringbuf, + KVM_RINGBUF_TAG_RELOAD, loaded); + kvm_x86_ops->prepare_guest_switch(vcpu); + + if (vcpu->fpu_active) + kvm_load_guest_fpu(vcpu); + } + cli(); /* enable NMI/IRQ window open exits if needed */ |