|
@@ -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, ®s);
|