xref: /kvmtool/kvm-cpu.c (revision 9b7359101f9f6f15eee493865678051c507e900c)
15c3d55faSPekka Enberg #include "kvm/kvm-cpu.h"
25c3d55faSPekka Enberg 
3b0b42ba0SPekka Enberg #include "kvm/symbol.h"
45c3d55faSPekka Enberg #include "kvm/util.h"
55c3d55faSPekka Enberg #include "kvm/kvm.h"
65c3d55faSPekka Enberg 
75c3d55faSPekka Enberg #include <sys/ioctl.h>
85c3d55faSPekka Enberg #include <sys/mman.h>
95ee154d1SPekka Enberg #include <signal.h>
105c3d55faSPekka Enberg #include <stdlib.h>
11b0b42ba0SPekka Enberg #include <string.h>
125c3d55faSPekka Enberg #include <errno.h>
135c3d55faSPekka Enberg #include <stdio.h>
145c3d55faSPekka Enberg 
15656be1b8SSasha Levin extern __thread struct kvm_cpu *current_kvm_cpu;
16656be1b8SSasha Levin 
1743835ac9SSasha Levin void kvm_cpu__enable_singlestep(struct kvm_cpu *vcpu)
185c3d55faSPekka Enberg {
195c3d55faSPekka Enberg 	struct kvm_guest_debug debug = {
205c3d55faSPekka Enberg 		.control	= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP,
215c3d55faSPekka Enberg 	};
225c3d55faSPekka Enberg 
2343835ac9SSasha Levin 	if (ioctl(vcpu->vcpu_fd, KVM_SET_GUEST_DEBUG, &debug) < 0)
244542f276SCyrill Gorcunov 		pr_warning("KVM_SET_GUEST_DEBUG failed");
255c3d55faSPekka Enberg }
265c3d55faSPekka Enberg 
2743835ac9SSasha Levin void kvm_cpu__run(struct kvm_cpu *vcpu)
285c3d55faSPekka Enberg {
295c3d55faSPekka Enberg 	int err;
305c3d55faSPekka Enberg 
31f9fdf5cdSAsias He 	if (!vcpu->is_running)
32f9fdf5cdSAsias He 		return;
33f9fdf5cdSAsias He 
3443835ac9SSasha Levin 	err = ioctl(vcpu->vcpu_fd, KVM_RUN, 0);
3576b75d32SMatt Evans 	if (err < 0 && (errno != EINTR && errno != EAGAIN))
365c3d55faSPekka Enberg 		die_perror("KVM_RUN failed");
375c3d55faSPekka Enberg }
3865bab644SPekka Enberg 
394298ddadSSasha Levin static void kvm_cpu_signal_handler(int signum)
4049e5227dSSasha Levin {
414298ddadSSasha Levin 	if (signum == SIGKVMEXIT) {
42714e5b7fSSasha Levin 		if (current_kvm_cpu && current_kvm_cpu->is_running) {
43656be1b8SSasha Levin 			current_kvm_cpu->is_running = false;
444346fd8fSSasha Levin 			kvm__continue(current_kvm_cpu->kvm);
45656be1b8SSasha Levin 		}
464298ddadSSasha Levin 	} else if (signum == SIGKVMPAUSE) {
474298ddadSSasha Levin 		current_kvm_cpu->paused = 1;
484298ddadSSasha Levin 	}
4949e5227dSSasha Levin }
5049e5227dSSasha Levin 
5173f7e5b3SSasha Levin static void kvm_cpu__handle_coalesced_mmio(struct kvm_cpu *cpu)
5273f7e5b3SSasha Levin {
5373f7e5b3SSasha Levin 	if (cpu->ring) {
5473f7e5b3SSasha Levin 		while (cpu->ring->first != cpu->ring->last) {
5573f7e5b3SSasha Levin 			struct kvm_coalesced_mmio *m;
5673f7e5b3SSasha Levin 			m = &cpu->ring->coalesced_mmio[cpu->ring->first];
57*9b735910SMarc Zyngier 			kvm_cpu__emulate_mmio(cpu,
5873f7e5b3SSasha Levin 					      m->phys_addr,
5973f7e5b3SSasha Levin 					      m->data,
6073f7e5b3SSasha Levin 					      m->len,
6173f7e5b3SSasha Levin 					      1);
6273f7e5b3SSasha Levin 			cpu->ring->first = (cpu->ring->first + 1) % KVM_COALESCED_MMIO_MAX;
6373f7e5b3SSasha Levin 		}
6473f7e5b3SSasha Levin 	}
6573f7e5b3SSasha Levin }
6673f7e5b3SSasha Levin 
67df4239fbSSasha Levin void kvm_cpu__reboot(struct kvm *kvm)
68714e5b7fSSasha Levin {
69c23d9748SSasha Levin 	int i;
70c23d9748SSasha Levin 
71df4239fbSSasha Levin 	/* The kvm->cpus array contains a null pointer in the last location */
72e2077857SMatt Evans 	for (i = 0; ; i++) {
73df4239fbSSasha Levin 		if (kvm->cpus[i])
74df4239fbSSasha Levin 			pthread_kill(kvm->cpus[i]->thread, SIGKVMEXIT);
75e2077857SMatt Evans 		else
76e2077857SMatt Evans 			break;
77e2077857SMatt Evans 	}
78714e5b7fSSasha Levin }
79714e5b7fSSasha Levin 
8065bab644SPekka Enberg int kvm_cpu__start(struct kvm_cpu *cpu)
8165bab644SPekka Enberg {
825ee154d1SPekka Enberg 	sigset_t sigset;
835ee154d1SPekka Enberg 
845ee154d1SPekka Enberg 	sigemptyset(&sigset);
855ee154d1SPekka Enberg 	sigaddset(&sigset, SIGALRM);
865ee154d1SPekka Enberg 
875ee154d1SPekka Enberg 	pthread_sigmask(SIG_BLOCK, &sigset, NULL);
885ee154d1SPekka Enberg 
894298ddadSSasha Levin 	signal(SIGKVMEXIT, kvm_cpu_signal_handler);
904298ddadSSasha Levin 	signal(SIGKVMPAUSE, kvm_cpu_signal_handler);
9149e5227dSSasha Levin 
925d1a249cSPekka Enberg 	kvm_cpu__reset_vcpu(cpu);
935d1a249cSPekka Enberg 
94809f088bSSasha Levin 	if (cpu->kvm->cfg.single_step)
956d6a4d41SPekka Enberg 		kvm_cpu__enable_singlestep(cpu);
966d6a4d41SPekka Enberg 
97c23d9748SSasha Levin 	while (cpu->is_running) {
984298ddadSSasha Levin 		if (cpu->paused) {
994298ddadSSasha Levin 			kvm__notify_paused();
1004298ddadSSasha Levin 			cpu->paused = 0;
1014298ddadSSasha Levin 		}
1024298ddadSSasha Levin 
1034b1c6f6eSSasha Levin 		if (cpu->needs_nmi) {
1044b1c6f6eSSasha Levin 			kvm_cpu__arch_nmi(cpu);
1054b1c6f6eSSasha Levin 			cpu->needs_nmi = 0;
1064b1c6f6eSSasha Levin 		}
1074b1c6f6eSSasha Levin 
10865bab644SPekka Enberg 		kvm_cpu__run(cpu);
10965bab644SPekka Enberg 
11065bab644SPekka Enberg 		switch (cpu->kvm_run->exit_reason) {
1111621292eSSasha Levin 		case KVM_EXIT_UNKNOWN:
1121621292eSSasha Levin 			break;
11365bab644SPekka Enberg 		case KVM_EXIT_DEBUG:
11465bab644SPekka Enberg 			kvm_cpu__show_registers(cpu);
11565bab644SPekka Enberg 			kvm_cpu__show_code(cpu);
11665bab644SPekka Enberg 			break;
11765bab644SPekka Enberg 		case KVM_EXIT_IO: {
11865bab644SPekka Enberg 			bool ret;
11965bab644SPekka Enberg 
12017edd536SMatt Evans 			ret = kvm_cpu__emulate_io(cpu->kvm,
12165bab644SPekka Enberg 						  cpu->kvm_run->io.port,
1223fdf659dSSasha Levin 						  (u8 *)cpu->kvm_run +
12365bab644SPekka Enberg 						  cpu->kvm_run->io.data_offset,
12465bab644SPekka Enberg 						  cpu->kvm_run->io.direction,
12565bab644SPekka Enberg 						  cpu->kvm_run->io.size,
12665bab644SPekka Enberg 						  cpu->kvm_run->io.count);
12765bab644SPekka Enberg 
12865bab644SPekka Enberg 			if (!ret)
12965bab644SPekka Enberg 				goto panic_kvm;
13065bab644SPekka Enberg 			break;
13165bab644SPekka Enberg 		}
13265bab644SPekka Enberg 		case KVM_EXIT_MMIO: {
13365bab644SPekka Enberg 			bool ret;
13465bab644SPekka Enberg 
135a7518f05SSasha Levin 			/*
136a7518f05SSasha Levin 			 * If we had MMIO exit, coalesced ring should be processed
137a7518f05SSasha Levin 			 * *before* processing the exit itself
138a7518f05SSasha Levin 			 */
139a7518f05SSasha Levin 			kvm_cpu__handle_coalesced_mmio(cpu);
140a7518f05SSasha Levin 
141*9b735910SMarc Zyngier 			ret = kvm_cpu__emulate_mmio(cpu,
14265bab644SPekka Enberg 						    cpu->kvm_run->mmio.phys_addr,
14365bab644SPekka Enberg 						    cpu->kvm_run->mmio.data,
14465bab644SPekka Enberg 						    cpu->kvm_run->mmio.len,
14565bab644SPekka Enberg 						    cpu->kvm_run->mmio.is_write);
14665bab644SPekka Enberg 
14765bab644SPekka Enberg 			if (!ret)
14865bab644SPekka Enberg 				goto panic_kvm;
14965bab644SPekka Enberg 			break;
15065bab644SPekka Enberg 		}
1515ee154d1SPekka Enberg 		case KVM_EXIT_INTR:
152656be1b8SSasha Levin 			if (cpu->is_running)
153656be1b8SSasha Levin 				break;
15449e5227dSSasha Levin 			goto exit_kvm;
15565bab644SPekka Enberg 		case KVM_EXIT_SHUTDOWN:
15665bab644SPekka Enberg 			goto exit_kvm;
157341ee0d4SMatt Evans 		default: {
158341ee0d4SMatt Evans 			bool ret;
159341ee0d4SMatt Evans 
160341ee0d4SMatt Evans 			ret = kvm_cpu__handle_exit(cpu);
161341ee0d4SMatt Evans 			if (!ret)
16265bab644SPekka Enberg 				goto panic_kvm;
163341ee0d4SMatt Evans 			break;
164341ee0d4SMatt Evans 		}
16565bab644SPekka Enberg 		}
16673f7e5b3SSasha Levin 		kvm_cpu__handle_coalesced_mmio(cpu);
16765bab644SPekka Enberg 	}
16865bab644SPekka Enberg 
16965bab644SPekka Enberg exit_kvm:
17065bab644SPekka Enberg 	return 0;
17165bab644SPekka Enberg 
17265bab644SPekka Enberg panic_kvm:
17365bab644SPekka Enberg 	return 1;
17465bab644SPekka Enberg }
175df4239fbSSasha Levin 
176df4239fbSSasha Levin int kvm_cpu__init(struct kvm *kvm)
177df4239fbSSasha Levin {
178df4239fbSSasha Levin 	int max_cpus, recommended_cpus, i;
179df4239fbSSasha Levin 
180df4239fbSSasha Levin 	max_cpus = kvm__max_cpus(kvm);
181df4239fbSSasha Levin 	recommended_cpus = kvm__recommended_cpus(kvm);
182df4239fbSSasha Levin 
183df4239fbSSasha Levin 	if (kvm->cfg.nrcpus > max_cpus) {
184df4239fbSSasha Levin 		printf("  # Limit the number of CPUs to %d\n", max_cpus);
185df4239fbSSasha Levin 		kvm->cfg.nrcpus = max_cpus;
186df4239fbSSasha Levin 	} else if (kvm->cfg.nrcpus > recommended_cpus) {
187df4239fbSSasha Levin 		printf("  # Warning: The maximum recommended amount of VCPUs"
188df4239fbSSasha Levin 			" is %d\n", recommended_cpus);
189df4239fbSSasha Levin 	}
190df4239fbSSasha Levin 
191df4239fbSSasha Levin 	kvm->nrcpus = kvm->cfg.nrcpus;
192df4239fbSSasha Levin 
193df4239fbSSasha Levin 	/* Alloc one pointer too many, so array ends up 0-terminated */
194df4239fbSSasha Levin 	kvm->cpus = calloc(kvm->nrcpus + 1, sizeof(void *));
195df4239fbSSasha Levin 	if (!kvm->cpus) {
196df4239fbSSasha Levin 		pr_warning("Couldn't allocate array for %d CPUs", kvm->nrcpus);
197df4239fbSSasha Levin 		return -ENOMEM;
198df4239fbSSasha Levin 	}
199df4239fbSSasha Levin 
200df4239fbSSasha Levin 	for (i = 0; i < kvm->nrcpus; i++) {
201df4239fbSSasha Levin 		kvm->cpus[i] = kvm_cpu__arch_init(kvm, i);
202df4239fbSSasha Levin 		if (!kvm->cpus[i]) {
203df4239fbSSasha Levin 			pr_warning("unable to initialize KVM VCPU");
204df4239fbSSasha Levin 			goto fail_alloc;
205df4239fbSSasha Levin 		}
206df4239fbSSasha Levin 	}
207df4239fbSSasha Levin 
208df4239fbSSasha Levin 	return 0;
209df4239fbSSasha Levin 
210df4239fbSSasha Levin fail_alloc:
211df4239fbSSasha Levin 	for (i = 0; i < kvm->nrcpus; i++)
212df4239fbSSasha Levin 		free(kvm->cpus[i]);
213df4239fbSSasha Levin 	return -ENOMEM;
214df4239fbSSasha Levin }
21549a8afd1SSasha Levin base_init(kvm_cpu__init);
216df4239fbSSasha Levin 
217df4239fbSSasha Levin int kvm_cpu__exit(struct kvm *kvm)
218df4239fbSSasha Levin {
219df4239fbSSasha Levin 	int i, r;
220df4239fbSSasha Levin 	void *ret = NULL;
221df4239fbSSasha Levin 
222df4239fbSSasha Levin 	kvm_cpu__delete(kvm->cpus[0]);
223df4239fbSSasha Levin 	kvm->cpus[0] = NULL;
224df4239fbSSasha Levin 
225df4239fbSSasha Levin 	for (i = 1; i < kvm->nrcpus; i++) {
226df4239fbSSasha Levin 		if (kvm->cpus[i]->is_running) {
227df4239fbSSasha Levin 			pthread_kill(kvm->cpus[i]->thread, SIGKVMEXIT);
228df4239fbSSasha Levin 			if (pthread_join(kvm->cpus[i]->thread, &ret) != 0)
229df4239fbSSasha Levin 				die("pthread_join");
230df4239fbSSasha Levin 			kvm_cpu__delete(kvm->cpus[i]);
231df4239fbSSasha Levin 		}
232df4239fbSSasha Levin 		if (ret == NULL)
233df4239fbSSasha Levin 			r = 0;
234df4239fbSSasha Levin 	}
235df4239fbSSasha Levin 
236df4239fbSSasha Levin 	free(kvm->cpus);
237df4239fbSSasha Levin 
238df4239fbSSasha Levin 	kvm->nrcpus = 0;
239df4239fbSSasha Levin 
240df4239fbSSasha Levin 	return r;
241df4239fbSSasha Levin }
24249a8afd1SSasha Levin late_exit(kvm_cpu__exit);
243