kvm: qemu: expose MSI capability to guest
[kvm-userspace.git] / user / main-ppc.c
blob5af59f846ef0782d2e8dac8344cada35b5ab42ae
1 /*
2 * Kernel-based Virtual Machine test driver
4 * This test driver provides a simple way of testing kvm, without a full
5 * device model.
7 * Copyright (C) 2006 Qumranet
8 * Copyright IBM Corp. 2008
10 * Authors:
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.
19 #define _GNU_SOURCE
21 #include <libkvm.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <semaphore.h>
29 #include <sys/types.h>
30 #include <errno.h>
31 #include <pthread.h>
32 #include <signal.h>
33 #include <pthread.h>
34 #include <sys/syscall.h>
35 #include <linux/unistd.h>
36 #include <getopt.h>
37 #include <stdbool.h>
38 #include <inttypes.h>
40 #include "iotable.h"
42 static int gettid(void)
44 return syscall(__NR_gettid);
47 kvm_context_t kvm;
49 #define IPI_SIGNAL (SIGRTMIN + 4)
51 struct io_table mmio_table;
53 static int ncpus = 1;
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;
60 struct vcpu_info {
61 pid_t tid;
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");
72 return 0;
75 static int test_halt(void *opaque, int vcpu)
77 int n;
79 sigwait(&ipi_sigmask, &n);
80 return 0;
83 static int test_io_window(void *opaque)
85 return 0;
88 static int test_try_push_interrupts(void *opaque)
90 return 0;
93 static void test_post_kvm_run(void *opaque, void *vcpu)
97 static int test_pre_kvm_run(void *opaque, void *vcpu)
99 return 0;
102 static int mmio_handler(void *opaque, int len, int is_write, uint64_t offset,
103 uint64_t *data)
105 int r = 0;
107 switch (offset) {
108 case 0: /* putc */
109 putc(*(char *)data, stdout);
110 fflush(stdout);
111 break;
112 case 1: /* exit */
113 r = *(char *)data;
114 break;
115 default:
116 printf("%s: offset %"PRIx64" len %d data %"PRIx64"\n",
117 __func__, offset, len, *(uint64_t *)data);
118 r = -EINVAL;
121 return r;
124 static int test_mem_read(void *opaque, uint64_t addr, uint8_t *data, int len)
126 struct io_table_entry *iodev;
128 #if 0
129 printf("%s: addr %"PRIx64" len %d\n", __func__, addr, len);
130 #endif
132 iodev = io_table_lookup(&mmio_table, addr);
133 if (!iodev) {
134 printf("couldn't find device\n");
135 return -ENODEV;
138 return iodev->handler(iodev->opaque, len, 0, addr - iodev->start,
139 (uint64_t *)data);
142 static int test_mem_write(void *opaque, uint64_t addr, uint8_t *data, int len)
144 struct io_table_entry *iodev;
146 #if 0
147 printf("%s: addr %"PRIx64" len %d data %"PRIx64"\n",
148 __func__, addr, len, *(uint64_t *)data);
149 #endif
151 iodev = io_table_lookup(&mmio_table, addr);
152 if (!iodev) {
153 printf("couldn't find device\n");
154 return -ENODEV;
157 return iodev->handler(iodev->opaque, len, 1, addr - iodev->start,
158 (uint64_t *)data);
161 static int test_dcr_read(int vcpu, uint32_t dcrn, uint32_t *data)
163 printf("%s: dcrn %04X\n", __func__, dcrn);
164 *data = 0;
165 return 0;
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);
171 return 0;
174 static struct kvm_callbacks test_callbacks = {
175 .mmio_read = test_mem_read,
176 .mmio_write = test_mem_write,
177 .debug = test_debug,
178 .halt = test_halt,
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)
189 ssize_t r;
190 int fd;
191 unsigned long bytes = 0;
193 fd = open(fname, O_RDONLY);
194 if (fd == -1) {
195 perror("open");
196 exit(1);
199 while ((r = read(fd, mem, 4096)) != -1 && r != 0) {
200 mem += r;
201 bytes += r;
204 if (r == -1) {
205 perror("read");
206 printf("read %d bytes\n", bytes);
207 exit(1);
210 return bytes;
213 #define ICACHE_LINE_SIZE 32
215 void sync_caches(void *mem, unsigned long len)
217 unsigned long i;
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();
234 vcpu = n;
235 kvm_set_signal_mask(kvm, n, &kernel_sigmask);
238 static void *do_create_vcpu(void *_n)
240 struct kvm_regs regs;
241 int n = (long)_n;
243 kvm_create_vcpu(kvm, n);
244 init_vcpu(n);
246 kvm_get_regs(kvm, n, &regs);
247 regs.pc = VM_TEST_LOAD_ADDRESS;
248 kvm_set_regs(kvm, n, &regs);
250 kvm_run(kvm, n, &vcpus[n]);
251 sem_post(&exited_sem);
252 return NULL;
255 static void start_vcpu(int n)
257 pthread_t thread;
259 pthread_create(&thread, NULL, do_create_vcpu, (void *)(long)n);
262 static void usage(const char *progname)
264 fprintf(stderr,
265 "Usage: %s [OPTIONS] [bootstrap] flatfile\n"
266 "KVM test harness.\n"
267 "\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"
272 "\n"
273 "Report bugs to <kvm-ppc@vger.kernel.org>.\n"
274 , progname);
277 static void sig_ignore(int sig)
279 write(1, "boo\n", 4);
282 int main(int argc, char **argv)
284 void *vm_mem;
285 unsigned long len;
286 int i;
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' },
292 { 0 },
294 int opt_ind, ch;
295 int nb_args;
296 char *endptr;
298 while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) {
299 switch (ch) {
300 case 's':
301 ncpus = atoi(optarg);
302 break;
303 case 'm':
304 memory_size = strtoull(optarg, &endptr, 0);
305 switch (*endptr) {
306 case 'G': case 'g':
307 memory_size <<= 30;
308 break;
309 case '\0':
310 case 'M': case 'm':
311 memory_size <<= 20;
312 break;
313 case 'K': case 'k':
314 memory_size <<= 10;
315 break;
316 default:
317 fprintf(stderr,
318 "Unrecongized memory suffix: %c\n",
319 *endptr);
320 exit(1);
322 if (memory_size == 0) {
323 fprintf(stderr,
324 "Invalid memory size: 0\n");
325 exit(1);
327 break;
328 case 'h':
329 usage(argv[0]);
330 exit(0);
331 case '?':
332 default:
333 fprintf(stderr,
334 "Try `%s --help' for more information.\n",
335 argv[0]);
336 exit(1);
340 nb_args = argc - optind;
341 if (nb_args < 1 || nb_args > 2) {
342 fprintf(stderr,
343 "Incorrect number of arguments.\n"
344 "Try `%s --help' for more information.\n",
345 argv[0]);
346 exit(1);
349 signal(IPI_SIGNAL, sig_ignore);
351 vcpus = calloc(ncpus, sizeof *vcpus);
352 if (!vcpus) {
353 fprintf(stderr, "calloc failed\n");
354 return 1;
357 kvm = kvm_init(&test_callbacks, 0);
358 if (!kvm) {
359 fprintf(stderr, "kvm_init failed\n");
360 return 1;
362 if (kvm_create(kvm, memory_size, &vm_mem) < 0) {
363 kvm_finalize(kvm);
364 fprintf(stderr, "kvm_create failed\n");
365 return 1;
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)
377 start_vcpu(i);
378 /* Wait for all vcpus to exit. */
379 for (i = 0; i < ncpus; ++i)
380 sem_wait(&exited_sem);
382 return 0;