]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/commitdiff
kvm: selftests: create a GDT and TSS
authorPaolo Bonzini <pbonzini@redhat.com>
Sat, 28 Jul 2018 16:09:44 +0000 (18:09 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 6 Aug 2018 15:32:02 +0000 (17:32 +0200)
The GDT and the TSS base were left to zero, and this has interesting effects
when the TSS descriptor is later read to set up a VMCS's TR_BASE.  Basically
it worked by chance, and this patch fixes it by setting up all the protected
mode data structures properly.

Because the GDT and TSS addresses are virtual, the page tables now always
exist at the time of vcpu setup.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
tools/testing/selftests/kvm/include/kvm_util.h
tools/testing/selftests/kvm/include/x86.h
tools/testing/selftests/kvm/lib/kvm_util.c
tools/testing/selftests/kvm/lib/kvm_util_internal.h
tools/testing/selftests/kvm/lib/x86.c

index 637b7017b6ee5360db21e12f54dc118769c1633b..87e05664c7f9db288974705a9af3fd90f4f9b5bf 100644 (file)
@@ -75,7 +75,7 @@ void vcpu_ioctl(struct kvm_vm *vm,
        uint32_t vcpuid, unsigned long ioctl, void *arg);
 void vm_ioctl(struct kvm_vm *vm, unsigned long ioctl, void *arg);
 void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags);
-void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid);
+void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid, int pgd_memslot, int gdt_memslot);
 vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min,
        uint32_t data_memslot, uint32_t pgd_memslot);
 void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa);
index 4a5b2c4c1a0fd6ef6bf9c8dcff28620657a47c07..560304e595125e42b40a271cb3e2ced25460dd66 100644 (file)
@@ -59,8 +59,8 @@ enum x86_register {
 struct desc64 {
        uint16_t limit0;
        uint16_t base0;
-       unsigned base1:8, type:5, dpl:2, p:1;
-       unsigned limit1:4, zero0:3, g:1, base2:8;
+       unsigned base1:8, s:1, type:4, dpl:2, p:1;
+       unsigned limit1:4, avl:1, l:1, db:1, g:1, base2:8;
        uint32_t base3;
        uint32_t zero1;
 } __attribute__((packed));
index 37e2a787d2fcc6cc6cedcb30b9620456e99e43c0..610d1326f03d0c48e5be20553d45d9fb1846b4b0 100644 (file)
@@ -701,7 +701,7 @@ static int vcpu_mmap_sz(void)
  * Creates and adds to the VM specified by vm and virtual CPU with
  * the ID given by vcpuid.
  */
-void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid)
+void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid, int pgd_memslot, int gdt_memslot)
 {
        struct vcpu *vcpu;
 
@@ -736,7 +736,7 @@ void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid)
        vcpu->next = vm->vcpu_head;
        vm->vcpu_head = vcpu;
 
-       vcpu_setup(vm, vcpuid);
+       vcpu_setup(vm, vcpuid, pgd_memslot, gdt_memslot);
 }
 
 /* VM Virtual Address Unused Gap
index a0bd1980c81c9bf9480df303777233225834b8d3..cbb40288890afc40e3e8aa315e9a755514f40b3b 100644 (file)
@@ -51,13 +51,16 @@ struct kvm_vm {
        struct userspace_mem_region *userspace_mem_region_head;
        struct sparsebit *vpages_valid;
        struct sparsebit *vpages_mapped;
+
        bool pgd_created;
        vm_paddr_t pgd;
+       vm_vaddr_t gdt;
+       vm_vaddr_t tss;
 };
 
 struct vcpu *vcpu_find(struct kvm_vm *vm,
        uint32_t vcpuid);
-void vcpu_setup(struct kvm_vm *vm, int vcpuid);
+void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_memslot);
 void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent);
 void regs_dump(FILE *stream, struct kvm_regs *regs,
        uint8_t indent);
index 2f17675f42751b21b0a20c63c266461e3517b2b1..024e95f1b47068db4e393efa9f8709c883301e7e 100644 (file)
@@ -239,25 +239,6 @@ void virt_pgd_alloc(struct kvm_vm *vm, uint32_t pgd_memslot)
                vm_paddr_t paddr = vm_phy_page_alloc(vm,
                        KVM_GUEST_PAGE_TABLE_MIN_PADDR, pgd_memslot);
                vm->pgd = paddr;
-
-               /* Set pointer to pgd tables in all the VCPUs that
-                * have already been created.  Future VCPUs will have
-                * the value set as each one is created.
-                */
-               for (struct vcpu *vcpu = vm->vcpu_head; vcpu;
-                       vcpu = vcpu->next) {
-                       struct kvm_sregs sregs;
-
-                       /* Obtain the current system register settings */
-                       vcpu_sregs_get(vm, vcpu->id, &sregs);
-
-                       /* Set and store the pointer to the start of the
-                        * pgd tables.
-                        */
-                       sregs.cr3 = vm->pgd;
-                       vcpu_sregs_set(vm, vcpu->id, &sregs);
-               }
-
                vm->pgd_created = true;
        }
 }
@@ -460,9 +441,32 @@ static void kvm_seg_set_unusable(struct kvm_segment *segp)
        segp->unusable = true;
 }
 
+static void kvm_seg_fill_gdt_64bit(struct kvm_vm *vm, struct kvm_segment *segp)
+{
+       void *gdt = addr_gva2hva(vm, vm->gdt);
+       struct desc64 *desc = gdt + (segp->selector >> 3) * 8;
+
+       desc->limit0 = segp->limit & 0xFFFF;
+       desc->base0 = segp->base & 0xFFFF;
+       desc->base1 = segp->base >> 16;
+       desc->s = segp->s;
+       desc->type = segp->type;
+       desc->dpl = segp->dpl;
+       desc->p = segp->present;
+       desc->limit1 = segp->limit >> 16;
+       desc->l = segp->l;
+       desc->db = segp->db;
+       desc->g = segp->g;
+       desc->base2 = segp->base >> 24;
+       if (!segp->s)
+               desc->base3 = segp->base >> 32;
+}
+
+
 /* Set Long Mode Flat Kernel Code Segment
  *
  * Input Args:
+ *   vm - VM whose GDT is being filled, or NULL to only write segp
  *   selector - selector value
  *
  * Output Args:
@@ -473,7 +477,7 @@ static void kvm_seg_set_unusable(struct kvm_segment *segp)
  * Sets up the KVM segment pointed to by segp, to be a code segment
  * with the selector value given by selector.
  */
-static void kvm_seg_set_kernel_code_64bit(uint16_t selector,
+static void kvm_seg_set_kernel_code_64bit(struct kvm_vm *vm, uint16_t selector,
        struct kvm_segment *segp)
 {
        memset(segp, 0, sizeof(*segp));
@@ -486,11 +490,14 @@ static void kvm_seg_set_kernel_code_64bit(uint16_t selector,
        segp->g = true;
        segp->l = true;
        segp->present = 1;
+       if (vm)
+               kvm_seg_fill_gdt_64bit(vm, segp);
 }
 
 /* Set Long Mode Flat Kernel Data Segment
  *
  * Input Args:
+ *   vm - VM whose GDT is being filled, or NULL to only write segp
  *   selector - selector value
  *
  * Output Args:
@@ -501,7 +508,7 @@ static void kvm_seg_set_kernel_code_64bit(uint16_t selector,
  * Sets up the KVM segment pointed to by segp, to be a data segment
  * with the selector value given by selector.
  */
-static void kvm_seg_set_kernel_data_64bit(uint16_t selector,
+static void kvm_seg_set_kernel_data_64bit(struct kvm_vm *vm, uint16_t selector,
        struct kvm_segment *segp)
 {
        memset(segp, 0, sizeof(*segp));
@@ -513,6 +520,8 @@ static void kvm_seg_set_kernel_data_64bit(uint16_t selector,
                                          */
        segp->g = true;
        segp->present = true;
+       if (vm)
+               kvm_seg_fill_gdt_64bit(vm, segp);
 }
 
 /* Address Guest Virtual to Guest Physical
@@ -575,13 +584,45 @@ unmapped_gva:
                    "gva: 0x%lx", gva);
 }
 
-void vcpu_setup(struct kvm_vm *vm, int vcpuid)
+static void kvm_setup_gdt(struct kvm_vm *vm, struct kvm_dtable *dt, int gdt_memslot,
+                         int pgd_memslot)
+{
+       if (!vm->gdt)
+               vm->gdt = vm_vaddr_alloc(vm, getpagesize(),
+                       KVM_UTIL_MIN_VADDR, gdt_memslot, pgd_memslot);
+
+       dt->base = vm->gdt;
+       dt->limit = getpagesize();
+}
+
+static void kvm_setup_tss_64bit(struct kvm_vm *vm, struct kvm_segment *segp,
+                               int selector, int gdt_memslot,
+                               int pgd_memslot)
+{
+       if (!vm->tss)
+               vm->tss = vm_vaddr_alloc(vm, getpagesize(),
+                       KVM_UTIL_MIN_VADDR, gdt_memslot, pgd_memslot);
+
+       memset(segp, 0, sizeof(*segp));
+       segp->base = vm->tss;
+       segp->limit = 0x67;
+       segp->selector = selector;
+       segp->type = 0xb;
+       segp->present = 1;
+       kvm_seg_fill_gdt_64bit(vm, segp);
+}
+
+void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_memslot)
 {
        struct kvm_sregs sregs;
 
        /* Set mode specific system register values. */
        vcpu_sregs_get(vm, vcpuid, &sregs);
 
+       sregs.idt.limit = 0;
+
+       kvm_setup_gdt(vm, &sregs.gdt, gdt_memslot, pgd_memslot);
+
        switch (vm->mode) {
        case VM_MODE_FLAT48PG:
                sregs.cr0 = X86_CR0_PE | X86_CR0_NE | X86_CR0_PG;
@@ -589,30 +630,18 @@ void vcpu_setup(struct kvm_vm *vm, int vcpuid)
                sregs.efer |= (EFER_LME | EFER_LMA | EFER_NX);
 
                kvm_seg_set_unusable(&sregs.ldt);
-               kvm_seg_set_kernel_code_64bit(0x8, &sregs.cs);
-               kvm_seg_set_kernel_data_64bit(0x10, &sregs.ds);
-               kvm_seg_set_kernel_data_64bit(0x10, &sregs.es);
+               kvm_seg_set_kernel_code_64bit(vm, 0x8, &sregs.cs);
+               kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.ds);
+               kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.es);
+               kvm_setup_tss_64bit(vm, &sregs.tr, 0x18, gdt_memslot, pgd_memslot);
                break;
 
        default:
                TEST_ASSERT(false, "Unknown guest mode, mode: 0x%x", vm->mode);
        }
-       vcpu_sregs_set(vm, vcpuid, &sregs);
-
-       /* If virtual translation table have been setup, set system register
-        * to point to the tables.  It's okay if they haven't been setup yet,
-        * in that the code that sets up the virtual translation tables, will
-        * go back through any VCPUs that have already been created and set
-        * their values.
-        */
-       if (vm->pgd_created) {
-               struct kvm_sregs sregs;
 
-               vcpu_sregs_get(vm, vcpuid, &sregs);
-
-               sregs.cr3 = vm->pgd;
-               vcpu_sregs_set(vm, vcpuid, &sregs);
-       }
+       sregs.cr3 = vm->pgd;
+       vcpu_sregs_set(vm, vcpuid, &sregs);
 }
 /* Adds a vCPU with reasonable defaults (i.e., a stack)
  *
@@ -629,7 +658,7 @@ void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code)
                                     DEFAULT_GUEST_STACK_VADDR_MIN, 0, 0);
 
        /* Create VCPU */
-       vm_vcpu_add(vm, vcpuid);
+       vm_vcpu_add(vm, vcpuid, 0, 0);
 
        /* Setup guest general purpose registers */
        vcpu_regs_get(vm, vcpuid, &regs);