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/x86/lib/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
;
74 struct vcpu_info
*vcpus
;
76 static uint32_t apic_sipi_addr
;
78 static void apic_send_sipi(int vcpu
)
80 sem_post(&vcpus
[vcpu
].sipi_sem
);
83 static void apic_send_ipi(int vcpu
)
87 if (vcpu
< 0 || vcpu
>= ncpus
)
90 tkill(v
->tid
, IPI_SIGNAL
);
93 static int apic_io(void *opaque
, int size
, int is_write
,
94 uint64_t addr
, uint64_t *value
)
99 switch (addr
- APIC_BASE
) {
108 case APIC_REG_SIPI_ADDR
:
110 *value
= apic_sipi_addr
;
112 apic_sipi_addr
= *value
;
114 case APIC_REG_SEND_SIPI
:
116 apic_send_sipi(*value
);
118 case APIC_REG_IPI_VECTOR
:
120 *value
= apic_ipi_vector
;
122 apic_ipi_vector
= *value
;
124 case APIC_REG_SEND_IPI
:
126 apic_send_ipi(*value
);
133 static int apic_init(void)
135 return io_table_register(&pio_table
, APIC_BASE
,
136 APIC_SIZE
, apic_io
, NULL
);
139 static int misc_io(void *opaque
, int size
, int is_write
,
140 uint64_t addr
, uint64_t *value
)
142 static int newline
= 1;
148 case 0xff: // irq injector
150 printf("injecting interrupt 0x%x\n", (uint8_t)*value
);
151 kvm_inject_irq(kvm
, 0, *value
);
157 fputs("GUEST: ", stdout
);
159 newline
= *value
== '\n';
164 *value
= memory_size
;
175 static int misc_init(void)
179 err
= io_table_register(&pio_table
, 0xff, 1, misc_io
, NULL
);
183 err
= io_table_register(&pio_table
, 0xf1, 1, misc_io
, NULL
);
187 err
= io_table_register(&pio_table
, 0xf4, 1, misc_io
, NULL
);
191 return io_table_register(&pio_table
, 0xd1, 1, misc_io
, NULL
);
194 static int test_inb(void *opaque
, uint16_t addr
, uint8_t *value
)
196 struct io_table_entry
*entry
;
198 entry
= io_table_lookup(&pio_table
, addr
);
201 entry
->handler(entry
->opaque
, 1, 0, addr
, &val
);
205 printf("inb 0x%x\n", addr
);
211 static int test_inw(void *opaque
, uint16_t addr
, uint16_t *value
)
213 struct io_table_entry
*entry
;
215 entry
= io_table_lookup(&pio_table
, addr
);
218 entry
->handler(entry
->opaque
, 2, 0, addr
, &val
);
222 printf("inw 0x%x\n", addr
);
228 static int test_inl(void *opaque
, uint16_t addr
, uint32_t *value
)
230 struct io_table_entry
*entry
;
232 entry
= io_table_lookup(&pio_table
, addr
);
235 entry
->handler(entry
->opaque
, 4, 0, addr
, &val
);
239 printf("inl 0x%x\n", addr
);
245 static int test_outb(void *opaque
, uint16_t addr
, uint8_t value
)
247 struct io_table_entry
*entry
;
249 entry
= io_table_lookup(&pio_table
, addr
);
251 uint64_t val
= value
;
252 entry
->handler(entry
->opaque
, 1, 1, addr
, &val
);
254 printf("outb $0x%x, 0x%x\n", value
, addr
);
259 static int test_outw(void *opaque
, uint16_t addr
, uint16_t value
)
261 struct io_table_entry
*entry
;
263 entry
= io_table_lookup(&pio_table
, addr
);
265 uint64_t val
= value
;
266 entry
->handler(entry
->opaque
, 2, 1, addr
, &val
);
268 printf("outw $0x%x, 0x%x\n", value
, addr
);
273 static int test_outl(void *opaque
, uint16_t addr
, uint32_t value
)
275 struct io_table_entry
*entry
;
277 entry
= io_table_lookup(&pio_table
, addr
);
279 uint64_t val
= value
;
280 entry
->handler(entry
->opaque
, 4, 1, addr
, &val
);
282 printf("outl $0x%x, 0x%x\n", value
, addr
);
287 static int test_debug(void *opaque
, int vcpu
)
289 printf("test_debug\n");
293 static int test_halt(void *opaque
, int vcpu
)
297 sigwait(&ipi_sigmask
, &n
);
298 kvm_inject_irq(kvm
, vcpu
, apic_ipi_vector
);
302 static int test_io_window(void *opaque
)
307 static int test_try_push_interrupts(void *opaque
)
312 static void test_post_kvm_run(void *opaque
, int vcpu
)
316 static int test_pre_kvm_run(void *opaque
, int vcpu
)
321 static int test_mem_read(void *opaque
, uint64_t addr
, uint8_t *data
, int len
)
323 if (addr
< IORAM_BASE_PHYS
|| addr
+ len
> IORAM_BASE_PHYS
+ IORAM_LEN
)
325 memcpy(data
, ioram
+ addr
- IORAM_BASE_PHYS
, len
);
329 static int test_mem_write(void *opaque
, uint64_t addr
, uint8_t *data
, int len
)
331 if (addr
< IORAM_BASE_PHYS
|| addr
+ len
> IORAM_BASE_PHYS
+ IORAM_LEN
)
333 memcpy(ioram
+ addr
- IORAM_BASE_PHYS
, data
, len
);
337 static struct kvm_callbacks test_callbacks
= {
344 .mmio_read
= test_mem_read
,
345 .mmio_write
= test_mem_write
,
348 .io_window
= test_io_window
,
349 .try_push_interrupts
= test_try_push_interrupts
,
350 .post_kvm_run
= test_post_kvm_run
,
351 .pre_kvm_run
= test_pre_kvm_run
,
354 static void load_file(void *mem
, const char *fname
)
359 fd
= open(fname
, O_RDONLY
);
364 while ((r
= read(fd
, mem
, 4096)) != -1 && r
!= 0)
372 static void enter_32(kvm_context_t kvm
)
374 struct kvm_regs regs
= {
375 .rsp
= 0x80000, /* 512KB */
376 .rip
= 0x100000, /* 1MB */
379 struct kvm_sregs sregs
= {
380 .cs
= { 0, -1u, 8, 11, 1, 0, 1, 1, 0, 1, 0, 0 },
381 .ds
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
382 .es
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
383 .fs
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
384 .gs
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
385 .ss
= { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
387 .tr
= { 0, 10000, 24, 11, 1, 0, 0, 0, 0, 0, 0, 0 },
388 .ldt
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
396 .interrupt_bitmap
= { 0 },
399 kvm_set_regs(kvm
, 0, ®s
);
400 kvm_set_sregs(kvm
, 0, &sregs
);
403 static void init_vcpu(int n
)
405 sigemptyset(&ipi_sigmask
);
406 sigaddset(&ipi_sigmask
, IPI_SIGNAL
);
407 sigprocmask(SIG_UNBLOCK
, &ipi_sigmask
, NULL
);
408 sigprocmask(SIG_BLOCK
, &ipi_sigmask
, &kernel_sigmask
);
409 vcpus
[n
].tid
= gettid();
411 kvm_set_signal_mask(kvm
, n
, &kernel_sigmask
);
415 static void *do_create_vcpu(void *_n
)
418 struct kvm_regs regs
;
420 kvm_create_vcpu(kvm
, n
);
422 sem_wait(&vcpus
[n
].sipi_sem
);
423 kvm_get_regs(kvm
, n
, ®s
);
424 regs
.rip
= apic_sipi_addr
;
425 kvm_set_regs(kvm
, n
, ®s
);
430 static void start_vcpu(int n
)
434 sem_init(&vcpus
[n
].sipi_sem
, 0, 0);
435 pthread_create(&thread
, NULL
, do_create_vcpu
, (void *)(long)n
);
438 static void usage(const char *progname
)
441 "Usage: %s [OPTIONS] [bootstrap] flatfile\n"
442 "KVM test harness.\n"
444 " -s, --smp=NUM create a VM with NUM virtual CPUs\n"
445 " -p, --protected-mode start VM in protected mode\n"
446 " -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n"
447 " can be used to change the unit (default: `M')\n"
448 " -h, --help display this help screen and exit\n"
450 "Report bugs to <kvm-devel@lists.sourceforge.net>.\n"
454 static void sig_ignore(int sig
)
456 write(1, "boo\n", 4);
459 int main(int argc
, char **argv
)
463 const char *sopts
= "s:phm:";
464 struct option lopts
[] = {
465 { "smp", 1, 0, 's' },
466 { "protected-mode", 0, 0, 'p' },
467 { "memory", 1, 0, 'm' },
468 { "help", 0, 0, 'h' },
472 bool enter_protected_mode
= false;
476 while ((ch
= getopt_long(argc
, argv
, sopts
, lopts
, &opt_ind
)) != -1) {
479 ncpus
= atoi(optarg
);
482 enter_protected_mode
= true;
485 memory_size
= strtoull(optarg
, &endptr
, 0);
499 "Unrecongized memory suffix: %c\n",
503 if (memory_size
== 0) {
505 "Invalid memory size: 0\n");
515 "Try `%s --help' for more information.\n",
521 nb_args
= argc
- optind
;
522 if (nb_args
< 1 || nb_args
> 2) {
524 "Incorrect number of arguments.\n"
525 "Try `%s --help' for more information.\n",
530 signal(IPI_SIGNAL
, sig_ignore
);
532 vcpus
= calloc(ncpus
, sizeof *vcpus
);
534 fprintf(stderr
, "calloc failed\n");
538 kvm
= kvm_init(&test_callbacks
, 0);
540 fprintf(stderr
, "kvm_init failed\n");
543 if (kvm_create(kvm
, memory_size
, &vm_mem
) < 0) {
545 fprintf(stderr
, "kvm_create failed\n");
549 vm_mem
= kvm_create_phys_mem(kvm
, 0, memory_size
, 0, 1);
551 if (enter_protected_mode
)
554 load_file(vm_mem
+ 0xf0000, argv
[optind
]);
557 load_file(vm_mem
+ 0x100000, argv
[optind
+ 1]);
562 sem_init(&init_sem
, 0, 0);
563 for (i
= 0; i
< ncpus
; ++i
)
565 for (i
= 0; i
< ncpus
; ++i
)