2 * Kernel-based Virtual Machine test driver
4 * This test driver provides a simple way of testing kvm, without a full
7 * Copyright (C) 2006 Qumranet
11 * Avi Kivity <avi@qumranet.com>
12 * Yaniv Kamay <yaniv@qumranet.com>
14 * This work is licensed under the GNU LGPL license, version 2.
20 #include "test/apic.h"
27 #include <semaphore.h>
28 #include <sys/types.h>
33 #include <sys/syscall.h>
34 #include <linux/unistd.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
);
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 *);
62 io_table_handler_t
*handler
;
69 struct io_table_entry entries
[MAX_IO_TABLE
];
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
;
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
)
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
];
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
)
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
;
123 static void apic_send_sipi(int vcpu
)
125 sem_post(&vcpus
[vcpu
].sipi_sem
);
128 static void apic_send_ipi(int vcpu
)
132 if (vcpu
< 0 || vcpu
>= ncpus
)
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
)
144 switch (addr
- APIC_BASE
) {
153 case APIC_REG_SIPI_ADDR
:
155 *value
= apic_sipi_addr
;
157 apic_sipi_addr
= *value
;
159 case APIC_REG_SEND_SIPI
:
161 apic_send_sipi(*value
);
163 case APIC_REG_IPI_VECTOR
:
165 *value
= apic_ipi_vector
;
167 apic_ipi_vector
= *value
;
169 case APIC_REG_SEND_IPI
:
171 apic_send_ipi(*value
);
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;
193 case 0xff: // irq injector
195 printf("injecting interrupt 0x%x\n", (uint8_t)*value
);
196 kvm_inject_irq(kvm
, 0, *value
);
202 fputs("GUEST: ", stdout
);
204 newline
= *value
== '\n';
209 *value
= memory_size
;
216 static int misc_init(void)
220 err
= io_table_register(&pio_table
, 0xff, 1, misc_io
, NULL
);
224 err
= io_table_register(&pio_table
, 0xf1, 1, misc_io
, NULL
);
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
);
238 entry
->handler(entry
->opaque
, 1, 0, addr
, &val
);
242 printf("inb 0x%x\n", addr
);
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
);
255 entry
->handler(entry
->opaque
, 2, 0, addr
, &val
);
259 printf("inw 0x%x\n", addr
);
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
);
272 entry
->handler(entry
->opaque
, 4, 0, addr
, &val
);
276 printf("inl 0x%x\n", addr
);
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
);
288 uint64_t val
= value
;
289 entry
->handler(entry
->opaque
, 1, 1, addr
, &val
);
291 printf("outb $0x%x, 0x%x\n", value
, addr
);
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
);
302 uint64_t val
= value
;
303 entry
->handler(entry
->opaque
, 2, 1, addr
, &val
);
305 printf("outw $0x%x, 0x%x\n", value
, addr
);
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
);
316 uint64_t val
= value
;
317 entry
->handler(entry
->opaque
, 4, 1, addr
, &val
);
319 printf("outl $0x%x, 0x%x\n", value
, addr
);
324 static int test_debug(void *opaque
, int vcpu
)
326 printf("test_debug\n");
330 static int test_halt(void *opaque
, int vcpu
)
334 sigwait(&ipi_sigmask
, &n
);
335 kvm_inject_irq(kvm
, vcpu
, apic_ipi_vector
);
339 static int test_io_window(void *opaque
)
344 static int test_try_push_interrupts(void *opaque
)
349 static void test_post_kvm_run(void *opaque
, int vcpu
)
353 static int test_pre_kvm_run(void *opaque
, int vcpu
)
358 static struct kvm_callbacks test_callbacks
= {
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
)
379 fd
= open(fname
, O_RDONLY
);
384 while ((r
= read(fd
, mem
, 4096)) != -1 && r
!= 0)
392 static void enter_32(kvm_context_t kvm
)
394 struct kvm_regs regs
= {
395 .rsp
= 0x80000, /* 512KB */
396 .rip
= 0x100000, /* 1MB */
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 },
416 .interrupt_bitmap
= { 0 },
419 kvm_set_regs(kvm
, 0, ®s
);
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();
431 kvm_set_signal_mask(kvm
, n
, &kernel_sigmask
);
435 static void *do_create_vcpu(void *_n
)
438 struct kvm_regs regs
;
440 kvm_create_vcpu(kvm
, n
);
442 sem_wait(&vcpus
[n
].sipi_sem
);
443 kvm_get_regs(kvm
, n
, ®s
);
444 regs
.rip
= apic_sipi_addr
;
445 kvm_set_regs(kvm
, n
, ®s
);
450 static void start_vcpu(int n
)
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
)
461 "Usage: %s [OPTIONS] [bootstrap] flatfile\n"
462 "KVM test harness.\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"
470 "Report bugs to <kvm-devel@lists.sourceforge.net>.\n"
474 static void sig_ignore(int sig
)
476 write(1, "boo\n", 4);
479 int main(int argc
, char **argv
)
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' },
492 bool enter_protected_mode
= false;
496 while ((ch
= getopt_long(argc
, argv
, sopts
, lopts
, &opt_ind
)) != -1) {
499 ncpus
= atoi(optarg
);
502 enter_protected_mode
= true;
505 memory_size
= strtoull(optarg
, &endptr
, 0);
519 "Unrecongized memory suffix: %c\n",
523 if (memory_size
== 0) {
525 "Invalid memory size: 0\n");
535 "Try `%s --help' for more information.\n",
541 nb_args
= argc
- optind
;
542 if (nb_args
< 1 || nb_args
> 2) {
544 "Incorrect number of arguments.\n"
545 "Try `%s --help' for more information.\n",
550 signal(IPI_SIGNAL
, sig_ignore
);
552 vcpus
= calloc(ncpus
, sizeof *vcpus
);
554 fprintf(stderr
, "calloc failed\n");
558 kvm
= kvm_init(&test_callbacks
, 0);
560 fprintf(stderr
, "kvm_init failed\n");
563 if (kvm_create(kvm
, memory_size
, &vm_mem
) < 0) {
565 fprintf(stderr
, "kvm_create failed\n");
569 if (enter_protected_mode
)
572 load_file(vm_mem
+ 0xf0000, argv
[optind
]);
575 load_file(vm_mem
+ 0x100000, argv
[optind
+ 1]);
580 sem_init(&init_sem
, 0, 0);
582 for (i
= 1; i
< ncpus
; ++i
)
584 for (i
= 0; i
< ncpus
; ++i
)