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/lib/x86/fake-apic.h"
21 #include "test/x86/ioram.h"
28 #include <semaphore.h>
29 #include <sys/types.h>
34 #include <sys/syscall.h>
35 #include <linux/unistd.h>
41 static uint8_t ioram
[IORAM_LEN
];
43 static int gettid(void)
45 return syscall(__NR_gettid
);
48 static int tkill(int pid
, int sig
)
50 return syscall(__NR_tkill
, pid
, sig
);
57 #define IPI_SIGNAL (SIGRTMIN + 4)
60 static sem_t init_sem
;
61 static __thread
int vcpu
;
62 static int apic_ipi_vector
= 0xff;
63 static sigset_t kernel_sigmask
;
64 static sigset_t ipi_sigmask
;
65 static uint64_t memory_size
= 128 * 1024 * 1024;
67 static struct io_table pio_table
;
75 struct vcpu_info
*vcpus
;
77 static uint32_t apic_sipi_addr
;
79 static void apic_send_sipi(int vcpu
)
81 sem_post(&vcpus
[vcpu
].sipi_sem
);
84 static void apic_send_ipi(int vcpu
)
88 if (vcpu
< 0 || vcpu
>= ncpus
)
91 tkill(v
->tid
, IPI_SIGNAL
);
94 static int apic_io(void *opaque
, int size
, int is_write
,
95 uint64_t addr
, uint64_t *value
)
100 switch (addr
- APIC_BASE
) {
109 case APIC_REG_SIPI_ADDR
:
111 *value
= apic_sipi_addr
;
113 apic_sipi_addr
= *value
;
115 case APIC_REG_SEND_SIPI
:
117 apic_send_sipi(*value
);
119 case APIC_REG_IPI_VECTOR
:
121 *value
= apic_ipi_vector
;
123 apic_ipi_vector
= *value
;
125 case APIC_REG_SEND_IPI
:
127 apic_send_ipi(*value
);
134 static int apic_init(void)
136 return io_table_register(&pio_table
, APIC_BASE
,
137 APIC_SIZE
, apic_io
, NULL
);
140 static int misc_io(void *opaque
, int size
, int is_write
,
141 uint64_t addr
, uint64_t *value
)
143 static int newline
= 1;
149 case 0xff: // irq injector
151 printf("injecting interrupt 0x%x\n", (uint8_t)*value
);
152 kvm_inject_irq(kvm
, 0, *value
);
158 fputs("GUEST: ", stdout
);
160 newline
= *value
== '\n';
165 *value
= memory_size
;
176 static int misc_init(void)
180 err
= io_table_register(&pio_table
, 0xff, 1, misc_io
, NULL
);
184 err
= io_table_register(&pio_table
, 0xf1, 1, misc_io
, NULL
);
188 err
= io_table_register(&pio_table
, 0xf4, 1, misc_io
, NULL
);
192 return io_table_register(&pio_table
, 0xd1, 1, misc_io
, NULL
);
195 static int test_inb(void *opaque
, uint16_t addr
, uint8_t *value
)
197 struct io_table_entry
*entry
;
199 entry
= io_table_lookup(&pio_table
, addr
);
202 entry
->handler(entry
->opaque
, 1, 0, addr
, &val
);
206 printf("inb 0x%x\n", addr
);
212 static int test_inw(void *opaque
, uint16_t addr
, uint16_t *value
)
214 struct io_table_entry
*entry
;
216 entry
= io_table_lookup(&pio_table
, addr
);
219 entry
->handler(entry
->opaque
, 2, 0, addr
, &val
);
223 printf("inw 0x%x\n", addr
);
229 static int test_inl(void *opaque
, uint16_t addr
, uint32_t *value
)
231 struct io_table_entry
*entry
;
233 entry
= io_table_lookup(&pio_table
, addr
);
236 entry
->handler(entry
->opaque
, 4, 0, addr
, &val
);
240 printf("inl 0x%x\n", addr
);
246 static int test_outb(void *opaque
, uint16_t addr
, uint8_t value
)
248 struct io_table_entry
*entry
;
250 entry
= io_table_lookup(&pio_table
, addr
);
252 uint64_t val
= value
;
253 entry
->handler(entry
->opaque
, 1, 1, addr
, &val
);
255 printf("outb $0x%x, 0x%x\n", value
, addr
);
260 static int test_outw(void *opaque
, uint16_t addr
, uint16_t value
)
262 struct io_table_entry
*entry
;
264 entry
= io_table_lookup(&pio_table
, addr
);
266 uint64_t val
= value
;
267 entry
->handler(entry
->opaque
, 2, 1, addr
, &val
);
269 printf("outw $0x%x, 0x%x\n", value
, addr
);
274 static int test_outl(void *opaque
, uint16_t addr
, uint32_t value
)
276 struct io_table_entry
*entry
;
278 entry
= io_table_lookup(&pio_table
, addr
);
280 uint64_t val
= value
;
281 entry
->handler(entry
->opaque
, 4, 1, addr
, &val
);
283 printf("outl $0x%x, 0x%x\n", value
, addr
);
288 static int test_debug(void *opaque
, void *vcpu
)
290 printf("test_debug\n");
294 static int test_halt(void *opaque
, void *_vcpu
)
296 struct vcpu_info
*vcpu
= _vcpu
;
299 sigwait(&ipi_sigmask
, &n
);
300 kvm_inject_irq(kvm
, vcpu
->id
, apic_ipi_vector
);
304 static int test_io_window(void *opaque
)
309 static int test_try_push_interrupts(void *opaque
)
314 static int test_try_push_nmi(void *opaque
)
319 static void test_post_kvm_run(void *opaque
, void *vcpu
)
323 static int test_pre_kvm_run(void *opaque
, void *vcpu
)
328 static int test_mem_read(void *opaque
, uint64_t addr
, uint8_t *data
, int len
)
330 if (addr
< IORAM_BASE_PHYS
|| addr
+ len
> IORAM_BASE_PHYS
+ IORAM_LEN
)
332 memcpy(data
, ioram
+ addr
- IORAM_BASE_PHYS
, len
);
336 static int test_mem_write(void *opaque
, uint64_t addr
, uint8_t *data
, int len
)
338 if (addr
< IORAM_BASE_PHYS
|| addr
+ len
> IORAM_BASE_PHYS
+ IORAM_LEN
)
340 memcpy(ioram
+ addr
- IORAM_BASE_PHYS
, data
, len
);
344 static int test_shutdown(void *opaque
, void *env
)
346 printf("shutdown\n");
347 kvm_show_regs(kvm
, 0);
352 static struct kvm_callbacks test_callbacks
= {
359 .mmio_read
= test_mem_read
,
360 .mmio_write
= test_mem_write
,
363 .io_window
= test_io_window
,
364 .try_push_interrupts
= test_try_push_interrupts
,
365 .try_push_nmi
= test_try_push_nmi
,
366 .post_kvm_run
= test_post_kvm_run
,
367 .pre_kvm_run
= test_pre_kvm_run
,
368 .shutdown
= test_shutdown
,
371 static void load_file(void *mem
, const char *fname
)
376 fd
= open(fname
, O_RDONLY
);
381 while ((r
= read(fd
, mem
, 4096)) != -1 && r
!= 0)
389 static void enter_32(kvm_context_t kvm
)
391 struct kvm_regs regs
= {
392 .rsp
= 0x80000, /* 512KB */
393 .rip
= 0x100000, /* 1MB */
396 struct kvm_sregs sregs
= {
397 .cs
= { 0, -1u, 8, 11, 1, 0, 1, 1, 0, 1, 0, 0 },
398 .ds
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
399 .es
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
400 .fs
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
401 .gs
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
402 .ss
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
404 .tr
= { 0, 10000, 24, 11, 1, 0, 0, 0, 0, 0, 0, 0 },
405 .ldt
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
413 .interrupt_bitmap
= { 0 },
416 kvm_set_regs(kvm
, 0, ®s
);
417 kvm_set_sregs(kvm
, 0, &sregs
);
420 static void init_vcpu(int n
)
422 sigemptyset(&ipi_sigmask
);
423 sigaddset(&ipi_sigmask
, IPI_SIGNAL
);
424 sigprocmask(SIG_UNBLOCK
, &ipi_sigmask
, NULL
);
425 sigprocmask(SIG_BLOCK
, &ipi_sigmask
, &kernel_sigmask
);
427 vcpus
[n
].tid
= gettid();
429 kvm_set_signal_mask(kvm
, n
, &kernel_sigmask
);
433 static void *do_create_vcpu(void *_n
)
436 struct kvm_regs regs
;
438 kvm_create_vcpu(kvm
, n
);
440 sem_wait(&vcpus
[n
].sipi_sem
);
441 kvm_get_regs(kvm
, n
, ®s
);
442 regs
.rip
= apic_sipi_addr
;
443 kvm_set_regs(kvm
, n
, ®s
);
444 kvm_run(kvm
, n
, &vcpus
[n
]);
448 static void start_vcpu(int n
)
452 sem_init(&vcpus
[n
].sipi_sem
, 0, 0);
453 pthread_create(&thread
, NULL
, do_create_vcpu
, (void *)(long)n
);
456 static void usage(const char *progname
)
459 "Usage: %s [OPTIONS] [bootstrap] flatfile\n"
460 "KVM test harness.\n"
462 " -s, --smp=NUM create a VM with NUM virtual CPUs\n"
463 " -p, --protected-mode start VM in protected mode\n"
464 " -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n"
465 " can be used to change the unit (default: `M')\n"
466 " -h, --help display this help screen and exit\n"
468 "Report bugs to <kvm-devel@lists.sourceforge.net>.\n"
472 static void sig_ignore(int sig
)
474 write(1, "boo\n", 4);
477 int main(int argc
, char **argv
)
481 const char *sopts
= "s:phm:";
482 struct option lopts
[] = {
483 { "smp", 1, 0, 's' },
484 { "protected-mode", 0, 0, 'p' },
485 { "memory", 1, 0, 'm' },
486 { "help", 0, 0, 'h' },
490 bool enter_protected_mode
= false;
494 while ((ch
= getopt_long(argc
, argv
, sopts
, lopts
, &opt_ind
)) != -1) {
497 ncpus
= atoi(optarg
);
500 enter_protected_mode
= true;
503 memory_size
= strtoull(optarg
, &endptr
, 0);
517 "Unrecongized memory suffix: %c\n",
521 if (memory_size
== 0) {
523 "Invalid memory size: 0\n");
533 "Try `%s --help' for more information.\n",
539 nb_args
= argc
- optind
;
540 if (nb_args
< 1 || nb_args
> 2) {
542 "Incorrect number of arguments.\n"
543 "Try `%s --help' for more information.\n",
548 signal(IPI_SIGNAL
, sig_ignore
);
550 vcpus
= calloc(ncpus
, sizeof *vcpus
);
552 fprintf(stderr
, "calloc failed\n");
556 kvm
= kvm_init(&test_callbacks
, 0);
558 fprintf(stderr
, "kvm_init failed\n");
561 if (kvm_create(kvm
, memory_size
, &vm_mem
) < 0) {
563 fprintf(stderr
, "kvm_create failed\n");
567 vm_mem
= kvm_create_phys_mem(kvm
, 0, memory_size
, 0, 1);
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);
581 for (i
= 0; i
< ncpus
; ++i
)
583 for (i
= 0; i
< ncpus
; ++i
)
586 kvm_run(kvm
, 0, &vcpus
[0]);