kvm: kvmctl: use a table to dispatch IO requests in kvmctl (v2)
[qemu-kvm/fedora.git] / kvm / user / main.c
blobb34b37c09c19b86c501da4725ddf2c00ed939897
1 /*
2 * Kernel-based Virtual Machine test driver
4 * This test driver provides a simple way of testing kvm, without a full
5 * device model.
7 * Copyright (C) 2006 Qumranet
9 * Authors:
11 * Avi Kivity <avi@qumranet.com>
12 * Yaniv Kamay <yaniv@qumranet.com>
14 * This work is licensed under the GNU LGPL license, version 2.
17 #define _GNU_SOURCE
19 #include "kvmctl.h"
20 #include "test/apic.h"
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <semaphore.h>
28 #include <sys/types.h>
29 #include <errno.h>
30 #include <pthread.h>
31 #include <signal.h>
32 #include <pthread.h>
33 #include <sys/syscall.h>
34 #include <linux/unistd.h>
35 #include <getopt.h>
36 #include <stdbool.h>
38 static int gettid(void)
40 return syscall(__NR_gettid);
43 static int tkill(int pid, int sig)
45 return syscall(__NR_tkill, pid, sig);
48 kvm_context_t kvm;
50 #define MAX_VCPUS 4
52 #define IPI_SIGNAL (SIGRTMIN + 4)
54 #define MAX_IO_TABLE 50
56 typedef int (io_table_handler_t)(void *, int, int, uint64_t, uint64_t *);
58 struct io_table_entry
60 uint64_t start;
61 uint64_t end;
62 io_table_handler_t *handler;
63 void *opaque;
66 struct io_table
68 int nr_entries;
69 struct io_table_entry entries[MAX_IO_TABLE];
72 static int ncpus = 1;
73 static sem_t init_sem;
74 static __thread int vcpu;
75 static int apic_ipi_vector = 0xff;
76 static sigset_t kernel_sigmask;
77 static sigset_t ipi_sigmask;
78 static uint64_t memory_size = 128 * 1024 * 1024;
80 static struct io_table pio_table;
82 struct vcpu_info {
83 pid_t tid;
84 sem_t sipi_sem;
87 struct vcpu_info *vcpus;
89 static uint32_t apic_sipi_addr;
91 struct io_table_entry *io_table_lookup(struct io_table *io_table, uint64_t addr)
93 int i;
95 for (i = 0; i < io_table->nr_entries; i++) {
96 if (io_table->entries[i].start <= addr &&
97 addr < io_table->entries[i].end)
98 return &io_table->entries[i];
101 return NULL;
104 int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size,
105 io_table_handler_t *handler, void *opaque)
107 struct io_table_entry *entry;
109 if (io_table->nr_entries == MAX_IO_TABLE)
110 return -ENOSPC;
112 entry = &io_table->entries[io_table->nr_entries];
113 io_table->nr_entries++;
115 entry->start = start;
116 entry->end = start + size;
117 entry->handler = handler;
118 entry->opaque = opaque;
120 return 0;
123 static void apic_send_sipi(int vcpu)
125 sem_post(&vcpus[vcpu].sipi_sem);
128 static void apic_send_ipi(int vcpu)
130 struct vcpu_info *v;
132 if (vcpu < 0 || vcpu >= ncpus)
133 return;
134 v = &vcpus[vcpu];
135 tkill(v->tid, IPI_SIGNAL);
138 static int apic_io(void *opaque, int size, int is_write,
139 uint64_t addr, uint64_t *value)
141 if (!is_write)
142 *value = -1u;
144 switch (addr - APIC_BASE) {
145 case APIC_REG_NCPU:
146 if (!is_write)
147 *value = ncpus;
148 break;
149 case APIC_REG_ID:
150 if (!is_write)
151 *value = vcpu;
152 break;
153 case APIC_REG_SIPI_ADDR:
154 if (!is_write)
155 *value = apic_sipi_addr;
156 else
157 apic_sipi_addr = *value;
158 break;
159 case APIC_REG_SEND_SIPI:
160 if (is_write)
161 apic_send_sipi(*value);
162 break;
163 case APIC_REG_IPI_VECTOR:
164 if (!is_write)
165 *value = apic_ipi_vector;
166 else
167 apic_ipi_vector = *value;
168 break;
169 case APIC_REG_SEND_IPI:
170 if (is_write)
171 apic_send_ipi(*value);
172 break;
175 return 0;
178 static int apic_init(void)
180 return io_table_register(&pio_table, APIC_BASE,
181 APIC_SIZE, apic_io, NULL);
184 static int misc_io(void *opaque, int size, int is_write,
185 uint64_t addr, uint64_t *value)
187 static int newline = 1;
189 if (!is_write)
190 *value = -1;
192 switch (addr) {
193 case 0xff: // irq injector
194 if (is_write) {
195 printf("injecting interrupt 0x%x\n", (uint8_t)*value);
196 kvm_inject_irq(kvm, 0, *value);
198 break;
199 case 0xf1: // serial
200 if (is_write) {
201 if (newline)
202 fputs("GUEST: ", stdout);
203 putchar(*value);
204 newline = *value == '\n';
206 break;
207 case 0xd1:
208 if (!is_write)
209 *value = memory_size;
210 break;
213 return 0;
216 static int misc_init(void)
218 int err;
220 err = io_table_register(&pio_table, 0xff, 1, misc_io, NULL);
221 if (err < 0)
222 return err;
224 err = io_table_register(&pio_table, 0xf1, 1, misc_io, NULL);
225 if (err < 0)
226 return err;
228 return io_table_register(&pio_table, 0xd1, 1, misc_io, NULL);
231 static int test_inb(void *opaque, uint16_t addr, uint8_t *value)
233 struct io_table_entry *entry;
235 entry = io_table_lookup(&pio_table, addr);
236 if (entry) {
237 uint64_t val;
238 entry->handler(entry->opaque, 1, 0, addr, &val);
239 *value = val;
240 } else {
241 *value = -1;
242 printf("inb 0x%x\n", addr);
245 return 0;
248 static int test_inw(void *opaque, uint16_t addr, uint16_t *value)
250 struct io_table_entry *entry;
252 entry = io_table_lookup(&pio_table, addr);
253 if (entry) {
254 uint64_t val;
255 entry->handler(entry->opaque, 2, 0, addr, &val);
256 *value = val;
257 } else {
258 *value = -1;
259 printf("inw 0x%x\n", addr);
262 return 0;
265 static int test_inl(void *opaque, uint16_t addr, uint32_t *value)
267 struct io_table_entry *entry;
269 entry = io_table_lookup(&pio_table, addr);
270 if (entry) {
271 uint64_t val;
272 entry->handler(entry->opaque, 4, 0, addr, &val);
273 *value = val;
274 } else {
275 *value = -1;
276 printf("inl 0x%x\n", addr);
279 return 0;
282 static int test_outb(void *opaque, uint16_t addr, uint8_t value)
284 struct io_table_entry *entry;
286 entry = io_table_lookup(&pio_table, addr);
287 if (entry) {
288 uint64_t val = value;
289 entry->handler(entry->opaque, 1, 1, addr, &val);
290 } else
291 printf("outb $0x%x, 0x%x\n", value, addr);
293 return 0;
296 static int test_outw(void *opaque, uint16_t addr, uint16_t value)
298 struct io_table_entry *entry;
300 entry = io_table_lookup(&pio_table, addr);
301 if (entry) {
302 uint64_t val = value;
303 entry->handler(entry->opaque, 2, 1, addr, &val);
304 } else
305 printf("outw $0x%x, 0x%x\n", value, addr);
307 return 0;
310 static int test_outl(void *opaque, uint16_t addr, uint32_t value)
312 struct io_table_entry *entry;
314 entry = io_table_lookup(&pio_table, addr);
315 if (entry) {
316 uint64_t val = value;
317 entry->handler(entry->opaque, 4, 1, addr, &val);
318 } else
319 printf("outl $0x%x, 0x%x\n", value, addr);
321 return 0;
324 static int test_debug(void *opaque, int vcpu)
326 printf("test_debug\n");
327 return 0;
330 static int test_halt(void *opaque, int vcpu)
332 int n;
334 sigwait(&ipi_sigmask, &n);
335 kvm_inject_irq(kvm, vcpu, apic_ipi_vector);
336 return 0;
339 static int test_io_window(void *opaque)
341 return 0;
344 static int test_try_push_interrupts(void *opaque)
346 return 0;
349 static void test_post_kvm_run(void *opaque, int vcpu)
353 static int test_pre_kvm_run(void *opaque, int vcpu)
355 return 0;
358 static struct kvm_callbacks test_callbacks = {
359 .inb = test_inb,
360 .inw = test_inw,
361 .inl = test_inl,
362 .outb = test_outb,
363 .outw = test_outw,
364 .outl = test_outl,
365 .debug = test_debug,
366 .halt = test_halt,
367 .io_window = test_io_window,
368 .try_push_interrupts = test_try_push_interrupts,
369 .post_kvm_run = test_post_kvm_run,
370 .pre_kvm_run = test_pre_kvm_run,
374 static void load_file(void *mem, const char *fname)
376 int r;
377 int fd;
379 fd = open(fname, O_RDONLY);
380 if (fd == -1) {
381 perror("open");
382 exit(1);
384 while ((r = read(fd, mem, 4096)) != -1 && r != 0)
385 mem += r;
386 if (r == -1) {
387 perror("read");
388 exit(1);
392 static void enter_32(kvm_context_t kvm)
394 struct kvm_regs regs = {
395 .rsp = 0x80000, /* 512KB */
396 .rip = 0x100000, /* 1MB */
397 .rflags = 2,
399 struct kvm_sregs sregs = {
400 .cs = { 0, -1u, 8, 11, 1, 0, 1, 1, 0, 1, 0, 0 },
401 .ds = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
402 .es = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
403 .fs = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
404 .gs = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
405 .ss = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
407 .tr = { 0, 10000, 24, 11, 1, 0, 0, 0, 0, 0, 0, 0 },
408 .ldt = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
409 .gdt = { 0, 0 },
410 .idt = { 0, 0 },
411 .cr0 = 0x37,
412 .cr3 = 0,
413 .cr4 = 0,
414 .efer = 0,
415 .apic_base = 0,
416 .interrupt_bitmap = { 0 },
419 kvm_set_regs(kvm, 0, &regs);
420 kvm_set_sregs(kvm, 0, &sregs);
423 static void init_vcpu(int n)
425 sigemptyset(&ipi_sigmask);
426 sigaddset(&ipi_sigmask, IPI_SIGNAL);
427 sigprocmask(SIG_UNBLOCK, &ipi_sigmask, NULL);
428 sigprocmask(SIG_BLOCK, &ipi_sigmask, &kernel_sigmask);
429 vcpus[n].tid = gettid();
430 vcpu = n;
431 kvm_set_signal_mask(kvm, n, &kernel_sigmask);
432 sem_post(&init_sem);
435 static void *do_create_vcpu(void *_n)
437 int n = (long)_n;
438 struct kvm_regs regs;
440 kvm_create_vcpu(kvm, n);
441 init_vcpu(n);
442 sem_wait(&vcpus[n].sipi_sem);
443 kvm_get_regs(kvm, n, &regs);
444 regs.rip = apic_sipi_addr;
445 kvm_set_regs(kvm, n, &regs);
446 kvm_run(kvm, n);
447 return NULL;
450 static void start_vcpu(int n)
452 pthread_t thread;
454 sem_init(&vcpus[n].sipi_sem, 0, 0);
455 pthread_create(&thread, NULL, do_create_vcpu, (void *)(long)n);
458 static void usage(const char *progname)
460 fprintf(stderr,
461 "Usage: %s [OPTIONS] [bootstrap] flatfile\n"
462 "KVM test harness.\n"
463 "\n"
464 " -s, --smp=NUM create a VM with NUM virtual CPUs\n"
465 " -p, --protected-mode start VM in protected mode\n"
466 " -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n"
467 " can be used to change the unit (default: `M')\n"
468 " -h, --help display this help screen and exit\n"
469 "\n"
470 "Report bugs to <kvm-devel@lists.sourceforge.net>.\n"
471 , progname);
474 static void sig_ignore(int sig)
476 write(1, "boo\n", 4);
479 int main(int argc, char **argv)
481 void *vm_mem;
482 int i;
483 const char *sopts = "s:phm:";
484 struct option lopts[] = {
485 { "smp", 1, 0, 's' },
486 { "protected-mode", 0, 0, 'p' },
487 { "memory", 1, 0, 'm' },
488 { "help", 0, 0, 'h' },
489 { 0 },
491 int opt_ind, ch;
492 bool enter_protected_mode = false;
493 int nb_args;
494 char *endptr;
496 while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) {
497 switch (ch) {
498 case 's':
499 ncpus = atoi(optarg);
500 break;
501 case 'p':
502 enter_protected_mode = true;
503 break;
504 case 'm':
505 memory_size = strtoull(optarg, &endptr, 0);
506 switch (*endptr) {
507 case 'G': case 'g':
508 memory_size <<= 30;
509 break;
510 case '\0':
511 case 'M': case 'm':
512 memory_size <<= 20;
513 break;
514 case 'K': case 'k':
515 memory_size <<= 10;
516 break;
517 default:
518 fprintf(stderr,
519 "Unrecongized memory suffix: %c\n",
520 *endptr);
521 exit(1);
523 if (memory_size == 0) {
524 fprintf(stderr,
525 "Invalid memory size: 0\n");
526 exit(1);
528 break;
529 case 'h':
530 usage(argv[0]);
531 exit(0);
532 case '?':
533 default:
534 fprintf(stderr,
535 "Try `%s --help' for more information.\n",
536 argv[0]);
537 exit(1);
541 nb_args = argc - optind;
542 if (nb_args < 1 || nb_args > 2) {
543 fprintf(stderr,
544 "Incorrect number of arguments.\n"
545 "Try `%s --help' for more information.\n",
546 argv[0]);
547 exit(1);
550 signal(IPI_SIGNAL, sig_ignore);
552 vcpus = calloc(ncpus, sizeof *vcpus);
553 if (!vcpus) {
554 fprintf(stderr, "calloc failed\n");
555 return 1;
558 kvm = kvm_init(&test_callbacks, 0);
559 if (!kvm) {
560 fprintf(stderr, "kvm_init failed\n");
561 return 1;
563 if (kvm_create(kvm, memory_size, &vm_mem) < 0) {
564 kvm_finalize(kvm);
565 fprintf(stderr, "kvm_create failed\n");
566 return 1;
569 if (enter_protected_mode)
570 enter_32(kvm);
571 else
572 load_file(vm_mem + 0xf0000, argv[optind]);
574 if (nb_args > 1)
575 load_file(vm_mem + 0x100000, argv[optind + 1]);
577 apic_init();
578 misc_init();
580 sem_init(&init_sem, 0, 0);
581 init_vcpu(0);
582 for (i = 1; i < ncpus; ++i)
583 start_vcpu(i);
584 for (i = 0; i < ncpus; ++i)
585 sem_wait(&init_sem);
587 kvm_run(kvm, 0);
589 return 0;