diff options
-rw-r--r-- | test/py/libvfio_user.py | 120 | ||||
-rw-r--r-- | test/py/test_vfu_create_ctx.py | 56 | ||||
-rw-r--r-- | test/py/test_vfu_realize_ctx.py | 120 | ||||
-rw-r--r-- | test/unit-tests.c | 128 |
4 files changed, 292 insertions, 132 deletions
diff --git a/test/py/libvfio_user.py b/test/py/libvfio_user.py index 8dee414..2d64269 100644 --- a/test/py/libvfio_user.py +++ b/test/py/libvfio_user.py @@ -77,6 +77,47 @@ VFIO_USER_F_TYPE_REPLY = 1 SIZEOF_VFIO_USER_HEADER = 16 +VFU_PCI_DEV_BAR0_REGION_IDX = 0 +VFU_PCI_DEV_BAR1_REGION_IDX = 1 +VFU_PCI_DEV_BAR2_REGION_IDX = 2 +VFU_PCI_DEV_BAR3_REGION_IDX = 3 +VFU_PCI_DEV_BAR4_REGION_IDX = 4 +VFU_PCI_DEV_BAR5_REGION_IDX = 5 +VFU_PCI_DEV_ROM_REGION_IDX = 6 +VFU_PCI_DEV_CFG_REGION_IDX = 7 +VFU_PCI_DEV_VGA_REGION_IDX = 8 +VFU_PCI_DEV_MIGR_REGION_IDX = 9 +VFU_PCI_DEV_NUM_REGIONS = 10 + +VFU_REGION_FLAG_READ = 1 +VFU_REGION_FLAG_WRITE = 2 +VFU_REGION_FLAG_RW = (VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE) +VFU_REGION_FLAG_MEM = 4 + +PCI_BARS_NR = 6 + +# enum vfu_dev_irq_type +VFU_DEV_INTX_IRQ = 0 +VFU_DEV_MSI_IRQ = 1 +VFU_DEV_MSIX_IRQ = 2 +VFU_DEV_ERR_IRQ = 3 +VFU_DEV_REQ_IRQ = 4 +VFU_DEV_NUM_IRQS = 5 + +# vfu_pci_type_t +VFU_PCI_TYPE_CONVENTIONAL = 0 +VFU_PCI_TYPE_PCI_X_1 = 1 +VFU_PCI_TYPE_PCI_X_2 = 2 +VFU_PCI_TYPE_EXPRESS = 3 + +PCI_HEADER_TYPE_NORMAL = 0 + +PCI_STD_HEADER_SIZEOF = 64 + +PCI_CFG_SPACE_SIZE = 256 +PCI_CAP_ID_PM = b'\1' + + topdir = os.path.realpath(os.path.dirname(__file__) + "/../..") build_type = os.getenv("BUILD_TYPE", default="dbg") libname = "%s/build/%s/lib/libvfio-user.so" % (topdir, build_type) @@ -89,10 +130,60 @@ lib.vfu_realize_ctx.argtypes = (c.c_void_p,) lib.vfu_attach_ctx.argtypes = (c.c_void_p,) lib.vfu_run_ctx.argtypes = (c.c_void_p,) lib.vfu_destroy_ctx.argtypes = (c.c_void_p,) +lib.vfu_setup_region.argtypes = (c.c_void_p, c.c_int, c.c_long, c.c_void_p, + c.c_int, c.c_void_p, c.c_int, c.c_int) +lib.vfu_pci_get_config_space.argtypes = (c.c_void_p,) +lib.vfu_pci_get_config_space.restype = (c.c_void_p) +lib.vfu_setup_device_nr_irqs.argtypes = (c.c_void_p, c.c_int, c.c_int) +lib.vfu_pci_init.argtypes = (c.c_void_p, c.c_int, c.c_int, c.c_int) +lib.vfu_pci_add_capability.argtypes = (c.c_void_p, c.c_long, c.c_int, + c.POINTER(c.c_byte)) msg_id = 1 # +# Structures +# +class vfu_bar_t(c.Union): + _pack_ = 1 + _fields_ = [ + ("mem", c.c_int), + ("io", c.c_int) + ] + +class vfu_pci_hdr_intr_t(c.Structure): + _pack_ = 1 + _fields_ = [ + ("iline", c.c_byte), + ("ipin", c.c_byte) + ] + +class vfu_pci_hdr_t(c.Structure): + _pack_ = 1 + _fields_ = [ + ("id", c.c_int), + ("cmd", c.c_short), + ("sts", c.c_short), + ("rid", c.c_byte), + ("cc_pi", c.c_byte), + ("cc_scc", c.c_byte), + ("cc_bcc", c.c_byte), + ("cls", c.c_byte), + ("mlt", c.c_byte), + ("htype", c.c_byte), + ("bist", c.c_byte), + ("bars", vfu_bar_t * PCI_BARS_NR), + ("ccptr", c.c_int), + ("ss", c.c_int), + ("erom", c.c_int), + ("cap", c.c_byte), + ("res1", c.c_byte * 7), + ("intr", vfu_pci_hdr_intr_t), + ("mgnt", c.c_byte), + ("mlat", c.c_byte) + ] + +# # Util functions # @@ -107,12 +198,13 @@ def get_reply(sock, expect=0): assert errno == expect return buf[16:] -# -# Parse JSON into an object with attributes (instead of using a dict) -# def parse_json(json_str): + """Parse JSON into an object with attributes (instead of using a dict).""" return json.loads(json_str, object_hook=lambda d: SimpleNamespace(**d)) +def get_pci_header(ctx): + ptr = lib.vfu_pci_get_config_space(ctx) + return c.cast(ptr, c.POINTER(vfu_pci_hdr_t)).contents # # Library wrappers @@ -157,10 +249,30 @@ def vfu_attach_ctx(ctx, expect=0): return ret def vfu_run_ctx(ctx): - lib.vfu_run_ctx(ctx) + return lib.vfu_run_ctx(ctx) def vfu_destroy_ctx(ctx): lib.vfu_destroy_ctx(ctx) ctx = None if os.path.exists(SOCK_PATH): os.remove(SOCK_PATH) + +def vfu_setup_region(ctx, index, size, flags=0): + assert ctx != None + ret = lib.vfu_setup_region(ctx, index, size, None, flags, None, 0, -1) + return ret + +def vfu_setup_device_nr_irqs(ctx, irqtype, count): + assert ctx != None + return lib.vfu_setup_device_nr_irqs(ctx, irqtype, count) + +def vfu_pci_init(ctx, pci_type=VFU_PCI_TYPE_EXPRESS, + hdr_type=PCI_HEADER_TYPE_NORMAL): + assert ctx != None + return lib.vfu_pci_init(ctx, pci_type, hdr_type, 0) + +def vfu_pci_add_capability(ctx, pos, flags, data): + assert ctx != None + + databuf = (c.c_byte * len(data)).from_buffer(bytearray(data)) + return lib.vfu_pci_add_capability(ctx, pos, flags, databuf) diff --git a/test/py/test_vfu_create_ctx.py b/test/py/test_vfu_create_ctx.py new file mode 100644 index 0000000..439ff69 --- /dev/null +++ b/test/py/test_vfu_create_ctx.py @@ -0,0 +1,56 @@ +# +# Copyright (c) 2021 Nutanix Inc. All rights reserved. +# +# Authors: John Levon <john.levon@nutanix.com> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Nutanix nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# + +from libvfio_user import * +import ctypes as c +import errno + +def test_vfu_create_ctx_bad_trans(): + ctx = vfu_create_ctx(trans=VFU_TRANS_SOCK + 1) + assert ctx == None + assert c.get_errno() == errno.ENOTSUP + +def test_vfu_create_ctx_bad_flags(): + ctx = vfu_create_ctx(flags=999) + assert ctx == None + assert c.get_errno() == errno.EINVAL + +def test_vfu_create_ctx_bad_dev_type(): + ctx = vfu_create_ctx(dev_type=VFU_DEV_TYPE_PCI + 4) + assert ctx == None + assert c.get_errno() == errno.ENOTSUP + +def test_vfu_create_ctx_default(): + ctx = vfu_create_ctx() + assert ctx != None + + ret = vfu_realize_ctx(ctx) + assert ret == 0 + + vfu_destroy_ctx(ctx) diff --git a/test/py/test_vfu_realize_ctx.py b/test/py/test_vfu_realize_ctx.py new file mode 100644 index 0000000..c35612c --- /dev/null +++ b/test/py/test_vfu_realize_ctx.py @@ -0,0 +1,120 @@ +# +# Copyright (c) 2021 Nutanix Inc. All rights reserved. +# +# Authors: John Levon <john.levon@nutanix.com> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Nutanix nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# + +import ctypes as c +import errno +from libvfio_user import * + +def test_vfu_realize_ctx_twice(): + ctx = vfu_create_ctx() + assert ctx != None + + ret = vfu_realize_ctx(ctx) + assert ret == 0 + + ret = vfu_realize_ctx(ctx) + assert ret == 0 + + vfu_destroy_ctx(ctx) + +def test_vfu_unrealized_ctx(): + ctx = vfu_create_ctx() + assert ctx != None + + ret = vfu_run_ctx(ctx) + assert ret == -1 + assert c.get_errno() == errno.EINVAL + + vfu_destroy_ctx(ctx) + +def test_vfu_realize_ctx_default(): + ctx = vfu_create_ctx() + assert ctx != None + + ret = vfu_realize_ctx(ctx) + assert ret == 0 + + vfu_destroy_ctx(ctx) + +def test_vfu_realize_ctx_pci_bars(): + ctx = vfu_create_ctx() + assert ctx != None + + ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_BAR0_REGION_IDX, size=4096, + flags=VFU_REGION_FLAG_RW) + assert ret == 0 + ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_BAR1_REGION_IDX, size=4096, + flags=(VFU_REGION_FLAG_RW | VFU_REGION_FLAG_MEM)) + assert ret == 0 + + ret = vfu_realize_ctx(ctx) + assert ret == 0 + + # region_type should be set non-MEM BAR, unset otherwise + hdr = get_pci_header(ctx) + assert hdr.bars[0].io == 0x1 + assert hdr.bars[1].io == 0 + + vfu_destroy_ctx(ctx) + +def test_vfu_realize_ctx_irqs(): + ctx = vfu_create_ctx() + assert ctx != None + + ret = vfu_setup_device_nr_irqs(ctx, VFU_DEV_INTX_IRQ, 1) + assert ret == 0 + + ret = vfu_realize_ctx(ctx) + assert ret == 0 + + # verify INTA# is available + hdr = get_pci_header(ctx) + assert hdr.intr.ipin == 0x1 + + vfu_destroy_ctx(ctx) + +def test_vfu_realize_ctx_caps(): + ctx = vfu_create_ctx() + assert ctx != None + + ret = vfu_pci_init(ctx) + assert ret == 0 + + pos = vfu_pci_add_capability(ctx, pos=0, flags=0, data=struct.pack( + "ccHH", PCI_CAP_ID_PM, b'\0', 0, 0)) + assert pos == PCI_STD_HEADER_SIZEOF + + ret = vfu_realize_ctx(ctx) + assert ret == 0 + + # hdr.sts.cl should be 0x1 + hdr = get_pci_header(ctx) + assert hdr.sts == (1 << 4) + + vfu_destroy_ctx(ctx) diff --git a/test/unit-tests.c b/test/unit-tests.c index 86cc11e..7fbb055 100644 --- a/test/unit-tests.c +++ b/test/unit-tests.c @@ -545,83 +545,11 @@ test_vfu_setup_device_dma(void **state UNUSED) free(vfu_ctx.dma); } -static void -test_realize_ctx(void **state UNUSED) -{ - vfu_reg_info_t reg_info[VFU_PCI_DEV_NUM_REGIONS + 1] = { { 0 } }; - vfu_reg_info_t *cfg_reg; - - vfu_ctx.reg_info = reg_info; - vfu_ctx.nr_regions = VFU_PCI_DEV_NUM_REGIONS + 1; - - assert_int_equal(0, vfu_realize_ctx(&vfu_ctx)); - assert_true(vfu_ctx.realized); - - cfg_reg = &vfu_ctx.reg_info[VFU_PCI_DEV_CFG_REGION_IDX]; - assert_int_equal(VFU_REGION_FLAG_RW, cfg_reg->flags); - assert_int_equal(PCI_CFG_SPACE_SIZE, cfg_reg->size); - - assert_non_null(vfu_ctx.pci.config_space); - assert_non_null(vfu_ctx.irqs); - assert_int_equal(0, vfu_ctx.pci.nr_caps); - assert_int_equal(0, vfu_ctx.pci.nr_ext_caps); - - free(vfu_ctx.irqs); - free(vfu_ctx.pci.config_space); -} - typedef struct { int fd; int conn_fd; } tran_sock_t; -static int -dummy_attach(vfu_ctx_t *vfu_ctx) -{ - assert(vfu_ctx != NULL); - - return 0; -} - -static void -test_attach_ctx(void **state UNUSED) -{ - struct transport_ops transport_ops = { - .attach = &dummy_attach, - }; - - vfu_ctx.tran = &transport_ops; - - assert_int_equal(0, vfu_attach_ctx(&vfu_ctx)); -} - -static void -test_run_ctx(UNUSED void **state) -{ - vfu_ctx.realized = false; - - // device un-realized - assert_int_equal(-1, vfu_run_ctx(&vfu_ctx)); - - // device realized, with NB vfu_ctx - vfu_ctx.realized = true; - vfu_ctx.flags = LIBVFIO_USER_FLAG_ATTACH_NB; - - patch("process_request"); - expect_value(process_request, vfu_ctx, &vfu_ctx); - will_return(process_request, 0); - assert_int_equal(0, vfu_run_ctx(&vfu_ctx)); - - // device realized, with blocking vfu_ctx - vfu_ctx.flags = 0; - expect_value(process_request, vfu_ctx, &vfu_ctx); - will_return(process_request, 0); - - expect_value(process_request, vfu_ctx, &vfu_ctx); - will_return(process_request, -1); - assert_int_equal(-1, vfu_run_ctx(&vfu_ctx)); -} - static void test_get_region_info(UNUSED void **state) { @@ -768,58 +696,6 @@ test_get_region_info(UNUSED void **state) /* FIXME add check for multiple sparse areas */ } -static void -test_vfu_ctx_create(void **state UNUSED) -{ - vfu_ctx_t *vfu_ctx = NULL; - struct pmcap pm = { { 0 } }; - - pm.hdr.id = PCI_CAP_ID_PM; - pm.pmcs.nsfrst = 0x1; - - vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK + 1, "", 0, NULL, VFU_DEV_TYPE_PCI); - assert_null(vfu_ctx); - assert_int_equal(ENOTSUP, errno); - - vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, "", 0, NULL, VFU_DEV_TYPE_PCI + 4); - assert_null(vfu_ctx); - assert_int_equal(ENOTSUP, errno); - - vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, "", 999, NULL, VFU_DEV_TYPE_PCI); - assert_null(vfu_ctx); - assert_int_equal(EINVAL, errno); - - vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, "", LIBVFIO_USER_FLAG_ATTACH_NB, - NULL, VFU_DEV_TYPE_PCI); - assert_non_null(vfu_ctx); - - assert_int_equal(1, vfu_ctx->irq_count[VFU_DEV_ERR_IRQ]); - assert_int_equal(1, vfu_ctx->irq_count[VFU_DEV_REQ_IRQ]); - assert_int_equal(0, vfu_pci_init(vfu_ctx, VFU_PCI_TYPE_CONVENTIONAL, - PCI_HEADER_TYPE_NORMAL, 0)); - assert_int_equal(PCI_STD_HEADER_SIZEOF, - vfu_pci_add_capability(vfu_ctx, 0, 0, &pm)); - assert_int_equal(0, vfu_realize_ctx(vfu_ctx)); - - patch("close"); - expect_value(close, fd, ((tran_sock_t *)vfu_ctx->tran_data)->fd); - will_return(close, 0); - - vfu_destroy_ctx(vfu_ctx); - - /* Test "bare" vfu_create_ctx(). */ - vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, "", LIBVFIO_USER_FLAG_ATTACH_NB, - NULL, VFU_DEV_TYPE_PCI); - assert_non_null(vfu_ctx); - - assert_int_equal(0, vfu_realize_ctx(vfu_ctx)); - - expect_value(close, fd, ((tran_sock_t *)vfu_ctx->tran_data)->fd); - will_return(close, 0); - - vfu_destroy_ctx(vfu_ctx); -} - static bool pci_caps_writing = true; static ssize_t @@ -1957,11 +1833,7 @@ main(void) cmocka_unit_test_setup(test_dma_map_sg, setup), cmocka_unit_test_setup(test_dma_addr_to_sg, setup), cmocka_unit_test_setup(test_vfu_setup_device_dma, setup), - cmocka_unit_test_setup(test_realize_ctx, setup), - cmocka_unit_test_setup(test_attach_ctx, setup), - cmocka_unit_test_setup(test_run_ctx, setup), cmocka_unit_test_setup(test_get_region_info, setup), - cmocka_unit_test_setup(test_vfu_ctx_create, setup), cmocka_unit_test_setup(test_pci_caps, setup), cmocka_unit_test_setup(test_pci_ext_caps, setup), cmocka_unit_test_setup(test_device_get_info, setup), |