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

virtio-net-test.c (9641B)


      1/*
      2 * QTest testcase for VirtIO NIC
      3 *
      4 * Copyright (c) 2014 SUSE LINUX Products GmbH
      5 *
      6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
      7 * See the COPYING file in the top-level directory.
      8 */
      9
     10#include "qemu/osdep.h"
     11#include "qemu-common.h"
     12#include "libqtest-single.h"
     13#include "qemu/iov.h"
     14#include "qemu/module.h"
     15#include "qapi/qmp/qdict.h"
     16#include "hw/virtio/virtio-net.h"
     17#include "libqos/qgraph.h"
     18#include "libqos/virtio-net.h"
     19
     20#ifndef ETH_P_RARP
     21#define ETH_P_RARP 0x8035
     22#endif
     23
     24#define PCI_SLOT_HP             0x06
     25#define PCI_SLOT                0x04
     26
     27#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
     28#define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf)
     29
     30#ifndef _WIN32
     31
     32static void rx_test(QVirtioDevice *dev,
     33                    QGuestAllocator *alloc, QVirtQueue *vq,
     34                    int socket)
     35{
     36    QTestState *qts = global_qtest;
     37    uint64_t req_addr;
     38    uint32_t free_head;
     39    char test[] = "TEST";
     40    char buffer[64];
     41    int len = htonl(sizeof(test));
     42    struct iovec iov[] = {
     43        {
     44            .iov_base = &len,
     45            .iov_len = sizeof(len),
     46        }, {
     47            .iov_base = test,
     48            .iov_len = sizeof(test),
     49        },
     50    };
     51    int ret;
     52
     53    req_addr = guest_alloc(alloc, 64);
     54
     55    free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false);
     56    qvirtqueue_kick(qts, dev, vq, free_head);
     57
     58    ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test));
     59    g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len));
     60
     61    qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
     62                           QVIRTIO_NET_TIMEOUT_US);
     63    memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test));
     64    g_assert_cmpstr(buffer, ==, "TEST");
     65
     66    guest_free(alloc, req_addr);
     67}
     68
     69static void tx_test(QVirtioDevice *dev,
     70                    QGuestAllocator *alloc, QVirtQueue *vq,
     71                    int socket)
     72{
     73    QTestState *qts = global_qtest;
     74    uint64_t req_addr;
     75    uint32_t free_head;
     76    uint32_t len;
     77    char buffer[64];
     78    int ret;
     79
     80    req_addr = guest_alloc(alloc, 64);
     81    memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4);
     82
     83    free_head = qvirtqueue_add(qts, vq, req_addr, 64, false, false);
     84    qvirtqueue_kick(qts, dev, vq, free_head);
     85
     86    qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
     87                           QVIRTIO_NET_TIMEOUT_US);
     88    guest_free(alloc, req_addr);
     89
     90    ret = qemu_recv(socket, &len, sizeof(len), 0);
     91    g_assert_cmpint(ret, ==, sizeof(len));
     92    len = ntohl(len);
     93
     94    ret = qemu_recv(socket, buffer, len, 0);
     95    g_assert_cmpstr(buffer, ==, "TEST");
     96}
     97
     98static void rx_stop_cont_test(QVirtioDevice *dev,
     99                              QGuestAllocator *alloc, QVirtQueue *vq,
    100                              int socket)
    101{
    102    QTestState *qts = global_qtest;
    103    uint64_t req_addr;
    104    uint32_t free_head;
    105    char test[] = "TEST";
    106    char buffer[64];
    107    int len = htonl(sizeof(test));
    108    QDict *rsp;
    109    struct iovec iov[] = {
    110        {
    111            .iov_base = &len,
    112            .iov_len = sizeof(len),
    113        }, {
    114            .iov_base = test,
    115            .iov_len = sizeof(test),
    116        },
    117    };
    118    int ret;
    119
    120    req_addr = guest_alloc(alloc, 64);
    121
    122    free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false);
    123    qvirtqueue_kick(qts, dev, vq, free_head);
    124
    125    rsp = qmp("{ 'execute' : 'stop'}");
    126    qobject_unref(rsp);
    127
    128    ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test));
    129    g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len));
    130
    131    /* We could check the status, but this command is more importantly to
    132     * ensure the packet data gets queued in QEMU, before we do 'cont'.
    133     */
    134    rsp = qmp("{ 'execute' : 'query-status'}");
    135    qobject_unref(rsp);
    136    rsp = qmp("{ 'execute' : 'cont'}");
    137    qobject_unref(rsp);
    138
    139    qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    140                           QVIRTIO_NET_TIMEOUT_US);
    141    memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test));
    142    g_assert_cmpstr(buffer, ==, "TEST");
    143
    144    guest_free(alloc, req_addr);
    145}
    146
    147static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc)
    148{
    149    QVirtioNet *net_if = obj;
    150    QVirtioDevice *dev = net_if->vdev;
    151    QVirtQueue *rx = net_if->queues[0];
    152    QVirtQueue *tx = net_if->queues[1];
    153    int *sv = data;
    154
    155    rx_test(dev, t_alloc, rx, sv[0]);
    156    tx_test(dev, t_alloc, tx, sv[0]);
    157}
    158
    159static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc)
    160{
    161    QVirtioNet *net_if = obj;
    162    QVirtioDevice *dev = net_if->vdev;
    163    QVirtQueue *rx = net_if->queues[0];
    164    int *sv = data;
    165
    166    rx_stop_cont_test(dev, t_alloc, rx, sv[0]);
    167}
    168
    169#endif
    170
    171static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
    172{
    173    QVirtioPCIDevice *dev = obj;
    174    QTestState *qts = dev->pdev->bus->qts;
    175    const char *arch = qtest_get_arch();
    176
    177    qtest_qmp_device_add(qts, "virtio-net-pci", "net1",
    178                         "{'addr': %s}", stringify(PCI_SLOT_HP));
    179
    180    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
    181        qpci_unplug_acpi_device_test(qts, "net1", PCI_SLOT_HP);
    182    }
    183}
    184
    185static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc)
    186{
    187    int *sv = data;
    188    char buffer[60];
    189    int len;
    190    QDict *rsp;
    191    int ret;
    192    uint16_t *proto = (uint16_t *)&buffer[12];
    193    size_t total_received = 0;
    194    uint64_t start, now, last_rxt, deadline;
    195
    196    /* Send a set of packets over a few second period */
    197    rsp = qmp("{ 'execute' : 'announce-self', "
    198                  " 'arguments': {"
    199                      " 'initial': 20, 'max': 100,"
    200                      " 'rounds': 300, 'step': 10, 'id': 'bob' } }");
    201    assert(!qdict_haskey(rsp, "error"));
    202    qobject_unref(rsp);
    203
    204    /* Catch the first packet and make sure it's a RARP */
    205    ret = qemu_recv(sv[0], &len, sizeof(len), 0);
    206    g_assert_cmpint(ret, ==,  sizeof(len));
    207    len = ntohl(len);
    208
    209    ret = qemu_recv(sv[0], buffer, len, 0);
    210    g_assert_cmpint(*proto, ==, htons(ETH_P_RARP));
    211
    212    /*
    213     * Stop the announcment by settings rounds to 0 on the
    214     * existing timer.
    215     */
    216    rsp = qmp("{ 'execute' : 'announce-self', "
    217                  " 'arguments': {"
    218                      " 'initial': 20, 'max': 100,"
    219                      " 'rounds': 0, 'step': 10, 'id': 'bob' } }");
    220    assert(!qdict_haskey(rsp, "error"));
    221    qobject_unref(rsp);
    222
    223    /* Now make sure the packets stop */
    224
    225    /* Times are in us */
    226    start = g_get_monotonic_time();
    227    /* 30 packets, max gap 100ms, * 4 for wiggle */
    228    deadline = start + 1000 * (100 * 30 * 4);
    229    last_rxt = start;
    230
    231    while (true) {
    232        int saved_err;
    233        ret = qemu_recv(sv[0], buffer, 60, MSG_DONTWAIT);
    234        saved_err = errno;
    235        now = g_get_monotonic_time();
    236        g_assert_cmpint(now, <, deadline);
    237
    238        if (ret >= 0) {
    239            if (ret) {
    240                last_rxt = now;
    241            }
    242            total_received += ret;
    243
    244            /* Check it's not spewing loads */
    245            g_assert_cmpint(total_received, <, 60 * 30 * 2);
    246        } else {
    247            g_assert_cmpint(saved_err, ==, EAGAIN);
    248
    249            /* 400ms, i.e. 4 worst case gaps */
    250            if ((now - last_rxt) > (1000 * 100 * 4)) {
    251                /* Nothings arrived for a while - must have stopped */
    252                break;
    253            };
    254
    255            /* 100ms */
    256            g_usleep(1000 * 100);
    257        }
    258    };
    259}
    260
    261static void virtio_net_test_cleanup(void *sockets)
    262{
    263    int *sv = sockets;
    264
    265    close(sv[0]);
    266    qos_invalidate_command_line();
    267    close(sv[1]);
    268    g_free(sv);
    269}
    270
    271static void *virtio_net_test_setup(GString *cmd_line, void *arg)
    272{
    273    int ret;
    274    int *sv = g_new(int, 2);
    275
    276    ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
    277    g_assert_cmpint(ret, !=, -1);
    278
    279    g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]);
    280
    281    g_test_queue_destroy(virtio_net_test_cleanup, sv);
    282    return sv;
    283}
    284
    285static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc)
    286{
    287    QVirtioNet *dev = obj;
    288    QVirtQueue *vq = dev->queues[1];
    289    uint64_t req_addr;
    290    uint32_t free_head;
    291    size_t alloc_size = (size_t)data / 64;
    292    QTestState *qts = global_qtest;
    293    int i;
    294
    295    /* Bypass the limitation by pointing several descriptors to a single
    296     * smaller area */
    297    req_addr = guest_alloc(t_alloc, alloc_size);
    298    free_head = qvirtqueue_add(qts, vq, req_addr, alloc_size, false, true);
    299
    300    for (i = 0; i < 64; i++) {
    301        qvirtqueue_add(qts, vq, req_addr, alloc_size, false, i != 63);
    302    }
    303    qvirtqueue_kick(qts, dev->vdev, vq, free_head);
    304
    305    qvirtio_wait_used_elem(qts, dev->vdev, vq, free_head, NULL,
    306                           QVIRTIO_NET_TIMEOUT_US);
    307    guest_free(t_alloc, req_addr);
    308}
    309
    310static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg)
    311{
    312    g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 ");
    313    return arg;
    314}
    315
    316static void register_virtio_net_test(void)
    317{
    318    QOSGraphTestOptions opts = {
    319        .before = virtio_net_test_setup,
    320    };
    321
    322    qos_add_test("hotplug", "virtio-pci", hotplug, &opts);
    323#ifndef _WIN32
    324    qos_add_test("basic", "virtio-net", send_recv_test, &opts);
    325    qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts);
    326#endif
    327    qos_add_test("announce-self", "virtio-net", announce_self, &opts);
    328
    329    /* These tests do not need a loopback backend.  */
    330    opts.before = virtio_net_test_setup_nosocket;
    331    opts.arg = (gpointer)UINT_MAX;
    332    qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts);
    333    opts.arg = (gpointer)NET_BUFSIZE;
    334    qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts);
    335}
    336
    337libqos_init(register_virtio_net_test);