diff options
-rw-r--r-- | docs/vfio-user.rst | 6 | ||||
-rw-r--r-- | include/libvfio-user.h | 13 | ||||
-rw-r--r-- | lib/libvfio-user.c | 20 | ||||
-rw-r--r-- | lib/private.h | 2 | ||||
-rw-r--r-- | samples/client.c | 8 | ||||
-rw-r--r-- | samples/gpio-pci-idio-16.c | 4 | ||||
-rw-r--r-- | samples/server.c | 83 | ||||
-rw-r--r-- | test/py/libvfio_user.py | 11 | ||||
-rw-r--r-- | test/py/test_device_get_region_info.py | 12 | ||||
-rw-r--r-- | test/unit-tests.c | 25 |
10 files changed, 86 insertions, 98 deletions
diff --git a/docs/vfio-user.rst b/docs/vfio-user.rst index e0c331c..46b2081 100644 --- a/docs/vfio-user.rst +++ b/docs/vfio-user.rst @@ -868,9 +868,9 @@ VFIO region info format The data structure it points is a VFIO cap header defined in ``<linux/vfio.h>``. * *size* is the size of the region. -* *offset* is the offset given to the mmap() system call for regions with the - MMAP attribute. It is also used as the base offset when mapping a VFIO - sparse mmap area, described below. +* *offset* is the offset that should be given to the mmap() system call for + regions with the MMAP attribute. It is also used as the base offset when + mapping a VFIO sparse mmap area, described below. The client sets the ``argsz`` field to indicate the maximum size of the response that the server can send, which must be at least the size of the diff --git a/include/libvfio-user.h b/include/libvfio-user.h index 371daaf..6cb817d 100644 --- a/include/libvfio-user.h +++ b/include/libvfio-user.h @@ -292,6 +292,7 @@ typedef ssize_t (vfu_region_access_cb_t)(vfu_ctx_t *vfu_ctx, char *buf, * @fd: file descriptor of the file backing the region if the region is * mappable; it is the server's responsibility to create a file suitable for * memory mapping by the client. + * @offset: offset of the region within the fd, or zero. * * @returns 0 on success, -1 on error, Sets errno. */ @@ -299,7 +300,7 @@ int vfu_setup_region(vfu_ctx_t *vfu_ctx, int region_idx, size_t size, vfu_region_access_cb_t *region_access, int flags, struct iovec *mmap_areas, uint32_t nr_mmap_areas, - int fd); + int fd, uint64_t offset); /* * Returns the size of the area needed to hold the migration registers at the @@ -857,16 +858,6 @@ size_t vfu_pci_find_next_capability(vfu_ctx_t *vfu_ctx, bool extended, size_t pos, int cap_id); -/** - * Returns the memory offset where the specific region starts in device memory. - * - * @region: the region to translate - * - * @returns the absolute offset - */ -uint64_t -vfu_region_to_offset(uint32_t region); - #ifdef __cplusplus } #endif diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c index c4f6c42..ffd603b 100644 --- a/lib/libvfio-user.c +++ b/lib/libvfio-user.c @@ -379,14 +379,6 @@ handle_device_get_info(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) return 0; } -#define VFU_REGION_SHIFT 40 - -static inline uint64_t -region_to_offset(uint32_t region) -{ - return (uint64_t)region << VFU_REGION_SHIFT; -} - int handle_device_get_region_info(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) { @@ -428,7 +420,7 @@ handle_device_get_region_info(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) /* This might be more than the buffer we actually return. */ out_info->argsz = sizeof(*out_info) + caps_size; out_info->index = in_info->index; - out_info->offset = region_to_offset(out_info->index); + out_info->offset = vfu_reg->offset; out_info->size = vfu_reg->size; out_info->flags = 0; @@ -1325,7 +1317,8 @@ validate_sparse_mmaps_for_migr_reg(vfu_reg_info_t *reg) int vfu_setup_region(vfu_ctx_t *vfu_ctx, int region_idx, size_t size, vfu_region_access_cb_t *cb, int flags, - struct iovec *mmap_areas, uint32_t nr_mmap_areas, int fd) + struct iovec *mmap_areas, uint32_t nr_mmap_areas, + int fd, uint64_t offset) { struct iovec whole_region = { .iov_base = 0, .iov_len = size }; vfu_reg_info_t *reg; @@ -1373,6 +1366,7 @@ vfu_setup_region(vfu_ctx_t *vfu_ctx, int region_idx, size_t size, reg->size = size; reg->cb = cb; reg->fd = fd; + reg->offset = offset; if (mmap_areas == NULL && reg->fd != -1) { mmap_areas = &whole_region; @@ -1596,10 +1590,4 @@ vfu_dma_write(vfu_ctx_t *vfu_ctx, dma_sg_t *sg, void *data) return ret; } -uint64_t -vfu_region_to_offset(uint32_t region) -{ - return region_to_offset(region); -} - /* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/lib/private.h b/lib/private.h index 2d84fb8..11e899c 100644 --- a/lib/private.h +++ b/lib/private.h @@ -115,6 +115,8 @@ typedef struct { int nr_mmap_areas; /* fd for a mappable region, or -1. */ int fd; + /* offset of region within fd. */ + uint64_t offset; } vfu_reg_info_t; struct pci_dev { diff --git a/samples/client.c b/samples/client.c index 420269c..a533d4f 100644 --- a/samples/client.c +++ b/samples/client.c @@ -262,7 +262,8 @@ do_get_device_region_info(int sock, struct vfio_region_info *region_info, } static void -mmap_sparse_areas(int *fds, struct vfio_region_info_cap_sparse_mmap *sparse) +mmap_sparse_areas(int *fds, struct vfio_region_info *region_info, + struct vfio_region_info_cap_sparse_mmap *sparse) { size_t i; @@ -281,7 +282,8 @@ mmap_sparse_areas(int *fds, struct vfio_region_info_cap_sparse_mmap *sparse) } buf[ret + 1] = '\0'; addr = mmap(NULL, sparse->areas[i].size, PROT_READ | PROT_WRITE, - MAP_SHARED, fds[i], sparse->areas[i].offset); + MAP_SHARED, fds[i], region_info->offset + + sparse->areas[i].offset); if (addr == MAP_FAILED) { err(EXIT_FAILURE, "failed to mmap sparse region #%lu in %s (%#llx-%#llx)", @@ -331,7 +333,7 @@ get_device_region_info(int sock, uint32_t index) assert((index == VFU_PCI_DEV_BAR1_REGION_IDX && nr_fds == 2) || (index == VFU_PCI_DEV_MIGR_REGION_IDX && nr_fds == 1)); assert(nr_fds == sparse->nr_areas); - mmap_sparse_areas(fds, sparse); + mmap_sparse_areas(fds, region_info, sparse); } } } diff --git a/samples/gpio-pci-idio-16.c b/samples/gpio-pci-idio-16.c index f9caa04..7bffb5e 100644 --- a/samples/gpio-pci-idio-16.c +++ b/samples/gpio-pci-idio-16.c @@ -205,13 +205,13 @@ main(int argc, char *argv[]) vfu_pci_set_id(vfu_ctx, 0x494f, 0x0dc8, 0x0, 0x0); ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR2_REGION_IDX, 0x100, - &bar2_access, VFU_REGION_FLAG_RW, NULL, 0, -1); + &bar2_access, VFU_REGION_FLAG_RW, NULL, 0, -1, 0); if (ret < 0) { err(EXIT_FAILURE, "failed to setup region"); } ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_MIGR_REGION_IDX, migr_size, - NULL, VFU_REGION_FLAG_RW, NULL, 0, -1); + NULL, VFU_REGION_FLAG_RW, NULL, 0, -1, 0); if (ret < 0) { err(EXIT_FAILURE, "failed to setup migration region"); } diff --git a/samples/server.c b/samples/server.c index 79d645a..6a31251 100644 --- a/samples/server.c +++ b/samples/server.c @@ -430,7 +430,7 @@ int main(int argc, char *argv[]) } }; vfu_ctx_t *vfu_ctx; - FILE *bar1_fp, *migr_fp; + FILE *fp; const vfu_migration_callbacks_t migr_callbacks = { .version = VFU_MIGR_CALLBACKS_VERS, .transition = &migration_device_state_transition, @@ -480,7 +480,7 @@ int main(int argc, char *argv[]) vfu_pci_set_id(vfu_ctx, 0xdead, 0xbeef, 0xcafe, 0xbabe); ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX, sizeof(time_t), - &bar0_access, VFU_REGION_FLAG_RW, NULL, 0, -1); + &bar0_access, VFU_REGION_FLAG_RW, NULL, 0, -1, 0); if (ret < 0) { err(EXIT_FAILURE, "failed to setup BAR0 region"); } @@ -490,16 +490,30 @@ int main(int argc, char *argv[]) * are mappable. The client can still mmap the 2nd page, we can't prohibit * this under Linux. If we really want to prohibit it we have to use * separate files for the same region. + * + * We choose to use a single file which contains both BAR1 and the migration + * registers. They could also be completely different files. */ - if ((bar1_fp = tmpfile()) == NULL) { - err(EXIT_FAILURE, "failed to create BAR1 file"); + if ((fp = tmpfile()) == NULL) { + err(EXIT_FAILURE, "failed to create backing file"); } + server_data.bar1_size = bar1_size; - if (ftruncate(fileno(bar1_fp), server_data.bar1_size) == -1) { - err(EXIT_FAILURE, "failed to truncate BAR1 file"); + + /* + * The migration registers aren't memory mappable, so in order to make the + * rest of the migration region memory mappable we must effectively reserve + * an entire page. + */ + migr_regs_size = vfu_get_migr_register_area_size(); + migr_data_size = page_align(bar1_size + sizeof(time_t)); + migr_size = migr_regs_size + migr_data_size; + + if (ftruncate(fileno(fp), server_data.bar1_size + migr_size) == -1) { + err(EXIT_FAILURE, "failed to truncate backing file"); } server_data.bar1 = mmap(NULL, server_data.bar1_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fileno(bar1_fp), 0); + MAP_SHARED, fileno(fp), 0); if (server_data.bar1 == MAP_FAILED) { err(EXIT_FAILURE, "failed to mmap BAR1"); } @@ -510,60 +524,53 @@ int main(int argc, char *argv[]) ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR1_REGION_IDX, server_data.bar1_size, &bar1_access, VFU_REGION_FLAG_RW, bar1_mmap_areas, 2, - fileno(bar1_fp)); + fileno(fp), 0); if (ret < 0) { err(EXIT_FAILURE, "failed to setup BAR1 region"); } - ret = vfu_setup_device_reset_cb(vfu_ctx, &device_reset); - if (ret < 0) { - err(EXIT_FAILURE, "failed to setup device reset callbacks"); - } - - ret = vfu_setup_device_dma(vfu_ctx, &dma_register, &dma_unregister); - if (ret < 0) { - err(EXIT_FAILURE, "failed to setup device DMA callbacks"); - } - - ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_INTX_IRQ, 1); - if (ret < 0) { - err(EXIT_FAILURE, "failed to setup irq counts"); - } - /* setup migration */ - /* - * The migration registers aren't memory mappable, so in order to make the - * rest of the migration region memory mappable we must effectively reserve - * an entire page. - */ - migr_regs_size = vfu_get_migr_register_area_size(); - migr_data_size = page_align(bar1_size + sizeof(time_t)); - migr_size = migr_regs_size + migr_data_size; - if ((migr_fp = tmpfile()) == NULL) { - err(EXIT_FAILURE, "failed to create migration file"); - } - if (ftruncate(fileno(migr_fp), migr_size) == -1) { - err(EXIT_FAILURE, "failed to truncate migration file"); - } struct iovec migr_mmap_areas[] = { [0] = { .iov_base = (void *)migr_regs_size, .iov_len = migr_data_size }, }; + + /* + * The migration region comes after bar1 in the backing file, so offset is + * server_data.bar1_size. + */ ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_MIGR_REGION_IDX, migr_size, NULL, VFU_REGION_FLAG_RW, migr_mmap_areas, - ARRAY_SIZE(migr_mmap_areas), fileno(migr_fp)); + ARRAY_SIZE(migr_mmap_areas), fileno(fp), + server_data.bar1_size); if (ret < 0) { err(EXIT_FAILURE, "failed to setup migration region"); } + ret = vfu_setup_device_migration_callbacks(vfu_ctx, &migr_callbacks, migr_regs_size); if (ret < 0) { err(EXIT_FAILURE, "failed to setup device migration"); } + ret = vfu_setup_device_reset_cb(vfu_ctx, &device_reset); + if (ret < 0) { + err(EXIT_FAILURE, "failed to setup device reset callbacks"); + } + + ret = vfu_setup_device_dma(vfu_ctx, &dma_register, &dma_unregister); + if (ret < 0) { + err(EXIT_FAILURE, "failed to setup device DMA callbacks"); + } + + ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_INTX_IRQ, 1); + if (ret < 0) { + err(EXIT_FAILURE, "failed to setup irq counts"); + } + ret = vfu_realize_ctx(vfu_ctx); if (ret < 0) { err(EXIT_FAILURE, "failed to realize device"); diff --git a/test/py/libvfio_user.py b/test/py/libvfio_user.py index e889d95..21f7732 100644 --- a/test/py/libvfio_user.py +++ b/test/py/libvfio_user.py @@ -227,7 +227,7 @@ 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_ulong, vfu_region_access_cb_t, c.c_int, c.c_void_p, - c.c_uint32, c.c_int) + c.c_uint32, c.c_int, c.c_ulong) 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_uint32) @@ -239,8 +239,6 @@ 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) -lib.vfu_region_to_offset.argtypes = (c.c_int,) -lib.vfu_region_to_offset.restype = (c.c_ulong) def to_byte(val): @@ -419,7 +417,7 @@ def vfu_destroy_ctx(ctx): os.remove(SOCK_PATH) def vfu_setup_region(ctx, index, size, cb=None, flags=0, - mmap_areas=None, fd=-1): + mmap_areas=None, fd=-1, offset=0): assert ctx != None nr_mmap_areas = 0 @@ -436,7 +434,7 @@ def vfu_setup_region(ctx, index, size, cb=None, flags=0, ret = lib.vfu_setup_region(ctx, index, size, c.cast(cb, vfu_region_access_cb_t), - flags, c_mmap_areas, nr_mmap_areas, fd) + flags, c_mmap_areas, nr_mmap_areas, fd, offset) return ret def vfu_setup_device_nr_irqs(ctx, irqtype, count): @@ -463,6 +461,3 @@ 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) - -def vfu_region_to_offset(region): - return lib.vfu_region_to_offset(region) diff --git a/test/py/test_device_get_region_info.py b/test/py/test_device_get_region_info.py index 101a561..742e689 100644 --- a/test/py/test_device_get_region_info.py +++ b/test/py/test_device_get_region_info.py @@ -50,9 +50,9 @@ def test_device_get_region_info_setup(): mmap_areas = [ (0x2000, 0x1000), (0x4000, 0x2000) ] - ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_BAR2_REGION_IDX, size=0x10000, + ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_BAR2_REGION_IDX, size=0x8000, flags=(VFU_REGION_FLAG_RW | VFU_REGION_FLAG_MEM), - mmap_areas=mmap_areas, fd=f.fileno()) + mmap_areas=mmap_areas, fd=f.fileno(), offset=0x8000) assert ret == 0 f = tempfile.TemporaryFile() @@ -118,7 +118,7 @@ def test_device_get_region_info_larger_argsz(): assert info.index == VFU_PCI_DEV_BAR1_REGION_IDX assert info.cap_off == 0 assert info.size == 4096 - assert info.offset == vfu_region_to_offset(VFU_PCI_DEV_BAR1_REGION_IDX) + assert info.offset == 0 def test_device_get_region_info_small_argsz_caps(): global sock @@ -139,8 +139,8 @@ def test_device_get_region_info_small_argsz_caps(): VFIO_REGION_INFO_FLAG_CAPS) assert info.index == VFU_PCI_DEV_BAR2_REGION_IDX assert info.cap_off == 0 - assert info.size == 0x10000 - assert info.offset == vfu_region_to_offset(VFU_PCI_DEV_BAR2_REGION_IDX) + assert info.size == 0x8000 + assert info.offset == 0x8000 # skip reading the SCM_RIGHTS disconnect_client(ctx, sock) @@ -165,6 +165,8 @@ def test_device_get_region_info_caps(): assert info.argsz == 80 assert info.cap_off == 32 + assert info.size == 0x8000 + assert info.offset == 0x8000 assert cap.id == VFIO_REGION_INFO_CAP_SPARSE_MMAP assert cap.version == 1 diff --git a/test/unit-tests.c b/test/unit-tests.c index cac4445..2d42f6f 100644 --- a/test/unit-tests.c +++ b/test/unit-tests.c @@ -572,18 +572,18 @@ test_setup_sparse_region(void **state UNUSED) /* invalid mappable settings */ ret = vfu_setup_region(&vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX, - 0x2000, NULL, 0, mmap_areas, 2, -1); + 0x2000, NULL, 0, mmap_areas, 2, -1, 0); assert_int_equal(-1, ret); assert_int_equal(EINVAL, errno); ret = vfu_setup_region(&vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX, - 0x2000, NULL, 0, mmap_areas, 0, 1); + 0x2000, NULL, 0, mmap_areas, 0, 1, 0); assert_int_equal(-1, ret); assert_int_equal(EINVAL, errno); /* default mmap area if not given */ ret = vfu_setup_region(&vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX, - 0x2000, NULL, 0, NULL, 0, 1); + 0x2000, NULL, 0, NULL, 0, 1, 0); assert_int_equal(0, ret); free(reg_info.mmap_areas); @@ -591,14 +591,14 @@ test_setup_sparse_region(void **state UNUSED) /* sparse region exceeds region size */ mmap_areas[1].iov_len = 0x1001; ret = vfu_setup_region(&vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX, - 0x2000, NULL, 0, mmap_areas, 2, 0); + 0x2000, NULL, 0, mmap_areas, 2, 0, 0); assert_int_equal(-1, ret); assert_int_equal(EINVAL, errno); /* sparse region within region size */ mmap_areas[1].iov_len = 0x1000; ret = vfu_setup_region(&vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX, - 0x2000, NULL, 0, mmap_areas, 2, 0); + 0x2000, NULL, 0, mmap_areas, 2, 0, 0); assert_int_equal(0, ret); free(reg_info.mmap_areas); @@ -960,7 +960,7 @@ test_setup_migration_region_too_small(void **state) vfu_ctx_t *v = get_vfu_ctx(state); int r = vfu_setup_region(v, VFU_PCI_DEV_MIGR_REGION_IDX, vfu_get_migr_register_area_size() - 1, NULL, - VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1); + VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1, 0); assert_int_equal(-1, r); assert_int_equal(EINVAL, errno); } @@ -971,7 +971,7 @@ test_setup_migration_region_size_ok(void **state) vfu_ctx_t *v = get_vfu_ctx(state); int r = vfu_setup_region(v, VFU_PCI_DEV_MIGR_REGION_IDX, vfu_get_migr_register_area_size(), NULL, - VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1); + VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1, 0); assert_int_equal(0, r); } @@ -981,7 +981,7 @@ test_setup_migration_region_fully_mappable(void **state) struct test_setup_migr_reg_dat *p = *state; int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL, VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, - 0xdeadbeef); + 0xdeadbeef, 0); assert_int_equal(-1, r); assert_int_equal(EINVAL, errno); } @@ -997,7 +997,8 @@ test_setup_migration_region_sparsely_mappable_over_migration_registers(void **st } }; int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL, - VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, mmap_areas, 1, 0xdeadbeef); + VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, mmap_areas, 1, + 0xdeadbeef, 0); assert_int_equal(-1, r); assert_int_equal(EINVAL, errno); } @@ -1014,7 +1015,7 @@ test_setup_migration_region_sparsely_mappable_valid(void **state) }; int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL, VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, mmap_areas, 1, - 0xdeadbeef); + 0xdeadbeef, 0); assert_int_equal(0, r); } @@ -1031,7 +1032,7 @@ test_setup_migration_callbacks_bad_data_offset(void **state) { struct test_setup_migr_reg_dat *p = *state; int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL, - VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1); + VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1, 0); assert_int_equal(0, r); r = vfu_setup_device_migration_callbacks(p->v, &p->c, vfu_get_migr_register_area_size() - 1); @@ -1043,7 +1044,7 @@ test_setup_migration_callbacks(void **state) { struct test_setup_migr_reg_dat *p = *state; int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL, - VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1); + VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1, 0); assert_int_equal(0, r); r = vfu_setup_device_migration_callbacks(p->v, &p->c, vfu_get_migr_register_area_size()); |