/* * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). You may * not use this file except in compliance with the License. A copy of the * License is located at * * http://aws.amazon.com/apache2.0/ * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include "fpga_pci_internal.h" /** * FPGA PCI BARs: */ static pthread_mutex_t bars_mutex = PTHREAD_MUTEX_INITIALIZER; static struct fpga_pci_bar { bool allocated; void *mem_base; size_t mem_size; } bars[FPGA_PCI_BARS_MAX]; static inline struct fpga_pci_bar * fpga_pci_bar_get(pci_bar_handle_t handle) { log_debug("handle=%d", handle); fail_on(handle >= FPGA_PCI_BARS_MAX, err, "Invalid handle=%d", handle); return &bars[handle]; err: return NULL; } static int fpga_pci_bar_set_mem_base_size(pci_bar_handle_t handle, void *mem_base, size_t mem_size) { log_debug("handle=%d", handle); struct fpga_pci_bar *bar = fpga_pci_bar_get(handle); fail_on(!bar, err, "fpga_pci_bar_get failed"); fail_on(!mem_base, err, "mem_base is NULL"); fail_on(!mem_size, err, "mem_size is 0"); bar->mem_base = mem_base; bar->mem_size = mem_size; return 0; err: return -EINVAL; } static inline void * fpga_pci_bar_get_mem_at_offset(pci_bar_handle_t handle, uint64_t offset, uint64_t xaction_size) { log_debug("handle=%d", handle); struct fpga_pci_bar *bar = fpga_pci_bar_get(handle); fail_on(!bar, err, "fpga_pci_bar_get failed"); fail_on(!bar->allocated, err, "Not attached"); fail_on(!bar->mem_base, err, "mem_base is NULL"); fail_on(((uint64_t)(offset + xaction_size)) > bar->mem_size, err, "Invalid offset + size =0x%" PRIx64 " exceeds range" , (uint64_t)(offset + xaction_size)); return bar->mem_base + offset; err: return NULL; } static int fpga_pci_bar_alloc(void) { log_debug("enter"); pthread_mutex_lock(&bars_mutex); int i; for (i = 0; i < FPGA_PCI_BARS_MAX; i++) { struct fpga_pci_bar *bar = &bars[i]; if (bar->allocated == false) { log_debug("allocated handle=%d", i); bar->allocated = true; pthread_mutex_unlock(&bars_mutex); return i; } } pthread_mutex_unlock(&bars_mutex); return -ENOMEM; } static int fpga_pci_bar_free(pci_bar_handle_t handle) { log_debug("handle=%d", handle); struct fpga_pci_bar *bar = fpga_pci_bar_get(handle); fail_on(!bar, err, "fpga_pci_bar_get failed"); pthread_mutex_lock(&bars_mutex); memset(bar, 0, sizeof(*bar)); pthread_mutex_unlock(&bars_mutex); return 0; err: return -EINVAL; } static int fpga_pci_check_file_id(char *path, uint16_t id) { fail_on(!path, err, "path is NULL"); log_debug("path=%s", path); int ret = 0; FILE *fp = fopen(path, "r"); fail_on(!fp, err_open, "Error opening %s", path); uint32_t tmp_id; ret = fscanf(fp, "%x", &tmp_id); fail_on(ret < 0, err_close, "Error parsing %s", path); fail_on(tmp_id != id, err_close, "path=%s, id(0x%04x) != expected id(0x%04x)", path, tmp_id, id); fclose(fp); return 0; err_close: fclose(fp); err: return -EINVAL; err_open: return -errno; } static int fpga_pci_bar_attach(struct fpga_slot_spec *spec, int pf_id, int bar_id, bool write_combining, int *handle) { int fd = -1; int ret = 0; log_debug("enter"); void *mem_base = NULL; fail_on_with_code(!spec, err, ret, FPGA_ERR_SOFTWARE_PROBLEM, "spec is NULL"); fail_on_with_code(!handle, err, ret, FPGA_ERR_SOFTWARE_PROBLEM, "handle is NULL"); struct fpga_pci_resource_map *map = &spec->map[pf_id]; log_info("vendor_id=0x%04x, device_id=0x%04x, DBDF:" PCI_DEV_FMT ", resource_num=%u, size=%" PRIu64 ", burstable=%u", map->vendor_id, map->device_id, map->domain, map->bus, map->dev, map->func, bar_id, map->resource_size[bar_id], map->resource_burstable[bar_id]); /** Invalid handle for error returns */ *handle = -1; /** Sanity check the vendor */ char sysfs_name[NAME_MAX + 1]; ret = snprintf(sysfs_name, sizeof(sysfs_name), "/sys/bus/pci/devices/" PCI_DEV_FMT "/vendor", map->domain, map->bus, map->dev, map->func); fail_on_with_code(ret < 0, err, ret, FPGA_ERR_SOFTWARE_PROBLEM, "Error building the sysfs path for vendor"); fail_on_with_code((size_t) ret >= sizeof(sysfs_name), err, ret, FPGA_ERR_SOFTWARE_PROBLEM, "sysfs path too long for vendor"); ret = fpga_pci_check_file_id(sysfs_name, map->vendor_id); fail_on_with_code(ret != 0, err, ret, FPGA_ERR_PCI_MISSING, "fpga_pci_check_file_id failed, sysfs_name=%s, vendor_id=0x%04x", sysfs_name, map->vendor_id); /** Sanity check the device */ ret = snprintf(sysfs_name, sizeof(sysfs_name), "/sys/bus/pci/devices/" PCI_DEV_FMT "/device", map->domain, map->bus, map->dev, map->func); fail_on_with_code(ret < 0, err, ret, FPGA_ERR_SOFTWARE_PROBLEM, "Error building the sysfs path for device"); fail_on_with_code((size_t) ret >= sizeof(sysfs_name), err, ret, FPGA_ERR_SOFTWARE_PROBLEM, "sysfs path too long for device"); ret = fpga_pci_check_file_id(sysfs_name, map->device_id); fail_on_with_code(ret != 0, err, ret, FPGA_ERR_PCI_MISSING, "fpga_pci_check_file_id failed, sysfs_name=%s, device_id=0x%04x", sysfs_name, map->device_id); char wc_suffix[4] = "\0"; if (map->resource_burstable[bar_id] && write_combining) { strncpy(wc_suffix, "_wc", sizeof(wc_suffix)); } /** * Open and memory map the resource. * -"_wc" is added if the memory bar is burstable. */ snprintf(sysfs_name, sizeof(sysfs_name), "/sys/bus/pci/devices/" PCI_DEV_FMT "/resource%u%s", map->domain, map->bus, map->dev, map->func, bar_id, wc_suffix); fail_on_with_code(ret < 0, err, ret, FPGA_ERR_SOFTWARE_PROBLEM, "Error building the sysfs path for resource"); fail_on_with_code((size_t) ret >= sizeof(sysfs_name), err, ret, FPGA_ERR_SOFTWARE_PROBLEM, "sysfs path too long for resource"); log_debug("Opening sysfs_name=%s", sysfs_name); fd = open(sysfs_name, O_RDWR | O_SYNC); fail_on_with_code(fd == -1, err, ret, FPGA_ERR_UNRESPONSIVE, "open failed"); mem_base = mmap(0, map->resource_size[bar_id], PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); fail_on_with_code(mem_base == MAP_FAILED, err, ret, FPGA_ERR_UNRESPONSIVE, "mmap failed"); close(fd); fd = -1; /** Allocate a bar */ int tmp_handle = fpga_pci_bar_alloc(); fail_on_with_code(tmp_handle < 0, err_unmap, ret, -ENOMEM, "fpga_pci_bar_alloc failed"); /** Set the bar's mem base and size */ ret = fpga_pci_bar_set_mem_base_size(tmp_handle, mem_base, map->resource_size[bar_id]); fail_on(ret != 0, err_unmap, "fpga_pci_set_mem_base_size failed"); /** Setup handle for return */ *handle = tmp_handle; return 0; err_unmap: if (mem_base) { ret = munmap(mem_base, map->resource_size[bar_id]); fail_on(ret != 0, err, "munmap failed"); } err: if (fd != -1) { close(fd); } errno = 0; return ret; } int fpga_pci_init() { log_info("FPGA_PCI_BARS_MAX=%u", FPGA_PCI_BARS_MAX); return 0; } int fpga_pci_attach(int slot_id, int pf_id, int bar_id, uint32_t flags, pci_bar_handle_t *handle) { bool write_combining; struct fpga_slot_spec spec; (void) flags; int ret = -EINVAL; fail_on(!handle, err, "handle is NULL"); fail_on(pf_id < 0 || pf_id >= FPGA_PF_MAX, err, "Invalid pf_id=%d", pf_id); fail_on(bar_id < 0 || bar_id >= FPGA_BAR_PER_PF_MAX, err, "Invalid bar_id=%d", bar_id); memset(&spec, 0, sizeof(struct fpga_slot_spec)); ret = fpga_pci_get_slot_spec(slot_id, &spec); fail_on(ret, err, "Unable to prefill the slot spec\n"); write_combining = false; if (flags & BURST_CAPABLE) { ret = (spec.map[pf_id].resource_burstable[bar_id]) ? 0 : -EINVAL; fail_on(ret, err, "bar is not BURST_CAPABLE (does not support write " "combining.)"); write_combining = true; } return fpga_pci_bar_attach(&spec, pf_id, bar_id, write_combining, handle); err: return ret; } int fpga_pci_detach(pci_bar_handle_t handle) { log_debug("handle=%d", handle); struct fpga_pci_bar *bar = fpga_pci_bar_get(handle); fail_on(!bar, err, "fpga_pci_bar_get failed"); int ret = munmap(bar->mem_base, bar->mem_size); fail_on(ret != 0, err, "munmap failed"); ret = fpga_pci_bar_free(handle); fail_on(ret != 0, err, "fpga_pci_bar_free failed"); return 0; err: errno = 0; return -EINVAL; } int fpga_pci_poke(pci_bar_handle_t handle, uint64_t offset, uint32_t value) { log_debug("handle=%d, offset=0x%" PRIx64 ", value=0x%08x", handle, offset, value); uint32_t *reg_ptr = (uint32_t *)fpga_pci_bar_get_mem_at_offset(handle, offset, sizeof(uint32_t)); fail_on(!reg_ptr, err, "fpga_pci_bar_get_mem_at_offset failed"); *reg_ptr = value; return 0; err: return -EINVAL; } int fpga_pci_poke8(pci_bar_handle_t handle, uint64_t offset, uint8_t value) { log_debug("handle=%d, offset=0x%" PRIx64 ", value=0x%08x", handle, offset, value); uint8_t *reg_ptr = (uint8_t *)fpga_pci_bar_get_mem_at_offset(handle, offset, sizeof(uint8_t)); fail_on(!reg_ptr, err, "fpga_pci_bar_get_mem_at_offset failed"); *reg_ptr = value; return 0; err: return -EINVAL; } int fpga_pci_poke64(pci_bar_handle_t handle, uint64_t offset, uint64_t value) { log_debug("handle=%d, offset=0x%" PRIx64 ", value=0x%" PRIx64, handle, offset, value); uint64_t *reg_ptr = (uint64_t *)fpga_pci_bar_get_mem_at_offset(handle, offset, sizeof(uint64_t)); fail_on(!reg_ptr, err, "fpga_plat_get_mem_at_offset failed"); *reg_ptr = value; return 0; err: return -EINVAL; } int fpga_pci_peek(pci_bar_handle_t handle, uint64_t offset, uint32_t *value) { fail_on(!value, err, "value is NULL"); uint32_t *reg_ptr = (uint32_t *)fpga_pci_bar_get_mem_at_offset(handle, offset, sizeof(uint32_t)); fail_on(!reg_ptr, err, "fpga_plat_get_mem_at_offset failed"); *value = *reg_ptr; log_debug("handle=%d, offset=0x%" PRIx64 ", value=0x%08x", handle, offset, *value); return 0; err: return -EINVAL; } int fpga_pci_peek8(pci_bar_handle_t handle, uint64_t offset, uint8_t *value) { fail_on(!value, err, "value is NULL"); uint8_t *reg_ptr = (uint8_t *)fpga_pci_bar_get_mem_at_offset(handle, offset, sizeof(uint8_t)); fail_on(!reg_ptr, err, "fpga_plat_get_mem_at_offset failed"); *value = *reg_ptr; log_debug("handle=%d, offset=0x%" PRIx64 ", value=0x%" PRIu8, handle, offset, *value); return 0; err: return -EINVAL; } int fpga_pci_peek64(pci_bar_handle_t handle, uint64_t offset, uint64_t *value) { fail_on(!value, err, "value is NULL"); uint64_t *reg_ptr = (uint64_t *)fpga_pci_bar_get_mem_at_offset(handle, offset, sizeof(uint64_t)); fail_on(!reg_ptr, err, "fpga_plat_get_mem_at_offset failed"); *value = *reg_ptr; log_debug("handle=%d, offset=0x%" PRIx64 ", value=0x%" PRIx64, handle, offset, *value); return 0; err: return -EINVAL; } int fpga_pci_write_burst(pci_bar_handle_t handle, uint64_t offset, uint32_t* datap, uint64_t dword_len) { uint64_t i; log_debug("handle=%d, offset=0x%" PRIx64, handle, offset); /** get the pointer to the beginning of the range */ uint32_t *reg_ptr = (uint32_t *)fpga_pci_bar_get_mem_at_offset(handle, offset, sizeof(uint32_t)*dword_len); fail_on(!reg_ptr, err, "fpga_plat_get_mem_at_offset failed"); /** memcpy */ for (i = 0; i < dword_len; ++i) { reg_ptr[i] = datap[i]; } return 0; err: return -EINVAL; } int fpga_pci_get_address(pci_bar_handle_t handle, uint64_t offset, size_t len, void **ptr) { fail_on(!ptr, err, "ptr is NULL"); *ptr = fpga_pci_bar_get_mem_at_offset(handle, offset, len); fail_on(!*ptr, err, "fpga_plat_get_mem_at_offset failed"); return 0; err: return -EINVAL; } int fpga_pci_memset(pci_bar_handle_t handle, uint64_t offset, uint32_t value, uint64_t dword_len) { uint64_t i; log_debug("handle=%d, offset=0x%" PRIx64, handle, offset); /** get the pointer to the beginning of the range */ uint32_t *reg_ptr = (uint32_t *)fpga_pci_bar_get_mem_at_offset(handle, offset, sizeof(uint32_t)*dword_len); fail_on(!reg_ptr, err, "fpga_plat_get_mem_at_offset failed"); /** memset */ for (i = 0; i < dword_len; ++i) { reg_ptr[i] = value; } return 0; err: return -EINVAL; }