qemu/tests/qtest/nvme-test.c
<<
>>
Prefs
   1/*
   2 * QTest testcase for NVMe
   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/module.h"
  12#include "qemu/units.h"
  13#include "libqos/libqtest.h"
  14#include "libqos/qgraph.h"
  15#include "libqos/pci.h"
  16#include "include/block/nvme.h"
  17
  18typedef struct QNvme QNvme;
  19
  20struct QNvme {
  21    QOSGraphObject obj;
  22    QPCIDevice dev;
  23};
  24
  25static void *nvme_get_driver(void *obj, const char *interface)
  26{
  27    QNvme *nvme = obj;
  28
  29    if (!g_strcmp0(interface, "pci-device")) {
  30        return &nvme->dev;
  31    }
  32
  33    fprintf(stderr, "%s not present in nvme\n", interface);
  34    g_assert_not_reached();
  35}
  36
  37static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
  38{
  39    QNvme *nvme = g_new0(QNvme, 1);
  40    QPCIBus *bus = pci_bus;
  41
  42    qpci_device_init(&nvme->dev, bus, addr);
  43    nvme->obj.get_driver = nvme_get_driver;
  44
  45    return &nvme->obj;
  46}
  47
  48/* This used to cause a NULL pointer dereference.  */
  49static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc)
  50{
  51    const int cmb_bar_size = 2 * MiB;
  52    QNvme *nvme = obj;
  53    QPCIDevice *pdev = &nvme->dev;
  54    QPCIBar bar;
  55
  56    qpci_device_enable(pdev);
  57    bar = qpci_iomap(pdev, 2, NULL);
  58
  59    qpci_io_writel(pdev, bar, 0, 0xccbbaa99);
  60    g_assert_cmpint(qpci_io_readb(pdev, bar, 0), ==, 0x99);
  61    g_assert_cmpint(qpci_io_readw(pdev, bar, 0), ==, 0xaa99);
  62
  63    /* Test partially out-of-bounds accesses.  */
  64    qpci_io_writel(pdev, bar, cmb_bar_size - 1, 0x44332211);
  65    g_assert_cmpint(qpci_io_readb(pdev, bar, cmb_bar_size - 1), ==, 0x11);
  66    g_assert_cmpint(qpci_io_readw(pdev, bar, cmb_bar_size - 1), !=, 0x2211);
  67    g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211);
  68}
  69
  70static void nvmetest_reg_read_test(void *obj, void *data, QGuestAllocator *alloc)
  71{
  72    QNvme *nvme = obj;
  73    QPCIDevice *pdev = &nvme->dev;
  74    QPCIBar bar;
  75    uint32_t cap_lo, cap_hi;
  76    uint64_t cap;
  77
  78    qpci_device_enable(pdev);
  79    bar = qpci_iomap(pdev, 0, NULL);
  80
  81    cap_lo = qpci_io_readl(pdev, bar, 0x0);
  82    g_assert_cmpint(NVME_CAP_MQES(cap_lo), ==, 0x7ff);
  83
  84    cap_hi = qpci_io_readl(pdev, bar, 0x4);
  85    g_assert_cmpint(NVME_CAP_MPSMAX((uint64_t)cap_hi << 32), ==, 0x4);
  86
  87    cap = qpci_io_readq(pdev, bar, 0x0);
  88    g_assert_cmpint(NVME_CAP_MQES(cap), ==, 0x7ff);
  89    g_assert_cmpint(NVME_CAP_MPSMAX(cap), ==, 0x4);
  90
  91    qpci_iounmap(pdev, bar);
  92}
  93
  94static void nvmetest_pmr_reg_test(void *obj, void *data, QGuestAllocator *alloc)
  95{
  96    QNvme *nvme = obj;
  97    QPCIDevice *pdev = &nvme->dev;
  98    QPCIBar pmr_bar, nvme_bar;
  99    uint32_t pmrcap, pmrsts;
 100
 101    qpci_device_enable(pdev);
 102    pmr_bar = qpci_iomap(pdev, 4, NULL);
 103
 104    /* Without Enabling PMRCTL check bar enablemet */
 105    qpci_io_writel(pdev, pmr_bar, 0, 0xccbbaa99);
 106    g_assert_cmpint(qpci_io_readb(pdev, pmr_bar, 0), !=, 0x99);
 107    g_assert_cmpint(qpci_io_readw(pdev, pmr_bar, 0), !=, 0xaa99);
 108
 109    /* Map NVMe Bar Register to Enable the Mem Region */
 110    nvme_bar = qpci_iomap(pdev, 0, NULL);
 111
 112    pmrcap = qpci_io_readl(pdev, nvme_bar, 0xe00);
 113    g_assert_cmpint(NVME_PMRCAP_RDS(pmrcap), ==, 0x1);
 114    g_assert_cmpint(NVME_PMRCAP_WDS(pmrcap), ==, 0x1);
 115    g_assert_cmpint(NVME_PMRCAP_BIR(pmrcap), ==, 0x4);
 116    g_assert_cmpint(NVME_PMRCAP_PMRWBM(pmrcap), ==, 0x2);
 117    g_assert_cmpint(NVME_PMRCAP_CMSS(pmrcap), ==, 0x1);
 118
 119    /* Enable PMRCTRL */
 120    qpci_io_writel(pdev, nvme_bar, 0xe04, 0x1);
 121
 122    qpci_io_writel(pdev, pmr_bar, 0, 0x44332211);
 123    g_assert_cmpint(qpci_io_readb(pdev, pmr_bar, 0), ==, 0x11);
 124    g_assert_cmpint(qpci_io_readw(pdev, pmr_bar, 0), ==, 0x2211);
 125    g_assert_cmpint(qpci_io_readl(pdev, pmr_bar, 0), ==, 0x44332211);
 126
 127    pmrsts = qpci_io_readl(pdev, nvme_bar, 0xe08);
 128    g_assert_cmpint(NVME_PMRSTS_NRDY(pmrsts), ==, 0x0);
 129
 130    /* Disable PMRCTRL */
 131    qpci_io_writel(pdev, nvme_bar, 0xe04, 0x0);
 132
 133    qpci_io_writel(pdev, pmr_bar, 0, 0x88776655);
 134    g_assert_cmpint(qpci_io_readb(pdev, pmr_bar, 0), !=, 0x55);
 135    g_assert_cmpint(qpci_io_readw(pdev, pmr_bar, 0), !=, 0x6655);
 136    g_assert_cmpint(qpci_io_readl(pdev, pmr_bar, 0), !=, 0x88776655);
 137
 138    pmrsts = qpci_io_readl(pdev, nvme_bar, 0xe08);
 139    g_assert_cmpint(NVME_PMRSTS_NRDY(pmrsts), ==, 0x1);
 140
 141    qpci_iounmap(pdev, nvme_bar);
 142    qpci_iounmap(pdev, pmr_bar);
 143}
 144
 145static void nvme_register_nodes(void)
 146{
 147    QOSGraphEdgeOptions opts = {
 148        .extra_device_opts = "addr=04.0,drive=drv0,serial=foo",
 149        .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,"
 150                           "file.read-zeroes=on,format=raw "
 151                           "-object memory-backend-ram,id=pmr0,"
 152                           "share=on,size=8",
 153    };
 154
 155    add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
 156
 157    qos_node_create_driver("nvme", nvme_create);
 158    qos_node_consumes("nvme", "pci-bus", &opts);
 159    qos_node_produces("nvme", "pci-device");
 160
 161    qos_add_test("oob-cmb-access", "nvme", nvmetest_oob_cmb_test, &(QOSGraphTestOptions) {
 162        .edge.extra_device_opts = "cmb_size_mb=2"
 163    });
 164
 165    qos_add_test("pmr-test-access", "nvme", nvmetest_pmr_reg_test,
 166                 &(QOSGraphTestOptions) {
 167        .edge.extra_device_opts = "pmrdev=pmr0"
 168    });
 169
 170    qos_add_test("reg-read", "nvme", nvmetest_reg_read_test, NULL);
 171}
 172
 173libqos_init(nvme_register_nodes);
 174