aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Levon <john.levon@nutanix.com>2021-05-10 15:01:50 +0100
committerGitHub <noreply@github.com>2021-05-10 15:01:50 +0100
commit702a4cad49c40406ff498e0da4a80c6d07b4fc83 (patch)
tree13dab2b60f6f5882e896174a4480654f7e6b3520
parente92b1f6b57d0131ec4aa581e57e10ab7d628a0cd (diff)
downloadlibvfio-user-702a4cad49c40406ff498e0da4a80c6d07b4fc83.zip
libvfio-user-702a4cad49c40406ff498e0da4a80c6d07b4fc83.tar.gz
libvfio-user-702a4cad49c40406ff498e0da4a80c6d07b4fc83.tar.bz2
python tests: add vfu_create_ctx(), vfu_realize_ctx() (#448)
Signed-off-by: John Levon <john.levon@nutanix.com> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
-rw-r--r--test/py/libvfio_user.py120
-rw-r--r--test/py/test_vfu_create_ctx.py56
-rw-r--r--test/py/test_vfu_realize_ctx.py120
-rw-r--r--test/unit-tests.c128
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),