]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
x86: Trap MSR access for AMD-V cells
authorValentine Sinitsyn <valentine.sinitsyn@gmail.com>
Sun, 28 Sep 2014 09:00:00 +0000 (15:00 +0600)
committerJan Kiszka <jan.kiszka@siemens.com>
Sat, 1 Nov 2014 19:10:09 +0000 (20:10 +0100)
Jailhouse traps read and write MSR access for two main purposes:

 * Emulating x2APIC on xAPIC-only hardware (all real-world AMD CPUs)
 * Preventing cells from clearing EFER.SVME bit

In the latter case, TLB also needs to be flushed.

Signed-off-by: Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/x86/include/asm/processor.h
hypervisor/arch/x86/svm.c

index d979d8c1bf5a3fe6c60eea05882444fcf5a1ffad..6394df2d779258aeeba520825a729756f0cb863f 100644 (file)
@@ -77,6 +77,7 @@
 
 #define EFER_LME                                       0x00000100
 #define EFER_LMA                                       0x00000400
+#define EFER_NXE                                       0x00000800
 
 #define GDT_DESC_NULL                                  0
 #define GDT_DESC_CODE                                  1
index a952d84f0ed9c2a56e38670f0d1248c54b4bd248..142c442a7504d647bcb2a3adf68fdd2bc35ee590 100644 (file)
@@ -442,6 +442,53 @@ bool vcpu_get_guest_paging_structs(struct guest_paging_structures *pg_structs)
        return true;
 }
 
+static bool svm_handle_msr_read(struct registers *guest_regs,
+               struct per_cpu *cpu_data)
+{
+       if (guest_regs->rcx >= MSR_X2APIC_BASE &&
+           guest_regs->rcx <= MSR_X2APIC_END) {
+               vcpu_skip_emulated_instruction(X86_INST_LEN_RDMSR);
+               x2apic_handle_read(guest_regs);
+               return true;
+       } else {
+               panic_printk("FATAL: Unhandled MSR read: %x\n",
+                            guest_regs->rcx);
+               return false;
+       }
+}
+
+static bool svm_handle_msr_write(struct registers *guest_regs,
+               struct per_cpu *cpu_data)
+{
+       struct vmcb *vmcb = &cpu_data->vmcb;
+       unsigned long efer;
+       bool result = true;
+
+       if (guest_regs->rcx >= MSR_X2APIC_BASE &&
+           guest_regs->rcx <= MSR_X2APIC_END) {
+               result = x2apic_handle_write(guest_regs, cpu_data);
+               goto out;
+       }
+       if (guest_regs->rcx == MSR_EFER) {
+               /* Never let a guest to disable SVME; see APMv2, Sect. 3.1.7 */
+               efer = (guest_regs->rax & 0xffffffff) |
+                       (guest_regs->rdx << 32) | EFER_SVME;
+               /* Flush TLB on LME/NXE change: See APMv2, Sect. 15.16 */
+               if ((efer ^ vmcb->efer) & (EFER_LME | EFER_NXE))
+                       vcpu_tlb_flush();
+               vmcb->efer = efer;
+               goto out;
+       }
+
+       result = false;
+       panic_printk("FATAL: Unhandled MSR write: %x\n",
+                    guest_regs->rcx);
+out:
+       if (result)
+               vcpu_skip_emulated_instruction(X86_INST_LEN_WRMSR);
+       return result;
+}
+
 static void dump_guest_regs(struct registers *guest_regs, struct vmcb *vmcb)
 {
        panic_printk("RIP: %p RSP: %p FLAGS: %x\n", vmcb->rip,
@@ -464,6 +511,7 @@ void vcpu_handle_exit(struct registers *guest_regs, struct per_cpu *cpu_data)
 {
        struct vmcb *vmcb = &cpu_data->vmcb;
        struct vcpu_execution_state x_state;
+       bool res = false;
 
        /* Restore GS value expected by per_cpu data accessors */
        write_msr(MSR_GS_BASE, (unsigned long)cpu_data);
@@ -482,6 +530,15 @@ void vcpu_handle_exit(struct registers *guest_regs, struct per_cpu *cpu_data)
                vcpu_vendor_get_execution_state(&x_state);
                vcpu_handle_hypercall(guest_regs, &x_state);
                return;
+       case VMEXIT_MSR:
+               cpu_data->stats[JAILHOUSE_CPU_STAT_VMEXITS_MSR]++;
+               if (!vmcb->exitinfo1)
+                       res = svm_handle_msr_read(guest_regs, cpu_data);
+               else
+                       res = svm_handle_msr_write(guest_regs, cpu_data);
+               if (res)
+                       return;
+               break;
        default:
                panic_printk("FATAL: Unexpected #VMEXIT, exitcode %x, "
                             "exitinfo1 %p exitinfo2 %p\n",