Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa3899ad authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

kvm: selftests: add basic test for state save and restore



The test calls KVM_RUN repeatedly, and creates an entirely new VM with the
old memory and vCPU state on every exit to userspace.  The kvm_util API is
expanded with two functions that manage the lifetime of a kvm_vm struct:
the first closes the file descriptors and leaves the memory allocated,
and the second opens the file descriptors and reuses the memory from
the previous incarnation of the kvm_vm struct.

For now the test is very basic, as it does not test for example XSAVE or
vCPU events.  However, it will test nested virtualization state starting
with the next patch.

Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 0a505fe6
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -2,3 +2,4 @@ cr4_cpuid_sync_test
set_sregs_test
set_sregs_test
sync_regs_test
sync_regs_test
vmx_tsc_adjust_test
vmx_tsc_adjust_test
state_test
+1 −0
Original line number Original line Diff line number Diff line
@@ -10,6 +10,7 @@ TEST_GEN_PROGS_x86_64 = set_sregs_test
TEST_GEN_PROGS_x86_64 += sync_regs_test
TEST_GEN_PROGS_x86_64 += sync_regs_test
TEST_GEN_PROGS_x86_64 += vmx_tsc_adjust_test
TEST_GEN_PROGS_x86_64 += vmx_tsc_adjust_test
TEST_GEN_PROGS_x86_64 += cr4_cpuid_sync_test
TEST_GEN_PROGS_x86_64 += cr4_cpuid_sync_test
TEST_GEN_PROGS_x86_64 += state_test


TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M))
TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M))
LIBKVM += $(LIBKVM_$(UNAME_M))
LIBKVM += $(LIBKVM_$(UNAME_M))
+2 −0
Original line number Original line Diff line number Diff line
@@ -53,6 +53,8 @@ int kvm_check_cap(long cap);


struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm);
struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm);
void kvm_vm_free(struct kvm_vm *vmp);
void kvm_vm_free(struct kvm_vm *vmp);
void kvm_vm_restart(struct kvm_vm *vmp, int perm);
void kvm_vm_release(struct kvm_vm *vmp);


int kvm_memcmp_hva_gva(void *hva,
int kvm_memcmp_hva_gva(void *hva,
	struct kvm_vm *vm, const vm_vaddr_t gva, size_t len);
	struct kvm_vm *vm, const vm_vaddr_t gva, size_t len);
+4 −0
Original line number Original line Diff line number Diff line
@@ -303,6 +303,10 @@ static inline unsigned long get_xmm(int n)
	return 0;
	return 0;
}
}


struct kvm_x86_state;
struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid);
void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_x86_state *state);

/*
/*
 * Basic CPU control in CR0
 * Basic CPU control in CR0
 */
 */
+66 −22
Original line number Original line Diff line number Diff line
@@ -62,6 +62,18 @@ int kvm_check_cap(long cap)
	return ret;
	return ret;
}
}


static void vm_open(struct kvm_vm *vm, int perm)
{
	vm->kvm_fd = open(KVM_DEV_PATH, perm);
	if (vm->kvm_fd < 0)
		exit(KSFT_SKIP);

	/* Create VM. */
	vm->fd = ioctl(vm->kvm_fd, KVM_CREATE_VM, NULL);
	TEST_ASSERT(vm->fd >= 0, "KVM_CREATE_VM ioctl failed, "
		"rc: %i errno: %i", vm->fd, errno);
}

/* VM Create
/* VM Create
 *
 *
 * Input Args:
 * Input Args:
@@ -90,16 +102,7 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
	TEST_ASSERT(vm != NULL, "Insufficent Memory");
	TEST_ASSERT(vm != NULL, "Insufficent Memory");


	vm->mode = mode;
	vm->mode = mode;
	kvm_fd = open(KVM_DEV_PATH, perm);
	vm_open(vm, perm);
	if (kvm_fd < 0)
		exit(KSFT_SKIP);

	/* Create VM. */
	vm->fd = ioctl(kvm_fd, KVM_CREATE_VM, NULL);
	TEST_ASSERT(vm->fd >= 0, "KVM_CREATE_VM ioctl failed, "
		"rc: %i errno: %i", vm->fd, errno);

	close(kvm_fd);


	/* Setup mode specific traits. */
	/* Setup mode specific traits. */
	switch (vm->mode) {
	switch (vm->mode) {
@@ -132,6 +135,39 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
	return vm;
	return vm;
}
}


/* VM Restart
 *
 * Input Args:
 *   vm - VM that has been released before
 *   perm - permission
 *
 * Output Args: None
 *
 * Reopens the file descriptors associated to the VM and reinstates the
 * global state, such as the irqchip and the memory regions that are mapped
 * into the guest.
 */
void kvm_vm_restart(struct kvm_vm *vmp, int perm)
{
	struct userspace_mem_region *region;

	vm_open(vmp, perm);
	if (vmp->has_irqchip)
		vm_create_irqchip(vmp);

	for (region = vmp->userspace_mem_region_head; region;
		region = region->next) {
		int ret = ioctl(vmp->fd, KVM_SET_USER_MEMORY_REGION, &region->region);
		TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed,\n"
			    "  rc: %i errno: %i\n"
			    "  slot: %u flags: 0x%x\n"
			    "  guest_phys_addr: 0x%lx size: 0x%lx",
			    ret, errno, region->region.slot, region->region.flags,
			    region->region.guest_phys_addr,
			    region->region.memory_size);
	}
}

/* Userspace Memory Region Find
/* Userspace Memory Region Find
 *
 *
 * Input Args:
 * Input Args:
@@ -256,6 +292,23 @@ static void vm_vcpu_rm(struct kvm_vm *vm, uint32_t vcpuid)
	free(vcpu);
	free(vcpu);
}
}


void kvm_vm_release(struct kvm_vm *vmp)
{
	int ret;

	/* Free VCPUs. */
	while (vmp->vcpu_head)
		vm_vcpu_rm(vmp, vmp->vcpu_head->id);

	/* Close file descriptor for the VM. */
	ret = close(vmp->fd);
	TEST_ASSERT(ret == 0, "Close of vm fd failed,\n"
		"  vmp->fd: %i rc: %i errno: %i", vmp->fd, ret, errno);

	close(vmp->kvm_fd);
	TEST_ASSERT(ret == 0, "Close of /dev/kvm fd failed,\n"
		"  vmp->kvm_fd: %i rc: %i errno: %i", vmp->kvm_fd, ret, errno);
}


/* Destroys and frees the VM pointed to by vmp.
/* Destroys and frees the VM pointed to by vmp.
 */
 */
@@ -286,22 +339,11 @@ void kvm_vm_free(struct kvm_vm *vmp)
		free(region);
		free(region);
	}
	}


	/* Free VCPUs. */
	while (vmp->vcpu_head)
		vm_vcpu_rm(vmp, vmp->vcpu_head->id);

	/* Free sparsebit arrays. */
	/* Free sparsebit arrays. */
	sparsebit_free(&vmp->vpages_valid);
	sparsebit_free(&vmp->vpages_valid);
	sparsebit_free(&vmp->vpages_mapped);
	sparsebit_free(&vmp->vpages_mapped);


	/* Close file descriptor for the VM. */
	kvm_vm_release(vmp);
	ret = close(vmp->fd);
	TEST_ASSERT(ret == 0, "Close of vm fd failed,\n"
		"  vmp->fd: %i rc: %i errno: %i", vmp->fd, ret, errno);

	close(vmp->kvm_fd);
	TEST_ASSERT(ret == 0, "Close of /dev/kvm fd failed,\n"
		"  vmp->kvm_fd: %i rc: %i errno: %i", vmp->kvm_fd, ret, errno);


	/* Free the structure describing the VM. */
	/* Free the structure describing the VM. */
	free(vmp);
	free(vmp);
@@ -965,6 +1007,8 @@ void vm_create_irqchip(struct kvm_vm *vm)
	ret = ioctl(vm->fd, KVM_CREATE_IRQCHIP, 0);
	ret = ioctl(vm->fd, KVM_CREATE_IRQCHIP, 0);
	TEST_ASSERT(ret == 0, "KVM_CREATE_IRQCHIP IOCTL failed, "
	TEST_ASSERT(ret == 0, "KVM_CREATE_IRQCHIP IOCTL failed, "
		"rc: %i errno: %i", ret, errno);
		"rc: %i errno: %i", ret, errno);

	vm->has_irqchip = true;
}
}


/* VM VCPU State
/* VM VCPU State
Loading