// Copyright(c) 2017-2020, Intel Corporation // // 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 Intel Corporation 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. #include #include #include #include #include #include #include #include "common_int.h" #include "opae_drv.h" #include "intel-fpga.h" #include "fpga-dfl.h" typedef struct _ioctl_ops { fpga_result (*get_fme_info)(int fd, opae_fme_info *info); fpga_result (*get_port_info)(int fd, opae_port_info *info); fpga_result (*get_port_region_info)(int fd, uint32_t index, opae_port_region_info *info); fpga_result (*port_map)(int fd, void *addr, uint64_t len, uint32_t flags, uint64_t *io_addr); fpga_result (*port_unmap)(int fd, uint64_t io_addr); fpga_result (*port_umsg_cfg)(int fd, uint32_t flags, uint32_t hint_bitmap); fpga_result (*port_umsg_set_base_addr)(int fd, uint32_t flags, uint64_t io_addr); fpga_result (*port_umsg_enable)(int fd); fpga_result (*port_umsg_disable)(int fd); fpga_result (*fme_set_err_irq)(int fd, uint32_t flags, int32_t eventfd); fpga_result (*port_set_err_irq)(int fd, uint32_t flags, int32_t eventfd); fpga_result (*port_set_user_irq)(int fd, uint32_t flags, uint32_t start, uint32_t count, int32_t *eventfd); fpga_result (*fme_port_assign)(int fd, uint32_t flags, uint32_t port_id); fpga_result (*fme_port_release)(int fd, uint32_t flags, uint32_t port_id); fpga_result (*fme_port_pr)(int fd, uint32_t flags, uint32_t port_id, uint32_t sz, uint64_t addr, uint64_t *status); fpga_result (*fme_port_reset)(int fd); } ioctl_ops; fpga_result opae_ioctl(int fd, int request, ...) { fpga_result res = FPGA_OK; va_list argp; va_start(argp, request); void *msg = va_arg(argp, void *); errno = 0; if (ioctl(fd, request, msg) != 0) { OPAE_MSG("error executing ioctl: %s", strerror(errno)); switch (errno) { case EINVAL: res = FPGA_INVALID_PARAM; break; case ENOTSUP: res = FPGA_NOT_SUPPORTED; break; default: // other errors could be // EBADF - fd is bad file descriptor // EFAULT - argp references an inaccessible // memory area // ENOTTY - fd is not associated with a char. // special device res = FPGA_EXCEPTION; break; } } va_end(argp); return res; } fpga_result intel_fpga_version(int fd) { return opae_ioctl(fd, FPGA_GET_API_VERSION, NULL); } fpga_result intel_get_fme_info(int fd, opae_fme_info *info) { ASSERT_NOT_NULL(info); struct fpga_fme_info fme_info = {.argsz = sizeof(fme_info), .flags = 0}; int res = opae_ioctl(fd, FPGA_FME_GET_INFO, &fme_info); if (!res) { info->flags = fme_info.flags; info->capability = fme_info.capability; } return res; } fpga_result intel_get_port_info(int fd, opae_port_info *info) { ASSERT_NOT_NULL(info); struct fpga_port_info pinfo = {.argsz = sizeof(pinfo), .flags = 0}; int res = opae_ioctl(fd, FPGA_PORT_GET_INFO, &pinfo); if (!res) { info->flags = pinfo.flags; info->capability = pinfo.capability; info->num_regions = pinfo.num_regions; info->num_umsgs = pinfo.num_umsgs; info->num_uafu_irqs = pinfo.num_uafu_irqs; } return res; } fpga_result intel_get_port_region_info(int fd, uint32_t index, opae_port_region_info *info) { ASSERT_NOT_NULL(info); struct fpga_port_region_info rinfo = { .argsz = sizeof(rinfo), .padding = 0, .index = index}; int res = opae_ioctl(fd, FPGA_PORT_GET_REGION_INFO, &rinfo); if (!res) { info->flags = rinfo.flags; info->size = rinfo.size; info->offset = rinfo.offset; } return res; } fpga_result intel_port_map(int fd, void *addr, uint64_t len, uint32_t flags, uint64_t *io_addr) { int res = 0; int req = 0; void *msg = NULL; /* Set ioctl fpga_port_dma_map struct parameters */ struct fpga_port_dma_map dma_map = {.argsz = sizeof(dma_map), .flags = flags, .user_addr = (__u64)addr, .length = (__u64)len, .iova = 0}; ASSERT_NOT_NULL(io_addr); /* Dispatch ioctl command */ req = FPGA_PORT_DMA_MAP; msg = &dma_map; res = opae_ioctl(fd, req, msg); if (!res) { *io_addr = dma_map.iova; } return res; } fpga_result intel_port_unmap(int fd, uint64_t io_addr) { /* Set ioctl fpga_port_dma_unmap struct parameters */ struct fpga_port_dma_unmap dma_unmap = { .argsz = sizeof(dma_unmap), .flags = 0, .iova = io_addr}; /* Dispatch ioctl command */ return opae_ioctl(fd, FPGA_PORT_DMA_UNMAP, &dma_unmap); } fpga_result intel_port_umsg_cfg(int fd, uint32_t flags, uint32_t hint_bitmap) { if (flags) { OPAE_MSG( "flags currently not supported in FPGA_PORT_UMSG_SET_MODE"); } struct fpga_port_umsg_cfg umsg_cfg = {.argsz = sizeof(umsg_cfg), .flags = 0, .hint_bitmap = hint_bitmap}; return opae_ioctl(fd, FPGA_PORT_UMSG_SET_MODE, &umsg_cfg); } fpga_result intel_port_umsg_set_base_addr(int fd, uint32_t flags, uint64_t io_addr) { if (flags) { OPAE_MSG( "flags currently not supported in FPGA_PORT_UMSG_SET_BASE_ADDR"); } struct fpga_port_umsg_base_addr baseaddr = { .argsz = sizeof(baseaddr), .flags = 0, .iova = io_addr}; return opae_ioctl(fd, FPGA_PORT_UMSG_SET_BASE_ADDR, &baseaddr); } fpga_result intel_port_umsg_enable(int fd) { return opae_ioctl(fd, FPGA_PORT_UMSG_ENABLE, NULL); } fpga_result intel_port_umsg_disable(int fd) { return opae_ioctl(fd, FPGA_PORT_UMSG_DISABLE, NULL); } fpga_result intel_fme_set_err_irq(int fd, uint32_t flags, int32_t evtfd) { if (flags) { OPAE_MSG( "flags currently not supported in FPGA_FME_ERR_SET_IRQ"); } struct fpga_fme_err_irq_set irq = { .argsz = sizeof(irq), .flags = flags, .evtfd = evtfd}; return opae_ioctl(fd, FPGA_FME_ERR_SET_IRQ, &irq); } fpga_result intel_port_set_err_irq(int fd, uint32_t flags, int32_t evtfd) { if (flags) { OPAE_MSG( "flags currently not supported in FPGA_FME_ERR_SET_IRQ"); } struct fpga_port_err_irq_set irq = { .argsz = sizeof(irq), .flags = flags, .evtfd = evtfd}; return opae_ioctl(fd, FPGA_PORT_ERR_SET_IRQ, &irq); } fpga_result intel_port_set_user_irq(int fd, uint32_t flags, uint32_t start, uint32_t count, int32_t *eventfd) { uint32_t sz = sizeof(struct fpga_port_uafu_irq_set) + count * sizeof(int32_t); struct fpga_port_uafu_irq_set *irq = NULL; int res = 0; ASSERT_NOT_NULL(eventfd); if (!count) { OPAE_ERR("set_user irq with emtpy count"); return FPGA_INVALID_PARAM; } if (flags) { OPAE_MSG( "flags currently not supported in FPGA_FME_ERR_SET_IRQ"); } irq = malloc(sz); if (!irq) { OPAE_ERR("Could not allocate memory for irq request"); return FPGA_NO_MEMORY; } irq->argsz = sz; irq->flags = 0; irq->start = start; irq->count = count; memcpy(irq->evtfd, eventfd, count * sizeof(int32_t)); res = opae_ioctl(fd, FPGA_PORT_UAFU_SET_IRQ, irq); free(irq); return res; } fpga_result intel_fme_port_assign(int fd, uint32_t flags, uint32_t port_id) { struct fpga_fme_port_assign assign = { .argsz = sizeof(assign), .flags = 0, .port_id = port_id}; if (flags) { OPAE_MSG( "flags currently not supported in FPGA_FME_PORT_ASSIGN"); } return opae_ioctl(fd, FPGA_FME_PORT_ASSIGN, &assign); } fpga_result intel_fme_port_release(int fd, uint32_t flags, uint32_t port_id) { struct fpga_fme_port_assign assign = { .argsz = sizeof(assign), .flags = 0, .port_id = port_id}; if (flags) { OPAE_MSG( "flags currently not supported in FPGA_FME_PORT_RELEASE"); } return opae_ioctl(fd, FPGA_FME_PORT_RELEASE, &assign); } fpga_result intel_fme_port_pr(int fd, uint32_t flags, uint32_t port_id, uint32_t sz, uint64_t addr, uint64_t *status) { struct fpga_fme_port_pr port_pr = {.argsz = sizeof(port_pr), .flags = 0, .port_id = port_id, .buffer_size = sz, .buffer_address = addr}; int res = FPGA_OK; if (flags) { OPAE_MSG("flags currently not supported in FPGA_FME_PORT_PR"); } ASSERT_NOT_NULL(status); res = opae_ioctl(fd, FPGA_FME_PORT_PR, &port_pr); *status = port_pr.status; return res; } fpga_result intel_fme_port_reset(int fd) { return opae_ioctl(fd, FPGA_PORT_RESET, NULL); } fpga_result dfl_fpga_version(int fd) { return opae_ioctl(fd, DFL_FPGA_GET_API_VERSION, NULL); } fpga_result dfl_get_port_info(int fd, opae_port_info *info) { ASSERT_NOT_NULL(info); struct dfl_fpga_port_info pinfo = {.argsz = sizeof(pinfo), .flags = 0}; int res = opae_ioctl(fd, DFL_FPGA_PORT_GET_INFO, &pinfo); if (!res) { info->flags = pinfo.flags; info->num_regions = pinfo.num_regions; info->num_umsgs = pinfo.num_umsgs; } return res; } fpga_result dfl_get_port_region_info(int fd, uint32_t index, opae_port_region_info *info) { ASSERT_NOT_NULL(info); struct dfl_fpga_port_region_info rinfo = { .argsz = sizeof(rinfo), .padding = 0, .index = index}; int res = opae_ioctl(fd, DFL_FPGA_PORT_GET_REGION_INFO, &rinfo); if (!res) { info->flags = rinfo.flags; info->size = rinfo.size; info->offset = rinfo.offset; } return res; } fpga_result dfl_port_map(int fd, void *addr, uint64_t len, uint32_t flags, uint64_t *io_addr) { int res = 0; /* Set ioctl fpga_port_dma_map struct parameters */ struct dfl_fpga_port_dma_map dma_map = {.argsz = sizeof(dma_map), .flags = flags, .user_addr = (__u64)addr, .length = (__u64)len, .iova = 0}; ASSERT_NOT_NULL(io_addr); /* Dispatch ioctl command */ res = opae_ioctl(fd, DFL_FPGA_PORT_DMA_MAP, &dma_map); if (!res) { *io_addr = dma_map.iova; } return res; } fpga_result dfl_port_unmap(int fd, uint64_t io_addr) { /* Set ioctl fpga_port_dma_unmap struct parameters */ struct dfl_fpga_port_dma_unmap dma_unmap = { .argsz = sizeof(dma_unmap), .flags = 0, .iova = io_addr}; /* Dispatch ioctl command */ return opae_ioctl(fd, DFL_FPGA_PORT_DMA_UNMAP, &dma_unmap); } fpga_result dfl_fme_port_assign(int fd, uint32_t flags, uint32_t port_id) { UNUSED_PARAM(flags); return opae_ioctl(fd, DFL_FPGA_FME_PORT_ASSIGN, port_id); } fpga_result dfl_fme_port_release(int fd, uint32_t flags, uint32_t port_id) { UNUSED_PARAM(flags); return opae_ioctl(fd, DFL_FPGA_FME_PORT_RELEASE, port_id); } fpga_result dfl_fme_port_pr(int fd, uint32_t flags, uint32_t port_id, uint32_t sz, uint64_t addr, uint64_t *status) { struct dfl_fpga_fme_port_pr port_pr = {.argsz = sizeof(port_pr), .flags = 0, .port_id = port_id, .buffer_size = sz, .buffer_address = addr}; int res = FPGA_OK; if (flags) { OPAE_MSG("flags currently not supported in FPGA_FME_PORT_PR"); } ASSERT_NOT_NULL(status); res = opae_ioctl(fd, DFL_FPGA_FME_PORT_PR, &port_pr); *status = 0; return res; } fpga_result dfl_fme_port_reset(int fd) { return opae_ioctl(fd, DFL_FPGA_PORT_RESET, NULL); } #define MAX_KERNEL_DRIVERS 2 static ioctl_ops ioctl_table[MAX_KERNEL_DRIVERS] = { {.get_fme_info = NULL, .get_port_info = dfl_get_port_info, .get_port_region_info = dfl_get_port_region_info, .port_map = dfl_port_map, .port_unmap = dfl_port_unmap, .port_umsg_cfg = NULL, .port_umsg_set_base_addr = NULL, .port_umsg_enable = NULL, .port_umsg_disable = NULL, .fme_set_err_irq = NULL, .port_set_err_irq = NULL, .port_set_user_irq = NULL, .fme_port_assign = dfl_fme_port_assign, .fme_port_release = dfl_fme_port_release, .fme_port_pr = dfl_fme_port_pr, .fme_port_reset = dfl_fme_port_reset}, {.get_fme_info = intel_get_fme_info, .get_port_info = intel_get_port_info, .get_port_region_info = intel_get_port_region_info, .port_map = intel_port_map, .port_unmap = intel_port_unmap, .port_umsg_cfg = intel_port_umsg_cfg, .port_umsg_set_base_addr = intel_port_umsg_set_base_addr, .port_umsg_enable = intel_port_umsg_enable, .port_umsg_disable = intel_port_umsg_disable, .fme_set_err_irq = intel_fme_set_err_irq, .port_set_err_irq = intel_port_set_err_irq, .port_set_user_irq = intel_port_set_user_irq, .fme_port_assign = intel_fme_port_assign, .fme_port_release = intel_fme_port_release, .fme_port_pr = intel_fme_port_pr, .fme_port_reset = intel_fme_port_reset} }; static ioctl_ops *io_ptr; int opae_ioctl_initialize(void) { struct stat st; if (!stat("/sys/class/fpga_region", &st)) { io_ptr = &ioctl_table[0]; return 0; } if (!stat("/sys/class/fpga", &st)) { io_ptr = &ioctl_table[1]; return 0; } return 1; } #define IOCTL(_FN, ...) \ do { \ if (!io_ptr) { \ OPAE_ERR("ioctl interface has not been initialized"); \ return FPGA_EXCEPTION; \ } \ if (!io_ptr->_FN) { \ OPAE_MSG("ioctl function not yet supported"); \ return FPGA_NOT_SUPPORTED; \ } \ return io_ptr->_FN(__VA_ARGS__); \ } while (0); fpga_result opae_get_fme_info(int fd, opae_fme_info *info) { IOCTL(get_fme_info, fd, info); } fpga_result opae_get_port_info(int fd, opae_port_info *info) { IOCTL(get_port_info, fd, info); } fpga_result opae_get_port_region_info(int fd, uint32_t index, opae_port_region_info *info) { IOCTL(get_port_region_info, fd, index, info); } fpga_result opae_port_map(int fd, void *addr, uint64_t len, uint32_t flags, uint64_t *io_addr) { IOCTL(port_map, fd, addr, len, flags, io_addr); } fpga_result opae_port_unmap(int fd, uint64_t io_addr) { IOCTL(port_unmap, fd, io_addr); } fpga_result opae_port_umsg_cfg(int fd, uint32_t flags, uint32_t hint_bitmap) { IOCTL(port_umsg_cfg, fd, flags, hint_bitmap); } fpga_result opae_port_umsg_set_base_addr(int fd, uint32_t flags, uint64_t io_addr) { IOCTL(port_umsg_set_base_addr, fd, flags, io_addr); } fpga_result opae_port_umsg_enable(int fd) { IOCTL(port_umsg_enable, fd); } fpga_result opae_port_umsg_disable(int fd) { IOCTL(port_umsg_disable, fd); } fpga_result opae_fme_set_err_irq(int fd, uint32_t flags, int32_t eventfd) { IOCTL(fme_set_err_irq, fd, flags, eventfd); } fpga_result opae_port_set_err_irq(int fd, uint32_t flags, int32_t eventfd) { IOCTL(port_set_err_irq, fd, flags, eventfd); } fpga_result opae_port_set_user_irq(int fd, uint32_t flags, uint32_t start, uint32_t count, int32_t *eventfd) { IOCTL(port_set_user_irq, fd, flags, start, count, eventfd); } fpga_result opae_fme_port_assign(int fd, uint32_t flags, uint32_t port_id) { IOCTL(fme_port_assign, fd, flags, port_id); } fpga_result opae_fme_port_release(int fd, uint32_t flags, uint32_t port_id) { IOCTL(fme_port_release, fd, flags, port_id); } fpga_result opae_fme_port_pr(int fd, uint32_t flags, uint32_t port_id, uint32_t sz, uint64_t addr, uint64_t *status) { IOCTL(fme_port_pr, fd, flags, port_id, sz, addr, status); } fpga_result opae_fme_port_reset(int fd) { IOCTL(fme_port_reset, fd); }