diff options
-rw-r--r-- | include/CMakeLists.txt | 2 | ||||
-rw-r--r-- | include/libvfio-user.h | 2 | ||||
-rw-r--r-- | include/pci_defs.h (renamed from include/pci.h) | 8 | ||||
-rw-r--r-- | lib/libvfio-user.c | 183 | ||||
-rw-r--r-- | lib/pci.c | 143 | ||||
-rw-r--r-- | lib/pci.h | 56 | ||||
-rw-r--r-- | lib/private.h | 23 |
7 files changed, 217 insertions, 200 deletions
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 07c6836..c209d6c 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -36,5 +36,5 @@ install(FILES "pci_caps/msi.h" DESTINATION ${VFIO_USER_HEADERS_DIR}/pci_caps) install(FILES "pci_caps/msix.h" DESTINATION ${VFIO_USER_HEADERS_DIR}/pci_caps) install(FILES "pci_caps/pm.h" DESTINATION ${VFIO_USER_HEADERS_DIR}/pci_caps) install(FILES "pci_caps/px.h" DESTINATION ${VFIO_USER_HEADERS_DIR}/pci_caps) -install(FILES "pci.h" DESTINATION ${VFIO_USER_HEADERS_DIR}) +install(FILES "pci_defs.h" DESTINATION ${VFIO_USER_HEADERS_DIR}) install(FILES "vfio-user.h" DESTINATION ${VFIO_USER_HEADERS_DIR}) diff --git a/include/libvfio-user.h b/include/libvfio-user.h index f92e0cd..75878c1 100644 --- a/include/libvfio-user.h +++ b/include/libvfio-user.h @@ -43,7 +43,7 @@ #include <unistd.h> #include <syslog.h> -#include "pci.h" +#include "pci_defs.h" #include "pci_caps/pm.h" #include "pci_caps/px.h" #include "pci_caps/msi.h" diff --git a/include/pci.h b/include/pci_defs.h index 112becf..5a77b65 100644 --- a/include/pci.h +++ b/include/pci_defs.h @@ -30,8 +30,8 @@ * */ -#ifndef LIBVFIO_USER_PCI_H -#define LIBVFIO_USER_PCI_H +#ifndef LIBVFIO_USER_PCI_DEFS_H +#define LIBVFIO_USER_PCI_DEFS_H #include <stdint.h> #include <stdbool.h> @@ -42,6 +42,8 @@ extern "C" { #endif /* + * PCI standard header definitions. + * * TODO lots of the sizes of each member are defined in pci_regs.h, use those * instead? */ @@ -194,6 +196,6 @@ _Static_assert(sizeof(vfu_pci_config_space_t) == 0x100, } #endif -#endif /* LIBVFIO_USER_PCI_H */ +#endif /* LIBVFIO_USER_PCI_DEFS_H */ /* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c index 599b17f..6d75e15 100644 --- a/lib/libvfio-user.c +++ b/lib/libvfio-user.c @@ -51,11 +51,12 @@ #include "cap.h" #include "dma.h" +#include "irq.h" #include "libvfio-user.h" +#include "migration.h" +#include "pci.h" #include "private.h" #include "tran_sock.h" -#include "migration.h" -#include "irq.h" void vfu_log(vfu_ctx_t *vfu_ctx, int level, const char *fmt, ...) @@ -77,13 +78,6 @@ vfu_log(vfu_ctx_t *vfu_ctx, int level, const char *fmt, ...) errno = _errno; } -static inline int -ERROR(int err) -{ - errno = err; - return -1; -} - static size_t get_vfio_caps_size(bool is_migr_reg, struct vfu_sparse_mmap_areas *m) { @@ -171,21 +165,6 @@ dev_get_caps(vfu_ctx_t *vfu_ctx, vfu_reg_info_t *vfu_reg, bool is_migr_reg, return 0; } -#define VFU_REGION_SHIFT 40 -#define VFU_REGION_MASK ((1ULL << VFU_REGION_SHIFT) - 1) - -uint64_t -region_to_offset(uint32_t region) -{ - return (uint64_t)region << VFU_REGION_SHIFT; -} - -uint32_t -offset_to_region(uint64_t offset) -{ - return (offset >> VFU_REGION_SHIFT) & VFU_REGION_MASK; -} - #ifdef VFU_VERBOSE_LOGGING void dump_buffer(const char *prefix, const char *buf, uint32_t count) @@ -293,47 +272,6 @@ vfu_get_region(loff_t pos, size_t count, loff_t *off) return r; } -static uint32_t -region_size(vfu_ctx_t *vfu_ctx, int region) -{ - assert(region >= VFU_PCI_DEV_BAR0_REGION_IDX && region <= VFU_PCI_DEV_VGA_REGION_IDX); - return vfu_ctx->reg_info[region].size; -} - -static uint32_t -pci_config_space_size(vfu_ctx_t *vfu_ctx) -{ - return region_size(vfu_ctx, VFU_PCI_DEV_CFG_REGION_IDX); -} - -/* - * Accesses the non-standard part (offset >= 0x40) of the PCI configuration - * space. - */ -static ssize_t -handle_pci_config_space_access(vfu_ctx_t *vfu_ctx, char *buf, size_t count, - loff_t pos, bool is_write) -{ - int ret; - - count = MIN(pci_config_space_size(vfu_ctx), count); - if (is_write) { - ret = cap_maybe_access(vfu_ctx, vfu_ctx->pci.caps, buf, count, pos); - if (ret < 0) { - vfu_log(vfu_ctx, LOG_ERR, "bad access to capabilities %#lx-%#lx\n", - pos, pos + count); - return ret; - } - } else { - /* - * It is legitimate to read the non-standard part of the PCI config - * space even if there are no capabilities. - */ - memcpy(buf, vfu_ctx->pci.config_space->raw + pos, count); - } - return count; -} - static ssize_t do_access(vfu_ctx_t *vfu_ctx, char *buf, uint8_t count, uint64_t pos, bool is_write) { @@ -356,8 +294,7 @@ do_access(vfu_ctx_t *vfu_ctx, char *buf, uint8_t count, uint64_t pos, bool is_wr } if (idx == VFU_PCI_DEV_CFG_REGION_IDX) { - return handle_pci_config_space_access(vfu_ctx, buf, count, offset, - is_write); + return pci_config_space_access(vfu_ctx, buf, count, offset, is_write); } if (is_migr_reg(vfu_ctx, idx)) { @@ -465,18 +402,21 @@ vfu_access(vfu_ctx_t *vfu_ctx, bool is_write, char *rwbuf, uint32_t count, #endif _count = count; - ret = vfu_pci_hdr_access(vfu_ctx, &_count, pos, is_write, rwbuf); - if (ret != 0) { - /* FIXME shouldn't we fail here? */ - vfu_log(vfu_ctx, LOG_ERR, "failed to access PCI header: %s", - strerror(-ret)); + + if (pci_is_hdr_access(*pos)) { + ret = pci_hdr_access(vfu_ctx, &_count, pos, is_write, rwbuf); + if (ret != 0) { + /* FIXME shouldn't we fail here? */ + vfu_log(vfu_ctx, LOG_ERR, "failed to access PCI header: %s", + strerror(-ret)); #ifdef VFU_VERBOSE_LOGGING - dump_buffer("buffer write", rwbuf, _count); + dump_buffer("buffer write", rwbuf, _count); #endif + } } /* - * count is how much has been processed by vfu_pci_hdr_access, + * count is how much has been processed by pci_hdr_access, * _count is how much there's left to be processed by vfu_access */ processed = count - _count; @@ -1151,7 +1091,7 @@ vfu_realize_ctx(vfu_ctx_t *vfu_ctx) cfg_reg->size = PCI_CFG_SPACE_SIZE; } - // This maybe allocated by vfu_setup_pci_config_hdr(). + // This may have been allocated by vfu_setup_pci_config_hdr(). if (vfu_ctx->pci.config_space == NULL) { vfu_ctx->pci.config_space = calloc(1, cfg_reg->size); if (vfu_ctx->pci.config_space == NULL) { @@ -1285,12 +1225,6 @@ vfu_attach_ctx(vfu_ctx_t *vfu_ctx) return vfu_ctx->trans->attach(vfu_ctx); } -bool -is_valid_pci_type(vfu_pci_type_t t) -{ - return t >= VFU_PCI_TYPE_CONVENTIONAL && t <= VFU_PCI_TYPE_EXPRESS; -} - vfu_ctx_t * vfu_create_ctx(vfu_trans_t trans, const char *path, int flags, void *pvt, vfu_dev_type_t dev_type) @@ -1380,83 +1314,6 @@ vfu_setup_log(vfu_ctx_t *vfu_ctx, vfu_log_fn_t *log, int log_level) return 0; } -int -vfu_pci_setup_config_hdr(vfu_ctx_t *vfu_ctx, vfu_pci_hdr_id_t id, - vfu_pci_hdr_ss_t ss, vfu_pci_hdr_cc_t cc, - vfu_pci_type_t pci_type, - int revision __attribute__((unused))) -{ - vfu_pci_config_space_t *config_space; - size_t size; - - assert(vfu_ctx != NULL); - - /* - * TODO there no real reason why we shouldn't allow this, we should just - * clean up and redo it. - */ - if (vfu_ctx->pci.config_space != NULL) { - vfu_log(vfu_ctx, LOG_ERR, "PCI configuration space header already setup"); - return ERROR(EEXIST); - } - - vfu_ctx->pci.type = pci_type; - switch (vfu_ctx->pci.type) { - case VFU_PCI_TYPE_CONVENTIONAL: - case VFU_PCI_TYPE_PCI_X_1: - size = PCI_CFG_SPACE_SIZE; - break; - case VFU_PCI_TYPE_PCI_X_2: - case VFU_PCI_TYPE_EXPRESS: - size = PCI_CFG_SPACE_EXP_SIZE; - break; - default: - vfu_log(vfu_ctx, LOG_ERR, "invalid PCI type %d", pci_type); - return ERROR(EINVAL); - } - - // Allocate a buffer for the config space. - config_space = calloc(1, size); - if (config_space == NULL) { - return ERROR(ENOMEM); - } - - config_space->hdr.id = id; - config_space->hdr.ss = ss; - config_space->hdr.cc = cc; - vfu_ctx->pci.config_space = config_space; - vfu_ctx->reg_info[VFU_PCI_DEV_CFG_REGION_IDX].size = size; - - return 0; -} - -int -vfu_pci_setup_caps(vfu_ctx_t *vfu_ctx, vfu_cap_t **caps, int nr_caps) -{ - int ret; - - assert(vfu_ctx != NULL); - - if (vfu_ctx->pci.caps != NULL) { - vfu_log(vfu_ctx, LOG_ERR, "capabilities are already setup"); - return ERROR(EEXIST); - } - - if (caps == NULL || nr_caps == 0) { - vfu_log(vfu_ctx, LOG_ERR, "Invalid args passed"); - return ERROR(EINVAL); - } - - vfu_ctx->pci.caps = caps_create(vfu_ctx, caps, nr_caps, &ret); - if (vfu_ctx->pci.caps == NULL) { - vfu_log(vfu_ctx, LOG_ERR, "failed to create PCI capabilities: %s", - strerror(ret)); - return ERROR(ret); - } - - return 0; -} - static int copy_sparse_mmap_areas(vfu_reg_info_t *reg_info, struct iovec *mmap_areas, uint32_t nr_mmap_areas) @@ -1633,16 +1490,6 @@ vfu_setup_device_migration(vfu_ctx_t *vfu_ctx, vfu_migration_t *migration) return 0; } -/* - * Returns a pointer to the PCI configuration space. - */ -inline vfu_pci_config_space_t * -vfu_pci_get_config_space(vfu_ctx_t *vfu_ctx) -{ - assert(vfu_ctx != NULL); - return vfu_ctx->pci.config_space; -} - inline vfu_reg_info_t * vfu_get_region_info(vfu_ctx_t *vfu_ctx) { @@ -30,22 +30,20 @@ * */ -#include <stdio.h> #include <assert.h> +#include <errno.h> +#include <stdio.h> #include <string.h> #include <sys/param.h> -#include <errno.h> - -#include <linux/pci_regs.h> -#include <linux/vfio.h> +#include "cap.h" #include "common.h" #include "libvfio-user.h" #include "pci.h" #include "private.h" static inline void -vfu_pci_hdr_write_bar(vfu_ctx_t *vfu_ctx, uint16_t bar_index, const char *buf) +pci_hdr_write_bar(vfu_ctx_t *vfu_ctx, uint16_t bar_index, const char *buf) { uint32_t cfg_addr; unsigned long mask; @@ -219,7 +217,7 @@ handle_erom_write(vfu_ctx_t *ctx, vfu_pci_config_space_t *pci, } static inline int -vfu_pci_hdr_write(vfu_ctx_t *vfu_ctx, uint16_t offset, +pci_hdr_write(vfu_ctx_t *vfu_ctx, uint16_t offset, const char *buf, size_t count) { vfu_pci_config_space_t *pci; @@ -255,7 +253,7 @@ vfu_pci_hdr_write(vfu_ctx_t *vfu_ctx, uint16_t offset, case PCI_BASE_ADDRESS_3: case PCI_BASE_ADDRESS_4: case PCI_BASE_ADDRESS_5: - vfu_pci_hdr_write_bar(vfu_ctx, BAR_INDEX(offset), buf); + pci_hdr_write_bar(vfu_ctx, BAR_INDEX(offset), buf); break; case PCI_ROM_ADDRESS: ret = handle_erom_write(vfu_ctx, pci, buf, count); @@ -280,10 +278,9 @@ vfu_pci_hdr_write(vfu_ctx_t *vfu_ctx, uint16_t offset, * @write: whether this is a PCI header write * @count: output parameter that receives the number of bytes read/written */ -static inline int -vfu_do_pci_hdr_access(vfu_ctx_t *vfu_ctx, uint32_t *count, - uint64_t *pos, bool is_write, - char *buf) +int +pci_hdr_access(vfu_ctx_t *vfu_ctx, uint32_t *count, + uint64_t *pos, bool is_write, char *buf) { uint32_t _count; loff_t _pos; @@ -298,7 +295,7 @@ vfu_do_pci_hdr_access(vfu_ctx_t *vfu_ctx, uint32_t *count, _count = MIN(*count, PCI_STD_HEADER_SIZEOF - _pos); if (is_write) { - err = vfu_pci_hdr_write(vfu_ctx, _pos, buf, _count); + err = pci_hdr_write(vfu_ctx, _pos, buf, _count); } else { memcpy(buf, vfu_pci_get_config_space(vfu_ctx)->hdr.raw + _pos, _count); } @@ -307,26 +304,122 @@ vfu_do_pci_hdr_access(vfu_ctx_t *vfu_ctx, uint32_t *count, return err; } -static inline bool -vfu_is_pci_hdr_access(uint64_t pos) +static uint32_t +pci_config_space_size(vfu_ctx_t *vfu_ctx) +{ + return vfu_ctx->reg_info[VFU_PCI_DEV_CFG_REGION_IDX].size; +} + +/* + * Accesses the non-standard part (offset >= 0x40) of the PCI configuration + * space. + */ +ssize_t +pci_config_space_access(vfu_ctx_t *vfu_ctx, char *buf, size_t count, + loff_t pos, bool is_write) { - const uint64_t off = region_to_offset(VFU_PCI_DEV_CFG_REGION_IDX); - return pos >= off && pos - off < PCI_STD_HEADER_SIZEOF; + int ret; + + count = MIN(pci_config_space_size(vfu_ctx), count); + if (is_write) { + ret = cap_maybe_access(vfu_ctx, vfu_ctx->pci.caps, buf, count, pos); + if (ret < 0) { + vfu_log(vfu_ctx, LOG_ERR, "bad access to capabilities %#lx-%#lx\n", + pos, pos + count); + return ret; + } + } else { + /* + * It is legitimate to read the non-standard part of the PCI config + * space even if there are no capabilities. + */ + memcpy(buf, vfu_ctx->pci.config_space->raw + pos, count); + } + return count; } -/* FIXME this function is misleading, remove it */ int -vfu_pci_hdr_access(vfu_ctx_t *vfu_ctx, uint32_t *count, - uint64_t *pos, bool is_write, char *buf) +vfu_pci_setup_config_hdr(vfu_ctx_t *vfu_ctx, vfu_pci_hdr_id_t id, + vfu_pci_hdr_ss_t ss, vfu_pci_hdr_cc_t cc, + vfu_pci_type_t pci_type, + int revision __attribute__((unused))) { + vfu_pci_config_space_t *config_space; + size_t size; + assert(vfu_ctx != NULL); - assert(count != NULL); - assert(pos != NULL); - if (!vfu_is_pci_hdr_access(*pos)) { - return 0; + /* + * TODO there no real reason why we shouldn't allow this, we should just + * clean up and redo it. + */ + if (vfu_ctx->pci.config_space != NULL) { + vfu_log(vfu_ctx, LOG_ERR, "PCI configuration space header already setup"); + return ERROR(EEXIST); + } + + vfu_ctx->pci.type = pci_type; + switch (vfu_ctx->pci.type) { + case VFU_PCI_TYPE_CONVENTIONAL: + case VFU_PCI_TYPE_PCI_X_1: + size = PCI_CFG_SPACE_SIZE; + break; + case VFU_PCI_TYPE_PCI_X_2: + case VFU_PCI_TYPE_EXPRESS: + size = PCI_CFG_SPACE_EXP_SIZE; + break; + default: + vfu_log(vfu_ctx, LOG_ERR, "invalid PCI type %d", pci_type); + return ERROR(EINVAL); + } + + // Allocate a buffer for the config space. + config_space = calloc(1, size); + if (config_space == NULL) { + return ERROR(ENOMEM); + } + + config_space->hdr.id = id; + config_space->hdr.ss = ss; + config_space->hdr.cc = cc; + vfu_ctx->pci.config_space = config_space; + vfu_ctx->reg_info[VFU_PCI_DEV_CFG_REGION_IDX].size = size; + + return 0; +} + +int +vfu_pci_setup_caps(vfu_ctx_t *vfu_ctx, vfu_cap_t **caps, int nr_caps) +{ + int ret; + + assert(vfu_ctx != NULL); + + if (vfu_ctx->pci.caps != NULL) { + vfu_log(vfu_ctx, LOG_ERR, "capabilities are already setup"); + return ERROR(EEXIST); + } + + if (caps == NULL || nr_caps == 0) { + vfu_log(vfu_ctx, LOG_ERR, "Invalid args passed"); + return ERROR(EINVAL); + } + + vfu_ctx->pci.caps = caps_create(vfu_ctx, caps, nr_caps, &ret); + if (vfu_ctx->pci.caps == NULL) { + vfu_log(vfu_ctx, LOG_ERR, "failed to create PCI capabilities: %s", + strerror(ret)); + return ERROR(ret); } - return vfu_do_pci_hdr_access(vfu_ctx, count, pos, is_write, buf); + + return 0; +} + +inline vfu_pci_config_space_t * +vfu_pci_get_config_space(vfu_ctx_t *vfu_ctx) +{ + assert(vfu_ctx != NULL); + return vfu_ctx->pci.config_space; } /* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/lib/pci.h b/lib/pci.h new file mode 100644 index 0000000..8678902 --- /dev/null +++ b/lib/pci.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Nutanix Inc. All rights reserved. + * + * Authors: Thanos Makatos <thanos@nutanix.com> + * Swapnil Ingle <swapnil.ingle@nutanix.com> + * Felipe Franciosi <felipe@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. + * + */ + +#ifndef LIB_VFIO_USER_PCI_H +#define LIB_VFIO_USER_PCI_H + +#include "libvfio-user.h" +#include "private.h" + +static inline bool +pci_is_hdr_access(uint64_t pos) +{ + const uint64_t off = region_to_offset(VFU_PCI_DEV_CFG_REGION_IDX); + return pos >= off && pos - off < PCI_STD_HEADER_SIZEOF; +} + +int +pci_hdr_access(vfu_ctx_t *vfu_ctx, uint32_t *count, + uint64_t *pos, bool is_write, char *buf); + +ssize_t +pci_config_space_access(vfu_ctx_t *vfu_ctx, char *buf, size_t count, + loff_t pos, bool is_write); + +#endif /* LIB_VFIO_USER_PCI_H */ + +/* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/lib/private.h b/lib/private.h index 968271d..871ba29 100644 --- a/lib/private.h +++ b/lib/private.h @@ -35,6 +35,16 @@ #include "dma.h" +#define VFU_REGION_SHIFT 40 +#define VFU_REGION_MASK ((1ULL << VFU_REGION_SHIFT) - 1) + +static inline int +ERROR(int err) +{ + errno = err; + return -1; +} + #ifdef VFU_VERBOSE_LOGGING void dump_buffer(const char *prefix, const char *buf, uint32_t count); @@ -136,8 +146,17 @@ vfu_pci_hdr_access(vfu_ctx_t *vfu_ctx, uint32_t *count, vfu_reg_info_t * vfu_get_region_info(vfu_ctx_t *vfu_ctx); -uint64_t -region_to_offset(uint32_t region); +static inline uint64_t +region_to_offset(uint32_t region) +{ + return (uint64_t)region << VFU_REGION_SHIFT; +} + +static inline uint32_t +offset_to_region(uint64_t offset) +{ + return (offset >> VFU_REGION_SHIFT) & VFU_REGION_MASK; +} int handle_dma_map_or_unmap(vfu_ctx_t *vfu_ctx, uint32_t size, bool map, |