Skip to content

Commit 4ecac3f

Browse files
committed
KVM: Handle virtualization instruction #UD faults during reboot
KVM turns off hardware virtualization extensions during reboot, in order to disassociate the memory used by the virtualization extensions from the processor, and in order to have the system in a consistent state. Unfortunately virtual machines may still be running while this goes on, and once virtualization extensions are turned off, any virtulization instruction will #UD on execution. Fix by adding an exception handler to virtualization instructions; if we get an exception during reboot, we simply spin waiting for the reset to complete. If it's a true exception, BUG() so we can have our stack trace. Signed-off-by: Avi Kivity <[email protected]>
1 parent 1b7fcd3 commit 4ecac3f

File tree

4 files changed

+64
-20
lines changed

4 files changed

+64
-20
lines changed

arch/x86/kvm/svm.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
#include <asm/desc.h>
2929

30+
#define __ex(x) __kvm_handle_fault_on_reboot(x)
31+
3032
MODULE_AUTHOR("Qumranet");
3133
MODULE_LICENSE("GPL");
3234

@@ -129,17 +131,17 @@ static inline void push_irq(struct kvm_vcpu *vcpu, u8 irq)
129131

130132
static inline void clgi(void)
131133
{
132-
asm volatile (SVM_CLGI);
134+
asm volatile (__ex(SVM_CLGI));
133135
}
134136

135137
static inline void stgi(void)
136138
{
137-
asm volatile (SVM_STGI);
139+
asm volatile (__ex(SVM_STGI));
138140
}
139141

140142
static inline void invlpga(unsigned long addr, u32 asid)
141143
{
142-
asm volatile (SVM_INVLPGA :: "a"(addr), "c"(asid));
144+
asm volatile (__ex(SVM_INVLPGA) :: "a"(addr), "c"(asid));
143145
}
144146

145147
static inline unsigned long kvm_read_cr2(void)
@@ -1758,17 +1760,17 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
17581760
/* Enter guest mode */
17591761
"push %%rax \n\t"
17601762
"mov %c[vmcb](%[svm]), %%rax \n\t"
1761-
SVM_VMLOAD "\n\t"
1762-
SVM_VMRUN "\n\t"
1763-
SVM_VMSAVE "\n\t"
1763+
__ex(SVM_VMLOAD) "\n\t"
1764+
__ex(SVM_VMRUN) "\n\t"
1765+
__ex(SVM_VMSAVE) "\n\t"
17641766
"pop %%rax \n\t"
17651767
#else
17661768
/* Enter guest mode */
17671769
"push %%eax \n\t"
17681770
"mov %c[vmcb](%[svm]), %%eax \n\t"
1769-
SVM_VMLOAD "\n\t"
1770-
SVM_VMRUN "\n\t"
1771-
SVM_VMSAVE "\n\t"
1771+
__ex(SVM_VMLOAD) "\n\t"
1772+
__ex(SVM_VMRUN) "\n\t"
1773+
__ex(SVM_VMSAVE) "\n\t"
17721774
"pop %%eax \n\t"
17731775
#endif
17741776

arch/x86/kvm/vmx.c

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <asm/io.h>
3131
#include <asm/desc.h>
3232

33+
#define __ex(x) __kvm_handle_fault_on_reboot(x)
34+
3335
MODULE_AUTHOR("Qumranet");
3436
MODULE_LICENSE("GPL");
3537

@@ -278,7 +280,7 @@ static inline void __invvpid(int ext, u16 vpid, gva_t gva)
278280
u64 gva;
279281
} operand = { vpid, 0, gva };
280282

281-
asm volatile (ASM_VMX_INVVPID
283+
asm volatile (__ex(ASM_VMX_INVVPID)
282284
/* CF==1 or ZF==1 --> rc = -1 */
283285
"; ja 1f ; ud2 ; 1:"
284286
: : "a"(&operand), "c"(ext) : "cc", "memory");
@@ -290,7 +292,7 @@ static inline void __invept(int ext, u64 eptp, gpa_t gpa)
290292
u64 eptp, gpa;
291293
} operand = {eptp, gpa};
292294

293-
asm volatile (ASM_VMX_INVEPT
295+
asm volatile (__ex(ASM_VMX_INVEPT)
294296
/* CF==1 or ZF==1 --> rc = -1 */
295297
"; ja 1f ; ud2 ; 1:\n"
296298
: : "a" (&operand), "c" (ext) : "cc", "memory");
@@ -311,7 +313,7 @@ static void vmcs_clear(struct vmcs *vmcs)
311313
u64 phys_addr = __pa(vmcs);
312314
u8 error;
313315

314-
asm volatile (ASM_VMX_VMCLEAR_RAX "; setna %0"
316+
asm volatile (__ex(ASM_VMX_VMCLEAR_RAX) "; setna %0"
315317
: "=g"(error) : "a"(&phys_addr), "m"(phys_addr)
316318
: "cc", "memory");
317319
if (error)
@@ -378,7 +380,7 @@ static unsigned long vmcs_readl(unsigned long field)
378380
{
379381
unsigned long value;
380382

381-
asm volatile (ASM_VMX_VMREAD_RDX_RAX
383+
asm volatile (__ex(ASM_VMX_VMREAD_RDX_RAX)
382384
: "=a"(value) : "d"(field) : "cc");
383385
return value;
384386
}
@@ -413,7 +415,7 @@ static void vmcs_writel(unsigned long field, unsigned long value)
413415
{
414416
u8 error;
415417

416-
asm volatile (ASM_VMX_VMWRITE_RAX_RDX "; setna %0"
418+
asm volatile (__ex(ASM_VMX_VMWRITE_RAX_RDX) "; setna %0"
417419
: "=q"(error) : "a"(value), "d"(field) : "cc");
418420
if (unlikely(error))
419421
vmwrite_error(field, value);
@@ -621,7 +623,7 @@ static void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
621623
u8 error;
622624

623625
per_cpu(current_vmcs, cpu) = vmx->vmcs;
624-
asm volatile (ASM_VMX_VMPTRLD_RAX "; setna %0"
626+
asm volatile (__ex(ASM_VMX_VMPTRLD_RAX) "; setna %0"
625627
: "=g"(error) : "a"(&phys_addr), "m"(phys_addr)
626628
: "cc");
627629
if (error)
@@ -1030,13 +1032,14 @@ static void hardware_enable(void *garbage)
10301032
MSR_IA32_FEATURE_CONTROL_LOCKED |
10311033
MSR_IA32_FEATURE_CONTROL_VMXON_ENABLED);
10321034
write_cr4(read_cr4() | X86_CR4_VMXE); /* FIXME: not cpu hotplug safe */
1033-
asm volatile (ASM_VMX_VMXON_RAX : : "a"(&phys_addr), "m"(phys_addr)
1035+
asm volatile (ASM_VMX_VMXON_RAX
1036+
: : "a"(&phys_addr), "m"(phys_addr)
10341037
: "memory", "cc");
10351038
}
10361039

10371040
static void hardware_disable(void *garbage)
10381041
{
1039-
asm volatile (ASM_VMX_VMXOFF : : : "cc");
1042+
asm volatile (__ex(ASM_VMX_VMXOFF) : : : "cc");
10401043
write_cr4(read_cr4() & ~X86_CR4_VMXE);
10411044
}
10421045

@@ -2834,7 +2837,7 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
28342837
"push %%edx; push %%ebp;"
28352838
"push %%ecx \n\t"
28362839
#endif
2837-
ASM_VMX_VMWRITE_RSP_RDX "\n\t"
2840+
__ex(ASM_VMX_VMWRITE_RSP_RDX) "\n\t"
28382841
/* Check if vmlaunch of vmresume is needed */
28392842
"cmpl $0, %c[launched](%0) \n\t"
28402843
/* Load guest registers. Don't clobber flags. */
@@ -2869,9 +2872,9 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
28692872
#endif
28702873
/* Enter guest mode */
28712874
"jne .Llaunched \n\t"
2872-
ASM_VMX_VMLAUNCH "\n\t"
2875+
__ex(ASM_VMX_VMLAUNCH) "\n\t"
28732876
"jmp .Lkvm_vmx_return \n\t"
2874-
".Llaunched: " ASM_VMX_VMRESUME "\n\t"
2877+
".Llaunched: " __ex(ASM_VMX_VMRESUME) "\n\t"
28752878
".Lkvm_vmx_return: "
28762879
/* Save guest registers, load host registers, keep flags */
28772880
#ifdef CONFIG_X86_64

include/asm-x86/kvm_host.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,4 +692,28 @@ enum {
692692
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
693693
vcpu, 0, 0, 0, 0, 0, 0)
694694

695+
#ifdef CONFIG_64BIT
696+
#define KVM_EX_ENTRY ".quad"
697+
#else
698+
#define KVM_EX_ENTRY ".long"
699+
#endif
700+
701+
/*
702+
* Hardware virtualization extension instructions may fault if a
703+
* reboot turns off virtualization while processes are running.
704+
* Trap the fault and ignore the instruction if that happens.
705+
*/
706+
asmlinkage void kvm_handle_fault_on_reboot(void);
707+
708+
#define __kvm_handle_fault_on_reboot(insn) \
709+
"666: " insn "\n\t" \
710+
".pushsection .text.fixup, \"ax\" \n" \
711+
"667: \n\t" \
712+
"push $666b \n\t" \
713+
"jmp kvm_handle_fault_on_reboot \n\t" \
714+
".popsection \n\t" \
715+
".pushsection __ex_table, \"a\" \n\t" \
716+
KVM_EX_ENTRY " 666b, 667b \n\t" \
717+
".popsection"
718+
695719
#endif

virt/kvm/kvm_main.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ struct dentry *kvm_debugfs_dir;
6565
static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
6666
unsigned long arg);
6767

68+
bool kvm_rebooting;
69+
6870
static inline int valid_vcpu(int n)
6971
{
7072
return likely(n >= 0 && n < KVM_MAX_VCPUS);
@@ -1301,6 +1303,18 @@ static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val,
13011303
return NOTIFY_OK;
13021304
}
13031305

1306+
1307+
asmlinkage void kvm_handle_fault_on_reboot(void)
1308+
{
1309+
if (kvm_rebooting)
1310+
/* spin while reset goes on */
1311+
while (true)
1312+
;
1313+
/* Fault while not rebooting. We want the trace. */
1314+
BUG();
1315+
}
1316+
EXPORT_SYMBOL_GPL(kvm_handle_fault_on_reboot);
1317+
13041318
static int kvm_reboot(struct notifier_block *notifier, unsigned long val,
13051319
void *v)
13061320
{
@@ -1310,6 +1324,7 @@ static int kvm_reboot(struct notifier_block *notifier, unsigned long val,
13101324
* in vmx root mode.
13111325
*/
13121326
printk(KERN_INFO "kvm: exiting hardware virtualization\n");
1327+
kvm_rebooting = true;
13131328
on_each_cpu(hardware_disable, NULL, 1);
13141329
}
13151330
return NOTIFY_OK;

0 commit comments

Comments
 (0)