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
8 * Copyright IBM Corp. 2008
12 * Avi Kivity <avi@qumranet.com>
13 * Yaniv Kamay <yaniv@qumranet.com>
14 * Hollis Blanchard <hollisb@us.ibm.com>
16 * This work is licensed under the GNU LGPL license, version 2.
28 #include <semaphore.h>
29 #include <sys/types.h>
34 #include <sys/syscall.h>
35 #include <linux/unistd.h>
42 static int gettid(void)
44 return syscall(__NR_gettid
);
49 #define IPI_SIGNAL (SIGRTMIN + 4)
51 struct io_table mmio_table
;
54 static sem_t init_sem
;
55 static __thread
int vcpu
;
56 static sigset_t kernel_sigmask
;
57 static sigset_t ipi_sigmask
;
58 static uint64_t memory_size
= 128 * 1024 * 1024;
64 struct vcpu_info
*vcpus
;
66 static int test_debug(void *opaque
, int vcpu
)
68 printf("test_debug\n");
72 static int test_halt(void *opaque
, int vcpu
)
76 sigwait(&ipi_sigmask
, &n
);
80 static int test_io_window(void *opaque
)
85 static int test_try_push_interrupts(void *opaque
)
90 static void test_post_kvm_run(void *opaque
, int vcpu
)
94 static int test_pre_kvm_run(void *opaque
, int vcpu
)
99 static int mmio_handler(void *opaque
, int len
, int is_write
, uint64_t offset
,
106 putc(*(char *)data
, stdout
);
113 printf("%s: offset %"PRIx64
" len %d data %"PRIx64
"\n",
114 __func__
, offset
, len
, *(uint64_t *)data
);
121 static int test_mem_read(void *opaque
, uint64_t addr
, uint8_t *data
, int len
)
123 struct io_table_entry
*iodev
;
126 printf("%s: addr %"PRIx64
" len %d\n", __func__
, addr
, len
);
129 iodev
= io_table_lookup(&mmio_table
, addr
);
131 printf("couldn't find device\n");
135 return iodev
->handler(iodev
->opaque
, len
, 0, addr
- iodev
->start
,
139 static int test_mem_write(void *opaque
, uint64_t addr
, uint8_t *data
, int len
)
141 struct io_table_entry
*iodev
;
144 printf("%s: addr %"PRIx64
" len %d data %"PRIx64
"\n",
145 __func__
, addr
, len
, *(uint64_t *)data
);
148 iodev
= io_table_lookup(&mmio_table
, addr
);
150 printf("couldn't find device\n");
154 return iodev
->handler(iodev
->opaque
, len
, 1, addr
- iodev
->start
,
158 static int test_dcr_read(uint32_t dcrn
, uint32_t *data
)
160 printf("%s: dcrn %04X\n", __func__
, dcrn
);
165 static int test_dcr_write(uint32_t dcrn
, uint32_t data
)
167 printf("%s: dcrn %04X data %04X\n", __func__
, dcrn
, data
);
171 static struct kvm_callbacks test_callbacks
= {
172 .mmio_read
= test_mem_read
,
173 .mmio_write
= test_mem_write
,
176 .io_window
= test_io_window
,
177 .try_push_interrupts
= test_try_push_interrupts
,
178 .post_kvm_run
= test_post_kvm_run
,
179 .pre_kvm_run
= test_pre_kvm_run
,
180 .powerpc_dcr_read
= test_dcr_read
,
181 .powerpc_dcr_write
= test_dcr_write
,
184 static unsigned long load_file(void *mem
, const char *fname
, int inval_icache
)
188 unsigned long bytes
= 0;
190 fd
= open(fname
, O_RDONLY
);
196 while ((r
= read(fd
, mem
, 4096)) != -1 && r
!= 0) {
209 #define ICACHE_LINE_SIZE 32
211 void sync_caches(void *mem
, unsigned long len
)
215 for (i
= 0; i
< len
; i
+= ICACHE_LINE_SIZE
)
216 asm volatile ("dcbst %0, %1" : : "g"(mem
), "r"(i
));
217 asm volatile ("sync");
218 for (i
= 0; i
< len
; i
+= ICACHE_LINE_SIZE
)
219 asm volatile ("icbi %0, %1" : : "g"(mem
), "r"(i
));
220 asm volatile ("sync; isync");
223 static void init_vcpu(int n
, unsigned long entry
)
225 /* XXX must set initial TLB state and stack
226 struct kvm_regs regs = {
230 kvm_set_regs(kvm, 0, ®s);
233 sigemptyset(&ipi_sigmask
);
234 sigaddset(&ipi_sigmask
, IPI_SIGNAL
);
235 sigprocmask(SIG_UNBLOCK
, &ipi_sigmask
, NULL
);
236 sigprocmask(SIG_BLOCK
, &ipi_sigmask
, &kernel_sigmask
);
237 vcpus
[n
].tid
= gettid();
239 kvm_set_signal_mask(kvm
, n
, &kernel_sigmask
);
243 static void *do_create_vcpu(void *_n
)
247 kvm_create_vcpu(kvm
, n
);
253 static void start_vcpu(int n
)
257 pthread_create(&thread
, NULL
, do_create_vcpu
, (void *)(long)n
);
260 static void usage(const char *progname
)
263 "Usage: %s [OPTIONS] [bootstrap] flatfile\n"
264 "KVM test harness.\n"
266 " -s, --smp=NUM create a VM with NUM virtual CPUs\n"
267 " -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n"
268 " can be used to change the unit (default: `M')\n"
269 " -h, --help display this help screen and exit\n"
271 "Report bugs to <kvm-devel@lists.sourceforge.net>.\n"
275 static void sig_ignore(int sig
)
277 write(1, "boo\n", 4);
280 int main(int argc
, char **argv
)
285 const char *sopts
= "s:phm:";
286 struct option lopts
[] = {
287 { "smp", 1, 0, 's' },
288 { "memory", 1, 0, 'm' },
289 { "help", 0, 0, 'h' },
296 while ((ch
= getopt_long(argc
, argv
, sopts
, lopts
, &opt_ind
)) != -1) {
299 ncpus
= atoi(optarg
);
302 memory_size
= strtoull(optarg
, &endptr
, 0);
316 "Unrecongized memory suffix: %c\n",
320 if (memory_size
== 0) {
322 "Invalid memory size: 0\n");
332 "Try `%s --help' for more information.\n",
338 nb_args
= argc
- optind
;
339 if (nb_args
< 1 || nb_args
> 2) {
341 "Incorrect number of arguments.\n"
342 "Try `%s --help' for more information.\n",
347 signal(IPI_SIGNAL
, sig_ignore
);
349 vcpus
= calloc(ncpus
, sizeof *vcpus
);
351 fprintf(stderr
, "calloc failed\n");
355 kvm
= kvm_init(&test_callbacks
, 0);
357 fprintf(stderr
, "kvm_init failed\n");
360 if (kvm_create(kvm
, memory_size
, &vm_mem
) < 0) {
362 fprintf(stderr
, "kvm_create failed\n");
366 vm_mem
= kvm_create_phys_mem(kvm
, 0, memory_size
, 0, 1);
368 len
= load_file(vm_mem
, argv
[optind
], 1);
369 sync_caches(vm_mem
, len
);
371 sem_init(&init_sem
, 0, 0);
373 for (i
= 1; i
< ncpus
; ++i
)
375 for (i
= 0; i
< ncpus
; ++i
)
378 io_table_register(&mmio_table
, 0xf0000000, 64, mmio_handler
, NULL
);
380 return kvm_run(kvm
, 0);