Re: [PATCH v2 2/2] tests/qtest/nvme-test: add boot partition read test
On Jun 1 20:07, Gollu Appalanaidu wrote: Add a test case for reading an NVMe Boot Partition without enabling the controller. Signed-off-by: Gollu Appalanaidu --- tests/qtest/nvme-test.c | 118 +++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/tests/qtest/nvme-test.c b/tests/qtest/nvme-test.c index d32c953a38..39d2e26f76 100644 --- a/tests/qtest/nvme-test.c +++ b/tests/qtest/nvme-test.c @@ -13,6 +13,19 @@ #include "libqos/libqtest.h" #include "libqos/qgraph.h" #include "libqos/pci.h" +#include "libqos/pci-pc.h" +#include "libqos/malloc-pc.h" +#include "libqos/malloc.h" +#include "libqos/libqos.h" +#include "include/block/nvme.h" +#include "include/hw/pci/pci.h" + +#define NVME_BPINFO_BPSZ_UNITS (128 * KiB) +#define NVME_BRS_BPSZ_UNITS (4 * KiB) +#define NVME_BRS_READ_MAX_TIME 100 +#define TEST_IMAGE_SIZE (2 * 128 * KiB) + +static char *t_path; typedef struct QNvme QNvme; @@ -44,6 +57,13 @@ static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr) return >obj; } +static void drive_destroy(void *path) +{ +unlink(path); +g_free(path); +qos_invalidate_command_line(); +} + /* This used to cause a NULL pointer dereference. */ static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc) { @@ -66,12 +86,100 @@ static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211); } +static void nvmetest_bp_read_test(void *obj, void *data, QGuestAllocator *alloc) +{ +uint16_t test_size = 32; +size_t bp_test_len = test_size * NVME_BRS_BPSZ_UNITS; +uint8_t *read_buf = g_malloc(bp_test_len); +uint8_t *cmp_buf = g_malloc(bp_test_len); +QNvme *nvme = obj; +QPCIDevice *pdev = >dev; +QPCIBar nvme_bar; +uint8_t brs = 0; +uint64_t sleep_time = 0; +uintptr_t guest_buf; +uint64_t buf_addr; + +memset(cmp_buf, 0x42, bp_test_len); This one byte pattern is too simple and won't catch a lot of possible bugs. The test case should use generate_pattern() (see tests/qtest/libqos/libqos.h). + +guest_buf = guest_alloc(alloc, bp_test_len); +buf_addr = cpu_to_le64(guest_buf); + +qpci_device_enable(pdev); +nvme_bar = qpci_iomap(pdev, 0, NULL); + +/* BPINFO */ +uint32_t bpinfo = qpci_io_readl(pdev, nvme_bar, 0x40); +uint16_t single_bp_size = bpinfo & BPINFO_BPSZ_MASK; +uint8_t active_bpid = bpinfo >> BPINFO_ABPID_SHIFT; +uint8_t read_select = (bpinfo >> BPINFO_BRS_SHIFT) & BPINFO_BRS_MASK; + +g_assert_cmpint(single_bp_size, ==, 0x1); +g_assert_cmpint(active_bpid, ==, 0); +g_assert_cmpint(read_select, ==, NVME_BPINFO_BRS_NOREAD); + +/* BPMBL */ +uint64_t bpmbl = buf_addr; +uint32_t bpmbl_low = bpmbl & 0x; +uint32_t bpmbl_hi = (bpmbl >> 32) & 0x; +qpci_io_writel(pdev, nvme_bar, 0x48, bpmbl_low); +qpci_io_writel(pdev, nvme_bar, 0x4c, bpmbl_hi); + +/* BPRSEL */ +qpci_io_writel(pdev, nvme_bar, 0x44, 32); + +while (true) { +usleep(1000); +sleep_time += 1000; +brs = qpci_io_readb(pdev, nvme_bar, 0x43) & BPINFO_BRS_MASK; +if (brs == NVME_BPINFO_BRS_SUCCESS || brs == NVME_BPINFO_BRS_ERROR || +sleep_time == NVME_BRS_READ_MAX_TIME) { +break; +} +} +g_assert_cmpint(brs, ==, NVME_BPINFO_BRS_SUCCESS); + +qtest_memread(pdev->bus->qts, guest_buf, read_buf, bp_test_len); +g_assert_cmpint(memcmp(cmp_buf, read_buf, bp_test_len), ==, 0); + +g_free(cmp_buf); +g_free(read_buf); +g_test_queue_destroy(drive_destroy, t_path); +} + static void nvme_register_nodes(void) { +int fd; +FILE *fh; +uint16_t bpsz = 2; +size_t bp_len = NVME_BPINFO_BPSZ_UNITS * bpsz; +size_t ret; +uint8_t *pattern = g_malloc(bp_len); + +t_path = g_strdup("/tmp/qtest.XX"); + +/* Create a temporary raw image */ +fd = mkstemp(t_path); +g_assert_cmpint(fd, >=, 0); +ret = ftruncate(fd, TEST_IMAGE_SIZE); +g_assert_cmpint(ret, ==, 0); +close(fd); + +memset(pattern, 0x42, bp_len); + +fh = fopen(t_path, "w+"); +ret = fwrite(pattern, NVME_BPINFO_BPSZ_UNITS, bpsz, fh); +g_assert_cmpint(ret, ==, bpsz); +fclose(fh); + +char *bp_cmd_line = g_strdup_printf("-drive id=bp0,file=%s,if=none," +"format=raw", t_path); + QOSGraphEdgeOptions opts = { .extra_device_opts = "addr=04.0,drive=drv0,serial=foo", .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," - "file.read-zeroes=on,format=raw", + "file.read-zeroes=on,format=raw ", + bp_cmd_line, }; add_qpci_address(, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); @@ -83,6 +191,14 @@ static void nvme_register_nodes(void) qos_add_test("oob-cmb-access", "nvme",
[PATCH v2 2/2] tests/qtest/nvme-test: add boot partition read test
Add a test case for reading an NVMe Boot Partition without enabling the controller. Signed-off-by: Gollu Appalanaidu --- tests/qtest/nvme-test.c | 118 +++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/tests/qtest/nvme-test.c b/tests/qtest/nvme-test.c index d32c953a38..39d2e26f76 100644 --- a/tests/qtest/nvme-test.c +++ b/tests/qtest/nvme-test.c @@ -13,6 +13,19 @@ #include "libqos/libqtest.h" #include "libqos/qgraph.h" #include "libqos/pci.h" +#include "libqos/pci-pc.h" +#include "libqos/malloc-pc.h" +#include "libqos/malloc.h" +#include "libqos/libqos.h" +#include "include/block/nvme.h" +#include "include/hw/pci/pci.h" + +#define NVME_BPINFO_BPSZ_UNITS (128 * KiB) +#define NVME_BRS_BPSZ_UNITS (4 * KiB) +#define NVME_BRS_READ_MAX_TIME 100 +#define TEST_IMAGE_SIZE (2 * 128 * KiB) + +static char *t_path; typedef struct QNvme QNvme; @@ -44,6 +57,13 @@ static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr) return >obj; } +static void drive_destroy(void *path) +{ +unlink(path); +g_free(path); +qos_invalidate_command_line(); +} + /* This used to cause a NULL pointer dereference. */ static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc) { @@ -66,12 +86,100 @@ static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211); } +static void nvmetest_bp_read_test(void *obj, void *data, QGuestAllocator *alloc) +{ +uint16_t test_size = 32; +size_t bp_test_len = test_size * NVME_BRS_BPSZ_UNITS; +uint8_t *read_buf = g_malloc(bp_test_len); +uint8_t *cmp_buf = g_malloc(bp_test_len); +QNvme *nvme = obj; +QPCIDevice *pdev = >dev; +QPCIBar nvme_bar; +uint8_t brs = 0; +uint64_t sleep_time = 0; +uintptr_t guest_buf; +uint64_t buf_addr; + +memset(cmp_buf, 0x42, bp_test_len); + +guest_buf = guest_alloc(alloc, bp_test_len); +buf_addr = cpu_to_le64(guest_buf); + +qpci_device_enable(pdev); +nvme_bar = qpci_iomap(pdev, 0, NULL); + +/* BPINFO */ +uint32_t bpinfo = qpci_io_readl(pdev, nvme_bar, 0x40); +uint16_t single_bp_size = bpinfo & BPINFO_BPSZ_MASK; +uint8_t active_bpid = bpinfo >> BPINFO_ABPID_SHIFT; +uint8_t read_select = (bpinfo >> BPINFO_BRS_SHIFT) & BPINFO_BRS_MASK; + +g_assert_cmpint(single_bp_size, ==, 0x1); +g_assert_cmpint(active_bpid, ==, 0); +g_assert_cmpint(read_select, ==, NVME_BPINFO_BRS_NOREAD); + +/* BPMBL */ +uint64_t bpmbl = buf_addr; +uint32_t bpmbl_low = bpmbl & 0x; +uint32_t bpmbl_hi = (bpmbl >> 32) & 0x; +qpci_io_writel(pdev, nvme_bar, 0x48, bpmbl_low); +qpci_io_writel(pdev, nvme_bar, 0x4c, bpmbl_hi); + +/* BPRSEL */ +qpci_io_writel(pdev, nvme_bar, 0x44, 32); + +while (true) { +usleep(1000); +sleep_time += 1000; +brs = qpci_io_readb(pdev, nvme_bar, 0x43) & BPINFO_BRS_MASK; +if (brs == NVME_BPINFO_BRS_SUCCESS || brs == NVME_BPINFO_BRS_ERROR || +sleep_time == NVME_BRS_READ_MAX_TIME) { +break; +} +} +g_assert_cmpint(brs, ==, NVME_BPINFO_BRS_SUCCESS); + +qtest_memread(pdev->bus->qts, guest_buf, read_buf, bp_test_len); +g_assert_cmpint(memcmp(cmp_buf, read_buf, bp_test_len), ==, 0); + +g_free(cmp_buf); +g_free(read_buf); +g_test_queue_destroy(drive_destroy, t_path); +} + static void nvme_register_nodes(void) { +int fd; +FILE *fh; +uint16_t bpsz = 2; +size_t bp_len = NVME_BPINFO_BPSZ_UNITS * bpsz; +size_t ret; +uint8_t *pattern = g_malloc(bp_len); + +t_path = g_strdup("/tmp/qtest.XX"); + +/* Create a temporary raw image */ +fd = mkstemp(t_path); +g_assert_cmpint(fd, >=, 0); +ret = ftruncate(fd, TEST_IMAGE_SIZE); +g_assert_cmpint(ret, ==, 0); +close(fd); + +memset(pattern, 0x42, bp_len); + +fh = fopen(t_path, "w+"); +ret = fwrite(pattern, NVME_BPINFO_BPSZ_UNITS, bpsz, fh); +g_assert_cmpint(ret, ==, bpsz); +fclose(fh); + +char *bp_cmd_line = g_strdup_printf("-drive id=bp0,file=%s,if=none," +"format=raw", t_path); + QOSGraphEdgeOptions opts = { .extra_device_opts = "addr=04.0,drive=drv0,serial=foo", .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," - "file.read-zeroes=on,format=raw", + "file.read-zeroes=on,format=raw ", + bp_cmd_line, }; add_qpci_address(, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); @@ -83,6 +191,14 @@ static void nvme_register_nodes(void) qos_add_test("oob-cmb-access", "nvme", nvmetest_oob_cmb_test, &(QOSGraphTestOptions) { .edge.extra_device_opts = "cmb_size_mb=2" }); + +qos_add_test("bp-read-access", "nvme", nvmetest_bp_read_test, +