diff options
author | John Levon <john.levon@nutanix.com> | 2021-05-15 00:20:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-15 00:20:16 +0100 |
commit | 8aeb1195af036961b4c9b0dcbcc69024042f8624 (patch) | |
tree | 369a47cdfd7b59f949c816fc6f0590e7ff99a53c | |
parent | 95f16fceadba22a7730c2a9c94cfd4028ef04a02 (diff) | |
download | libvfio-user-8aeb1195af036961b4c9b0dcbcc69024042f8624.zip libvfio-user-8aeb1195af036961b4c9b0dcbcc69024042f8624.tar.gz libvfio-user-8aeb1195af036961b4c9b0dcbcc69024042f8624.tar.bz2 |
move PCI capability testing to Python (#453)
This also adds a couple of additional tests (as well as more fully testing the
region access path).
Signed-off-by: John Levon <john.levon@nutanix.com>
Reviewed-by: Swapnil Ingle <swapnil.ingle@nutanix.com>
-rw-r--r-- | lib/pci_caps.c | 3 | ||||
-rw-r--r-- | test/py/libvfio_user.py | 165 | ||||
-rw-r--r-- | test/py/test_negotiate.py | 5 | ||||
-rw-r--r-- | test/py/test_pci_caps.py | 305 | ||||
-rw-r--r-- | test/py/test_pci_ext_caps.py | 302 | ||||
-rw-r--r-- | test/py/test_vfu_realize_ctx.py | 2 | ||||
-rw-r--r-- | test/unit-tests.c | 416 |
7 files changed, 758 insertions, 440 deletions
diff --git a/lib/pci_caps.c b/lib/pci_caps.c index 8ed5643..550d373 100644 --- a/lib/pci_caps.c +++ b/lib/pci_caps.c @@ -646,6 +646,9 @@ vfu_pci_add_capability(vfu_ctx_t *vfu_ctx, size_t pos, int flags, void *data) return ret; } + vfu_log(vfu_ctx, LOG_DEBUG, "added PCI cap \"%s\" size=%#zx offset=%#zx", + cap.name, cap.size, cap.off); + if (extended) { memcpy(&vfu_ctx->pci.ext_caps[vfu_ctx->pci.nr_ext_caps], &cap, sizeof(cap)); diff --git a/test/py/libvfio_user.py b/test/py/libvfio_user.py index 2d64269..60a93a8 100644 --- a/test/py/libvfio_user.py +++ b/test/py/libvfio_user.py @@ -40,7 +40,32 @@ import socket import struct import syslog -SOCK_PATH = b"/tmp/vfio-user.sock.%d" % os.getpid() +# from linux/pci_regs.h and linux/pci_defs.h + +PCI_HEADER_TYPE_NORMAL = 0 + +PCI_STD_HEADER_SIZEOF = 64 + +PCI_BARS_NR = 6 + +PCI_PM_SIZEOF = 8 + +PCI_CFG_SPACE_SIZE = 256 +PCI_CFG_SPACE_EXP_SIZE = 4096 + +PCI_CAP_LIST_NEXT = 1 + +PCI_CAP_ID_PM = 0x1 +PCI_CAP_ID_VNDR = 0x9 + +PCI_EXT_CAP_ID_DSN = 0x03 +PCI_EXT_CAP_ID_VNDR = 0x0b + +PCI_EXT_CAP_DSN_SIZEOF = 12 + +PCI_EXT_CAP_VNDR_HDR_SIZEOF = 8 + +# libvfio-user defines VFU_TRANS_SOCK = 0 LIBVFIO_USER_FLAG_ATTACH_NB = (1 << 0) @@ -94,8 +119,6 @@ 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 @@ -110,13 +133,11 @@ 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' +VFU_CAP_FLAG_EXTENDED = (1 << 0) +VFU_CAP_FLAG_CALLBACK = (1 << 1) +VFU_CAP_FLAG_READONLY = (1 << 2) +SOCK_PATH = b"/tmp/vfio-user.sock.%d" % os.getpid() topdir = os.path.realpath(os.path.dirname(__file__) + "/../..") build_type = os.getenv("BUILD_TYPE", default="dbg") @@ -130,14 +151,22 @@ 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) +vfu_region_access_cb_t = c.CFUNCTYPE(c.c_int, c.c_void_p, c.POINTER(c.c_char), + c.c_ulong, c.c_long, c.c_bool) +lib.vfu_setup_region.argtypes = (c.c_void_p, c.c_int, c.c_long, + vfu_region_access_cb_t, c.c_int, c.c_void_p, + c.c_uint32, 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_setup_device_nr_irqs.argtypes = (c.c_void_p, c.c_int, c.c_uint32) 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, +lib.vfu_pci_add_capability.argtypes = (c.c_void_p, c.c_ulong, c.c_int, c.POINTER(c.c_byte)) +lib.vfu_pci_find_capability.argtypes = (c.c_void_p, c.c_bool, c.c_int) +lib.vfu_pci_find_capability.restype = (c.c_ulong) +lib.vfu_pci_find_next_capability.argtypes = (c.c_void_p, c.c_bool, c.c_ulong, + c.c_int) +lib.vfu_pci_find_next_capability.restype = (c.c_ulong) msg_id = 1 @@ -187,25 +216,111 @@ class vfu_pci_hdr_t(c.Structure): # Util functions # +def to_byte(val): + """Cast an int to a byte value.""" + return val.to_bytes(1, 'little') + +def ext_cap_hdr(buf, offset): + """Read an extended cap header.""" + + # struct pcie_ext_cap_hdr + cap_id, cap_next = struct.unpack('HH', buf[offset:offset+4]) + cap_next >>= 4 + return cap_id, cap_next + +def skip(data, fmt): + """Return the data remaining after skipping the given elements.""" + return data[struct.calcsize(fmt):] + +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 connect_sock(): sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect(SOCK_PATH) return sock +def connect_client(ctx): + sock = connect_sock() + + json = b'{ "capabilities": { "max_fds": 8 } }' + # struct vfio_user_version + payload = struct.pack("HH%dsc" % len(json), LIBVFIO_USER_MAJOR, + LIBVFIO_USER_MINOR, json, b'\0') + hdr = vfio_user_header(VFIO_USER_VERSION, size=len(payload)) + sock.send(hdr + payload) + vfu_attach_ctx(ctx, expect=0) + payload = get_reply(sock, expect=0) + return sock + +def disconnect_client(ctx, sock): + sock.close() + + # notice client closed connection + vfu_run_ctx(ctx) + def get_reply(sock, expect=0): buf = sock.recv(4096) (msg_id, cmd, msg_size, flags, errno) = struct.unpack("HHIII", buf[0:16]) + assert (flags & VFIO_USER_F_TYPE_REPLY) != 0 assert errno == expect return buf[16:] -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 +def get_pci_cfg_space(ctx): + ptr = lib.vfu_pci_get_config_space(ctx) + return c.cast(ptr, c.POINTER(c.c_char))[0:PCI_CFG_SPACE_SIZE] + +def get_pci_ext_cfg_space(ctx): + ptr = lib.vfu_pci_get_config_space(ctx) + return c.cast(ptr, c.POINTER(c.c_char))[0:PCI_CFG_SPACE_EXP_SIZE] + +def read_pci_cfg_space(ctx, buf, count, offset, extended=False): + space = get_pci_ext_cfg_space(ctx) if extended else get_pci_cfg_space(ctx) + + for i in range(count): + buf[i] = space[offset+i] + return count + +def write_pci_cfg_space(ctx, buf, count, offset, extended=False): + max_offset = PCI_CFG_SPACE_EXP_SIZE if extended else PCI_CFG_SPACE_SIZE + + assert offset + count <= max_offset + + space = c.cast(lib.vfu_pci_get_config_space(ctx), c.POINTER(c.c_char)) + + for i in range(count): + space[offset+i] = buf[i] + return count + +def access_region(ctx, sock, is_write, region, offset, count, + data=None, expect=0): + # struct vfio_user_region_access + payload = struct.pack("QII", offset, region, count) + if is_write: + payload += data + + cmd = VFIO_USER_REGION_WRITE if is_write else VFIO_USER_REGION_READ + hdr = vfio_user_header(cmd, size=len(payload)) + sock.send(hdr + payload) + vfu_run_ctx(ctx) + result = get_reply(sock, expect=expect) + + if is_write: + return None + + return skip(result, "QII") + +def write_region(ctx, sock, region, offset, count, data, expect=0): + access_region(ctx, sock, True, region, offset, count, data, expect=expect) + +def read_region(ctx, sock, region, offset, count, expect=0): + return access_region(ctx, sock, False, region, offset, count, expect=expect) + # # Library wrappers # @@ -257,9 +372,11 @@ def vfu_destroy_ctx(ctx): if os.path.exists(SOCK_PATH): os.remove(SOCK_PATH) -def vfu_setup_region(ctx, index, size, flags=0): +def vfu_setup_region(ctx, index, size, flags=0, cb=None): assert ctx != None - ret = lib.vfu_setup_region(ctx, index, size, None, flags, None, 0, -1) + ret = lib.vfu_setup_region(ctx, index, size, + c.cast(cb, vfu_region_access_cb_t), + flags, None, 0, -1) return ret def vfu_setup_device_nr_irqs(ctx, irqtype, count): @@ -276,3 +393,13 @@ def vfu_pci_add_capability(ctx, pos, flags, data): databuf = (c.c_byte * len(data)).from_buffer(bytearray(data)) return lib.vfu_pci_add_capability(ctx, pos, flags, databuf) + +def vfu_pci_find_capability(ctx, extended, cap_id): + assert ctx != None + + return lib.vfu_pci_find_capability(ctx, extended, cap_id) + +def vfu_pci_find_next_capability(ctx, extended, offset, cap_id): + assert ctx != None + + return lib.vfu_pci_find_next_capability(ctx, extended, offset, cap_id) diff --git a/test/py/test_negotiate.py b/test/py/test_negotiate.py index 09edf99..e05b450 100644 --- a/test/py/test_negotiate.py +++ b/test/py/test_negotiate.py @@ -155,10 +155,7 @@ def test_valid_negotiate_no_json(): assert json.capabilities.max_msg_size == SERVER_MAX_MSG_SIZE # FIXME: migration object checks - sock.close() - - # notice client closed connection - vfu_run_ctx(ctx) + disconnect_client(ctx, sock) def test_valid_negotiate_empty_json(): client_version_json(json=b'{}') diff --git a/test/py/test_pci_caps.py b/test/py/test_pci_caps.py new file mode 100644 index 0000000..9691fe1 --- /dev/null +++ b/test/py/test_pci_caps.py @@ -0,0 +1,305 @@ +# +# 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 + +ctx = None + +def test_pci_cap_setup(): + global ctx + + ctx = vfu_create_ctx(flags=LIBVFIO_USER_FLAG_ATTACH_NB) + assert ctx != None + + ret = vfu_pci_init(ctx, pci_type=VFU_PCI_TYPE_CONVENTIONAL) + assert ret == 0 + + ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_CFG_REGION_IDX, + size=PCI_CFG_SPACE_SIZE, flags=VFU_REGION_FLAG_RW) + assert ret == 0 + +def test_pci_cap_bad_flags(): + pos = vfu_pci_add_capability(ctx, pos=0, flags=999, + data=struct.pack("ccHH", to_byte(PCI_CAP_ID_PM), b'\0', 0, 0)) + assert pos == -1 + assert c.get_errno() == errno.EINVAL + +def test_pci_cap_no_cb(): + pos = vfu_pci_add_capability(ctx, pos=0, flags=VFU_CAP_FLAG_CALLBACK, + data=struct.pack("ccHH", to_byte(PCI_CAP_ID_PM), b'\0', 0, 0)) + assert pos == -1 + assert c.get_errno() == errno.EINVAL + +def test_pci_cap_unknown_cap(): + pos = vfu_pci_add_capability(ctx, pos=0, flags=0, + data=struct.pack("ccHH", b'\x81', b'\0', 0, 0)) + assert pos == -1 + assert c.get_errno() == errno.ENOTSUP + +def test_pci_cap_bad_pos(): + pos = vfu_pci_add_capability(ctx, pos=PCI_CFG_SPACE_SIZE, flags=0, + data=struct.pack("ccHH", to_byte(PCI_CAP_ID_PM), b'\0', 0, 0)) + assert pos == -1 + assert c.get_errno() == errno.EINVAL + +@c.CFUNCTYPE(c.c_int, c.c_void_p, c.POINTER(c.c_char), + c.c_long, c.c_long, c.c_int) +def pci_region_cb(ctx, buf, count, offset, is_write): + if not is_write: + return read_pci_cfg_space(ctx, buf, count, offset) + + return write_pci_cfg_space(ctx, buf, count, offset) + +def test_pci_cap_setup_cb(): + global ctx + + vfu_destroy_ctx(ctx) + + ctx = vfu_create_ctx(flags=LIBVFIO_USER_FLAG_ATTACH_NB) + assert ctx != None + + ret = vfu_pci_init(ctx, pci_type=VFU_PCI_TYPE_CONVENTIONAL) + assert ret == 0 + + ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_CFG_REGION_IDX, + size=PCI_CFG_SPACE_SIZE, cb=pci_region_cb, + flags=VFU_REGION_FLAG_RW) + assert ret == 0 + +cap_offsets = ( + PCI_STD_HEADER_SIZEOF, + PCI_STD_HEADER_SIZEOF + PCI_PM_SIZEOF, + # NB: note 4-byte alignment of vsc2 + PCI_STD_HEADER_SIZEOF + PCI_PM_SIZEOF + 8, + 0x80, + 0x90 +) + +def test_add_caps(): + pos = vfu_pci_add_capability(ctx, pos=0, flags=0, + data=struct.pack("ccHH", to_byte(PCI_CAP_ID_PM), b'\0', 0, 0)) + assert pos == cap_offsets[0] + + data = b"abc" + cap = struct.pack("ccc%ds" % len(data), to_byte(PCI_CAP_ID_VNDR), b'\0', + to_byte(3 + len(data)), data) + pos = vfu_pci_add_capability(ctx, pos=0, flags=VFU_CAP_FLAG_READONLY, + data=cap) + + assert pos == cap_offsets[1] + + data = b"Hello world." + cap = struct.pack("ccc%ds" % len(data), to_byte(PCI_CAP_ID_VNDR), b'\0', + to_byte(3 + len(data)), data) + + pos = vfu_pci_add_capability(ctx, pos=0, flags=VFU_CAP_FLAG_CALLBACK, + data=cap) + assert pos == cap_offsets[2] + + pos = vfu_pci_add_capability(ctx, pos=cap_offsets[3], flags=0, data=cap) + assert pos == cap_offsets[3] + + pos = vfu_pci_add_capability(ctx, pos=cap_offsets[4], flags=0, data=cap) + assert pos == cap_offsets[4] + + ret = vfu_realize_ctx(ctx) + assert ret == 0 + +def test_find_caps(): + offset = vfu_pci_find_capability(ctx, False, PCI_CAP_ID_PM) + assert offset == cap_offsets[0] + + space = get_pci_cfg_space(ctx) + + assert space[offset] == PCI_CAP_ID_PM + assert space[offset + PCI_CAP_LIST_NEXT] == cap_offsets[1] + + offset = vfu_pci_find_next_capability(ctx, False, offset, PCI_CAP_ID_PM) + assert offset == 0 + + offset = vfu_pci_find_capability(ctx, False, PCI_CAP_ID_VNDR) + assert offset == cap_offsets[1] + assert space[offset] == PCI_CAP_ID_VNDR + assert space[offset + PCI_CAP_LIST_NEXT] == cap_offsets[2] + + offset = vfu_pci_find_next_capability(ctx, False, offset, PCI_CAP_ID_PM) + assert offset == 0 + + offset = vfu_pci_find_next_capability(ctx, False, 0, PCI_CAP_ID_VNDR) + assert offset == cap_offsets[1] + assert space[offset] == PCI_CAP_ID_VNDR + assert space[offset + PCI_CAP_LIST_NEXT] == cap_offsets[2] + + offset = vfu_pci_find_next_capability(ctx, False, offset, PCI_CAP_ID_VNDR) + assert offset == cap_offsets[2] + assert space[offset] == PCI_CAP_ID_VNDR + assert space[offset + PCI_CAP_LIST_NEXT] == cap_offsets[3] + + offset = vfu_pci_find_next_capability(ctx, False, offset, PCI_CAP_ID_VNDR) + assert offset == cap_offsets[3] + offset = vfu_pci_find_next_capability(ctx, False, offset, PCI_CAP_ID_VNDR) + assert offset == cap_offsets[4] + offset = vfu_pci_find_next_capability(ctx, False, offset, PCI_CAP_ID_VNDR) + assert offset == 0 + + # check for invalid offsets + + offset = vfu_pci_find_next_capability(ctx, False, 8192, PCI_CAP_ID_PM) + assert offset == 0 + assert c.get_errno() == errno.EINVAL + + offset = vfu_pci_find_next_capability(ctx, False, 256, PCI_CAP_ID_PM) + assert offset == 0 + assert c.get_errno() == errno.EINVAL + + offset = vfu_pci_find_next_capability(ctx, False, 255, PCI_CAP_ID_PM) + assert offset == 0 + assert c.get_errno() == errno.EINVAL + + offset = vfu_pci_find_next_capability(ctx, False, + PCI_STD_HEADER_SIZEOF + + PCI_PM_SIZEOF + 1, + PCI_CAP_ID_VNDR) + assert offset == 0 + assert c.get_errno() == errno.ENOENT + +def test_pci_cap_write_hdr(): + sock = connect_client(ctx) + + # offset of struct cap_hdr + offset=cap_offsets[0] + data=b'\x01' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EPERM) + + disconnect_client(ctx, sock) + +def test_pci_cap_readonly(): + sock = connect_client(ctx) + + # start of vendor payload + offset=cap_offsets[1] + 2 + data=b'\x01' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EPERM) + + # offsetof(struct vsc, data) + offset=cap_offsets[1] + 3 + payload = read_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=3) + assert payload == b'abc' + + disconnect_client(ctx, sock) + +def test_pci_cap_callback(): + sock = connect_client(ctx) + + # offsetof(struct vsc, data) + offset=cap_offsets[2] + 3 + data = b"Hello world." + + payload = read_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data)) + assert payload == data + + data = b"Bye world." + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data) + + payload = read_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data)) + assert payload == data + + disconnect_client(ctx, sock) + +def test_pci_cap_write_pmcs(): + sock = connect_client(ctx) + + # struct pc + + offset=cap_offsets[0] + 3 + data=b'\x01\x02' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EINVAL) + + offset=cap_offsets[0] + 2 + data=b'\x01' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EINVAL) + + offset=cap_offsets[0] + 2 + data=b'\x01\x02' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.ENOTSUP) + + # struct pmcs + + offset=cap_offsets[0] + 5 + data=b'\x01\x02' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EINVAL) + + offset=cap_offsets[0] + 4 + data=b'\x01' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EINVAL) + + offset = cap_offsets[0] + 4 + data=b'\x01\x02' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data) + + assert get_pci_cfg_space(ctx)[offset:offset+2] == data + + # pmcsr_se + offset=cap_offsets[0] + 6 + data=b'\x01' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.ENOTSUP) + + # data + offset=cap_offsets[0] + 7 + data=b'\x01' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.ENOTSUP) + + disconnect_client(ctx, sock) + +def test_pci_cap_write_px(): + # FIXME + pass + +def test_pci_cap_write_msix(): + # FIXME + pass + +def test_pci_cap_cleanup(): + vfu_destroy_ctx(ctx) diff --git a/test/py/test_pci_ext_caps.py b/test/py/test_pci_ext_caps.py new file mode 100644 index 0000000..70f8253 --- /dev/null +++ b/test/py/test_pci_ext_caps.py @@ -0,0 +1,302 @@ +# +# 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 +# SERVICESLOSS 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 + +ctx = None + +def test_pci_ext_cap_conventional(): + ctx = vfu_create_ctx(flags=LIBVFIO_USER_FLAG_ATTACH_NB) + assert ctx != None + + ret = vfu_pci_init(ctx, pci_type=VFU_PCI_TYPE_CONVENTIONAL) + assert ret == 0 + + ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_CFG_REGION_IDX, + size=PCI_CFG_SPACE_SIZE, + flags=VFU_REGION_FLAG_RW) + assert ret == 0 + + # struct dsncap + cap = struct.pack("HHII", PCI_EXT_CAP_ID_DSN, 0, 0, 0) + + pos = vfu_pci_add_capability(ctx, pos=0, flags=VFU_CAP_FLAG_EXTENDED, + data=cap) + assert pos == -1 + assert c.get_errno() == errno.EINVAL + + vfu_destroy_ctx(ctx) + +def test_pci_ext_cap_setup(): + global ctx + + ctx = vfu_create_ctx(flags=LIBVFIO_USER_FLAG_ATTACH_NB) + assert ctx != None + + ret = vfu_pci_init(ctx) + assert ret == 0 + + ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_CFG_REGION_IDX, + size=PCI_CFG_SPACE_EXP_SIZE, cb=pci_region_cb, + flags=VFU_REGION_FLAG_RW) + assert ret == 0 + +def test_pci_ext_cap_unknown_cap(): + cap = struct.pack("HHII", PCI_EXT_CAP_ID_DSN + 99, 0, 0, 0) + + pos = vfu_pci_add_capability(ctx, pos=0, flags=VFU_CAP_FLAG_EXTENDED, + data=cap) + assert pos == -1 + assert c.get_errno() == errno.ENOTSUP + +def test_pci_ext_cap_bad_pos(): + cap = struct.pack("HHII", PCI_EXT_CAP_ID_DSN, 0, 0, 0) + + pos = vfu_pci_add_capability(ctx, pos=(PCI_CFG_SPACE_EXP_SIZE - 2), + flags=VFU_CAP_FLAG_EXTENDED, data=cap) + assert pos == -1 + assert c.get_errno() == errno.EINVAL + + # first cap must be at 256 + pos = vfu_pci_add_capability(ctx, pos=512, + flags=VFU_CAP_FLAG_EXTENDED, data=cap) + assert pos == -1 + assert c.get_errno() == errno.EINVAL + +@c.CFUNCTYPE(c.c_int, c.c_void_p, c.POINTER(c.c_char), + c.c_long, c.c_long, c.c_int) +def pci_region_cb(ctx, buf, count, offset, is_write): + if not is_write: + return read_pci_cfg_space(ctx, buf, count, offset, extended=True) + + return write_pci_cfg_space(ctx, buf, count, offset, extended=True) + +cap_offsets = ( + PCI_CFG_SPACE_SIZE, + PCI_CFG_SPACE_SIZE + PCI_EXT_CAP_DSN_SIZEOF, + PCI_CFG_SPACE_SIZE + PCI_EXT_CAP_DSN_SIZEOF + + PCI_EXT_CAP_VNDR_HDR_SIZEOF + 8, + 512, + 600 +) + +def test_add_ext_caps(): + cap = struct.pack("HHII", PCI_EXT_CAP_ID_DSN, 0, 4, 8) + + pos = vfu_pci_add_capability(ctx, pos=0, flags=VFU_CAP_FLAG_EXTENDED, + data=cap) + assert pos == cap_offsets[0] + + # struct pcie_ext_cap_vsc_hdr + data = b"abcde" + cap = struct.pack("HHHH%ds" % len(data), PCI_EXT_CAP_ID_VNDR, 0, 0x1a, + (len(data) + 8) << 4, data) + + pos = vfu_pci_add_capability(ctx, pos=0, flags=(VFU_CAP_FLAG_EXTENDED | + VFU_CAP_FLAG_READONLY), data=cap) + assert pos == cap_offsets[1] + + data = b"Hello world." + cap = struct.pack("HHHH%ds" % len(data), PCI_EXT_CAP_ID_VNDR, 0, 0x1b, + (len(data) + 8) << 4, data) + + pos = vfu_pci_add_capability(ctx, pos=0, flags=(VFU_CAP_FLAG_EXTENDED | + VFU_CAP_FLAG_CALLBACK), data=cap) + assert pos == cap_offsets[2] + + cap = struct.pack("HHHH%ds" % len(data), PCI_EXT_CAP_ID_VNDR, 0, 0x1c, + (len(data) + 8) << 4, data) + + pos = vfu_pci_add_capability(ctx, pos=cap_offsets[3], + flags=(VFU_CAP_FLAG_EXTENDED | + VFU_CAP_FLAG_CALLBACK), data=cap) + assert pos == cap_offsets[3] + + cap = struct.pack("HHHH%ds" % len(data), PCI_EXT_CAP_ID_VNDR, 0, 0x1d, + (len(data) + 8) << 4, data) + + pos = vfu_pci_add_capability(ctx, pos=cap_offsets[4], + flags=(VFU_CAP_FLAG_EXTENDED | + VFU_CAP_FLAG_CALLBACK), data=cap) + assert pos == cap_offsets[4] + + ret = vfu_realize_ctx(ctx) + assert ret == 0 + +def test_find_ext_caps(): + offset = vfu_pci_find_capability(ctx, True, PCI_EXT_CAP_ID_DSN) + assert offset == cap_offsets[0] + + space = get_pci_ext_cfg_space(ctx) + + cap_id, cap_next = ext_cap_hdr(space, offset) + assert cap_id == PCI_EXT_CAP_ID_DSN + assert cap_next == cap_offsets[1] + + offset = vfu_pci_find_next_capability(ctx, True, offset, PCI_EXT_CAP_ID_DSN) + assert offset == 0 + + offset = vfu_pci_find_capability(ctx, True, PCI_EXT_CAP_ID_VNDR) + assert offset == cap_offsets[1] + cap_id, cap_next = ext_cap_hdr(space, offset) + assert cap_id == PCI_EXT_CAP_ID_VNDR + assert cap_next == cap_offsets[2] + + offset = vfu_pci_find_next_capability(ctx, True, offset, PCI_EXT_CAP_ID_DSN) + assert offset == 0 + + offset = vfu_pci_find_next_capability(ctx, True, 0, PCI_EXT_CAP_ID_VNDR) + assert offset == cap_offsets[1] + cap_id, cap_next = ext_cap_hdr(space, offset) + assert cap_id == PCI_EXT_CAP_ID_VNDR + assert cap_next == cap_offsets[2] + + offset = vfu_pci_find_next_capability(ctx, True, offset, + PCI_EXT_CAP_ID_VNDR) + assert offset == cap_offsets[2] + cap_id, cap_next = ext_cap_hdr(space, offset) + assert cap_id == PCI_EXT_CAP_ID_VNDR + assert cap_next == cap_offsets[3] + + offset = vfu_pci_find_next_capability(ctx, True, offset, + PCI_EXT_CAP_ID_VNDR) + assert offset == cap_offsets[3] + offset = vfu_pci_find_next_capability(ctx, True, offset, + PCI_EXT_CAP_ID_VNDR) + assert offset == cap_offsets[4] + offset = vfu_pci_find_next_capability(ctx, True, offset, + PCI_EXT_CAP_ID_VNDR) + assert offset == 0 + + # check for invalid offsets + + offset = vfu_pci_find_next_capability(ctx, True, 8192, PCI_EXT_CAP_ID_DSN) + assert offset == 0 + assert c.get_errno() == errno.EINVAL + offset = vfu_pci_find_next_capability(ctx, True, 4096, PCI_EXT_CAP_ID_DSN) + assert offset == 0 + assert c.get_errno() == errno.EINVAL + offset = vfu_pci_find_next_capability(ctx, True, 4095, PCI_EXT_CAP_ID_DSN) + assert offset == 0 + assert c.get_errno() == errno.EINVAL + + offset = vfu_pci_find_next_capability(ctx, True, cap_offsets[1] + 1, + PCI_EXT_CAP_ID_DSN) + assert offset == 0 + assert c.get_errno() == errno.ENOENT + +def test_pci_ext_cap_write_hdr(): + sock = connect_client(ctx) + + # struct pcie_ext_cap_hdr + offset = cap_offsets[0] + data = b'\x01' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EPERM) + + # struct pcie_ext_cap_vsc_hdr also + offset = cap_offsets[1] + 4 + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EPERM) + + disconnect_client(ctx, sock) + +def test_pci_ext_cap_readonly(): + sock = connect_client(ctx) + + # start of vendor payload + offset = cap_offsets[1] + 8 + data = b'\x01' + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EPERM) + + offset = cap_offsets[1] + 8 + payload = read_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=5) + assert payload == b'abcde' + + disconnect_client(ctx, sock) + +def test_pci_ext_cap_callback(): + sock = connect_client(ctx) + + # start of vendor payload + offset = cap_offsets[2] + 8 + data = b"Hello world." + + payload = read_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data)) + assert payload == data + + data = b"Bye world." + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data) + + payload = read_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data)) + assert payload == data + + disconnect_client(ctx, sock) + +def test_pci_ext_cap_write_dsn(): + sock = connect_client(ctx) + + data = struct.pack("II", 1, 2); + offset = cap_offsets[0] + 4 + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data, expect=errno.EPERM) + + payload = read_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data)) + + # unchanged! + assert payload == struct.pack("II", 4, 8) + + disconnect_client(ctx, sock) + +def test_pci_ext_cap_write_vendor(): + sock = connect_client(ctx) + + data = struct.pack("II", 0x1, 0x2); + # start of vendor payload + offset = cap_offsets[2] + 8 + write_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data), data=data) + + payload = read_region(ctx, sock, VFU_PCI_DEV_CFG_REGION_IDX, offset=offset, + count=len(data)) + + assert payload == data + + disconnect_client(ctx, sock) + +def test_pci_ext_cap_cleanup(): + vfu_destroy_ctx(ctx) diff --git a/test/py/test_vfu_realize_ctx.py b/test/py/test_vfu_realize_ctx.py index c35612c..9b863cb 100644 --- a/test/py/test_vfu_realize_ctx.py +++ b/test/py/test_vfu_realize_ctx.py @@ -107,7 +107,7 @@ def test_vfu_realize_ctx_caps(): 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)) + "ccHH", to_byte(PCI_CAP_ID_PM), b'\0', 0, 0)) assert pos == PCI_STD_HEADER_SIZEOF ret = vfu_realize_ctx(ctx) diff --git a/test/unit-tests.c b/test/unit-tests.c index df51b08..00c71c4 100644 --- a/test/unit-tests.c +++ b/test/unit-tests.c @@ -696,420 +696,6 @@ test_get_region_info(UNUSED void **state) /* FIXME add check for multiple sparse areas */ } -static bool pci_caps_writing = true; - -static ssize_t -test_pci_caps_region_cb(vfu_ctx_t *vfu_ctx, char *buf, size_t count, - loff_t offset, bool is_write) -{ - uint8_t *ptr = pci_config_space_ptr(vfu_ctx, offset); - - if (!pci_caps_writing) { - assert_int_equal(is_write, false); - memcpy(buf, ptr, count); - return count; - } - - assert_int_equal(is_write, true); - assert_int_equal(offset, PCI_STD_HEADER_SIZEOF + PCI_PM_SIZEOF + 8 + - offsetof(struct vsc, data)); - assert_int_equal(count, 10); - assert_memory_equal(ptr, "Hello world.", 10); - memcpy(ptr, buf, count); - return count; -} - -static void -test_pci_caps(void **state UNUSED) -{ - vfu_pci_config_space_t config_space; - struct vsc *vsc1 = alloca(sizeof(*vsc1) + 3); - struct vsc *vsc2 = alloca(sizeof(*vsc2) + 13); - struct vsc *vsc3 = alloca(sizeof(*vsc3) + 13); - struct vsc *vsc4 = alloca(sizeof(*vsc4) + 13); - struct pmcap pm = { { 0 } }; - size_t expoffsets[] = { - PCI_STD_HEADER_SIZEOF, - PCI_STD_HEADER_SIZEOF + PCI_PM_SIZEOF, - /* NB: note 4-byte alignment of vsc2. */ - PCI_STD_HEADER_SIZEOF + PCI_PM_SIZEOF + 8, - 0x80, - 0x90 - }; - size_t offset; - ssize_t ret; - char buf[256]; - - vfu_ctx.pci.config_space = &config_space; - - memset(&config_space, 0, sizeof(config_space)); - - vfu_ctx.reg_info = calloc(VFU_PCI_DEV_NUM_REGIONS, - sizeof(*vfu_ctx.reg_info)); - - pm.hdr.id = PCI_CAP_ID_PM; - pm.pmcs.raw = 0xef01; - - vsc1->hdr.id = PCI_CAP_ID_VNDR; - vsc1->size = 6; - memcpy(vsc1->data, "abc", 3); - - vsc2->hdr.id = PCI_CAP_ID_VNDR; - vsc2->size = 16; - memcpy(vsc2->data, "Hello world.", 13); - - vsc3->hdr.id = PCI_CAP_ID_VNDR; - vsc3->size = 16; - memcpy(vsc3->data, "Hello world.", 13); - - vsc4->hdr.id = PCI_CAP_ID_VNDR; - vsc4->size = 16; - memcpy(vsc4->data, "Hello world.", 13); - - offset = vfu_pci_add_capability(&vfu_ctx, 0, VFU_CAP_FLAG_CALLBACK, &pm); - assert_int_equal(-1, offset); - assert_int_equal(EINVAL, errno); - - offset = vfu_pci_add_capability(&vfu_ctx, 256, 0, &pm); - assert_int_equal(-1, offset); - assert_int_equal(EINVAL, errno); - - vfu_ctx.reg_info[VFU_PCI_DEV_CFG_REGION_IDX].cb = test_pci_caps_region_cb; - vfu_ctx.reg_info[VFU_PCI_DEV_CFG_REGION_IDX].size = PCI_CFG_SPACE_SIZE; - - offset = vfu_pci_add_capability(&vfu_ctx, 0, 0, &pm); - assert_int_equal(expoffsets[0], offset); - offset = vfu_pci_add_capability(&vfu_ctx, 0, VFU_CAP_FLAG_READONLY, vsc1); - assert_int_equal(expoffsets[1], offset); - offset = vfu_pci_add_capability(&vfu_ctx, 0, VFU_CAP_FLAG_CALLBACK, vsc2); - assert_int_equal(expoffsets[2], offset); - offset = vfu_pci_add_capability(&vfu_ctx, expoffsets[3], 0, vsc3); - assert_int_equal(expoffsets[3], offset); - offset = vfu_pci_add_capability(&vfu_ctx, expoffsets[4], 0, vsc4); - assert_int_equal(expoffsets[4], offset); - - offset = vfu_pci_find_capability(&vfu_ctx, false, PCI_CAP_ID_PM); - assert_int_equal(expoffsets[0], offset); - assert_int_equal(PCI_CAP_ID_PM, config_space.raw[offset]); - assert_int_equal(expoffsets[1], - config_space.raw[offset + PCI_CAP_LIST_NEXT]); - - offset = vfu_pci_find_next_capability(&vfu_ctx, false, offset, - PCI_CAP_ID_PM); - assert_int_equal(0, offset); - - offset = vfu_pci_find_capability(&vfu_ctx, false, PCI_CAP_ID_VNDR); - assert_int_equal(expoffsets[1], offset); - assert_int_equal(PCI_CAP_ID_VNDR, config_space.raw[offset]); - assert_int_equal(expoffsets[2], - config_space.raw[offset + PCI_CAP_LIST_NEXT]); - - offset = vfu_pci_find_next_capability(&vfu_ctx, false, offset, - PCI_CAP_ID_PM); - assert_int_equal(0, offset); - - offset = vfu_pci_find_next_capability(&vfu_ctx, false, 0, PCI_CAP_ID_VNDR); - assert_int_equal(expoffsets[1], offset); - assert_int_equal(PCI_CAP_ID_VNDR, config_space.raw[offset]); - assert_int_equal(expoffsets[2], - config_space.raw[offset + PCI_CAP_LIST_NEXT]); - - offset = vfu_pci_find_next_capability(&vfu_ctx, false, - offset, PCI_CAP_ID_VNDR); - assert_int_equal(expoffsets[2], offset); - assert_int_equal(PCI_CAP_ID_VNDR, config_space.raw[offset]); - assert_int_equal(expoffsets[3], - config_space.raw[offset + PCI_CAP_LIST_NEXT]); - - offset = vfu_pci_find_next_capability(&vfu_ctx, false, - offset, PCI_CAP_ID_VNDR); - assert_int_equal(expoffsets[3], offset); - offset = vfu_pci_find_next_capability(&vfu_ctx, false, - offset, PCI_CAP_ID_VNDR); - assert_int_equal(expoffsets[4], offset); - offset = vfu_pci_find_next_capability(&vfu_ctx, false, - offset, PCI_CAP_ID_VNDR); - assert_int_equal(0, offset); - - /* check for invalid offsets */ - - offset = vfu_pci_find_next_capability(&vfu_ctx, false, 8192, PCI_CAP_ID_PM); - assert_int_equal(0, offset); - assert_int_equal(EINVAL, errno); - offset = vfu_pci_find_next_capability(&vfu_ctx, false, 256, PCI_CAP_ID_PM); - assert_int_equal(0, offset); - assert_int_equal(EINVAL, errno); - offset = vfu_pci_find_next_capability(&vfu_ctx, false, 255, PCI_CAP_ID_PM); - assert_int_equal(0, offset); - assert_int_equal(EINVAL, errno); - - offset = vfu_pci_find_next_capability(&vfu_ctx, false, - PCI_STD_HEADER_SIZEOF + - PCI_PM_SIZEOF + 1, - PCI_CAP_ID_VNDR); - assert_int_equal(0, offset); - assert_int_equal(ENOENT, errno); - - /* check writing PMCS */ - - pm.pmcs.raw = 0xffff; - - ret = pci_config_space_access(&vfu_ctx, (char *)&pm.pmcs, - sizeof(struct pmcs), expoffsets[0] + - offsetof(struct pmcap, pmcs), true); - - assert_int_equal(sizeof(struct pmcs), ret); - - assert_memory_equal(pci_config_space_ptr(&vfu_ctx, expoffsets[0] + - offsetof(struct pmcap, pmcs)), - &pm.pmcs, sizeof(struct pmcs)); - - /* check read only capability */ - - ret = pci_config_space_access(&vfu_ctx, (char *)vsc1->data, 3, - expoffsets[1] + offsetof(struct vsc, data), - false); - assert_int_equal(ret, 3); - assert_memory_equal(vsc1->data, "abc", 3); - - ret = pci_config_space_access(&vfu_ctx, "ced", 3, - expoffsets[1] + offsetof(struct vsc, data), - true); - assert_int_equal(-1, ret); - assert_int_equal(EPERM, errno); - - /* check capability callback */ - - ret = pci_config_space_access(&vfu_ctx, "Bye world.", 10, - expoffsets[2] + offsetof(struct vsc, data), - true); - - assert_int_equal(ret, 10); - assert_memory_equal(pci_config_space_ptr(&vfu_ctx, - expoffsets[2] + offsetof(struct vsc, data)), - "Bye world.", 10); - - /* check straddling read */ - - pci_caps_writing = false; - - ret = pci_config_space_access(&vfu_ctx, buf, sizeof (buf), 0, false); - - assert_int_equal(ret, sizeof (buf)); - assert_memory_equal(pci_config_space_ptr(&vfu_ctx, 0), buf, sizeof (buf)); - - free(vfu_ctx.reg_info); -} - -static bool pci_ext_caps_writing = true; - -static ssize_t -test_pci_ext_caps_region_cb(vfu_ctx_t *vfu_ctx, char *buf, size_t count, - loff_t offset, bool is_write) -{ - uint8_t *ptr = pci_config_space_ptr(vfu_ctx, offset); - - if (!pci_ext_caps_writing) { - assert_int_equal(is_write, false); - memcpy(buf, ptr, count); - return count; - } - - assert_int_equal(is_write, true); - assert_int_equal(offset, PCI_CFG_SPACE_SIZE + sizeof(struct dsncap) + - sizeof(struct pcie_ext_cap_vsc_hdr) + 8 + - sizeof(struct pcie_ext_cap_vsc_hdr)); - assert_int_equal(count, 10); - assert_memory_equal(ptr, "Hello world.", 10); - memcpy(ptr, buf, count); - return count; -} - -static void -test_pci_ext_caps(void **state UNUSED) -{ - uint8_t config_space[PCI_CFG_SPACE_EXP_SIZE] = { 0, }; - struct pcie_ext_cap_hdr *hdr; - size_t explens[] = { - sizeof(struct pcie_ext_cap_vsc_hdr) + 5, - sizeof(struct pcie_ext_cap_vsc_hdr) + 13, - sizeof(struct pcie_ext_cap_vsc_hdr) + 13, - sizeof(struct pcie_ext_cap_vsc_hdr) + 13, - }; - struct pcie_ext_cap_vsc_hdr *vsc1 = alloca(explens[0]); - struct pcie_ext_cap_vsc_hdr *vsc2 = alloca(explens[1]); - struct pcie_ext_cap_vsc_hdr *vsc3 = alloca(explens[2]); - struct pcie_ext_cap_vsc_hdr *vsc4 = alloca(explens[3]); - size_t expoffsets[] = { - PCI_CFG_SPACE_SIZE, - PCI_CFG_SPACE_SIZE + sizeof(struct dsncap), - PCI_CFG_SPACE_SIZE + sizeof(struct dsncap) + sizeof(*vsc1) + 8, - 512, - 600 - }; - struct dsncap dsn; - size_t offset; - ssize_t ret; - char buf[512]; - - vfu_ctx.pci.config_space = (void *)&config_space; - vfu_ctx.pci.type = VFU_PCI_TYPE_EXPRESS; - vfu_ctx.reg_info = calloc(VFU_PCI_DEV_NUM_REGIONS, - sizeof(*vfu_ctx.reg_info)); - vfu_ctx.reg_info[VFU_PCI_DEV_CFG_REGION_IDX].cb = test_pci_ext_caps_region_cb; - vfu_ctx.reg_info[VFU_PCI_DEV_CFG_REGION_IDX].size = PCI_CFG_SPACE_EXP_SIZE; - - memset(&dsn, 0, sizeof (dsn)); - - dsn.hdr.id = PCI_EXT_CAP_ID_DSN; - dsn.sn_lo = 0x4; - dsn.sn_hi = 0x8; - - memset(vsc1, 0, explens[0]); - vsc1->len = explens[0]; - vsc1->hdr.id = PCI_EXT_CAP_ID_VNDR; - memcpy(vsc1->data, "abcde", 5); - - memset(vsc2, 0, explens[1]); - vsc2->len = explens[1]; - vsc2->hdr.id = PCI_EXT_CAP_ID_VNDR; - memcpy(vsc2->data, "Hello world.", 13); - - memset(vsc3, 0, explens[2]); - vsc3->len = explens[2]; - vsc3->hdr.id = PCI_EXT_CAP_ID_VNDR; - memcpy(vsc3->data, "Hello world.", 13); - - memset(vsc4, 0, explens[3]); - vsc4->len = explens[3]; - vsc4->hdr.id = PCI_EXT_CAP_ID_VNDR; - memcpy(vsc4->data, "Hello world.", 13); - - offset = vfu_pci_add_capability(&vfu_ctx, 4096, VFU_CAP_FLAG_EXTENDED, &dsn); - assert_int_equal(-1, offset); - assert_int_equal(EINVAL, errno); - - /* First cap must be at 256 */ - offset = vfu_pci_add_capability(&vfu_ctx, 512, VFU_CAP_FLAG_EXTENDED, &dsn); - assert_int_equal(-1, offset); - assert_int_equal(EINVAL, errno); - - offset = vfu_pci_add_capability(&vfu_ctx, 0, VFU_CAP_FLAG_EXTENDED, &dsn); - assert_int_equal(expoffsets[0], offset); - offset = vfu_pci_add_capability(&vfu_ctx, 0, VFU_CAP_FLAG_EXTENDED | - VFU_CAP_FLAG_READONLY, vsc1); - assert_int_equal(expoffsets[1], offset); - offset = vfu_pci_add_capability(&vfu_ctx, 0, VFU_CAP_FLAG_EXTENDED | - VFU_CAP_FLAG_CALLBACK, vsc2); - assert_int_equal(expoffsets[2], offset); - offset = vfu_pci_add_capability(&vfu_ctx, expoffsets[3], - VFU_CAP_FLAG_EXTENDED, vsc3); - assert_int_equal(expoffsets[3], offset); - offset = vfu_pci_add_capability(&vfu_ctx, expoffsets[4], - VFU_CAP_FLAG_EXTENDED, vsc4); - assert_int_equal(expoffsets[4], offset); - - offset = vfu_pci_find_capability(&vfu_ctx, true, PCI_EXT_CAP_ID_DSN); - assert_int_equal(expoffsets[0], offset); - hdr = (struct pcie_ext_cap_hdr *)&config_space[offset]; - assert_int_equal(PCI_EXT_CAP_ID_DSN, hdr->id); - assert_int_equal(expoffsets[1], hdr->next); - - offset = vfu_pci_find_next_capability(&vfu_ctx, true, offset, - PCI_EXT_CAP_ID_DSN); - assert_int_equal(0, offset); - - offset = vfu_pci_find_capability(&vfu_ctx, true, PCI_EXT_CAP_ID_VNDR); - assert_int_equal(expoffsets[1], offset); - hdr = (struct pcie_ext_cap_hdr *)&config_space[offset]; - assert_int_equal(PCI_EXT_CAP_ID_VNDR, hdr->id); - assert_int_equal(expoffsets[2], hdr->next); - - offset = vfu_pci_find_next_capability(&vfu_ctx, true, offset, - PCI_EXT_CAP_ID_DSN); - assert_int_equal(0, offset); - - offset = vfu_pci_find_next_capability(&vfu_ctx, true, 0, PCI_EXT_CAP_ID_VNDR); - assert_int_equal(expoffsets[1], offset); - hdr = (struct pcie_ext_cap_hdr *)&config_space[offset]; - assert_int_equal(PCI_EXT_CAP_ID_VNDR, hdr->id); - assert_int_equal(expoffsets[2], hdr->next); - - offset = vfu_pci_find_next_capability(&vfu_ctx, true, - offset, PCI_EXT_CAP_ID_VNDR); - assert_int_equal(expoffsets[2], offset); - hdr = (struct pcie_ext_cap_hdr *)&config_space[offset]; - assert_int_equal(PCI_EXT_CAP_ID_VNDR, hdr->id); - assert_int_equal(expoffsets[3], hdr->next); - - offset = vfu_pci_find_next_capability(&vfu_ctx, true, - offset, PCI_EXT_CAP_ID_VNDR); - assert_int_equal(expoffsets[3], offset); - offset = vfu_pci_find_next_capability(&vfu_ctx, true, - offset, PCI_EXT_CAP_ID_VNDR); - assert_int_equal(expoffsets[4], offset); - offset = vfu_pci_find_next_capability(&vfu_ctx, true, - offset, PCI_EXT_CAP_ID_VNDR); - assert_int_equal(0, offset); - - /* check for invalid offsets */ - - offset = vfu_pci_find_next_capability(&vfu_ctx, true, 8192, - PCI_EXT_CAP_ID_DSN); - assert_int_equal(0, offset); - assert_int_equal(EINVAL, errno); - offset = vfu_pci_find_next_capability(&vfu_ctx, true, 4096, - PCI_EXT_CAP_ID_DSN); - assert_int_equal(0, offset); - assert_int_equal(EINVAL, errno); - offset = vfu_pci_find_next_capability(&vfu_ctx, true, 4095, - PCI_EXT_CAP_ID_DSN); - assert_int_equal(0, offset); - assert_int_equal(EINVAL, errno); - - offset = vfu_pci_find_next_capability(&vfu_ctx, true, - expoffsets[1] + 1, - PCI_EXT_CAP_ID_DSN); - assert_int_equal(0, offset); - assert_int_equal(ENOENT, errno); - - /* check read only capability */ - - ret = pci_config_space_access(&vfu_ctx, (char *)vsc1->data, 5, - expoffsets[1] + offsetof(struct pcie_ext_cap_vsc_hdr, data), - false); - assert_int_equal(ret, 5); - assert_memory_equal(vsc1->data, "abcde", 5); - - ret = pci_config_space_access(&vfu_ctx, "ced", 3, - expoffsets[1] + offsetof(struct pcie_ext_cap_vsc_hdr, data), - true); - assert_int_equal(-1, ret); - assert_int_equal(EPERM, errno); - - /* check capability callback */ - - ret = pci_config_space_access(&vfu_ctx, "Bye world.", 10, - expoffsets[2] + offsetof(struct pcie_ext_cap_vsc_hdr, data), - true); - - assert_int_equal(ret, 10); - assert_memory_equal(pci_config_space_ptr(&vfu_ctx, - expoffsets[2] + offsetof(struct pcie_ext_cap_vsc_hdr, data)), - "Bye world.", 10); - - /* check straddling read */ - - pci_ext_caps_writing = false; - - ret = pci_config_space_access(&vfu_ctx, buf, sizeof (buf), 0, false); - - assert_int_equal(ret, sizeof (buf)); - assert_memory_equal(pci_config_space_ptr(&vfu_ctx, 0), buf, sizeof (buf)); - - free(vfu_ctx.reg_info); -} - static void test_device_get_info(void **state UNUSED) { @@ -1852,8 +1438,6 @@ main(void) 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_get_region_info, 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), cmocka_unit_test_setup(test_setup_sparse_region, setup), cmocka_unit_test_setup(test_dirty_pages_without_dma, setup), |