From: Liran Alon <liran.alon@xxxxxxxxxx> KVM isolation enter/exit is done by switching between the KVM address space and the kernel address space. Signed-off-by: Liran Alon <liran.alon@xxxxxxxxxx> Signed-off-by: Alexandre Chartre <alexandre.chartre@xxxxxxxxxx> --- arch/x86/kvm/isolation.c | 30 ++++++++++++++++++++++++------ arch/x86/mm/tlb.c | 1 + include/linux/sched.h | 1 + 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/isolation.c b/arch/x86/kvm/isolation.c index db0a7ce..b0c789f 100644 --- a/arch/x86/kvm/isolation.c +++ b/arch/x86/kvm/isolation.c @@ -1383,11 +1383,13 @@ static bool kvm_page_fault(struct pt_regs *regs, unsigned long error_code, printk(KERN_DEFAULT "KVM isolation: page fault %ld at %pS on %lx (%pS) while switching mm\n" " cr3=%lx\n" " kvm_mm=%px pgd=%px\n" - " active_mm=%px pgd=%px\n", + " active_mm=%px pgd=%px\n" + " kvm_prev_mm=%px pgd=%px\n", error_code, (void *)regs->ip, address, (void *)address, cr3, &kvm_mm, kvm_mm.pgd, - active_mm, active_mm->pgd); + active_mm, active_mm->pgd, + current->kvm_prev_mm, current->kvm_prev_mm->pgd); dump_stack(); return false; @@ -1649,11 +1651,27 @@ void kvm_may_access_sensitive_data(struct kvm_vcpu *vcpu) kvm_isolation_exit(); } +static void kvm_switch_mm(struct mm_struct *mm) +{ + unsigned long flags; + + /* + * Disable interrupt before updating active_mm, otherwise if an + * interrupt occurs during the switch then the interrupt handler + * can be mislead about the mm effectively in use. + */ + local_irq_save(flags); + current->kvm_prev_mm = current->active_mm; + current->active_mm = mm; + switch_mm_irqs_off(current->kvm_prev_mm, mm, NULL); + local_irq_restore(flags); +} + void kvm_isolation_enter(void) { int err; - if (kvm_isolation()) { + if (kvm_isolation() && current->active_mm != &kvm_mm) { /* * Switches to kvm_mm should happen from vCPU thread, * which should not be a kernel thread with no mm @@ -1666,14 +1684,14 @@ void kvm_isolation_enter(void) current); return; } - /* TODO: switch to kvm_mm */ + kvm_switch_mm(&kvm_mm); } } void kvm_isolation_exit(void) { - if (kvm_isolation()) { + if (kvm_isolation() && current->active_mm == &kvm_mm) { /* TODO: Kick sibling hyperthread before switch to host mm */ - /* TODO: switch back to original mm */ + kvm_switch_mm(current->kvm_prev_mm); } } diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index a4db7f5..7ad5ad1 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -444,6 +444,7 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, switch_ldt(real_prev, next); } } +EXPORT_SYMBOL_GPL(switch_mm_irqs_off); /* * Please ignore the name of this function. It should be called diff --git a/include/linux/sched.h b/include/linux/sched.h index 80e1d75..b03680d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1202,6 +1202,7 @@ struct task_struct { #ifdef CONFIG_HAVE_KVM /* Is the task mapped into the KVM address space? */ bool kvm_mapped; + struct mm_struct *kvm_prev_mm; #endif /* -- 1.7.1