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 exited_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 /* Must match flat.lds linker script */
67 #define VM_TEST_LOAD_ADDRESS 0x100000
69 static int test_debug(void *opaque
, void *vcpu
)
71 printf("test_debug\n");
75 static int test_halt(void *opaque
, int vcpu
)
79 sigwait(&ipi_sigmask
, &n
);
83 static int test_io_window(void *opaque
)
88 static int test_try_push_interrupts(void *opaque
)
93 static void test_post_kvm_run(void *opaque
, void *vcpu
)
97 static int test_pre_kvm_run(void *opaque
, void *vcpu
)
102 static int mmio_handler(void *opaque
, int len
, int is_write
, uint64_t offset
,
109 putc(*(char *)data
, stdout
);
116 printf("%s: offset %"PRIx64
" len %d data %"PRIx64
"\n",
117 __func__
, offset
, len
, *(uint64_t *)data
);
124 static int test_mem_read(void *opaque
, uint64_t addr
, uint8_t *data
, int len
)
126 struct io_table_entry
*iodev
;
129 printf("%s: addr %"PRIx64
" len %d\n", __func__
, addr
, len
);
132 iodev
= io_table_lookup(&mmio_table
, addr
);
134 printf("couldn't find device\n");
138 return iodev
->handler(iodev
->opaque
, len
, 0, addr
- iodev
->start
,
142 static int test_mem_write(void *opaque
, uint64_t addr
, uint8_t *data
, int len
)
144 struct io_table_entry
*iodev
;
147 printf("%s: addr %"PRIx64
" len %d data %"PRIx64
"\n",
148 __func__
, addr
, len
, *(uint64_t *)data
);
151 iodev
= io_table_lookup(&mmio_table
, addr
);
153 printf("couldn't find device\n");
157 return iodev
->handler(iodev
->opaque
, len
, 1, addr
- iodev
->start
,
161 static int test_dcr_read(int vcpu
, uint32_t dcrn
, uint32_t *data
)
163 printf("%s: dcrn %04X\n", __func__
, dcrn
);
168 static int test_dcr_write(int vcpu
, uint32_t dcrn
, uint32_t data
)
170 printf("%s: dcrn %04X data %04X\n", __func__
, dcrn
, data
);
174 static struct kvm_callbacks test_callbacks
= {
175 .mmio_read
= test_mem_read
,
176 .mmio_write
= test_mem_write
,
179 .io_window
= test_io_window
,
180 .try_push_interrupts
= test_try_push_interrupts
,
181 .post_kvm_run
= test_post_kvm_run
,
182 .pre_kvm_run
= test_pre_kvm_run
,
183 .powerpc_dcr_read
= test_dcr_read
,
184 .powerpc_dcr_write
= test_dcr_write
,
187 static unsigned long load_file(void *mem
, const char *fname
, int inval_icache
)
191 unsigned long bytes
= 0;
193 fd
= open(fname
, O_RDONLY
);
199 while ((r
= read(fd
, mem
, 4096)) != -1 && r
!= 0) {
206 printf("read %d bytes\n", bytes
);
213 #define ICACHE_LINE_SIZE 32
215 void sync_caches(void *mem
, unsigned long len
)
219 for (i
= 0; i
< len
; i
+= ICACHE_LINE_SIZE
)
220 asm volatile ("dcbst %0, %1" : : "g"(mem
), "r"(i
));
221 asm volatile ("sync");
222 for (i
= 0; i
< len
; i
+= ICACHE_LINE_SIZE
)
223 asm volatile ("icbi %0, %1" : : "g"(mem
), "r"(i
));
224 asm volatile ("sync; isync");
227 static void init_vcpu(int n
)
229 sigemptyset(&ipi_sigmask
);
230 sigaddset(&ipi_sigmask
, IPI_SIGNAL
);
231 sigprocmask(SIG_UNBLOCK
, &ipi_sigmask
, NULL
);
232 sigprocmask(SIG_BLOCK
, &ipi_sigmask
, &kernel_sigmask
);
233 vcpus
[n
].tid
= gettid();
235 kvm_set_signal_mask(kvm
, n
, &kernel_sigmask
);
238 static void *do_create_vcpu(void *_n
)
240 struct kvm_regs regs
;
243 kvm_create_vcpu(kvm
, n
);
246 kvm_get_regs(kvm
, n
, ®s
);
247 regs
.pc
= VM_TEST_LOAD_ADDRESS
;
248 kvm_set_regs(kvm
, n
, ®s
);
250 kvm_run(kvm
, n
, &vcpus
[n
]);
251 sem_post(&exited_sem
);
255 static void start_vcpu(int n
)
259 pthread_create(&thread
, NULL
, do_create_vcpu
, (void *)(long)n
);
262 static void usage(const char *progname
)
265 "Usage: %s [OPTIONS] [bootstrap] flatfile\n"
266 "KVM test harness.\n"
268 " -s, --smp=NUM create a VM with NUM virtual CPUs\n"
269 " -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n"
270 " can be used to change the unit (default: `M')\n"
271 " -h, --help display this help screen and exit\n"
273 "Report bugs to <kvm-ppc@vger.kernel.org>.\n"
277 static void sig_ignore(int sig
)
279 write(1, "boo\n", 4);
282 int main(int argc
, char **argv
)
287 const char *sopts
= "s:phm:";
288 struct option lopts
[] = {
289 { "smp", 1, 0, 's' },
290 { "memory", 1, 0, 'm' },
291 { "help", 0, 0, 'h' },
298 while ((ch
= getopt_long(argc
, argv
, sopts
, lopts
, &opt_ind
)) != -1) {
301 ncpus
= atoi(optarg
);
304 memory_size
= strtoull(optarg
, &endptr
, 0);
318 "Unrecongized memory suffix: %c\n",
322 if (memory_size
== 0) {
324 "Invalid memory size: 0\n");
334 "Try `%s --help' for more information.\n",
340 nb_args
= argc
- optind
;
341 if (nb_args
< 1 || nb_args
> 2) {
343 "Incorrect number of arguments.\n"
344 "Try `%s --help' for more information.\n",
349 signal(IPI_SIGNAL
, sig_ignore
);
351 vcpus
= calloc(ncpus
, sizeof *vcpus
);
353 fprintf(stderr
, "calloc failed\n");
357 kvm
= kvm_init(&test_callbacks
, 0);
359 fprintf(stderr
, "kvm_init failed\n");
362 if (kvm_create(kvm
, memory_size
, &vm_mem
) < 0) {
364 fprintf(stderr
, "kvm_create failed\n");
368 vm_mem
= kvm_create_phys_mem(kvm
, 0, memory_size
, 0, 1);
370 len
= load_file(vm_mem
+ VM_TEST_LOAD_ADDRESS
, argv
[optind
], 1);
371 sync_caches(vm_mem
+ VM_TEST_LOAD_ADDRESS
, len
);
373 io_table_register(&mmio_table
, 0xf0000000, 64, mmio_handler
, NULL
);
375 sem_init(&exited_sem
, 0, 0);
376 for (i
= 0; i
< ncpus
; ++i
)
378 /* Wait for all vcpus to exit. */
379 for (i
= 0; i
< ncpus
; ++i
)
380 sem_wait(&exited_sem
);