Skip to content

Commit fa3899a

Browse files
committed
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: Paolo Bonzini <pbonzini@redhat.com>
1 parent 0a505fe commit fa3899a

File tree

8 files changed

+311
-22
lines changed

8 files changed

+311
-22
lines changed

tools/testing/selftests/kvm/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ cr4_cpuid_sync_test
22
set_sregs_test
33
sync_regs_test
44
vmx_tsc_adjust_test
5+
state_test

tools/testing/selftests/kvm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ TEST_GEN_PROGS_x86_64 = set_sregs_test
1010
TEST_GEN_PROGS_x86_64 += sync_regs_test
1111
TEST_GEN_PROGS_x86_64 += vmx_tsc_adjust_test
1212
TEST_GEN_PROGS_x86_64 += cr4_cpuid_sync_test
13+
TEST_GEN_PROGS_x86_64 += state_test
1314

1415
TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M))
1516
LIBKVM += $(LIBKVM_$(UNAME_M))

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ int kvm_check_cap(long cap);
5353

5454
struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm);
5555
void kvm_vm_free(struct kvm_vm *vmp);
56+
void kvm_vm_restart(struct kvm_vm *vmp, int perm);
57+
void kvm_vm_release(struct kvm_vm *vmp);
5658

5759
int kvm_memcmp_hva_gva(void *hva,
5860
struct kvm_vm *vm, const vm_vaddr_t gva, size_t len);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ static inline unsigned long get_xmm(int n)
303303
return 0;
304304
}
305305

306+
struct kvm_x86_state;
307+
struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid);
308+
void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_x86_state *state);
309+
306310
/*
307311
* Basic CPU control in CR0
308312
*/

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

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ int kvm_check_cap(long cap)
6262
return ret;
6363
}
6464

65+
static void vm_open(struct kvm_vm *vm, int perm)
66+
{
67+
vm->kvm_fd = open(KVM_DEV_PATH, perm);
68+
if (vm->kvm_fd < 0)
69+
exit(KSFT_SKIP);
70+
71+
/* Create VM. */
72+
vm->fd = ioctl(vm->kvm_fd, KVM_CREATE_VM, NULL);
73+
TEST_ASSERT(vm->fd >= 0, "KVM_CREATE_VM ioctl failed, "
74+
"rc: %i errno: %i", vm->fd, errno);
75+
}
76+
6577
/* VM Create
6678
*
6779
* Input Args:
@@ -90,16 +102,7 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
90102
TEST_ASSERT(vm != NULL, "Insufficent Memory");
91103

92104
vm->mode = mode;
93-
kvm_fd = open(KVM_DEV_PATH, perm);
94-
if (kvm_fd < 0)
95-
exit(KSFT_SKIP);
96-
97-
/* Create VM. */
98-
vm->fd = ioctl(kvm_fd, KVM_CREATE_VM, NULL);
99-
TEST_ASSERT(vm->fd >= 0, "KVM_CREATE_VM ioctl failed, "
100-
"rc: %i errno: %i", vm->fd, errno);
101-
102-
close(kvm_fd);
105+
vm_open(vm, perm);
103106

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

138+
/* VM Restart
139+
*
140+
* Input Args:
141+
* vm - VM that has been released before
142+
* perm - permission
143+
*
144+
* Output Args: None
145+
*
146+
* Reopens the file descriptors associated to the VM and reinstates the
147+
* global state, such as the irqchip and the memory regions that are mapped
148+
* into the guest.
149+
*/
150+
void kvm_vm_restart(struct kvm_vm *vmp, int perm)
151+
{
152+
struct userspace_mem_region *region;
153+
154+
vm_open(vmp, perm);
155+
if (vmp->has_irqchip)
156+
vm_create_irqchip(vmp);
157+
158+
for (region = vmp->userspace_mem_region_head; region;
159+
region = region->next) {
160+
int ret = ioctl(vmp->fd, KVM_SET_USER_MEMORY_REGION, &region->region);
161+
TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed,\n"
162+
" rc: %i errno: %i\n"
163+
" slot: %u flags: 0x%x\n"
164+
" guest_phys_addr: 0x%lx size: 0x%lx",
165+
ret, errno, region->region.slot, region->region.flags,
166+
region->region.guest_phys_addr,
167+
region->region.memory_size);
168+
}
169+
}
170+
135171
/* Userspace Memory Region Find
136172
*
137173
* Input Args:
@@ -256,6 +292,23 @@ static void vm_vcpu_rm(struct kvm_vm *vm, uint32_t vcpuid)
256292
free(vcpu);
257293
}
258294

295+
void kvm_vm_release(struct kvm_vm *vmp)
296+
{
297+
int ret;
298+
299+
/* Free VCPUs. */
300+
while (vmp->vcpu_head)
301+
vm_vcpu_rm(vmp, vmp->vcpu_head->id);
302+
303+
/* Close file descriptor for the VM. */
304+
ret = close(vmp->fd);
305+
TEST_ASSERT(ret == 0, "Close of vm fd failed,\n"
306+
" vmp->fd: %i rc: %i errno: %i", vmp->fd, ret, errno);
307+
308+
close(vmp->kvm_fd);
309+
TEST_ASSERT(ret == 0, "Close of /dev/kvm fd failed,\n"
310+
" vmp->kvm_fd: %i rc: %i errno: %i", vmp->kvm_fd, ret, errno);
311+
}
259312

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

289-
/* Free VCPUs. */
290-
while (vmp->vcpu_head)
291-
vm_vcpu_rm(vmp, vmp->vcpu_head->id);
292-
293342
/* Free sparsebit arrays. */
294343
sparsebit_free(&vmp->vpages_valid);
295344
sparsebit_free(&vmp->vpages_mapped);
296345

297-
/* Close file descriptor for the VM. */
298-
ret = close(vmp->fd);
299-
TEST_ASSERT(ret == 0, "Close of vm fd failed,\n"
300-
" vmp->fd: %i rc: %i errno: %i", vmp->fd, ret, errno);
301-
302-
close(vmp->kvm_fd);
303-
TEST_ASSERT(ret == 0, "Close of /dev/kvm fd failed,\n"
304-
" vmp->kvm_fd: %i rc: %i errno: %i", vmp->kvm_fd, ret, errno);
346+
kvm_vm_release(vmp);
305347

306348
/* Free the structure describing the VM. */
307349
free(vmp);
@@ -965,6 +1007,8 @@ void vm_create_irqchip(struct kvm_vm *vm)
9651007
ret = ioctl(vm->fd, KVM_CREATE_IRQCHIP, 0);
9661008
TEST_ASSERT(ret == 0, "KVM_CREATE_IRQCHIP IOCTL failed, "
9671009
"rc: %i errno: %i", ret, errno);
1010+
1011+
vm->has_irqchip = true;
9681012
}
9691013

9701014
/* VM VCPU State

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct vcpu {
4343

4444
struct kvm_vm {
4545
int mode;
46+
int kvm_fd;
4647
int fd;
4748
unsigned int page_size;
4849
unsigned int page_shift;
@@ -52,6 +53,7 @@ struct kvm_vm {
5253
struct sparsebit *vpages_valid;
5354
struct sparsebit *vpages_mapped;
5455

56+
bool has_irqchip;
5557
bool pgd_created;
5658
vm_paddr_t pgd;
5759
vm_vaddr_t gdt;

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

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,3 +727,119 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, void *guest_code)
727727

728728
return vm;
729729
}
730+
731+
struct kvm_x86_state {
732+
struct kvm_vcpu_events events;
733+
struct kvm_mp_state mp_state;
734+
struct kvm_regs regs;
735+
struct kvm_xsave xsave;
736+
struct kvm_xcrs xcrs;
737+
struct kvm_sregs sregs;
738+
struct kvm_debugregs debugregs;
739+
struct kvm_msrs msrs;
740+
};
741+
742+
static int kvm_get_num_msrs(struct kvm_vm *vm)
743+
{
744+
struct kvm_msr_list nmsrs;
745+
int r;
746+
747+
nmsrs.nmsrs = 0;
748+
r = ioctl(vm->kvm_fd, KVM_GET_MSR_INDEX_LIST, &nmsrs);
749+
TEST_ASSERT(r == -1 && errno == E2BIG, "Unexpected result from KVM_GET_MSR_INDEX_LIST probe, r: %i",
750+
r);
751+
752+
return nmsrs.nmsrs;
753+
}
754+
755+
struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid)
756+
{
757+
struct vcpu *vcpu = vcpu_find(vm, vcpuid);
758+
struct kvm_msr_list *list;
759+
struct kvm_x86_state *state;
760+
int nmsrs, r, i;
761+
762+
nmsrs = kvm_get_num_msrs(vm);
763+
list = malloc(sizeof(*list) + nmsrs * sizeof(list->indices[0]));
764+
list->nmsrs = nmsrs;
765+
r = ioctl(vm->kvm_fd, KVM_GET_MSR_INDEX_LIST, list);
766+
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_MSR_INDEX_LIST, r: %i",
767+
r);
768+
769+
state = malloc(sizeof(*state) + nmsrs * sizeof(state->msrs.entries[0]));
770+
r = ioctl(vcpu->fd, KVM_GET_VCPU_EVENTS, &state->events);
771+
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_VCPU_EVENTS, r: %i",
772+
r);
773+
774+
r = ioctl(vcpu->fd, KVM_GET_MP_STATE, &state->mp_state);
775+
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_MP_STATE, r: %i",
776+
r);
777+
778+
r = ioctl(vcpu->fd, KVM_GET_REGS, &state->regs);
779+
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_REGS, r: %i",
780+
r);
781+
782+
r = ioctl(vcpu->fd, KVM_GET_XSAVE, &state->xsave);
783+
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_XSAVE, r: %i",
784+
r);
785+
786+
r = ioctl(vcpu->fd, KVM_GET_XCRS, &state->xcrs);
787+
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_XCRS, r: %i",
788+
r);
789+
790+
r = ioctl(vcpu->fd, KVM_GET_SREGS, &state->sregs);
791+
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_SREGS, r: %i",
792+
r);
793+
794+
state->msrs.nmsrs = nmsrs;
795+
for (i = 0; i < nmsrs; i++)
796+
state->msrs.entries[i].index = list->indices[i];
797+
r = ioctl(vcpu->fd, KVM_GET_MSRS, &state->msrs);
798+
TEST_ASSERT(r == nmsrs, "Unexpected result from KVM_GET_MSRS, r: %i (failed at %x)",
799+
r, r == nmsrs ? -1 : list->indices[r]);
800+
801+
r = ioctl(vcpu->fd, KVM_GET_DEBUGREGS, &state->debugregs);
802+
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_DEBUGREGS, r: %i",
803+
r);
804+
805+
free(list);
806+
return state;
807+
}
808+
809+
void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_x86_state *state)
810+
{
811+
struct vcpu *vcpu = vcpu_find(vm, vcpuid);
812+
int r;
813+
814+
r = ioctl(vcpu->fd, KVM_SET_XSAVE, &state->xsave);
815+
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_XSAVE, r: %i",
816+
r);
817+
818+
r = ioctl(vcpu->fd, KVM_SET_XCRS, &state->xcrs);
819+
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_XCRS, r: %i",
820+
r);
821+
822+
r = ioctl(vcpu->fd, KVM_SET_SREGS, &state->sregs);
823+
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_SREGS, r: %i",
824+
r);
825+
826+
r = ioctl(vcpu->fd, KVM_SET_MSRS, &state->msrs);
827+
TEST_ASSERT(r == state->msrs.nmsrs, "Unexpected result from KVM_SET_MSRS, r: %i (failed at %x)",
828+
r, r == state->msrs.nmsrs ? -1 : state->msrs.entries[r].index);
829+
830+
r = ioctl(vcpu->fd, KVM_SET_VCPU_EVENTS, &state->events);
831+
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_VCPU_EVENTS, r: %i",
832+
r);
833+
834+
r = ioctl(vcpu->fd, KVM_SET_MP_STATE, &state->mp_state);
835+
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_MP_STATE, r: %i",
836+
r);
837+
838+
r = ioctl(vcpu->fd, KVM_SET_DEBUGREGS, &state->debugregs);
839+
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_DEBUGREGS, r: %i",
840+
r);
841+
842+
r = ioctl(vcpu->fd, KVM_SET_REGS, &state->regs);
843+
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_REGS, r: %i",
844+
r);
845+
}

0 commit comments

Comments
 (0)