Skip to content

Commit 2305339

Browse files
committed
kvm: selftests: create a GDT and TSS
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>
1 parent 44883f0 commit 2305339

File tree

5 files changed

+79
-47
lines changed

5 files changed

+79
-47
lines changed

tools/testing/selftests/kvm/include/kvm_util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ void vcpu_ioctl(struct kvm_vm *vm,
7575
uint32_t vcpuid, unsigned long ioctl, void *arg);
7676
void vm_ioctl(struct kvm_vm *vm, unsigned long ioctl, void *arg);
7777
void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags);
78-
void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid);
78+
void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid, int pgd_memslot, int gdt_memslot);
7979
vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min,
8080
uint32_t data_memslot, uint32_t pgd_memslot);
8181
void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa);

tools/testing/selftests/kvm/include/x86.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ enum x86_register {
5959
struct desc64 {
6060
uint16_t limit0;
6161
uint16_t base0;
62-
unsigned base1:8, type:5, dpl:2, p:1;
63-
unsigned limit1:4, zero0:3, g:1, base2:8;
62+
unsigned base1:8, s:1, type:4, dpl:2, p:1;
63+
unsigned limit1:4, avl:1, l:1, db:1, g:1, base2:8;
6464
uint32_t base3;
6565
uint32_t zero1;
6666
} __attribute__((packed));

tools/testing/selftests/kvm/lib/kvm_util.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ static int vcpu_mmap_sz(void)
701701
* Creates and adds to the VM specified by vm and virtual CPU with
702702
* the ID given by vcpuid.
703703
*/
704-
void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid)
704+
void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid, int pgd_memslot, int gdt_memslot)
705705
{
706706
struct vcpu *vcpu;
707707

@@ -736,7 +736,7 @@ void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid)
736736
vcpu->next = vm->vcpu_head;
737737
vm->vcpu_head = vcpu;
738738

739-
vcpu_setup(vm, vcpuid);
739+
vcpu_setup(vm, vcpuid, pgd_memslot, gdt_memslot);
740740
}
741741

742742
/* VM Virtual Address Unused Gap

tools/testing/selftests/kvm/lib/kvm_util_internal.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,16 @@ struct kvm_vm {
5151
struct userspace_mem_region *userspace_mem_region_head;
5252
struct sparsebit *vpages_valid;
5353
struct sparsebit *vpages_mapped;
54+
5455
bool pgd_created;
5556
vm_paddr_t pgd;
57+
vm_vaddr_t gdt;
58+
vm_vaddr_t tss;
5659
};
5760

5861
struct vcpu *vcpu_find(struct kvm_vm *vm,
5962
uint32_t vcpuid);
60-
void vcpu_setup(struct kvm_vm *vm, int vcpuid);
63+
void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_memslot);
6164
void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent);
6265
void regs_dump(FILE *stream, struct kvm_regs *regs,
6366
uint8_t indent);

tools/testing/selftests/kvm/lib/x86.c

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -239,25 +239,6 @@ void virt_pgd_alloc(struct kvm_vm *vm, uint32_t pgd_memslot)
239239
vm_paddr_t paddr = vm_phy_page_alloc(vm,
240240
KVM_GUEST_PAGE_TABLE_MIN_PADDR, pgd_memslot);
241241
vm->pgd = paddr;
242-
243-
/* Set pointer to pgd tables in all the VCPUs that
244-
* have already been created. Future VCPUs will have
245-
* the value set as each one is created.
246-
*/
247-
for (struct vcpu *vcpu = vm->vcpu_head; vcpu;
248-
vcpu = vcpu->next) {
249-
struct kvm_sregs sregs;
250-
251-
/* Obtain the current system register settings */
252-
vcpu_sregs_get(vm, vcpu->id, &sregs);
253-
254-
/* Set and store the pointer to the start of the
255-
* pgd tables.
256-
*/
257-
sregs.cr3 = vm->pgd;
258-
vcpu_sregs_set(vm, vcpu->id, &sregs);
259-
}
260-
261242
vm->pgd_created = true;
262243
}
263244
}
@@ -460,9 +441,32 @@ static void kvm_seg_set_unusable(struct kvm_segment *segp)
460441
segp->unusable = true;
461442
}
462443

444+
static void kvm_seg_fill_gdt_64bit(struct kvm_vm *vm, struct kvm_segment *segp)
445+
{
446+
void *gdt = addr_gva2hva(vm, vm->gdt);
447+
struct desc64 *desc = gdt + (segp->selector >> 3) * 8;
448+
449+
desc->limit0 = segp->limit & 0xFFFF;
450+
desc->base0 = segp->base & 0xFFFF;
451+
desc->base1 = segp->base >> 16;
452+
desc->s = segp->s;
453+
desc->type = segp->type;
454+
desc->dpl = segp->dpl;
455+
desc->p = segp->present;
456+
desc->limit1 = segp->limit >> 16;
457+
desc->l = segp->l;
458+
desc->db = segp->db;
459+
desc->g = segp->g;
460+
desc->base2 = segp->base >> 24;
461+
if (!segp->s)
462+
desc->base3 = segp->base >> 32;
463+
}
464+
465+
463466
/* Set Long Mode Flat Kernel Code Segment
464467
*
465468
* Input Args:
469+
* vm - VM whose GDT is being filled, or NULL to only write segp
466470
* selector - selector value
467471
*
468472
* Output Args:
@@ -473,7 +477,7 @@ static void kvm_seg_set_unusable(struct kvm_segment *segp)
473477
* Sets up the KVM segment pointed to by segp, to be a code segment
474478
* with the selector value given by selector.
475479
*/
476-
static void kvm_seg_set_kernel_code_64bit(uint16_t selector,
480+
static void kvm_seg_set_kernel_code_64bit(struct kvm_vm *vm, uint16_t selector,
477481
struct kvm_segment *segp)
478482
{
479483
memset(segp, 0, sizeof(*segp));
@@ -486,11 +490,14 @@ static void kvm_seg_set_kernel_code_64bit(uint16_t selector,
486490
segp->g = true;
487491
segp->l = true;
488492
segp->present = 1;
493+
if (vm)
494+
kvm_seg_fill_gdt_64bit(vm, segp);
489495
}
490496

491497
/* Set Long Mode Flat Kernel Data Segment
492498
*
493499
* Input Args:
500+
* vm - VM whose GDT is being filled, or NULL to only write segp
494501
* selector - selector value
495502
*
496503
* Output Args:
@@ -501,7 +508,7 @@ static void kvm_seg_set_kernel_code_64bit(uint16_t selector,
501508
* Sets up the KVM segment pointed to by segp, to be a data segment
502509
* with the selector value given by selector.
503510
*/
504-
static void kvm_seg_set_kernel_data_64bit(uint16_t selector,
511+
static void kvm_seg_set_kernel_data_64bit(struct kvm_vm *vm, uint16_t selector,
505512
struct kvm_segment *segp)
506513
{
507514
memset(segp, 0, sizeof(*segp));
@@ -513,6 +520,8 @@ static void kvm_seg_set_kernel_data_64bit(uint16_t selector,
513520
*/
514521
segp->g = true;
515522
segp->present = true;
523+
if (vm)
524+
kvm_seg_fill_gdt_64bit(vm, segp);
516525
}
517526

518527
/* Address Guest Virtual to Guest Physical
@@ -575,44 +584,64 @@ vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
575584
"gva: 0x%lx", gva);
576585
}
577586

578-
void vcpu_setup(struct kvm_vm *vm, int vcpuid)
587+
static void kvm_setup_gdt(struct kvm_vm *vm, struct kvm_dtable *dt, int gdt_memslot,
588+
int pgd_memslot)
589+
{
590+
if (!vm->gdt)
591+
vm->gdt = vm_vaddr_alloc(vm, getpagesize(),
592+
KVM_UTIL_MIN_VADDR, gdt_memslot, pgd_memslot);
593+
594+
dt->base = vm->gdt;
595+
dt->limit = getpagesize();
596+
}
597+
598+
static void kvm_setup_tss_64bit(struct kvm_vm *vm, struct kvm_segment *segp,
599+
int selector, int gdt_memslot,
600+
int pgd_memslot)
601+
{
602+
if (!vm->tss)
603+
vm->tss = vm_vaddr_alloc(vm, getpagesize(),
604+
KVM_UTIL_MIN_VADDR, gdt_memslot, pgd_memslot);
605+
606+
memset(segp, 0, sizeof(*segp));
607+
segp->base = vm->tss;
608+
segp->limit = 0x67;
609+
segp->selector = selector;
610+
segp->type = 0xb;
611+
segp->present = 1;
612+
kvm_seg_fill_gdt_64bit(vm, segp);
613+
}
614+
615+
void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_memslot)
579616
{
580617
struct kvm_sregs sregs;
581618

582619
/* Set mode specific system register values. */
583620
vcpu_sregs_get(vm, vcpuid, &sregs);
584621

622+
sregs.idt.limit = 0;
623+
624+
kvm_setup_gdt(vm, &sregs.gdt, gdt_memslot, pgd_memslot);
625+
585626
switch (vm->mode) {
586627
case VM_MODE_FLAT48PG:
587628
sregs.cr0 = X86_CR0_PE | X86_CR0_NE | X86_CR0_PG;
588629
sregs.cr4 |= X86_CR4_PAE;
589630
sregs.efer |= (EFER_LME | EFER_LMA | EFER_NX);
590631

591632
kvm_seg_set_unusable(&sregs.ldt);
592-
kvm_seg_set_kernel_code_64bit(0x8, &sregs.cs);
593-
kvm_seg_set_kernel_data_64bit(0x10, &sregs.ds);
594-
kvm_seg_set_kernel_data_64bit(0x10, &sregs.es);
633+
kvm_seg_set_kernel_code_64bit(vm, 0x8, &sregs.cs);
634+
kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.ds);
635+
kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.es);
636+
kvm_setup_tss_64bit(vm, &sregs.tr, 0x18, gdt_memslot, pgd_memslot);
595637
break;
596638

597639
default:
598640
TEST_ASSERT(false, "Unknown guest mode, mode: 0x%x", vm->mode);
599641
}
600-
vcpu_sregs_set(vm, vcpuid, &sregs);
601-
602-
/* If virtual translation table have been setup, set system register
603-
* to point to the tables. It's okay if they haven't been setup yet,
604-
* in that the code that sets up the virtual translation tables, will
605-
* go back through any VCPUs that have already been created and set
606-
* their values.
607-
*/
608-
if (vm->pgd_created) {
609-
struct kvm_sregs sregs;
610642

611-
vcpu_sregs_get(vm, vcpuid, &sregs);
612-
613-
sregs.cr3 = vm->pgd;
614-
vcpu_sregs_set(vm, vcpuid, &sregs);
615-
}
643+
sregs.cr3 = vm->pgd;
644+
vcpu_sregs_set(vm, vcpuid, &sregs);
616645
}
617646
/* Adds a vCPU with reasonable defaults (i.e., a stack)
618647
*
@@ -629,7 +658,7 @@ void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code)
629658
DEFAULT_GUEST_STACK_VADDR_MIN, 0, 0);
630659

631660
/* Create VCPU */
632-
vm_vcpu_add(vm, vcpuid);
661+
vm_vcpu_add(vm, vcpuid, 0, 0);
633662

634663
/* Setup guest general purpose registers */
635664
vcpu_regs_get(vm, vcpuid, &regs);

0 commit comments

Comments
 (0)