aboutsummaryrefslogtreecommitdiff
path: root/test/py/libvfio_user.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/py/libvfio_user.py')
-rw-r--r--test/py/libvfio_user.py290
1 files changed, 249 insertions, 41 deletions
diff --git a/test/py/libvfio_user.py b/test/py/libvfio_user.py
index cbe6156..60c5fbf 100644
--- a/test/py/libvfio_user.py
+++ b/test/py/libvfio_user.py
@@ -41,6 +41,8 @@ import os
import socket
import struct
import syslog
+import copy
+import tempfile
UINT64_MAX = 18446744073709551615
@@ -280,6 +282,23 @@ class iovec_t(Structure):
("iov_len", c.c_int32)
]
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return False
+ return self.iov_base == other.iov_base \
+ and self.iov_len == other.iov_len
+
+ def __str__(self):
+ return "%s-%s" % \
+ (hex(self.iov_base or 0), hex((self.iov_base or 0) + self.iov_len))
+
+ def __copy__(self):
+ cls = self.__class__
+ result = cls.__new__(cls)
+ result.iov_base = self.iov_base
+ result.iov_len = self.iov_len
+ return result
+
class vfio_irq_info(Structure):
_pack_ = 1
@@ -438,6 +457,30 @@ class vfu_dma_info_t(Structure):
("prot", c.c_uint32)
]
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return False
+ return self.iova == other.iova \
+ and self.vaddr == other.vaddr \
+ and self.mapping == other.mapping \
+ and self.page_size == other.page_size \
+ and self.prot == other.prot
+
+ def __str__(self):
+ return "IOVA=%s vaddr=%s mapping=%s page_size=%s prot=%s" % \
+ (self.iova, self.vaddr, self.mapping, hex(self.page_size),
+ bin(self.prot))
+
+ def __copy__(self):
+ cls = self.__class__
+ result = cls.__new__(cls)
+ result.iova = self.iova
+ result.vaddr = self.vaddr
+ result.mapping = self.mapping
+ result.page_size = self.page_size
+ result.prot = self.prot
+ return result
+
class vfio_user_dirty_pages(Structure):
_pack_ = 1
@@ -497,6 +540,23 @@ class dma_sg_t(Structure):
("le_prev", c.c_void_p),
]
+ def __str__(self):
+ return "DMA addr=%s, region index=%s, length=%s, offset=%s, RW=%s" % \
+ (hex(self.dma_addr), self.region, hex(self.length),
+ hex(self.offset), self.writeable)
+
+
+class vfio_user_migration_info(Structure):
+ _pack_ = 1
+ _fields_ = [
+ ("device_state", c.c_uint32),
+ ("reserved", c.c_uint32),
+ ("pending_bytes", c.c_uint64),
+ ("data_offset", c.c_uint64),
+ ("data_size", c.c_uint64),
+ ]
+
+
#
# Util functions
#
@@ -529,10 +589,14 @@ 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)
lib.vfu_irq_trigger.argtypes = (c.c_void_p, c.c_uint)
+vfu_device_quiesce_cb_t = c.CFUNCTYPE(c.c_int, c.c_void_p, use_errno=True)
+lib.vfu_setup_device_quiesce_cb.argtypes = (c.c_void_p,
+ vfu_device_quiesce_cb_t)
vfu_dma_register_cb_t = c.CFUNCTYPE(None, c.c_void_p,
- c.POINTER(vfu_dma_info_t))
-vfu_dma_unregister_cb_t = c.CFUNCTYPE(c.c_int, c.c_void_p,
- c.POINTER(vfu_dma_info_t))
+ c.POINTER(vfu_dma_info_t), use_errno=True)
+vfu_dma_unregister_cb_t = c.CFUNCTYPE(None, c.c_void_p,
+ c.POINTER(vfu_dma_info_t),
+ use_errno=True)
lib.vfu_setup_device_dma.argtypes = (c.c_void_p, vfu_dma_register_cb_t,
vfu_dma_unregister_cb_t)
lib.vfu_setup_device_migration_callbacks.argtypes = (c.c_void_p,
@@ -548,7 +612,7 @@ lib.vfu_create_ioeventfd.argtypes = (c.c_void_p, c.c_uint32, c.c_int,
c.c_size_t, c.c_uint32, c.c_uint32,
c.c_uint64)
-lib.vfu_migr_done.argtypes = (c.c_void_p, c.c_int)
+lib.vfu_device_quiesced.argtypes = (c.c_void_p, c.c_int)
def to_byte(val):
@@ -597,7 +661,7 @@ def disconnect_client(ctx, sock):
# notice client closed connection
ret = vfu_run_ctx(ctx)
assert ret == -1
- assert c.get_errno() == errno.ENOTCONN
+ assert c.get_errno() == errno.ENOTCONN, os.strerror(c.get_errno())
def get_reply(sock, expect=0):
@@ -608,7 +672,8 @@ def get_reply(sock, expect=0):
return buf[16:]
-def msg(ctx, sock, cmd, payload, expect=0, fds=None, rsp=True):
+def msg(ctx, sock, cmd, payload, expect_reply_errno=0, fds=None, rsp=True,
+ expect_run_ctx_errno=None):
"""Round trip a request and reply to the server."""
hdr = vfio_user_header(cmd, size=len(payload))
@@ -618,12 +683,13 @@ def msg(ctx, sock, cmd, payload, expect=0, fds=None, rsp=True):
else:
sock.send(hdr + payload)
- ret = vfu_run_ctx(ctx)
- assert ret >= 0
+ ret = vfu_run_ctx(ctx, expect_errno=expect_run_ctx_errno)
+ if expect_run_ctx_errno is None:
+ assert ret >= 0, os.strerror(c.get_errno())
if not rsp:
return
- return get_reply(sock, expect=expect)
+ return get_reply(sock, expect=expect_reply_errno)
def get_reply_fds(sock, expect=0):
@@ -698,7 +764,7 @@ def write_pci_cfg_space(ctx, buf, count, offset, extended=False):
def access_region(ctx, sock, is_write, region, offset, count,
- data=None, expect=0, rsp=True):
+ data=None, expect=0, rsp=True, expect_run_ctx_errno=None):
# struct vfio_user_region_access
payload = struct.pack("QII", offset, region, count)
if is_write:
@@ -706,22 +772,26 @@ def access_region(ctx, sock, is_write, region, offset, count,
cmd = VFIO_USER_REGION_WRITE if is_write else VFIO_USER_REGION_READ
- result = msg(ctx, sock, cmd, payload, expect=expect, rsp=rsp)
+ result = msg(ctx, sock, cmd, payload, expect_reply_errno=expect, rsp=rsp,
+ expect_run_ctx_errno=expect_run_ctx_errno)
if is_write:
return None
- return skip("QII", result)
+ if rsp:
+ return skip("QII", result)
-def write_region(ctx, sock, region, offset, count, data, expect=0, rsp=True):
+def write_region(ctx, sock, region, offset, count, data, expect=0, rsp=True,
+ expect_run_ctx_errno=None):
access_region(ctx, sock, True, region, offset, count, data, expect=expect,
- rsp=rsp)
+ rsp=rsp, expect_run_ctx_errno=expect_run_ctx_errno)
-def read_region(ctx, sock, region, offset, count, expect=0):
+def read_region(ctx, sock, region, offset, count, expect=0, rsp=True,
+ expect_run_ctx_errno=None):
return access_region(ctx, sock, False, region, offset, count,
- expect=expect)
+ expect=expect, rsp=rsp, expect_run_ctx_errno=expect_run_ctx_errno)
def ext_cap_hdr(buf, offset):
@@ -733,18 +803,60 @@ def ext_cap_hdr(buf, offset):
return cap_id, cap_next
-@vfu_dma_register_cb_t
def dma_register(ctx, info):
pass
-@vfu_dma_unregister_cb_t
+@vfu_dma_register_cb_t
+def __dma_register(ctx, info):
+ # The copy is required because in case of deliberate failure (e.g.
+ # test_dma_map_busy_reply_fail) the memory gets deallocated and mock only
+ # records the pointer, so the contents are all null/zero.
+ dma_register(ctx, copy.copy(info.contents))
+
+
def dma_unregister(ctx, info):
pass
+
+
+@vfu_dma_unregister_cb_t
+def __dma_unregister(ctx, info):
+ dma_unregister(ctx, info)
+
+
+def quiesce_cb(ctx):
return 0
-def prepare_ctx_for_dma():
+@vfu_device_quiesce_cb_t
+def _quiesce_cb(ctx):
+ return quiesce_cb(ctx)
+
+
+def vfu_setup_device_quiesce_cb(ctx, quiesce_cb=_quiesce_cb):
+ assert ctx is not None
+ lib.vfu_setup_device_quiesce_cb(ctx,
+ c.cast(quiesce_cb,
+ vfu_device_quiesce_cb_t))
+
+
+def reset_cb(ctx, reset_type):
+ return 0
+
+
+@vfu_reset_cb_t
+def _reset_cb(ctx, reset_type):
+ return reset_cb(ctx, reset_type)
+
+
+def vfu_setup_device_reset_cb(ctx, cb=_reset_cb):
+ assert ctx is not None
+ return lib.vfu_setup_device_reset_cb(ctx, c.cast(cb, vfu_reset_cb_t))
+
+
+def prepare_ctx_for_dma(dma_register=__dma_register,
+ dma_unregister=__dma_unregister, quiesce=_quiesce_cb,
+ reset=_reset_cb):
ctx = vfu_create_ctx(flags=LIBVFIO_USER_FLAG_ATTACH_NB)
assert ctx is not None
@@ -754,6 +866,23 @@ def prepare_ctx_for_dma():
ret = vfu_setup_device_dma(ctx, dma_register, dma_unregister)
assert ret == 0
+ if quiesce is not None:
+ vfu_setup_device_quiesce_cb(ctx, quiesce)
+
+ if reset is not None:
+ ret = vfu_setup_device_reset_cb(ctx, reset)
+ assert ret == 0
+
+ f = tempfile.TemporaryFile()
+ f.truncate(0x2000)
+
+ mmap_areas = [(0x1000, 0x1000)]
+
+ ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_MIGR_REGION_IDX, size=0x2000,
+ flags=VFU_REGION_FLAG_RW, mmap_areas=mmap_areas,
+ fd=f.fileno())
+ assert ret == 0
+
ret = vfu_realize_ctx(ctx)
assert ret == 0
@@ -769,7 +898,15 @@ msg_id = 1
@c.CFUNCTYPE(None, c.c_void_p, c.c_int, c.c_char_p)
def log(ctx, level, msg):
- print(msg.decode("utf-8"))
+ lvl2str = {syslog.LOG_EMERG: "EMERGENCY",
+ syslog.LOG_ALERT: "ALERT",
+ syslog.LOG_CRIT: "CRITICAL",
+ syslog.LOG_ERR: "ERROR",
+ syslog.LOG_WARNING: "WANRING",
+ syslog.LOG_NOTICE: "NOTICE",
+ syslog.LOG_INFO: "INFO",
+ syslog.LOG_DEBUG: "DEBUG"}
+ print(lvl2str[level] + ": " + msg.decode("utf-8"))
def vfio_user_header(cmd, size, no_reply=False, error=False, error_no=0):
@@ -803,15 +940,21 @@ def vfu_realize_ctx(ctx):
def vfu_attach_ctx(ctx, expect=0):
ret = lib.vfu_attach_ctx(ctx)
if expect == 0:
- assert ret == 0
+ assert ret == 0, "failed to attach: %s" % os.strerror(c.get_errno())
else:
assert ret == -1
assert c.get_errno() == expect
return ret
-def vfu_run_ctx(ctx):
- return lib.vfu_run_ctx(ctx)
+def vfu_run_ctx(ctx, expect_errno=None):
+ ret = lib.vfu_run_ctx(ctx)
+ if expect_errno is not None:
+ assert ret < 0 and expect_errno == c.get_errno(), \
+ "expected '%s' (%d), actual '%s' (%s)" % \
+ (os.strerror(expect_errno), expect_errno,
+ os.strerror(c.get_errno()), c.get_errno())
+ return ret
def vfu_destroy_ctx(ctx):
@@ -821,7 +964,16 @@ def vfu_destroy_ctx(ctx):
os.remove(SOCK_PATH)
-def vfu_setup_region(ctx, index, size, cb=None, flags=0,
+def pci_region_cb(ctx, buf, count, offset, is_write):
+ pass
+
+
+@vfu_region_access_cb_t
+def __pci_region_cb(ctx, buf, count, offset, is_write):
+ return pci_region_cb(ctx, buf, count, offset, is_write)
+
+
+def vfu_setup_region(ctx, index, size, cb=__pci_region_cb, flags=0,
mmap_areas=None, nr_mmap_areas=None, fd=-1, offset=0):
assert ctx is not None
@@ -851,11 +1003,6 @@ def vfu_setup_region(ctx, index, size, cb=None, flags=0,
return ret
-def vfu_setup_device_reset_cb(ctx, cb):
- assert ctx is not None
- return lib.vfu_setup_device_reset_cb(ctx, c.cast(cb, vfu_reset_cb_t))
-
-
def vfu_setup_device_nr_irqs(ctx, irqtype, count):
assert ctx is not None
return lib.vfu_setup_device_nr_irqs(ctx, irqtype, count)
@@ -901,22 +1048,75 @@ def vfu_setup_device_dma(ctx, register_cb=None, unregister_cb=None):
vfu_dma_unregister_cb_t))
+# FIXME some of the migration arguments are probably wrong as in the C version
+# they're pointer. Check how we handle the read/write region callbacks.
+
+def migr_trans_cb(ctx, state):
+ pass
+
+
+@transition_cb_t
+def __migr_trans_cb(ctx, state):
+ return migr_trans_cb(ctx, state)
+
+
+def migr_get_pending_bytes_cb(ctx):
+ pass
+
+
+@get_pending_bytes_cb_t
+def __migr_get_pending_bytes_cb(ctx):
+ return migr_get_pending_bytes_cb(ctx)
+
+
+def migr_prepare_data_cb(ctx, offset, size):
+ pass
+
+
+@prepare_data_cb_t
+def __migr_prepare_data_cb(ctx, offset, size):
+ return migr_prepare_data_cb(ctx, offset, size)
+
+
+def migr_read_data_cb(ctx, buf, count, offset):
+ pass
+
+
+@read_data_cb_t
+def __migr_read_data_cb(ctx, buf, count, offset):
+ return migr_read_data_cb(ctx, buf, count, offset)
+
+
+def migr_write_data_cb(ctx, buf, count, offset):
+ pass
+
+
+@write_data_cb_t
+def __migr_write_data_cb(ctx, buf, count, offset):
+ return migr_write_data_cb(ctx, buf, count, offset)
+
+
+def migr_data_written_cb(ctx, count):
+ pass
+
+
+@data_written_cb_t
+def __migr_data_written_cb(ctx, count):
+ return migr_data_written_cb(ctx, count)
+
+
def vfu_setup_device_migration_callbacks(ctx, cbs=None, offset=0):
assert ctx is not None
- @c.CFUNCTYPE(c.c_int)
- def stub():
- return 0
-
if not cbs:
cbs = vfu_migration_callbacks_t()
cbs.version = VFU_MIGR_CALLBACKS_VERS
- cbs.transition = c.cast(stub, transition_cb_t)
- cbs.get_pending_bytes = c.cast(stub, get_pending_bytes_cb_t)
- cbs.prepare_data = c.cast(stub, prepare_data_cb_t)
- cbs.read_data = c.cast(stub, read_data_cb_t)
- cbs.write_data = c.cast(stub, write_data_cb_t)
- cbs.data_written = c.cast(stub, data_written_cb_t)
+ cbs.transition = __migr_trans_cb
+ cbs.get_pending_bytes = __migr_get_pending_bytes_cb
+ cbs.prepare_data = __migr_prepare_data_cb
+ cbs.read_data = __migr_read_data_cb
+ cbs.write_data = __migr_write_data_cb
+ cbs.data_written = __migr_data_written_cb
return lib.vfu_setup_device_migration_callbacks(ctx, cbs, offset)
@@ -945,7 +1145,15 @@ def vfu_create_ioeventfd(ctx, region_idx, fd, offset, size, flags, datamatch):
flags, datamatch)
-def vfu_migr_done(ctx, err):
- return lib.vfu_migr_done(ctx, err)
+def vfu_device_quiesced(ctx, err):
+ return lib.vfu_device_quiesced(ctx, err)
+
+
+def fail_with_errno(err):
+ def side_effect(args, *kwargs):
+ c.set_errno(err)
+ return -1
+ return side_effect
+
# ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: #