ivshmem-test.c 12.7 KB
Newer Older
A
Andreas Färber 已提交
1 2 3 4
/*
 * QTest testcase for ivshmem
 *
 * Copyright (c) 2014 SUSE LINUX Products GmbH
M
Marc-André Lureau 已提交
5
 * Copyright (c) 2015 Red Hat, Inc.
A
Andreas Färber 已提交
6 7 8 9 10
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

P
Peter Maydell 已提交
11
#include "qemu/osdep.h"
M
Marc-André Lureau 已提交
12 13
#include <glib/gstdio.h>
#include "contrib/ivshmem-server/ivshmem-server.h"
14
#include "libqos/libqos-pc.h"
A
Andreas Färber 已提交
15
#include "libqtest.h"
M
Marc-André Lureau 已提交
16
#include "qemu-common.h"
A
Andreas Färber 已提交
17

M
Marc-André Lureau 已提交
18 19 20 21 22
#define TMPSHMSIZE (1 << 20)
static char *tmpshm;
static void *tmpshmem;
static char *tmpdir;
static char *tmpserver;
A
Andreas Färber 已提交
23

M
Marc-André Lureau 已提交
24
static void save_fn(QPCIDevice *dev, int devfn, void *data)
A
Andreas Färber 已提交
25
{
M
Marc-André Lureau 已提交
26 27 28 29 30
    QPCIDevice **pdev = (QPCIDevice **) data;

    *pdev = dev;
}

M
Marc-André Lureau 已提交
31
static QPCIDevice *get_device(QPCIBus *pcibus)
M
Marc-André Lureau 已提交
32 33 34
{
    QPCIDevice *dev;

35
    dev = NULL;
M
Marc-André Lureau 已提交
36 37 38 39 40 41 42
    qpci_device_foreach(pcibus, 0x1af4, 0x1110, save_fn, &dev);
    g_assert(dev != NULL);

    return dev;
}

typedef struct _IVState {
43
    QOSState *qs;
44
    QPCIBar reg_bar, mem_bar;
M
Marc-André Lureau 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
    QPCIDevice *dev;
} IVState;

enum Reg {
    INTRMASK = 0,
    INTRSTATUS = 4,
    IVPOSITION = 8,
    DOORBELL = 12,
};

static const char* reg2str(enum Reg reg) {
    switch (reg) {
    case INTRMASK:
        return "IntrMask";
    case INTRSTATUS:
        return "IntrStatus";
    case IVPOSITION:
        return "IVPosition";
    case DOORBELL:
        return "DoorBell";
    default:
        return NULL;
    }
}

static inline unsigned in_reg(IVState *s, enum Reg reg)
{
    const char *name = reg2str(reg);
    QTestState *qtest = global_qtest;
    unsigned res;

76
    global_qtest = s->qs->qts;
77
    res = qpci_io_readl(s->dev, s->reg_bar, reg);
M
Marc-André Lureau 已提交
78 79 80 81 82 83 84 85 86 87 88
    g_test_message("*%s -> %x\n", name, res);
    global_qtest = qtest;

    return res;
}

static inline void out_reg(IVState *s, enum Reg reg, unsigned v)
{
    const char *name = reg2str(reg);
    QTestState *qtest = global_qtest;

89
    global_qtest = s->qs->qts;
M
Marc-André Lureau 已提交
90
    g_test_message("%x -> *%s\n", v, name);
91
    qpci_io_writel(s->dev, s->reg_bar, reg, v);
M
Marc-André Lureau 已提交
92 93 94
    global_qtest = qtest;
}

95 96 97 98
static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len)
{
    QTestState *qtest = global_qtest;

99
    global_qtest = s->qs->qts;
100
    qpci_memread(s->dev, s->mem_bar, off, buf, len);
101 102 103 104 105 106 107 108
    global_qtest = qtest;
}

static inline void write_mem(IVState *s, uint64_t off,
                             const void *buf, size_t len)
{
    QTestState *qtest = global_qtest;

109
    global_qtest = s->qs->qts;
110
    qpci_memwrite(s->dev, s->mem_bar, off, buf, len);
111 112 113
    global_qtest = qtest;
}

M
Marc-André Lureau 已提交
114 115 116
static void cleanup_vm(IVState *s)
{
    g_free(s->dev);
117
    qtest_shutdown(s->qs);
M
Marc-André Lureau 已提交
118 119
}

M
Marc-André Lureau 已提交
120 121 122
static void setup_vm_cmd(IVState *s, const char *cmd, bool msix)
{
    uint64_t barsize;
123
    const char *arch = qtest_get_arch();
M
Marc-André Lureau 已提交
124

125 126 127 128 129 130 131
    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
        s->qs = qtest_pc_boot(cmd);
    } else {
        g_printerr("ivshmem-test tests are only available on x86\n");
        exit(EXIT_FAILURE);
    }
    s->dev = get_device(s->qs->pcibus);
M
Marc-André Lureau 已提交
132

133
    s->reg_bar = qpci_iomap(s->dev, 0, &barsize);
134
    g_assert_cmpuint(barsize, ==, 256);
M
Marc-André Lureau 已提交
135 136 137 138 139

    if (msix) {
        qpci_msix_enable(s->dev);
    }

140
    s->mem_bar = qpci_iomap(s->dev, 2, &barsize);
141
    g_assert_cmpuint(barsize, ==, TMPSHMSIZE);
M
Marc-André Lureau 已提交
142 143 144 145 146 147

    qpci_device_enable(s->dev);
}

static void setup_vm(IVState *s)
{
148 149 150
    char *cmd = g_strdup_printf("-object memory-backend-file"
                                ",id=mb1,size=1M,share,mem-path=/dev/shm%s"
                                " -device ivshmem-plain,memdev=mb1", tmpshm);
M
Marc-André Lureau 已提交
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

    setup_vm_cmd(s, cmd, false);

    g_free(cmd);
}

static void test_ivshmem_single(void)
{
    IVState state, *s;
    uint32_t data[1024];
    int i;

    setup_vm(&state);
    s = &state;

166 167 168 169
    /* initial state of readable registers */
    g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0);
    g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0);
    g_assert_cmpuint(in_reg(s, IVPOSITION), ==, 0);
M
Marc-André Lureau 已提交
170

171
    /* trigger interrupt via registers */
M
Marc-André Lureau 已提交
172 173 174
    out_reg(s, INTRMASK, 0xffffffff);
    g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0xffffffff);
    out_reg(s, INTRSTATUS, 1);
175
    /* check interrupt status */
M
Marc-André Lureau 已提交
176
    g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1);
177 178 179
    /* reading clears */
    g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0);
    /* TODO intercept actual interrupt (needs qtest work) */
M
Marc-André Lureau 已提交
180

181
    /* invalid register access */
M
Marc-André Lureau 已提交
182
    out_reg(s, IVPOSITION, 1);
183 184 185
    in_reg(s, DOORBELL);

    /* ring the (non-functional) doorbell */
M
Marc-André Lureau 已提交
186 187
    out_reg(s, DOORBELL, 8 << 16);

188
    /* write shared memory */
M
Marc-André Lureau 已提交
189 190 191
    for (i = 0; i < G_N_ELEMENTS(data); i++) {
        data[i] = i;
    }
192
    write_mem(s, 0, data, sizeof(data));
M
Marc-André Lureau 已提交
193

194
    /* verify write */
M
Marc-André Lureau 已提交
195 196 197 198
    for (i = 0; i < G_N_ELEMENTS(data); i++) {
        g_assert_cmpuint(((uint32_t *)tmpshmem)[i], ==, i);
    }

199
    /* read it back and verify read */
M
Marc-André Lureau 已提交
200
    memset(data, 0, sizeof(data));
201
    read_mem(s, 0, data, sizeof(data));
M
Marc-André Lureau 已提交
202 203 204 205
    for (i = 0; i < G_N_ELEMENTS(data); i++) {
        g_assert_cmpuint(data[i], ==, i);
    }

M
Marc-André Lureau 已提交
206
    cleanup_vm(s);
M
Marc-André Lureau 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
}

static void test_ivshmem_pair(void)
{
    IVState state1, state2, *s1, *s2;
    char *data;
    int i;

    setup_vm(&state1);
    s1 = &state1;
    setup_vm(&state2);
    s2 = &state2;

    data = g_malloc0(TMPSHMSIZE);

    /* host write, guest 1 & 2 read */
    memset(tmpshmem, 0x42, TMPSHMSIZE);
224
    read_mem(s1, 0, data, TMPSHMSIZE);
M
Marc-André Lureau 已提交
225 226 227
    for (i = 0; i < TMPSHMSIZE; i++) {
        g_assert_cmpuint(data[i], ==, 0x42);
    }
228
    read_mem(s2, 0, data, TMPSHMSIZE);
M
Marc-André Lureau 已提交
229 230 231 232 233 234
    for (i = 0; i < TMPSHMSIZE; i++) {
        g_assert_cmpuint(data[i], ==, 0x42);
    }

    /* guest 1 write, guest 2 read */
    memset(data, 0x43, TMPSHMSIZE);
235
    write_mem(s1, 0, data, TMPSHMSIZE);
M
Marc-André Lureau 已提交
236
    memset(data, 0, TMPSHMSIZE);
237
    read_mem(s2, 0, data, TMPSHMSIZE);
M
Marc-André Lureau 已提交
238 239 240 241 242 243
    for (i = 0; i < TMPSHMSIZE; i++) {
        g_assert_cmpuint(data[i], ==, 0x43);
    }

    /* guest 2 write, guest 1 read */
    memset(data, 0x44, TMPSHMSIZE);
244
    write_mem(s2, 0, data, TMPSHMSIZE);
M
Marc-André Lureau 已提交
245
    memset(data, 0, TMPSHMSIZE);
246
    read_mem(s1, 0, data, TMPSHMSIZE);
M
Marc-André Lureau 已提交
247 248 249 250
    for (i = 0; i < TMPSHMSIZE; i++) {
        g_assert_cmpuint(data[i], ==, 0x44);
    }

M
Marc-André Lureau 已提交
251 252
    cleanup_vm(s1);
    cleanup_vm(s2);
M
Marc-André Lureau 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
    g_free(data);
}

typedef struct ServerThread {
    GThread *thread;
    IvshmemServer *server;
    int pipe[2]; /* to handle quit */
} ServerThread;

static void *server_thread(void *data)
{
    ServerThread *t = data;
    IvshmemServer *server = t->server;

    while (true) {
        fd_set fds;
        int maxfd, ret;

        FD_ZERO(&fds);
        FD_SET(t->pipe[0], &fds);
        maxfd = t->pipe[0] + 1;

        ivshmem_server_get_fds(server, &fds, &maxfd);

        ret = select(maxfd, &fds, NULL, NULL, NULL);

        if (ret < 0) {
            if (errno == EINTR) {
                continue;
            }

            g_critical("select error: %s\n", strerror(errno));
            break;
        }
        if (ret == 0) {
            continue;
        }

        if (FD_ISSET(t->pipe[0], &fds)) {
            break;
        }

        if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) {
            g_critical("ivshmem_server_handle_fds() failed\n");
            break;
        }
    }

    return NULL;
}

304
static void setup_vm_with_server(IVState *s, int nvectors, bool msi)
M
Marc-André Lureau 已提交
305 306
{
    char *cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s,nowait "
307 308 309 310
                                "-device ivshmem%s,chardev=chr0,vectors=%d",
                                tmpserver,
                                msi ? "-doorbell" : ",size=1M,msi=off",
                                nvectors);
M
Marc-André Lureau 已提交
311

312
    setup_vm_cmd(s, cmd, msi);
M
Marc-André Lureau 已提交
313 314 315 316

    g_free(cmd);
}

317
static void test_ivshmem_server(bool msi)
M
Marc-André Lureau 已提交
318 319 320 321 322 323 324 325
{
    IVState state1, state2, *s1, *s2;
    ServerThread thread;
    IvshmemServer server;
    int ret, vm1, vm2;
    int nvectors = 2;
    guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;

326
    ret = ivshmem_server_init(&server, tmpserver, tmpshm, true,
M
Marc-André Lureau 已提交
327 328 329 330 331 332 333 334 335 336 337 338 339
                              TMPSHMSIZE, nvectors,
                              g_test_verbose());
    g_assert_cmpint(ret, ==, 0);

    ret = ivshmem_server_start(&server);
    g_assert_cmpint(ret, ==, 0);

    thread.server = &server;
    ret = pipe(thread.pipe);
    g_assert_cmpint(ret, ==, 0);
    thread.thread = g_thread_new("ivshmem-server", server_thread, &thread);
    g_assert(thread.thread != NULL);

340 341 342 343
    setup_vm_with_server(&state1, nvectors, msi);
    s1 = &state1;
    setup_vm_with_server(&state2, nvectors, msi);
    s2 = &state2;
M
Marc-André Lureau 已提交
344 345 346 347

    /* check got different VM ids */
    vm1 = in_reg(s1, IVPOSITION);
    vm2 = in_reg(s2, IVPOSITION);
348 349 350
    g_assert_cmpint(vm1, >=, 0);
    g_assert_cmpint(vm2, >=, 0);
    g_assert_cmpint(vm1, !=, vm2);
M
Marc-André Lureau 已提交
351

352
    /* check number of MSI-X vectors */
353
    global_qtest = s1->qs->qts;
354 355 356 357
    if (msi) {
        ret = qpci_msix_table_size(s1->dev);
        g_assert_cmpuint(ret, ==, nvectors);
    }
M
Marc-André Lureau 已提交
358

359 360 361
    /* TODO test behavior before MSI-X is enabled */

    /* ping vm2 -> vm1 on vector 0 */
362 363 364 365
    if (msi) {
        ret = qpci_msix_pending(s1->dev, 0);
        g_assert_cmpuint(ret, ==, 0);
    } else {
366
        g_assert_cmpuint(in_reg(s1, INTRSTATUS), ==, 0);
367
    }
M
Marc-André Lureau 已提交
368 369 370
    out_reg(s2, DOORBELL, vm1 << 16);
    do {
        g_usleep(10000);
371
        ret = msi ? qpci_msix_pending(s1->dev, 0) : in_reg(s1, INTRSTATUS);
M
Marc-André Lureau 已提交
372 373 374
    } while (ret == 0 && g_get_monotonic_time() < end_time);
    g_assert_cmpuint(ret, !=, 0);

375
    /* ping vm1 -> vm2 on vector 1 */
376
    global_qtest = s2->qs->qts;
377
    if (msi) {
378
        ret = qpci_msix_pending(s2->dev, 1);
379 380
        g_assert_cmpuint(ret, ==, 0);
    } else {
381
        g_assert_cmpuint(in_reg(s2, INTRSTATUS), ==, 0);
382
    }
383
    out_reg(s1, DOORBELL, vm2 << 16 | 1);
M
Marc-André Lureau 已提交
384 385
    do {
        g_usleep(10000);
386
        ret = msi ? qpci_msix_pending(s2->dev, 1) : in_reg(s2, INTRSTATUS);
M
Marc-André Lureau 已提交
387 388 389
    } while (ret == 0 && g_get_monotonic_time() < end_time);
    g_assert_cmpuint(ret, !=, 0);

M
Marc-André Lureau 已提交
390 391
    cleanup_vm(s2);
    cleanup_vm(s1);
M
Marc-André Lureau 已提交
392 393 394 395 396 397 398 399 400 401 402 403

    if (qemu_write_full(thread.pipe[1], "q", 1) != 1) {
        g_error("qemu_write_full: %s", g_strerror(errno));
    }

    g_thread_join(thread.thread);

    ivshmem_server_close(&server);
    close(thread.pipe[1]);
    close(thread.pipe[0]);
}

404 405 406 407 408 409 410 411 412 413
static void test_ivshmem_server_msi(void)
{
    test_ivshmem_server(true);
}

static void test_ivshmem_server_irq(void)
{
    test_ivshmem_server(false);
}

M
Marc-André Lureau 已提交
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
#define PCI_SLOT_HP             0x06

static void test_ivshmem_hotplug(void)
{
    gchar *opts;

    qtest_start("");

    opts = g_strdup_printf("'shm': '%s', 'size': '1M'", tmpshm);

    qpci_plug_device_test("ivshmem", "iv1", PCI_SLOT_HP, opts);
    qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP);

    qtest_end();
    g_free(opts);
}

M
Marc-André Lureau 已提交
431 432 433 434 435 436
static void test_ivshmem_memdev(void)
{
    IVState state;

    /* just for the sake of checking memory-backend property */
    setup_vm_cmd(&state, "-object memory-backend-ram,size=1M,id=mb1"
437
                 " -device ivshmem-plain,memdev=mb1", false);
M
Marc-André Lureau 已提交
438

M
Marc-André Lureau 已提交
439
    cleanup_vm(&state);
M
Marc-André Lureau 已提交
440 441
}

M
Marc-André Lureau 已提交
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
static void cleanup(void)
{
    if (tmpshmem) {
        munmap(tmpshmem, TMPSHMSIZE);
        tmpshmem = NULL;
    }

    if (tmpshm) {
        shm_unlink(tmpshm);
        g_free(tmpshm);
        tmpshm = NULL;
    }

    if (tmpserver) {
        g_unlink(tmpserver);
        g_free(tmpserver);
        tmpserver = NULL;
    }

    if (tmpdir) {
        g_rmdir(tmpdir);
        tmpdir = NULL;
    }
}

static void abrt_handler(void *data)
{
    cleanup();
}

static gchar *mktempshm(int size, int *fd)
{
    while (true) {
        gchar *name;

        name = g_strdup_printf("/qtest-%u-%u", getpid(), g_random_int());
        *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL,
                       S_IRWXU|S_IRWXG|S_IRWXO);
        if (*fd > 0) {
            g_assert(ftruncate(*fd, size) == 0);
            return name;
        }

        g_free(name);

        if (errno != EEXIST) {
            perror("shm_open");
            return NULL;
        }
    }
A
Andreas Färber 已提交
492 493 494 495 496
}

int main(int argc, char **argv)
{
    int ret, fd;
M
Marc-André Lureau 已提交
497 498 499 500 501 502 503
    gchar dir[] = "/tmp/ivshmem-test.XXXXXX";

#if !GLIB_CHECK_VERSION(2, 31, 0)
    if (!g_thread_supported()) {
        g_thread_init(NULL);
    }
#endif
A
Andreas Färber 已提交
504 505 506

    g_test_init(&argc, &argv, NULL);

M
Marc-André Lureau 已提交
507 508 509 510 511 512 513 514 515 516 517 518 519 520
    qtest_add_abrt_handler(abrt_handler, NULL);
    /* shm */
    tmpshm = mktempshm(TMPSHMSIZE, &fd);
    if (!tmpshm) {
        return 0;
    }
    tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    g_assert(tmpshmem != MAP_FAILED);
    /* server */
    if (mkdtemp(dir) == NULL) {
        g_error("mkdtemp: %s", g_strerror(errno));
    }
    tmpdir = dir;
    tmpserver = g_strconcat(tmpdir, "/server", NULL);
A
Andreas Färber 已提交
521

M
Marc-André Lureau 已提交
522 523
    qtest_add_func("/ivshmem/single", test_ivshmem_single);
    qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug);
M
Marc-André Lureau 已提交
524
    qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev);
525 526
    if (g_test_slow()) {
        qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
527 528
        qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi);
        qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq);
529
    }
A
Andreas Färber 已提交
530 531 532

    ret = g_test_run();

M
Marc-André Lureau 已提交
533
    cleanup();
A
Andreas Färber 已提交
534 535 536

    return ret;
}