11e8a1faeSThomas Huth /*
21e8a1faeSThomas Huth * QTest testcase for NVMe
31e8a1faeSThomas Huth *
41e8a1faeSThomas Huth * Copyright (c) 2014 SUSE LINUX Products GmbH
51e8a1faeSThomas Huth *
61e8a1faeSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later.
71e8a1faeSThomas Huth * See the COPYING file in the top-level directory.
81e8a1faeSThomas Huth */
91e8a1faeSThomas Huth
101e8a1faeSThomas Huth #include "qemu/osdep.h"
111e8a1faeSThomas Huth #include "qemu/module.h"
121e8a1faeSThomas Huth #include "qemu/units.h"
13907b5105SMarc-André Lureau #include "libqtest.h"
141e8a1faeSThomas Huth #include "libqos/qgraph.h"
151e8a1faeSThomas Huth #include "libqos/pci.h"
16*8b4d80bbSPhilippe Mathieu-Daudé #include "block/nvme.h"
171e8a1faeSThomas Huth
181e8a1faeSThomas Huth typedef struct QNvme QNvme;
191e8a1faeSThomas Huth
201e8a1faeSThomas Huth struct QNvme {
211e8a1faeSThomas Huth QOSGraphObject obj;
221e8a1faeSThomas Huth QPCIDevice dev;
231e8a1faeSThomas Huth };
241e8a1faeSThomas Huth
nvme_get_driver(void * obj,const char * interface)251e8a1faeSThomas Huth static void *nvme_get_driver(void *obj, const char *interface)
261e8a1faeSThomas Huth {
271e8a1faeSThomas Huth QNvme *nvme = obj;
281e8a1faeSThomas Huth
291e8a1faeSThomas Huth if (!g_strcmp0(interface, "pci-device")) {
301e8a1faeSThomas Huth return &nvme->dev;
311e8a1faeSThomas Huth }
321e8a1faeSThomas Huth
331e8a1faeSThomas Huth fprintf(stderr, "%s not present in nvme\n", interface);
341e8a1faeSThomas Huth g_assert_not_reached();
351e8a1faeSThomas Huth }
361e8a1faeSThomas Huth
nvme_create(void * pci_bus,QGuestAllocator * alloc,void * addr)371e8a1faeSThomas Huth static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
381e8a1faeSThomas Huth {
391e8a1faeSThomas Huth QNvme *nvme = g_new0(QNvme, 1);
401e8a1faeSThomas Huth QPCIBus *bus = pci_bus;
411e8a1faeSThomas Huth
421e8a1faeSThomas Huth qpci_device_init(&nvme->dev, bus, addr);
431e8a1faeSThomas Huth nvme->obj.get_driver = nvme_get_driver;
441e8a1faeSThomas Huth
451e8a1faeSThomas Huth return &nvme->obj;
461e8a1faeSThomas Huth }
471e8a1faeSThomas Huth
481e8a1faeSThomas Huth /* This used to cause a NULL pointer dereference. */
nvmetest_oob_cmb_test(void * obj,void * data,QGuestAllocator * alloc)491e8a1faeSThomas Huth static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc)
501e8a1faeSThomas Huth {
511e8a1faeSThomas Huth const int cmb_bar_size = 2 * MiB;
521e8a1faeSThomas Huth QNvme *nvme = obj;
531e8a1faeSThomas Huth QPCIDevice *pdev = &nvme->dev;
541e8a1faeSThomas Huth QPCIBar bar;
551e8a1faeSThomas Huth
561e8a1faeSThomas Huth qpci_device_enable(pdev);
571e8a1faeSThomas Huth bar = qpci_iomap(pdev, 2, NULL);
581e8a1faeSThomas Huth
591e8a1faeSThomas Huth qpci_io_writel(pdev, bar, 0, 0xccbbaa99);
601e8a1faeSThomas Huth g_assert_cmpint(qpci_io_readb(pdev, bar, 0), ==, 0x99);
611e8a1faeSThomas Huth g_assert_cmpint(qpci_io_readw(pdev, bar, 0), ==, 0xaa99);
621e8a1faeSThomas Huth
631e8a1faeSThomas Huth /* Test partially out-of-bounds accesses. */
641e8a1faeSThomas Huth qpci_io_writel(pdev, bar, cmb_bar_size - 1, 0x44332211);
651e8a1faeSThomas Huth g_assert_cmpint(qpci_io_readb(pdev, bar, cmb_bar_size - 1), ==, 0x11);
661e8a1faeSThomas Huth g_assert_cmpint(qpci_io_readw(pdev, bar, cmb_bar_size - 1), !=, 0x2211);
671e8a1faeSThomas Huth g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211);
681e8a1faeSThomas Huth }
691e8a1faeSThomas Huth
nvmetest_reg_read_test(void * obj,void * data,QGuestAllocator * alloc)709631a8abSKlaus Jensen static void nvmetest_reg_read_test(void *obj, void *data, QGuestAllocator *alloc)
719631a8abSKlaus Jensen {
729631a8abSKlaus Jensen QNvme *nvme = obj;
739631a8abSKlaus Jensen QPCIDevice *pdev = &nvme->dev;
749631a8abSKlaus Jensen QPCIBar bar;
759631a8abSKlaus Jensen uint32_t cap_lo, cap_hi;
769631a8abSKlaus Jensen uint64_t cap;
779631a8abSKlaus Jensen
789631a8abSKlaus Jensen qpci_device_enable(pdev);
799631a8abSKlaus Jensen bar = qpci_iomap(pdev, 0, NULL);
809631a8abSKlaus Jensen
819631a8abSKlaus Jensen cap_lo = qpci_io_readl(pdev, bar, 0x0);
829631a8abSKlaus Jensen g_assert_cmpint(NVME_CAP_MQES(cap_lo), ==, 0x7ff);
839631a8abSKlaus Jensen
849631a8abSKlaus Jensen cap_hi = qpci_io_readl(pdev, bar, 0x4);
859631a8abSKlaus Jensen g_assert_cmpint(NVME_CAP_MPSMAX((uint64_t)cap_hi << 32), ==, 0x4);
869631a8abSKlaus Jensen
879631a8abSKlaus Jensen cap = qpci_io_readq(pdev, bar, 0x0);
889631a8abSKlaus Jensen g_assert_cmpint(NVME_CAP_MQES(cap), ==, 0x7ff);
899631a8abSKlaus Jensen g_assert_cmpint(NVME_CAP_MPSMAX(cap), ==, 0x4);
909631a8abSKlaus Jensen
919631a8abSKlaus Jensen qpci_iounmap(pdev, bar);
929631a8abSKlaus Jensen }
939631a8abSKlaus Jensen
nvmetest_pmr_reg_test(void * obj,void * data,QGuestAllocator * alloc)9451e90178SGollu Appalanaidu static void nvmetest_pmr_reg_test(void *obj, void *data, QGuestAllocator *alloc)
9551e90178SGollu Appalanaidu {
9651e90178SGollu Appalanaidu QNvme *nvme = obj;
9751e90178SGollu Appalanaidu QPCIDevice *pdev = &nvme->dev;
9851e90178SGollu Appalanaidu QPCIBar pmr_bar, nvme_bar;
9951e90178SGollu Appalanaidu uint32_t pmrcap, pmrsts;
10051e90178SGollu Appalanaidu
10151e90178SGollu Appalanaidu qpci_device_enable(pdev);
10251e90178SGollu Appalanaidu pmr_bar = qpci_iomap(pdev, 4, NULL);
10351e90178SGollu Appalanaidu
10451e90178SGollu Appalanaidu /* Without Enabling PMRCTL check bar enablemet */
10551e90178SGollu Appalanaidu qpci_io_writel(pdev, pmr_bar, 0, 0xccbbaa99);
10651e90178SGollu Appalanaidu g_assert_cmpint(qpci_io_readb(pdev, pmr_bar, 0), !=, 0x99);
10751e90178SGollu Appalanaidu g_assert_cmpint(qpci_io_readw(pdev, pmr_bar, 0), !=, 0xaa99);
10851e90178SGollu Appalanaidu
10951e90178SGollu Appalanaidu /* Map NVMe Bar Register to Enable the Mem Region */
11051e90178SGollu Appalanaidu nvme_bar = qpci_iomap(pdev, 0, NULL);
11151e90178SGollu Appalanaidu
11251e90178SGollu Appalanaidu pmrcap = qpci_io_readl(pdev, nvme_bar, 0xe00);
11351e90178SGollu Appalanaidu g_assert_cmpint(NVME_PMRCAP_RDS(pmrcap), ==, 0x1);
11451e90178SGollu Appalanaidu g_assert_cmpint(NVME_PMRCAP_WDS(pmrcap), ==, 0x1);
11551e90178SGollu Appalanaidu g_assert_cmpint(NVME_PMRCAP_BIR(pmrcap), ==, 0x4);
11651e90178SGollu Appalanaidu g_assert_cmpint(NVME_PMRCAP_PMRWBM(pmrcap), ==, 0x2);
11751e90178SGollu Appalanaidu g_assert_cmpint(NVME_PMRCAP_CMSS(pmrcap), ==, 0x1);
11851e90178SGollu Appalanaidu
11951e90178SGollu Appalanaidu /* Enable PMRCTRL */
12051e90178SGollu Appalanaidu qpci_io_writel(pdev, nvme_bar, 0xe04, 0x1);
12151e90178SGollu Appalanaidu
12251e90178SGollu Appalanaidu qpci_io_writel(pdev, pmr_bar, 0, 0x44332211);
12351e90178SGollu Appalanaidu g_assert_cmpint(qpci_io_readb(pdev, pmr_bar, 0), ==, 0x11);
12451e90178SGollu Appalanaidu g_assert_cmpint(qpci_io_readw(pdev, pmr_bar, 0), ==, 0x2211);
12551e90178SGollu Appalanaidu g_assert_cmpint(qpci_io_readl(pdev, pmr_bar, 0), ==, 0x44332211);
12651e90178SGollu Appalanaidu
12751e90178SGollu Appalanaidu pmrsts = qpci_io_readl(pdev, nvme_bar, 0xe08);
12851e90178SGollu Appalanaidu g_assert_cmpint(NVME_PMRSTS_NRDY(pmrsts), ==, 0x0);
12951e90178SGollu Appalanaidu
13051e90178SGollu Appalanaidu /* Disable PMRCTRL */
13151e90178SGollu Appalanaidu qpci_io_writel(pdev, nvme_bar, 0xe04, 0x0);
13251e90178SGollu Appalanaidu
13351e90178SGollu Appalanaidu qpci_io_writel(pdev, pmr_bar, 0, 0x88776655);
13451e90178SGollu Appalanaidu g_assert_cmpint(qpci_io_readb(pdev, pmr_bar, 0), !=, 0x55);
13551e90178SGollu Appalanaidu g_assert_cmpint(qpci_io_readw(pdev, pmr_bar, 0), !=, 0x6655);
13651e90178SGollu Appalanaidu g_assert_cmpint(qpci_io_readl(pdev, pmr_bar, 0), !=, 0x88776655);
13751e90178SGollu Appalanaidu
13851e90178SGollu Appalanaidu pmrsts = qpci_io_readl(pdev, nvme_bar, 0xe08);
13951e90178SGollu Appalanaidu g_assert_cmpint(NVME_PMRSTS_NRDY(pmrsts), ==, 0x1);
14051e90178SGollu Appalanaidu
14151e90178SGollu Appalanaidu qpci_iounmap(pdev, nvme_bar);
14251e90178SGollu Appalanaidu qpci_iounmap(pdev, pmr_bar);
14351e90178SGollu Appalanaidu }
14451e90178SGollu Appalanaidu
nvme_register_nodes(void)1451e8a1faeSThomas Huth static void nvme_register_nodes(void)
1461e8a1faeSThomas Huth {
1471e8a1faeSThomas Huth QOSGraphEdgeOptions opts = {
1481e8a1faeSThomas Huth .extra_device_opts = "addr=04.0,drive=drv0,serial=foo",
1491e8a1faeSThomas Huth .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,"
15051e90178SGollu Appalanaidu "file.read-zeroes=on,format=raw "
15151e90178SGollu Appalanaidu "-object memory-backend-ram,id=pmr0,"
15251e90178SGollu Appalanaidu "share=on,size=8",
1531e8a1faeSThomas Huth };
1541e8a1faeSThomas Huth
1551e8a1faeSThomas Huth add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
1561e8a1faeSThomas Huth
1571e8a1faeSThomas Huth qos_node_create_driver("nvme", nvme_create);
1581e8a1faeSThomas Huth qos_node_consumes("nvme", "pci-bus", &opts);
1591e8a1faeSThomas Huth qos_node_produces("nvme", "pci-device");
1601e8a1faeSThomas Huth
1611e8a1faeSThomas Huth qos_add_test("oob-cmb-access", "nvme", nvmetest_oob_cmb_test, &(QOSGraphTestOptions) {
1621e8a1faeSThomas Huth .edge.extra_device_opts = "cmb_size_mb=2"
1631e8a1faeSThomas Huth });
16451e90178SGollu Appalanaidu
16551e90178SGollu Appalanaidu qos_add_test("pmr-test-access", "nvme", nvmetest_pmr_reg_test,
16651e90178SGollu Appalanaidu &(QOSGraphTestOptions) {
16751e90178SGollu Appalanaidu .edge.extra_device_opts = "pmrdev=pmr0"
16851e90178SGollu Appalanaidu });
1699631a8abSKlaus Jensen
1709631a8abSKlaus Jensen qos_add_test("reg-read", "nvme", nvmetest_reg_read_test, NULL);
1711e8a1faeSThomas Huth }
1721e8a1faeSThomas Huth
1731e8a1faeSThomas Huth libqos_init(nvme_register_nodes);
174