cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

ivshmem-test.c (12241B)


      1/*
      2 * QTest testcase for ivshmem
      3 *
      4 * Copyright (c) 2014 SUSE LINUX Products GmbH
      5 * Copyright (c) 2015 Red Hat, Inc.
      6 *
      7 * This work is licensed under the terms of the GNU GPL, version 2 or later.
      8 * See the COPYING file in the top-level directory.
      9 */
     10
     11#include "qemu/osdep.h"
     12#include <glib/gstdio.h>
     13#include "contrib/ivshmem-server/ivshmem-server.h"
     14#include "libqos/libqos-pc.h"
     15#include "libqos/libqos-spapr.h"
     16#include "libqos/libqtest.h"
     17#include "qemu-common.h"
     18
     19#define TMPSHMSIZE (1 << 20)
     20static char *tmpshm;
     21static void *tmpshmem;
     22static char *tmpdir;
     23static char *tmpserver;
     24
     25static void save_fn(QPCIDevice *dev, int devfn, void *data)
     26{
     27    QPCIDevice **pdev = (QPCIDevice **) data;
     28
     29    *pdev = dev;
     30}
     31
     32static QPCIDevice *get_device(QPCIBus *pcibus)
     33{
     34    QPCIDevice *dev;
     35
     36    dev = NULL;
     37    qpci_device_foreach(pcibus, 0x1af4, 0x1110, save_fn, &dev);
     38    g_assert(dev != NULL);
     39
     40    return dev;
     41}
     42
     43typedef struct _IVState {
     44    QOSState *qs;
     45    QPCIBar reg_bar, mem_bar;
     46    QPCIDevice *dev;
     47} IVState;
     48
     49enum Reg {
     50    INTRMASK = 0,
     51    INTRSTATUS = 4,
     52    IVPOSITION = 8,
     53    DOORBELL = 12,
     54};
     55
     56static const char* reg2str(enum Reg reg) {
     57    switch (reg) {
     58    case INTRMASK:
     59        return "IntrMask";
     60    case INTRSTATUS:
     61        return "IntrStatus";
     62    case IVPOSITION:
     63        return "IVPosition";
     64    case DOORBELL:
     65        return "DoorBell";
     66    default:
     67        return NULL;
     68    }
     69}
     70
     71static inline unsigned in_reg(IVState *s, enum Reg reg)
     72{
     73    const char *name = reg2str(reg);
     74    unsigned res;
     75
     76    res = qpci_io_readl(s->dev, s->reg_bar, reg);
     77    g_test_message("*%s -> %x", name, res);
     78
     79    return res;
     80}
     81
     82static inline void out_reg(IVState *s, enum Reg reg, unsigned v)
     83{
     84    const char *name = reg2str(reg);
     85
     86    g_test_message("%x -> *%s", v, name);
     87    qpci_io_writel(s->dev, s->reg_bar, reg, v);
     88}
     89
     90static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len)
     91{
     92    qpci_memread(s->dev, s->mem_bar, off, buf, len);
     93}
     94
     95static inline void write_mem(IVState *s, uint64_t off,
     96                             const void *buf, size_t len)
     97{
     98    qpci_memwrite(s->dev, s->mem_bar, off, buf, len);
     99}
    100
    101static void cleanup_vm(IVState *s)
    102{
    103    g_free(s->dev);
    104    qtest_shutdown(s->qs);
    105}
    106
    107static void setup_vm_cmd(IVState *s, const char *cmd, bool msix)
    108{
    109    uint64_t barsize;
    110    const char *arch = qtest_get_arch();
    111
    112    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
    113        s->qs = qtest_pc_boot(cmd);
    114    } else if (strcmp(arch, "ppc64") == 0) {
    115        s->qs = qtest_spapr_boot(cmd);
    116    } else {
    117        g_printerr("ivshmem-test tests are only available on x86 or ppc64\n");
    118        exit(EXIT_FAILURE);
    119    }
    120    s->dev = get_device(s->qs->pcibus);
    121
    122    s->reg_bar = qpci_iomap(s->dev, 0, &barsize);
    123    g_assert_cmpuint(barsize, ==, 256);
    124
    125    if (msix) {
    126        qpci_msix_enable(s->dev);
    127    }
    128
    129    s->mem_bar = qpci_iomap(s->dev, 2, &barsize);
    130    g_assert_cmpuint(barsize, ==, TMPSHMSIZE);
    131
    132    qpci_device_enable(s->dev);
    133}
    134
    135static void setup_vm(IVState *s)
    136{
    137    char *cmd = g_strdup_printf("-object memory-backend-file"
    138                                ",id=mb1,size=1M,share=on,mem-path=/dev/shm%s"
    139                                " -device ivshmem-plain,memdev=mb1", tmpshm);
    140
    141    setup_vm_cmd(s, cmd, false);
    142
    143    g_free(cmd);
    144}
    145
    146static void test_ivshmem_single(void)
    147{
    148    IVState state, *s;
    149    uint32_t data[1024];
    150    int i;
    151
    152    setup_vm(&state);
    153    s = &state;
    154
    155    /* initial state of readable registers */
    156    g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0);
    157    g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0);
    158    g_assert_cmpuint(in_reg(s, IVPOSITION), ==, 0);
    159
    160    /* trigger interrupt via registers */
    161    out_reg(s, INTRMASK, 0xffffffff);
    162    g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0xffffffff);
    163    out_reg(s, INTRSTATUS, 1);
    164    /* check interrupt status */
    165    g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1);
    166    /* reading clears */
    167    g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0);
    168    /* TODO intercept actual interrupt (needs qtest work) */
    169
    170    /* invalid register access */
    171    out_reg(s, IVPOSITION, 1);
    172    in_reg(s, DOORBELL);
    173
    174    /* ring the (non-functional) doorbell */
    175    out_reg(s, DOORBELL, 8 << 16);
    176
    177    /* write shared memory */
    178    for (i = 0; i < G_N_ELEMENTS(data); i++) {
    179        data[i] = i;
    180    }
    181    write_mem(s, 0, data, sizeof(data));
    182
    183    /* verify write */
    184    for (i = 0; i < G_N_ELEMENTS(data); i++) {
    185        g_assert_cmpuint(((uint32_t *)tmpshmem)[i], ==, i);
    186    }
    187
    188    /* read it back and verify read */
    189    memset(data, 0, sizeof(data));
    190    read_mem(s, 0, data, sizeof(data));
    191    for (i = 0; i < G_N_ELEMENTS(data); i++) {
    192        g_assert_cmpuint(data[i], ==, i);
    193    }
    194
    195    cleanup_vm(s);
    196}
    197
    198static void test_ivshmem_pair(void)
    199{
    200    IVState state1, state2, *s1, *s2;
    201    char *data;
    202    int i;
    203
    204    setup_vm(&state1);
    205    s1 = &state1;
    206    setup_vm(&state2);
    207    s2 = &state2;
    208
    209    data = g_malloc0(TMPSHMSIZE);
    210
    211    /* host write, guest 1 & 2 read */
    212    memset(tmpshmem, 0x42, TMPSHMSIZE);
    213    read_mem(s1, 0, data, TMPSHMSIZE);
    214    for (i = 0; i < TMPSHMSIZE; i++) {
    215        g_assert_cmpuint(data[i], ==, 0x42);
    216    }
    217    read_mem(s2, 0, data, TMPSHMSIZE);
    218    for (i = 0; i < TMPSHMSIZE; i++) {
    219        g_assert_cmpuint(data[i], ==, 0x42);
    220    }
    221
    222    /* guest 1 write, guest 2 read */
    223    memset(data, 0x43, TMPSHMSIZE);
    224    write_mem(s1, 0, data, TMPSHMSIZE);
    225    memset(data, 0, TMPSHMSIZE);
    226    read_mem(s2, 0, data, TMPSHMSIZE);
    227    for (i = 0; i < TMPSHMSIZE; i++) {
    228        g_assert_cmpuint(data[i], ==, 0x43);
    229    }
    230
    231    /* guest 2 write, guest 1 read */
    232    memset(data, 0x44, TMPSHMSIZE);
    233    write_mem(s2, 0, data, TMPSHMSIZE);
    234    memset(data, 0, TMPSHMSIZE);
    235    read_mem(s1, 0, data, TMPSHMSIZE);
    236    for (i = 0; i < TMPSHMSIZE; i++) {
    237        g_assert_cmpuint(data[i], ==, 0x44);
    238    }
    239
    240    cleanup_vm(s1);
    241    cleanup_vm(s2);
    242    g_free(data);
    243}
    244
    245typedef struct ServerThread {
    246    GThread *thread;
    247    IvshmemServer *server;
    248    int pipe[2]; /* to handle quit */
    249} ServerThread;
    250
    251static void *server_thread(void *data)
    252{
    253    ServerThread *t = data;
    254    IvshmemServer *server = t->server;
    255
    256    while (true) {
    257        fd_set fds;
    258        int maxfd, ret;
    259
    260        FD_ZERO(&fds);
    261        FD_SET(t->pipe[0], &fds);
    262        maxfd = t->pipe[0] + 1;
    263
    264        ivshmem_server_get_fds(server, &fds, &maxfd);
    265
    266        ret = select(maxfd, &fds, NULL, NULL, NULL);
    267
    268        if (ret < 0) {
    269            if (errno == EINTR) {
    270                continue;
    271            }
    272
    273            g_critical("select error: %s\n", strerror(errno));
    274            break;
    275        }
    276        if (ret == 0) {
    277            continue;
    278        }
    279
    280        if (FD_ISSET(t->pipe[0], &fds)) {
    281            break;
    282        }
    283
    284        if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) {
    285            g_critical("ivshmem_server_handle_fds() failed\n");
    286            break;
    287        }
    288    }
    289
    290    return NULL;
    291}
    292
    293static void setup_vm_with_server(IVState *s, int nvectors)
    294{
    295    char *cmd;
    296
    297    cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s "
    298                          "-device ivshmem-doorbell,chardev=chr0,vectors=%d",
    299                          tmpserver, nvectors);
    300
    301    setup_vm_cmd(s, cmd, true);
    302
    303    g_free(cmd);
    304}
    305
    306static void test_ivshmem_server(void)
    307{
    308    IVState state1, state2, *s1, *s2;
    309    ServerThread thread;
    310    IvshmemServer server;
    311    int ret, vm1, vm2;
    312    int nvectors = 2;
    313    guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
    314
    315    ret = ivshmem_server_init(&server, tmpserver, tmpshm, true,
    316                              TMPSHMSIZE, nvectors,
    317                              g_test_verbose());
    318    g_assert_cmpint(ret, ==, 0);
    319
    320    ret = ivshmem_server_start(&server);
    321    g_assert_cmpint(ret, ==, 0);
    322
    323    thread.server = &server;
    324    ret = pipe(thread.pipe);
    325    g_assert_cmpint(ret, ==, 0);
    326    thread.thread = g_thread_new("ivshmem-server", server_thread, &thread);
    327    g_assert(thread.thread != NULL);
    328
    329    setup_vm_with_server(&state1, nvectors);
    330    s1 = &state1;
    331    setup_vm_with_server(&state2, nvectors);
    332    s2 = &state2;
    333
    334    /* check got different VM ids */
    335    vm1 = in_reg(s1, IVPOSITION);
    336    vm2 = in_reg(s2, IVPOSITION);
    337    g_assert_cmpint(vm1, >=, 0);
    338    g_assert_cmpint(vm2, >=, 0);
    339    g_assert_cmpint(vm1, !=, vm2);
    340
    341    /* check number of MSI-X vectors */
    342    ret = qpci_msix_table_size(s1->dev);
    343    g_assert_cmpuint(ret, ==, nvectors);
    344
    345    /* TODO test behavior before MSI-X is enabled */
    346
    347    /* ping vm2 -> vm1 on vector 0 */
    348    ret = qpci_msix_pending(s1->dev, 0);
    349    g_assert_cmpuint(ret, ==, 0);
    350    out_reg(s2, DOORBELL, vm1 << 16);
    351    do {
    352        g_usleep(10000);
    353        ret = qpci_msix_pending(s1->dev, 0);
    354    } while (ret == 0 && g_get_monotonic_time() < end_time);
    355    g_assert_cmpuint(ret, !=, 0);
    356
    357    /* ping vm1 -> vm2 on vector 1 */
    358    ret = qpci_msix_pending(s2->dev, 1);
    359    g_assert_cmpuint(ret, ==, 0);
    360    out_reg(s1, DOORBELL, vm2 << 16 | 1);
    361    do {
    362        g_usleep(10000);
    363        ret = qpci_msix_pending(s2->dev, 1);
    364    } while (ret == 0 && g_get_monotonic_time() < end_time);
    365    g_assert_cmpuint(ret, !=, 0);
    366
    367    cleanup_vm(s2);
    368    cleanup_vm(s1);
    369
    370    if (qemu_write_full(thread.pipe[1], "q", 1) != 1) {
    371        g_error("qemu_write_full: %s", g_strerror(errno));
    372    }
    373
    374    g_thread_join(thread.thread);
    375
    376    ivshmem_server_close(&server);
    377    close(thread.pipe[1]);
    378    close(thread.pipe[0]);
    379}
    380
    381#define PCI_SLOT_HP             0x06
    382
    383static void test_ivshmem_hotplug(void)
    384{
    385    QTestState *qts;
    386    const char *arch = qtest_get_arch();
    387
    388    qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1");
    389
    390    qtest_qmp_device_add(qts, "ivshmem-plain", "iv1",
    391                         "{'addr': %s, 'memdev': 'mb1'}",
    392                         stringify(PCI_SLOT_HP));
    393    if (strcmp(arch, "ppc64") != 0) {
    394        qpci_unplug_acpi_device_test(qts, "iv1", PCI_SLOT_HP);
    395    }
    396
    397    qtest_quit(qts);
    398}
    399
    400static void test_ivshmem_memdev(void)
    401{
    402    IVState state;
    403
    404    /* just for the sake of checking memory-backend property */
    405    setup_vm_cmd(&state, "-object memory-backend-ram,size=1M,id=mb1"
    406                 " -device ivshmem-plain,memdev=mb1", false);
    407
    408    cleanup_vm(&state);
    409}
    410
    411static void cleanup(void)
    412{
    413    if (tmpshmem) {
    414        munmap(tmpshmem, TMPSHMSIZE);
    415        tmpshmem = NULL;
    416    }
    417
    418    if (tmpshm) {
    419        shm_unlink(tmpshm);
    420        g_free(tmpshm);
    421        tmpshm = NULL;
    422    }
    423
    424    if (tmpserver) {
    425        g_unlink(tmpserver);
    426        g_free(tmpserver);
    427        tmpserver = NULL;
    428    }
    429
    430    if (tmpdir) {
    431        g_rmdir(tmpdir);
    432        tmpdir = NULL;
    433    }
    434}
    435
    436static void abrt_handler(void *data)
    437{
    438    cleanup();
    439}
    440
    441static gchar *mktempshm(int size, int *fd)
    442{
    443    while (true) {
    444        gchar *name;
    445
    446        name = g_strdup_printf("/qtest-%u-%u", getpid(), g_test_rand_int());
    447        *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL,
    448                       S_IRWXU|S_IRWXG|S_IRWXO);
    449        if (*fd > 0) {
    450            g_assert(ftruncate(*fd, size) == 0);
    451            return name;
    452        }
    453
    454        g_free(name);
    455
    456        if (errno != EEXIST) {
    457            perror("shm_open");
    458            return NULL;
    459        }
    460    }
    461}
    462
    463int main(int argc, char **argv)
    464{
    465    int ret, fd;
    466    const char *arch = qtest_get_arch();
    467    gchar dir[] = "/tmp/ivshmem-test.XXXXXX";
    468
    469    g_test_init(&argc, &argv, NULL);
    470
    471    qtest_add_abrt_handler(abrt_handler, NULL);
    472    /* shm */
    473    tmpshm = mktempshm(TMPSHMSIZE, &fd);
    474    if (!tmpshm) {
    475        goto out;
    476    }
    477    tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    478    g_assert(tmpshmem != MAP_FAILED);
    479    /* server */
    480    if (mkdtemp(dir) == NULL) {
    481        g_error("mkdtemp: %s", g_strerror(errno));
    482    }
    483    tmpdir = dir;
    484    tmpserver = g_strconcat(tmpdir, "/server", NULL);
    485
    486    qtest_add_func("/ivshmem/single", test_ivshmem_single);
    487    qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug);
    488    qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev);
    489    if (g_test_slow()) {
    490        qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
    491        if (strcmp(arch, "ppc64") != 0) {
    492            qtest_add_func("/ivshmem/server", test_ivshmem_server);
    493        }
    494    }
    495
    496out:
    497    ret = g_test_run();
    498    cleanup();
    499    return ret;
    500}