[RFC PATCH Xilinx Alveo 6/6] Add user physical function driver

From: sonal.santan
Date: Tue Mar 19 2019 - 17:54:36 EST


From: Sonal Santan <sonal.santan@xxxxxxxxxx>

Signed-off-by: Sonal Santan <sonal.santan@xxxxxxxxxx>
---
drivers/gpu/drm/xocl/userpf/common.h | 157 +++
drivers/gpu/drm/xocl/userpf/xocl_bo.c | 1255 ++++++++++++++++++++++
drivers/gpu/drm/xocl/userpf/xocl_bo.h | 119 ++
drivers/gpu/drm/xocl/userpf/xocl_drm.c | 640 +++++++++++
drivers/gpu/drm/xocl/userpf/xocl_drv.c | 743 +++++++++++++
drivers/gpu/drm/xocl/userpf/xocl_ioctl.c | 396 +++++++
drivers/gpu/drm/xocl/userpf/xocl_sysfs.c | 344 ++++++
7 files changed, 3654 insertions(+)
create mode 100644 drivers/gpu/drm/xocl/userpf/common.h
create mode 100644 drivers/gpu/drm/xocl/userpf/xocl_bo.c
create mode 100644 drivers/gpu/drm/xocl/userpf/xocl_bo.h
create mode 100644 drivers/gpu/drm/xocl/userpf/xocl_drm.c
create mode 100644 drivers/gpu/drm/xocl/userpf/xocl_drv.c
create mode 100644 drivers/gpu/drm/xocl/userpf/xocl_ioctl.c
create mode 100644 drivers/gpu/drm/xocl/userpf/xocl_sysfs.c

diff --git a/drivers/gpu/drm/xocl/userpf/common.h b/drivers/gpu/drm/xocl/userpf/common.h
new file mode 100644
index 000000000000..c7dd4a68441c
--- /dev/null
+++ b/drivers/gpu/drm/xocl/userpf/common.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors:
+ * Lizhi Hou <lizhi.hou@xxxxxxxxxx>
+ *
+ */
+
+#ifndef _USERPF_COMMON_H
+#define _USERPF_COMMON_H
+
+#include "../xocl_drv.h"
+#include "xocl_bo.h"
+#include "../xocl_drm.h"
+#include <drm/xocl_drm.h>
+#include <linux/hashtable.h>
+
+#define XOCL_DRIVER_DESC "Xilinx PCIe Accelerator Device Manager"
+#define XOCL_DRIVER_DATE "20180612"
+#define XOCL_DRIVER_MAJOR 2018
+#define XOCL_DRIVER_MINOR 2
+#define XOCL_DRIVER_PATCHLEVEL 8
+
+#define XOCL_MAX_CONCURRENT_CLIENTS 32
+
+#define XOCL_DRIVER_VERSION \
+ __stringify(XOCL_DRIVER_MAJOR) "." \
+ __stringify(XOCL_DRIVER_MINOR) "." \
+ __stringify(XOCL_DRIVER_PATCHLEVEL)
+
+#define XOCL_DRIVER_VERSION_NUMBER \
+ ((XOCL_DRIVER_MAJOR)*1000 + (XOCL_DRIVER_MINOR)*100 + \
+ XOCL_DRIVER_PATCHLEVEL)
+
+#define userpf_err(d, args...) \
+ xocl_err(&XDEV(d)->pdev->dev, ##args)
+#define userpf_info(d, args...) \
+ xocl_info(&XDEV(d)->pdev->dev, ##args)
+#define userpf_dbg(d, args...) \
+ xocl_dbg(&XDEV(d)->pdev->dev, ##args)
+
+#define xocl_get_root_dev(dev, root) \
+ for (root = dev; root->bus && root->bus->self; root = root->bus->self)
+
+#define XOCL_USER_PROC_HASH_SZ 256
+
+#define XOCL_U32_MASK 0xFFFFFFFF
+
+#define MAX_SLOTS 128
+#define MAX_CUS 128
+#define MAX_U32_SLOT_MASKS (((MAX_SLOTS-1)>>5) + 1)
+#define MAX_U32_CU_MASKS (((MAX_CUS-1)>>5) + 1)
+#define MAX_DEPS 8
+
+#define XOCL_DRM_FREE_MALLOC
+
+#define XOCL_PA_SECTION_SHIFT 28
+
+struct xocl_dev {
+ struct xocl_dev_core core;
+
+ bool offline;
+
+ /* health thread */
+ struct task_struct *health_thread;
+ struct xocl_health_thread_arg thread_arg;
+
+ u32 p2p_bar_idx;
+ resource_size_t p2p_bar_len;
+ void * __iomem p2p_bar_addr;
+
+ /*should be removed after mailbox is supported */
+ struct percpu_ref ref;
+ struct completion cmp;
+
+ struct dev_pagemap pgmap;
+ struct list_head ctx_list;
+ struct mutex ctx_list_lock;
+ unsigned int needs_reset; /* bool aligned */
+ atomic_t outstanding_execs;
+ atomic64_t total_execs;
+ void *p2p_res_grp;
+};
+
+/**
+ * struct client_ctx: Manage user space client attached to device
+ *
+ * @link: Client context is added to list in device
+ * @xclbin_id: UUID for xclbin loaded by client, or nullid if no xclbin loaded
+ * @xclbin_locked: Flag to denote that this context locked the xclbin
+ * @trigger: Poll wait counter for number of completed exec buffers
+ * @outstanding_execs: Counter for number outstanding exec buffers
+ * @abort: Flag to indicate that this context has detached from user space (ctrl-c)
+ * @num_cus: Number of resources (CUs) explcitly aquired
+ * @lock: Mutex lock for exclusive access
+ * @cu_bitmap: CUs reserved by this context, may contain implicit resources
+ */
+struct client_ctx {
+ struct list_head link;
+ uuid_t xclbin_id;
+ unsigned int xclbin_locked;
+ unsigned int abort;
+ unsigned int num_cus; /* number of resource locked explicitly by client */
+ atomic_t trigger; /* count of poll notification to acknowledge */
+ atomic_t outstanding_execs;
+ struct mutex lock;
+ struct xocl_dev *xdev;
+ DECLARE_BITMAP(cu_bitmap, MAX_CUS); /* may contain implicitly aquired resources such as CDMA */
+ struct pid *pid;
+};
+
+struct xocl_mm_wrapper {
+ struct drm_mm *mm;
+ struct drm_xocl_mm_stat *mm_usage_stat;
+ uint64_t start_addr;
+ uint64_t size;
+ uint32_t ddr;
+ struct hlist_node node;
+};
+
+/* ioctl functions */
+int xocl_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
+int xocl_execbuf_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_ctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
+int xocl_user_intr_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_read_axlf_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_hot_reset_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_reclock_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+
+/* sysfs functions */
+int xocl_init_sysfs(struct device *dev);
+void xocl_fini_sysfs(struct device *dev);
+
+/* helper functions */
+int64_t xocl_hot_reset(struct xocl_dev *xdev, bool force);
+void xocl_p2p_mem_release(struct xocl_dev *xdev, bool recov_bar_sz);
+int xocl_p2p_mem_reserve(struct xocl_dev *xdev);
+int xocl_get_p2p_bar(struct xocl_dev *xdev, u64 *bar_size);
+int xocl_pci_resize_resource(struct pci_dev *dev, int resno, int size);
+void xocl_reset_notify(struct pci_dev *pdev, bool prepare);
+void user_pci_reset_prepare(struct pci_dev *pdev);
+void user_pci_reset_done(struct pci_dev *pdev);
+
+uint get_live_client_size(struct xocl_dev *xdev);
+void reset_notify_client_ctx(struct xocl_dev *xdev);
+
+void get_pcie_link_info(struct xocl_dev *xdev,
+ unsigned short *link_width, unsigned short *link_speed, bool is_cap);
+int xocl_reclock(struct xocl_dev *xdev, void *data);
+#endif
diff --git a/drivers/gpu/drm/xocl/userpf/xocl_bo.c b/drivers/gpu/drm/xocl/userpf/xocl_bo.c
new file mode 100644
index 000000000000..546ce5f7e428
--- /dev/null
+++ b/drivers/gpu/drm/xocl/userpf/xocl_bo.c
@@ -0,0 +1,1255 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors:
+ * Sonal Santan <sonal.santan@xxxxxxxxxx>
+ * Sarabjeet Singh <sarabjeet.singh@xxxxxxxxxx>
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/swap.h>
+#include <linux/dma-buf.h>
+#include <linux/pagemap.h>
+#include <linux/version.h>
+#include <drm/drmP.h>
+#include "common.h"
+
+#ifdef _XOCL_BO_DEBUG
+#define BO_ENTER(fmt, args...) \
+ pr_info("[BO] Entering %s:"fmt"\n", __func__, ##args)
+#define BO_DEBUG(fmt, args...) \
+ pr_info("[BO] %s:%d:"fmt"\n", __func__, __LINE__, ##args)
+#else
+#define BO_ENTER(fmt, args...)
+#define BO_DEBUG(fmt, args...)
+#endif
+
+#if defined(XOCL_DRM_FREE_MALLOC)
+static inline void drm_free_large(void *ptr)
+{
+ kvfree(ptr);
+}
+
+static inline void *drm_malloc_ab(size_t nmemb, size_t size)
+{
+ return kvmalloc_array(nmemb, sizeof(struct page *), GFP_KERNEL);
+}
+#endif
+
+static inline void xocl_release_pages(struct page **pages, int nr, bool cold)
+{
+ release_pages(pages, nr);
+}
+
+
+static inline void __user *to_user_ptr(u64 address)
+{
+ return (void __user *)(uintptr_t)address;
+}
+
+static size_t xocl_bo_physical_addr(const struct drm_xocl_bo *xobj)
+{
+ uint64_t paddr = xobj->mm_node ? xobj->mm_node->start : 0xffffffffffffffffull;
+
+ //Sarab: Need to check for number of hops & size of DDRs
+ if (xobj->type & XOCL_BO_ARE)
+ paddr |= XOCL_ARE_HOP;
+ return paddr;
+}
+
+void xocl_describe(const struct drm_xocl_bo *xobj)
+{
+ size_t size_in_kb = xobj->base.size / 1024;
+ size_t physical_addr = xocl_bo_physical_addr(xobj);
+ unsigned int ddr = xocl_bo_ddr_idx(xobj->flags);
+ unsigned int userptr = xocl_bo_userptr(xobj) ? 1 : 0;
+
+ DRM_DEBUG("%p: H[%p] SIZE[0x%zxKB] D[0x%zx] DDR[%u] UPTR[%u] SGLCOUNT[%u]\n",
+ xobj, xobj->vmapping ? xobj->vmapping : xobj->bar_vmapping, size_in_kb,
+ physical_addr, ddr, userptr, xobj->sgt->orig_nents);
+}
+
+static void xocl_free_mm_node(struct drm_xocl_bo *xobj)
+{
+ struct drm_device *ddev = xobj->base.dev;
+ struct xocl_drm *drm_p = ddev->dev_private;
+ unsigned int ddr = xocl_bo_ddr_idx(xobj->flags);
+
+ mutex_lock(&drm_p->mm_lock);
+ BO_ENTER("xobj %p, mm_node %p", xobj, xobj->mm_node);
+ if (!xobj->mm_node)
+ goto end;
+
+ xocl_mm_update_usage_stat(drm_p, ddr, xobj->base.size, -1);
+ BO_DEBUG("remove mm_node:%p, start:%llx size: %llx", xobj->mm_node,
+ xobj->mm_node->start, xobj->mm_node->size);
+ drm_mm_remove_node(xobj->mm_node);
+ kfree(xobj->mm_node);
+ xobj->mm_node = NULL;
+end:
+ mutex_unlock(&drm_p->mm_lock);
+}
+
+static void xocl_free_bo(struct drm_gem_object *obj)
+{
+ struct drm_xocl_bo *xobj = to_xocl_bo(obj);
+ struct drm_device *ddev = xobj->base.dev;
+ struct xocl_drm *drm_p = ddev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ int npages = obj->size >> PAGE_SHIFT;
+
+ DRM_DEBUG("Freeing BO %p\n", xobj);
+
+ BO_ENTER("xobj %p pages %p", xobj, xobj->pages);
+ if (xobj->vmapping)
+ vunmap(xobj->vmapping);
+ xobj->vmapping = NULL;
+
+ if (xobj->dmabuf)
+ unmap_mapping_range(xobj->dmabuf->file->f_mapping, 0, 0, 1);
+
+ if (xobj->dma_nsg) {
+ pci_unmap_sg(xdev->core.pdev, xobj->sgt->sgl, xobj->dma_nsg,
+ PCI_DMA_BIDIRECTIONAL);
+ }
+
+ if (xobj->pages) {
+ if (xocl_bo_userptr(xobj)) {
+ xocl_release_pages(xobj->pages, npages, 0);
+ drm_free_large(xobj->pages);
+ } else if (xocl_bo_p2p(xobj)) {
+ drm_free_large(xobj->pages);
+ /*devm_* will release all the pages while unload xocl driver*/
+ xobj->bar_vmapping = NULL;
+ } else if (!xocl_bo_import(xobj)) {
+ drm_gem_put_pages(obj, xobj->pages, false, false);
+ }
+ }
+ xobj->pages = NULL;
+
+ if (!xocl_bo_import(xobj)) {
+ DRM_DEBUG("Freeing regular buffer\n");
+ if (xobj->sgt) {
+ sg_free_table(xobj->sgt);
+ kfree(xobj->sgt);
+ }
+ xobj->sgt = NULL;
+ xocl_free_mm_node(xobj);
+ } else {
+ DRM_DEBUG("Freeing imported buffer\n");
+ if (!(xobj->type & XOCL_BO_ARE))
+ xocl_free_mm_node(xobj);
+
+ if (obj->import_attach) {
+ DRM_DEBUG("Unnmapping attached dma buf\n");
+ dma_buf_unmap_attachment(obj->import_attach, xobj->sgt, DMA_TO_DEVICE);
+ drm_prime_gem_destroy(obj, NULL);
+ }
+ }
+
+ /* If it is imported BO then we do not delete SG Table
+ * And if is imported from ARE device then we do not free the mm_node
+ * as well
+ * Call detach here........
+ * to let the exporting device know that importing device do not need
+ * it anymore..
+ * else free_bo i.e this function is not called for exporting device
+ * as it assumes that the exported buffer is still being used
+ * dmabuf->ops->release(dmabuf);
+ * The drm_driver.gem_free_object callback is responsible for cleaning
+ * up the dma_buf attachment and references acquired at import time.
+ *
+ * This crashes machine.. Using above code instead
+ * drm_prime_gem_destroy calls detach function..
+ * struct dma_buf *imported_dma_buf = obj->dma_buf;
+ * if (imported_dma_buf->ops->detach)
+ * imported_dma_buf->ops->detach(imported_dma_buf, obj->import_attach);
+ */
+
+ drm_gem_object_release(obj);
+ kfree(xobj);
+}
+
+void xocl_drm_free_bo(struct drm_gem_object *obj)
+{
+ xocl_free_bo(obj);
+}
+
+static inline int check_bo_user_reqs(const struct drm_device *dev,
+ unsigned int flags, unsigned int type)
+{
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ u16 ddr_count;
+ unsigned int ddr;
+
+ if (type == DRM_XOCL_BO_EXECBUF)
+ return 0;
+ if (type == DRM_XOCL_BO_CMA)
+ return -EINVAL;
+
+ //From "mem_topology" or "feature rom" depending on
+ //unified or non-unified dsa
+ ddr_count = XOCL_DDR_COUNT(xdev);
+
+ if (ddr_count == 0)
+ return -EINVAL;
+ ddr = xocl_bo_ddr_idx(flags);
+ if (ddr >= ddr_count)
+ return -EINVAL;
+ if (XOCL_MEM_TOPOLOGY(xdev)->m_mem_data[ddr].m_type == MEM_STREAMING)
+ return -EINVAL;
+ if (!XOCL_IS_DDR_USED(xdev, ddr)) {
+ userpf_err(xdev, "Bank %d is marked as unused in axlf", ddr);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct drm_xocl_bo *xocl_create_bo(struct drm_device *dev,
+ uint64_t unaligned_size,
+ unsigned int user_flags,
+ unsigned int user_type)
+{
+ size_t size = PAGE_ALIGN(unaligned_size);
+ struct drm_xocl_bo *xobj;
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ unsigned int ddr = xocl_bo_ddr_idx(user_flags);
+ u16 ddr_count = 0;
+ bool xobj_inited = false;
+ int err = 0;
+
+ BO_DEBUG("New create bo flags:%u type:%u", user_flags, user_type);
+ if (!size)
+ return ERR_PTR(-EINVAL);
+
+ /* Either none or only one DDR should be specified */
+ /* Check the type */
+ if (check_bo_user_reqs(dev, user_flags, user_type))
+ return ERR_PTR(-EINVAL);
+
+ xobj = kzalloc(sizeof(*xobj), GFP_KERNEL);
+ if (!xobj)
+ return ERR_PTR(-ENOMEM);
+
+ BO_ENTER("xobj %p", xobj);
+ err = drm_gem_object_init(dev, &xobj->base, size);
+ if (err)
+ goto failed;
+ xobj_inited = true;
+
+ if (user_type == DRM_XOCL_BO_EXECBUF) {
+ xobj->type = XOCL_BO_EXECBUF;
+ xobj->metadata.state = DRM_XOCL_EXECBUF_STATE_ABORT;
+ return xobj;
+ }
+
+ if (user_type & DRM_XOCL_BO_P2P)
+ xobj->type = XOCL_BO_P2P;
+
+ xobj->mm_node = kzalloc(sizeof(*xobj->mm_node), GFP_KERNEL);
+ if (!xobj->mm_node) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ ddr_count = XOCL_DDR_COUNT(xdev);
+
+ mutex_lock(&drm_p->mm_lock);
+ /* Attempt to allocate buffer on the requested DDR */
+ xocl_xdev_dbg(xdev, "alloc bo from bank%u", ddr);
+ err = xocl_mm_insert_node(drm_p, ddr, xobj->mm_node,
+ xobj->base.size);
+ BO_DEBUG("insert mm_node:%p, start:%llx size: %llx",
+ xobj->mm_node, xobj->mm_node->start,
+ xobj->mm_node->size);
+ if (err)
+ goto failed;
+
+ xocl_mm_update_usage_stat(drm_p, ddr, xobj->base.size, 1);
+ mutex_unlock(&drm_p->mm_lock);
+ /* Record the DDR we allocated the buffer on */
+ //xobj->flags |= (1 << ddr);
+ xobj->flags = ddr;
+
+ return xobj;
+failed:
+ mutex_unlock(&drm_p->mm_lock);
+ kfree(xobj->mm_node);
+
+ if (xobj_inited)
+ drm_gem_object_release(&xobj->base);
+
+ kfree(xobj);
+
+ return ERR_PTR(err);
+}
+
+struct drm_xocl_bo *xocl_drm_create_bo(struct xocl_drm *drm_p,
+ uint64_t unaligned_size,
+ unsigned int user_flags,
+ unsigned int user_type)
+{
+ return xocl_create_bo(drm_p->ddev, unaligned_size, user_flags,
+ user_type);
+}
+
+static struct page **xocl_p2p_get_pages(void *bar_vaddr, int npages)
+{
+ struct page *p, **pages;
+ int i;
+ uint64_t page_offset_enum = 0;
+
+ pages = drm_malloc_ab(npages, sizeof(struct page *));
+
+ if (pages == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < npages; i++) {
+ p = virt_to_page(bar_vaddr+page_offset_enum);
+ pages[i] = p;
+
+ if (IS_ERR(p))
+ goto fail;
+
+ page_offset_enum += PAGE_SIZE;
+ }
+
+ return pages;
+fail:
+ kvfree(pages);
+ return ERR_CAST(p);
+}
+
+/*
+ * For ARE device do not reserve DDR space
+ * In below import it will reuse the mm_node which is already created by other application
+ */
+
+static struct drm_xocl_bo *xocl_create_bo_forARE(struct drm_device *dev,
+ uint64_t unaligned_size,
+ struct drm_mm_node *exporting_mm_node)
+{
+ struct drm_xocl_bo *xobj;
+ size_t size = PAGE_ALIGN(unaligned_size);
+ int err = 0;
+
+ if (!size)
+ return ERR_PTR(-EINVAL);
+
+ xobj = kzalloc(sizeof(*xobj), GFP_KERNEL);
+ if (!xobj)
+ return ERR_PTR(-ENOMEM);
+
+ BO_ENTER("xobj %p", xobj);
+ err = drm_gem_object_init(dev, &xobj->base, size);
+ if (err)
+ goto out3;
+
+ xobj->mm_node = exporting_mm_node;
+ if (!xobj->mm_node) {
+ err = -ENOMEM;
+ goto out3;
+ }
+
+ /* Record that this buffer is on remote device to be access over ARE*/
+ //xobj->flags = XOCL_BO_ARE;
+ xobj->type |= XOCL_BO_ARE;
+ return xobj;
+out3:
+ kfree(xobj);
+ return ERR_PTR(err);
+}
+
+
+int xocl_create_bo_ioctl(struct drm_device *dev,
+ void *data,
+ struct drm_file *filp)
+{
+ int ret;
+ struct drm_xocl_bo *xobj;
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ struct drm_xocl_create_bo *args = data;
+ //unsigned ddr = args->flags & XOCL_MEM_BANK_MSK;
+ unsigned int ddr = args->flags;
+ //unsigned bar_mapped = (args->flags & DRM_XOCL_BO_P2P) ? 1 : 0;
+ unsigned int bar_mapped = (args->type & DRM_XOCL_BO_P2P) ? 1 : 0;
+
+// //Only one bit should be set in ddr. Other bits are now in "type"
+// if (hweight_long(ddr) > 1)
+// return -EINVAL;
+//// if (args->flags && (args->flags != DRM_XOCL_BO_EXECBUF)) {
+// if (hweight_long(ddr) > 1)
+// return -EINVAL;
+// }
+
+ if (bar_mapped) {
+ if (!xdev->p2p_bar_addr) {
+ xocl_xdev_err(xdev, "No P2P mem region available, Can't create p2p BO");
+ return -EINVAL;
+ }
+ }
+
+ xobj = xocl_create_bo(dev, args->size, args->flags, args->type);
+
+ BO_ENTER("xobj %p, mm_node %p", xobj, xobj->mm_node);
+ if (IS_ERR(xobj)) {
+ DRM_DEBUG("object creation failed\n");
+ return PTR_ERR(xobj);
+ }
+
+ if (bar_mapped) {
+ ddr = xocl_bo_ddr_idx(xobj->flags);
+ /*
+ * DRM allocate contiguous pages, shift the vmapping with
+ * bar address offset
+ */
+ xobj->bar_vmapping = xdev->p2p_bar_addr +
+ drm_p->mm_p2p_off[ddr] + xobj->mm_node->start -
+ XOCL_MEM_TOPOLOGY(xdev)->m_mem_data[ddr].m_base_address;
+ }
+
+ if (bar_mapped)
+ xobj->pages = xocl_p2p_get_pages(xobj->bar_vmapping, xobj->base.size >> PAGE_SHIFT);
+ else
+ xobj->pages = drm_gem_get_pages(&xobj->base);
+
+ if (IS_ERR(xobj->pages)) {
+ ret = PTR_ERR(xobj->pages);
+ goto out_free;
+ }
+
+ xobj->sgt = drm_prime_pages_to_sg(xobj->pages, xobj->base.size >> PAGE_SHIFT);
+ if (IS_ERR(xobj->sgt)) {
+ ret = PTR_ERR(xobj->sgt);
+ goto out_free;
+ }
+
+ if (!bar_mapped) {
+ xobj->vmapping = vmap(xobj->pages, xobj->base.size >> PAGE_SHIFT, VM_MAP, PAGE_KERNEL);
+ if (!xobj->vmapping) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ }
+
+ ret = drm_gem_create_mmap_offset(&xobj->base);
+ if (ret < 0)
+ goto out_free;
+ ret = drm_gem_handle_create(filp, &xobj->base, &args->handle);
+ if (ret < 0)
+ goto out_free;
+
+ xocl_describe(xobj);
+//PORT4_20
+// drm_gem_object_unreference_unlocked(&xobj->base);
+ drm_gem_object_put_unlocked(&xobj->base);
+ return ret;
+
+out_free:
+ xocl_free_bo(&xobj->base);
+ return ret;
+}
+
+int xocl_userptr_bo_ioctl(struct drm_device *dev,
+ void *data,
+ struct drm_file *filp)
+{
+ int ret;
+ struct drm_xocl_bo *xobj;
+ unsigned int page_count;
+ struct drm_xocl_userptr_bo *args = data;
+ //unsigned ddr = args->flags & XOCL_MEM_BANK_MSK;
+ //unsigned ddr = args->flags;
+
+ if (offset_in_page(args->addr))
+ return -EINVAL;
+
+ if (args->type & DRM_XOCL_BO_EXECBUF)
+ return -EINVAL;
+
+ if (args->type & DRM_XOCL_BO_CMA)
+ return -EINVAL;
+
+// if (args->flags && (hweight_long(ddr) > 1))
+// return -EINVAL;
+
+ xobj = xocl_create_bo(dev, args->size, args->flags, args->type);
+ BO_ENTER("xobj %p", xobj);
+
+ if (IS_ERR(xobj)) {
+ DRM_DEBUG("object creation failed\n");
+ return PTR_ERR(xobj);
+ }
+
+ /* Use the page rounded size so we can accurately account for number of pages */
+ page_count = xobj->base.size >> PAGE_SHIFT;
+
+ xobj->pages = drm_malloc_ab(page_count, sizeof(*xobj->pages));
+ if (!xobj->pages) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+ ret = get_user_pages_fast(args->addr, page_count, 1, xobj->pages);
+
+ if (ret != page_count)
+ goto out0;
+
+ xobj->sgt = drm_prime_pages_to_sg(xobj->pages, page_count);
+ if (IS_ERR(xobj->sgt)) {
+ ret = PTR_ERR(xobj->sgt);
+ goto out0;
+ }
+
+ /* TODO: resolve the cache issue */
+ xobj->vmapping = vmap(xobj->pages, page_count, VM_MAP, PAGE_KERNEL);
+
+ if (!xobj->vmapping) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ ret = drm_gem_handle_create(filp, &xobj->base, &args->handle);
+ if (ret)
+ goto out1;
+
+ xobj->type |= XOCL_BO_USERPTR;
+ xocl_describe(xobj);
+//PORT4_20
+// drm_gem_object_unreference_unlocked(&xobj->base);
+ drm_gem_object_put_unlocked(&xobj->base);
+ return ret;
+
+out0:
+ drm_free_large(xobj->pages);
+ xobj->pages = NULL;
+out1:
+ xocl_free_bo(&xobj->base);
+ DRM_DEBUG("handle creation failed\n");
+ return ret;
+}
+
+
+int xocl_map_bo_ioctl(struct drm_device *dev,
+ void *data,
+ struct drm_file *filp)
+{
+ int ret = 0;
+ struct drm_xocl_map_bo *args = data;
+ struct drm_gem_object *obj;
+ struct drm_xocl_bo *xobj;
+
+ obj = xocl_gem_object_lookup(dev, filp, args->handle);
+ xobj = to_xocl_bo(obj);
+
+ if (!obj) {
+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle);
+ return -ENOENT;
+ }
+
+ BO_ENTER("xobj %p", xobj);
+ if (xocl_bo_userptr(xobj)) {
+ ret = -EPERM;
+ goto out;
+ }
+ /* The mmap offset was set up at BO allocation time. */
+ args->offset = drm_vma_node_offset_addr(&obj->vma_node);
+ xocl_describe(to_xocl_bo(obj));
+out:
+//PORT4_20
+// drm_gem_object_unreference_unlocked(obj);
+ drm_gem_object_put_unlocked(obj);
+ return ret;
+}
+
+static struct sg_table *alloc_onetime_sg_table(struct page **pages, uint64_t offset, uint64_t size)
+{
+ int ret;
+ unsigned int nr_pages;
+ struct sg_table *sgt = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ pages += (offset >> PAGE_SHIFT);
+ offset &= (~PAGE_MASK);
+ nr_pages = PAGE_ALIGN(size + offset) >> PAGE_SHIFT;
+
+ ret = sg_alloc_table_from_pages(sgt, pages, nr_pages, offset, size, GFP_KERNEL);
+ if (ret)
+ goto cleanup;
+ return sgt;
+
+cleanup:
+ kfree(sgt);
+ return ERR_PTR(-ENOMEM);
+}
+
+int xocl_sync_bo_ioctl(struct drm_device *dev,
+ void *data,
+ struct drm_file *filp)
+{
+ const struct drm_xocl_bo *xobj;
+ struct sg_table *sgt;
+ u64 paddr = 0;
+ int channel = 0;
+ ssize_t ret = 0;
+ const struct drm_xocl_sync_bo *args = data;
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+
+ u32 dir = (args->dir == DRM_XOCL_SYNC_BO_TO_DEVICE) ? 1 : 0;
+ struct drm_gem_object *gem_obj = xocl_gem_object_lookup(dev, filp,
+ args->handle);
+ if (!gem_obj) {
+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle);
+ return -ENOENT;
+ }
+
+ xobj = to_xocl_bo(gem_obj);
+ BO_ENTER("xobj %p", xobj);
+ sgt = xobj->sgt;
+
+ if (xocl_bo_p2p(xobj)) {
+ DRM_DEBUG("P2P_BO doesn't support sync_bo\n");
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ //Sarab: If it is a remote BO then why do sync over ARE.
+ //We should do sync directly using the other device which this bo locally.
+ //So that txfer is: HOST->PCIE->DDR; Else it will be HOST->PCIE->ARE->DDR
+ paddr = xocl_bo_physical_addr(xobj);
+
+ if (paddr == 0xffffffffffffffffull)
+ return -EINVAL;
+
+ /* If device is offline (due to error), reject all DMA requests */
+ if (xdev->offline)
+ return -ENODEV;
+
+
+ if ((args->offset + args->size) > gem_obj->size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* only invalidate the range of addresses requested by the user */
+ paddr += args->offset;
+
+ if (args->offset || (args->size != xobj->base.size)) {
+ sgt = alloc_onetime_sg_table(xobj->pages, args->offset, args->size);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto out;
+ }
+ }
+
+ //drm_clflush_sg(sgt);
+ channel = xocl_acquire_channel(xdev, dir);
+
+ if (channel < 0) {
+ ret = -EINVAL;
+ goto clear;
+ }
+ /* Now perform DMA */
+ ret = xocl_migrate_bo(xdev, sgt, dir, paddr, channel, args->size);
+ if (ret >= 0)
+ ret = (ret == args->size) ? 0 : -EIO;
+ xocl_release_channel(xdev, dir, channel);
+clear:
+ if (args->offset || (args->size != xobj->base.size)) {
+ sg_free_table(sgt);
+ kfree(sgt);
+ }
+out:
+//PORT4_20
+ drm_gem_object_put_unlocked(gem_obj);
+ return ret;
+}
+
+int xocl_info_bo_ioctl(struct drm_device *dev,
+ void *data,
+ struct drm_file *filp)
+{
+ const struct drm_xocl_bo *xobj;
+ struct drm_xocl_info_bo *args = data;
+ struct drm_gem_object *gem_obj = xocl_gem_object_lookup(dev, filp,
+ args->handle);
+
+ if (!gem_obj) {
+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle);
+ return -ENOENT;
+ }
+
+ xobj = to_xocl_bo(gem_obj);
+ BO_ENTER("xobj %p", xobj);
+
+ args->size = xobj->base.size;
+
+ args->paddr = xocl_bo_physical_addr(xobj);
+ xocl_describe(xobj);
+//PORT4_20
+ drm_gem_object_put_unlocked(gem_obj);
+
+ return 0;
+}
+
+int xocl_pwrite_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct drm_xocl_bo *xobj;
+ const struct drm_xocl_pwrite_bo *args = data;
+ struct drm_gem_object *gem_obj = xocl_gem_object_lookup(dev, filp,
+ args->handle);
+ char __user *user_data = to_user_ptr(args->data_ptr);
+ int ret = 0;
+ void *kaddr;
+
+ if (!gem_obj) {
+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle);
+ return -ENOENT;
+ }
+
+ if ((args->offset > gem_obj->size) || (args->size > gem_obj->size)
+ || ((args->offset + args->size) > gem_obj->size)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (args->size == 0) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!access_ok(user_data, args->size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ xobj = to_xocl_bo(gem_obj);
+ BO_ENTER("xobj %p", xobj);
+
+ if (xocl_bo_userptr(xobj)) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ kaddr = xobj->vmapping ? xobj->vmapping : xobj->bar_vmapping;
+ kaddr += args->offset;
+
+ ret = copy_from_user(kaddr, user_data, args->size);
+out:
+//PORT4_20
+ drm_gem_object_put_unlocked(gem_obj);
+
+ return ret;
+}
+
+int xocl_pread_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct drm_xocl_bo *xobj;
+ const struct drm_xocl_pread_bo *args = data;
+ struct drm_gem_object *gem_obj = xocl_gem_object_lookup(dev, filp,
+ args->handle);
+ char __user *user_data = to_user_ptr(args->data_ptr);
+ int ret = 0;
+ void *kaddr;
+
+ if (!gem_obj) {
+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle);
+ return -ENOENT;
+ }
+
+ if (xocl_bo_userptr(to_xocl_bo(gem_obj))) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ if ((args->offset > gem_obj->size) || (args->size > gem_obj->size)
+ || ((args->offset + args->size) > gem_obj->size)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (args->size == 0) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!access_ok(user_data, args->size)) {
+ ret = EFAULT;
+ goto out;
+ }
+
+ xobj = to_xocl_bo(gem_obj);
+ BO_ENTER("xobj %p", xobj);
+ kaddr = xobj->vmapping ? xobj->vmapping : xobj->bar_vmapping;
+ kaddr += args->offset;
+
+ ret = copy_to_user(user_data, kaddr, args->size);
+
+out:
+//PORT4_20
+ drm_gem_object_put_unlocked(gem_obj);
+
+ return ret;
+}
+
+int xocl_copy_bo_ioctl(struct drm_device *dev,
+ void *data,
+ struct drm_file *filp)
+{
+ const struct drm_xocl_bo *dst_xobj, *src_xobj;
+ struct sg_table *sgt;
+ u64 paddr = 0;
+ int channel = 0;
+ ssize_t ret = 0;
+ const struct drm_xocl_copy_bo *args = data;
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ u32 dir = 0; //always write data from source to destination
+ struct drm_gem_object *dst_gem_obj, *src_gem_obj;
+
+ dst_gem_obj = xocl_gem_object_lookup(dev, filp,
+ args->dst_handle);
+ if (!dst_gem_obj) {
+ DRM_ERROR("Failed to look up Destination GEM BO %d\n", args->dst_handle);
+ return -ENOENT;
+ }
+ src_gem_obj = xocl_gem_object_lookup(dev, filp,
+ args->src_handle);
+ if (!src_gem_obj) {
+ DRM_ERROR("Failed to look up Source GEM BO %d\n", args->src_handle);
+ ret = -ENOENT;
+ goto src_lookup_fail;
+ }
+
+ dst_xobj = to_xocl_bo(dst_gem_obj);
+ src_xobj = to_xocl_bo(src_gem_obj);
+
+ if (!xocl_bo_p2p(src_xobj)) {
+ DRM_ERROR("src_bo must be p2p bo, copy_bo aborted");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ DRM_DEBUG("dst_xobj %p, src_xobj %p", dst_xobj, src_xobj);
+ DRM_DEBUG("dst_xobj->sgt %p, src_xobj->sgt %p", dst_xobj->sgt, src_xobj->sgt);
+ sgt = dst_xobj->sgt;
+
+ paddr = xocl_bo_physical_addr(src_xobj);
+
+ if (paddr == 0xffffffffffffffffull) {
+ ret = -EINVAL;
+ goto out;
+ }
+ /* If device is offline (due to error), reject all DMA requests */
+ if (xdev->offline) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (((args->src_offset + args->size) > src_gem_obj->size) ||
+ ((args->dst_offset + args->size) > dst_gem_obj->size)) {
+ DRM_ERROR("offsize + sizes out of boundary, copy_bo abort");
+ ret = -EINVAL;
+ goto out;
+ }
+ paddr += args->src_offset;
+
+ DRM_DEBUG("%s, xobj->pages = %p\n", __func__, dst_xobj->pages);
+
+
+ if (args->dst_offset || (args->size != dst_xobj->base.size)) {
+ sgt = alloc_onetime_sg_table(dst_xobj->pages, args->dst_offset, args->size);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto out;
+ }
+ }
+
+ channel = xocl_acquire_channel(xdev, dir);
+
+ if (channel < 0) {
+ ret = -EINVAL;
+ goto clear;
+ }
+ /* Now perform DMA */
+ ret = xocl_migrate_bo(xdev, sgt, dir, paddr, channel,
+ args->size);
+
+ if (ret >= 0)
+ ret = (ret == args->size) ? 0 : -EIO;
+ xocl_release_channel(xdev, dir, channel);
+
+
+clear:
+ if (args->dst_offset || (args->size != dst_xobj->base.size)) {
+ sg_free_table(sgt);
+ kfree(sgt);
+ }
+out:
+//PORT4_20
+ drm_gem_object_put_unlocked(src_gem_obj);
+src_lookup_fail:
+//PORT4_20
+ drm_gem_object_put_unlocked(dst_gem_obj);
+ return ret;
+
+}
+
+
+struct sg_table *xocl_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct drm_xocl_bo *xobj = to_xocl_bo(obj);
+
+ BO_ENTER("xobj %p", xobj);
+ return drm_prime_pages_to_sg(xobj->pages, xobj->base.size >> PAGE_SHIFT);
+}
+
+
+static struct drm_xocl_bo *xocl_is_exporting_xare(struct drm_device *dev, struct dma_buf_attachment *attach)
+{
+ struct drm_gem_object *exporting_gem_obj;
+ struct drm_device *exporting_drm_dev;
+ struct xocl_drm *exporting_drmp;
+ struct xocl_dev *exporting_xdev;
+
+ struct device_driver *importing_dma_driver = dev->dev->driver;
+ struct dma_buf *exporting_dma_buf = attach->dmabuf;
+ struct device_driver *exporting_dma_driver = attach->dev->driver;
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+
+ if (xocl_is_are(xdev))
+ return NULL;
+
+ //We don't know yet if the exporting device is Xilinx/XOCL or third party or USB device
+ //So checking it in below code
+ if (importing_dma_driver != exporting_dma_driver)
+ return NULL;
+
+ //Exporting devices have same driver as us. So this is Xilinx device
+ //So now we can get gem_object, drm_device & xocl_dev
+ exporting_gem_obj = exporting_dma_buf->priv;
+ exporting_drm_dev = exporting_gem_obj->dev;
+ exporting_drmp = exporting_drm_dev->dev_private;
+ exporting_xdev = exporting_drmp->xdev;
+ //exporting_xdev->header;//This has FeatureROM header
+ if (xocl_is_are(exporting_xdev))
+ return to_xocl_bo(exporting_gem_obj);
+
+ return NULL;
+}
+
+struct drm_gem_object *xocl_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach, struct sg_table *sgt)
+{
+ int ret = 0;
+ struct drm_xocl_bo *exporting_xobj;
+ struct drm_xocl_bo *importing_xobj;
+
+ /*
+ * For ARE device resue the mm node from exporting xobj
+ * For non ARE devices we need to create a full BO but share the SG
+ * table
+ * ???? add flags to create_bo.. for DDR bank??
+ */
+
+ exporting_xobj = xocl_is_exporting_xare(dev, attach);
+ importing_xobj = exporting_xobj ?
+ xocl_create_bo_forARE(dev, attach->dmabuf->size,
+ exporting_xobj->mm_node) :
+ xocl_create_bo(dev, attach->dmabuf->size, 0, 0);
+
+ BO_ENTER("xobj %p", importing_xobj);
+
+ if (IS_ERR(importing_xobj)) {
+ DRM_DEBUG("object creation failed\n");
+ return (struct drm_gem_object *)importing_xobj;
+ }
+
+ importing_xobj->type |= XOCL_BO_IMPORT;
+ importing_xobj->sgt = sgt;
+ importing_xobj->pages = drm_malloc_ab(attach->dmabuf->size >> PAGE_SHIFT, sizeof(*importing_xobj->pages));
+ if (!importing_xobj->pages) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ ret = drm_prime_sg_to_page_addr_arrays(sgt, importing_xobj->pages,
+ NULL, attach->dmabuf->size >> PAGE_SHIFT);
+ if (ret)
+ goto out_free;
+
+ importing_xobj->vmapping = vmap(importing_xobj->pages, importing_xobj->base.size >> PAGE_SHIFT, VM_MAP,
+ PAGE_KERNEL);
+
+ if (!importing_xobj->vmapping) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ ret = drm_gem_create_mmap_offset(&importing_xobj->base);
+ if (ret < 0)
+ goto out_free;
+
+ xocl_describe(importing_xobj);
+ return &importing_xobj->base;
+
+out_free:
+ xocl_free_bo(&importing_xobj->base);
+ DRM_ERROR("Buffer import failed\n");
+ return ERR_PTR(ret);
+}
+
+void *xocl_gem_prime_vmap(struct drm_gem_object *obj)
+{
+ struct drm_xocl_bo *xobj = to_xocl_bo(obj);
+
+ BO_ENTER("xobj %p", xobj);
+ return xobj->vmapping;
+}
+
+void xocl_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+
+}
+
+int xocl_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ struct drm_xocl_bo *xobj = to_xocl_bo(obj);
+ int ret;
+
+ BO_ENTER("obj %p", obj);
+ if (obj->size < vma->vm_end - vma->vm_start)
+ return -EINVAL;
+
+ if (!obj->filp)
+ return -ENODEV;
+
+ ret = obj->filp->f_op->mmap(obj->filp, vma);
+ if (ret)
+ return ret;
+
+ fput(vma->vm_file);
+ if (!IS_ERR(xobj->dmabuf)) {
+ vma->vm_file = get_file(xobj->dmabuf->file);
+ vma->vm_ops = xobj->dmabuf_vm_ops;
+ vma->vm_private_data = obj;
+ vma->vm_flags |= VM_MIXEDMAP;
+ }
+
+ return 0;
+}
+
+int xocl_init_unmgd(struct drm_xocl_unmgd *unmgd, uint64_t data_ptr,
+ uint64_t size, u32 write)
+{
+ int ret;
+ char __user *user_data = to_user_ptr(data_ptr);
+
+ if (!access_ok(user_data, size))
+ return -EFAULT;
+
+ memset(unmgd, 0, sizeof(struct drm_xocl_unmgd));
+
+ unmgd->npages = (((unsigned long)user_data + size + PAGE_SIZE - 1) -
+ ((unsigned long)user_data & PAGE_MASK)) >> PAGE_SHIFT;
+
+ unmgd->pages = drm_malloc_ab(unmgd->npages, sizeof(*unmgd->pages));
+ if (!unmgd->pages)
+ return -ENOMEM;
+
+ ret = get_user_pages_fast(data_ptr, unmgd->npages, (write == 0) ? 1 : 0, unmgd->pages);
+
+ if (ret != unmgd->npages)
+ goto clear_pages;
+
+ unmgd->sgt = alloc_onetime_sg_table(unmgd->pages, data_ptr & ~PAGE_MASK, size);
+ if (IS_ERR(unmgd->sgt)) {
+ ret = PTR_ERR(unmgd->sgt);
+ goto clear_release;
+ }
+
+ return 0;
+
+clear_release:
+ xocl_release_pages(unmgd->pages, unmgd->npages, 0);
+clear_pages:
+ drm_free_large(unmgd->pages);
+ unmgd->pages = NULL;
+ return ret;
+}
+
+void xocl_finish_unmgd(struct drm_xocl_unmgd *unmgd)
+{
+ if (!unmgd->pages)
+ return;
+ sg_free_table(unmgd->sgt);
+ kfree(unmgd->sgt);
+ xocl_release_pages(unmgd->pages, unmgd->npages, 0);
+ drm_free_large(unmgd->pages);
+ unmgd->pages = NULL;
+}
+
+static bool xocl_validate_paddr(struct xocl_dev *xdev, u64 paddr, u64 size)
+{
+ struct mem_data *mem_data;
+ int i;
+ uint64_t addr;
+ bool start_check = false;
+ bool end_check = false;
+
+ for (i = 0; i < XOCL_MEM_TOPOLOGY(xdev)->m_count; i++) {
+ mem_data = &XOCL_MEM_TOPOLOGY(xdev)->m_mem_data[i];
+ addr = mem_data->m_base_address;
+ start_check = (paddr >= addr);
+ end_check = (paddr + size <= addr + mem_data->m_size * 1024);
+ if (mem_data->m_used && start_check && end_check)
+ return true;
+ }
+
+ return false;
+}
+
+int xocl_pwrite_unmgd_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ int channel;
+ struct drm_xocl_unmgd unmgd;
+ const struct drm_xocl_pwrite_unmgd *args = data;
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ u32 dir = 1;
+ ssize_t ret = 0;
+
+ if (args->address_space != 0) {
+ userpf_err(xdev, "invalid addr space");
+ return -EFAULT;
+ }
+
+ if (args->size == 0)
+ return 0;
+
+ if (!xocl_validate_paddr(xdev, args->paddr, args->size)) {
+ userpf_err(xdev, "invalid paddr: 0x%llx, size:0x%llx",
+ args->paddr, args->size);
+ /* currently we are not able to return error because
+ * it is unclear that what addresses are valid other than
+ * ddr area. we should revisit this sometime.
+ * return -EINVAL;
+ */
+ }
+
+ ret = xocl_init_unmgd(&unmgd, args->data_ptr, args->size, dir);
+ if (ret) {
+ userpf_err(xdev, "init unmgd failed %ld", ret);
+ return ret;
+ }
+
+ channel = xocl_acquire_channel(xdev, dir);
+ if (channel < 0) {
+ userpf_err(xdev, "acquire channel failed");
+ ret = -EINVAL;
+ goto clear;
+ }
+ /* Now perform DMA */
+ ret = xocl_migrate_bo(xdev, unmgd.sgt, dir, args->paddr, channel,
+ args->size);
+ if (ret >= 0)
+ ret = (ret == args->size) ? 0 : -EIO;
+ xocl_release_channel(xdev, dir, channel);
+clear:
+ xocl_finish_unmgd(&unmgd);
+ return ret;
+}
+
+int xocl_pread_unmgd_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ int channel;
+ struct drm_xocl_unmgd unmgd;
+ const struct drm_xocl_pwrite_unmgd *args = data;
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ u32 dir = 0; /* read */
+ ssize_t ret = 0;
+
+ if (args->address_space != 0) {
+ userpf_err(xdev, "invalid addr space");
+ return -EFAULT;
+ }
+
+ if (args->size == 0)
+ return 0;
+
+ if (!xocl_validate_paddr(xdev, args->paddr, args->size)) {
+ userpf_err(xdev, "invalid paddr: 0x%llx, size:0x%llx",
+ args->paddr, args->size);
+ /* currently we are not able to return error because
+ * it is unclear that what addresses are valid other than
+ * ddr area. we should revisit this sometime.
+ * return -EINVAL;
+ */
+ }
+
+ ret = xocl_init_unmgd(&unmgd, args->data_ptr, args->size, dir);
+ if (ret) {
+ userpf_err(xdev, "init unmgd failed %ld", ret);
+ return ret;
+ }
+
+ channel = xocl_acquire_channel(xdev, dir);
+
+ if (channel < 0) {
+ userpf_err(xdev, "acquire channel failed");
+ ret = -EINVAL;
+ goto clear;
+ }
+ /* Now perform DMA */
+ ret = xocl_migrate_bo(xdev, unmgd.sgt, dir, args->paddr, channel,
+ args->size);
+ if (ret >= 0)
+ ret = (ret == args->size) ? 0 : -EIO;
+
+ xocl_release_channel(xdev, dir, channel);
+clear:
+ xocl_finish_unmgd(&unmgd);
+ return ret;
+}
+
+int xocl_usage_stat_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ struct drm_xocl_usage_stat *args = data;
+ int i;
+
+ args->mm_channel_count = XOCL_DDR_COUNT(xdev);
+ if (args->mm_channel_count > 8)
+ args->mm_channel_count = 8;
+ for (i = 0; i < args->mm_channel_count; i++)
+ xocl_mm_get_usage_stat(drm_p, i, args->mm + i);
+
+ args->dma_channel_count = xocl_get_chan_count(xdev);
+ if (args->dma_channel_count > 8)
+ args->dma_channel_count = 8;
+
+ for (i = 0; i < args->dma_channel_count; i++) {
+ args->h2c[i] = xocl_get_chan_stat(xdev, i, 1);
+ args->c2h[i] = xocl_get_chan_stat(xdev, i, 0);
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/xocl/userpf/xocl_bo.h b/drivers/gpu/drm/xocl/userpf/xocl_bo.h
new file mode 100644
index 000000000000..38ac78cd59f6
--- /dev/null
+++ b/drivers/gpu/drm/xocl/userpf/xocl_bo.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2018 Xilinx, Inc. All rights reserved.
+ *
+ * Authors:
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _XOCL_BO_H
+#define _XOCL_BO_H
+
+#include <drm/xocl_drm.h>
+#include "../xocl_drm.h"
+
+#define XOCL_BO_USERPTR (1 << 31)
+#define XOCL_BO_IMPORT (1 << 30)
+#define XOCL_BO_EXECBUF (1 << 29)
+#define XOCL_BO_CMA (1 << 28)
+#define XOCL_BO_P2P (1 << 27)
+
+#define XOCL_BO_DDR0 (1 << 0)
+#define XOCL_BO_DDR1 (1 << 1)
+#define XOCL_BO_DDR2 (1 << 2)
+#define XOCL_BO_DDR3 (1 << 3)
+
+
+
+//#define XOCL_MEM_BANK_MSK (0xFFFFFF)
+/*
+ * When the BO is imported from an ARE device. This is remote BO to
+ * be accessed over ARE
+ */
+#define XOCL_BO_ARE (1 << 26)
+
+static inline bool xocl_bo_userptr(const struct drm_xocl_bo *bo)
+{
+ return (bo->type & XOCL_BO_USERPTR);
+}
+
+static inline bool xocl_bo_import(const struct drm_xocl_bo *bo)
+{
+ return (bo->type & XOCL_BO_IMPORT);
+}
+
+static inline bool xocl_bo_execbuf(const struct drm_xocl_bo *bo)
+{
+ return (bo->type & XOCL_BO_EXECBUF);
+}
+
+static inline bool xocl_bo_cma(const struct drm_xocl_bo *bo)
+{
+ return (bo->type & XOCL_BO_CMA);
+}
+static inline bool xocl_bo_p2p(const struct drm_xocl_bo *bo)
+{
+ return (bo->type & XOCL_BO_P2P);
+}
+
+static inline struct drm_gem_object *xocl_gem_object_lookup(struct drm_device *dev,
+ struct drm_file *filp,
+ u32 handle)
+{
+ return drm_gem_object_lookup(filp, handle);
+}
+
+static inline struct drm_xocl_dev *bo_xocl_dev(const struct drm_xocl_bo *bo)
+{
+ return bo->base.dev->dev_private;
+}
+
+static inline unsigned int xocl_bo_ddr_idx(unsigned int flags)
+{
+ return flags;
+}
+
+int xocl_create_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_userptr_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_sync_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_copy_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_map_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_info_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_pwrite_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_pread_bo_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_ctx_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_pwrite_unmgd_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_pread_unmgd_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+int xocl_usage_stat_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+
+struct sg_table *xocl_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *xocl_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach, struct sg_table *sgt);
+void *xocl_gem_prime_vmap(struct drm_gem_object *obj);
+void xocl_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+int xocl_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+
+#endif
diff --git a/drivers/gpu/drm/xocl/userpf/xocl_drm.c b/drivers/gpu/drm/xocl/userpf/xocl_drm.c
new file mode 100644
index 000000000000..e07f5f8a054a
--- /dev/null
+++ b/drivers/gpu/drm/xocl/userpf/xocl_drm.c
@@ -0,0 +1,640 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2018 Xilinx, Inc. All rights reserved.
+ *
+ * Authors:
+ *
+ */
+
+#include <linux/version.h>
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_mm.h>
+#include "../version.h"
+#include "../lib/libxdma_api.h"
+#include "common.h"
+
+#if defined(__PPC64__)
+#define XOCL_FILE_PAGE_OFFSET 0x10000
+#else
+#define XOCL_FILE_PAGE_OFFSET 0x100000
+#endif
+
+#ifndef VM_RESERVED
+#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
+#endif
+
+#ifdef _XOCL_DRM_DEBUG
+#define DRM_ENTER(fmt, args...) \
+ printk(KERN_INFO "[DRM] Entering %s:"fmt"\n", __func__, ##args)
+#define DRM_DBG(fmt, args...) \
+ printk(KERN_INFO "[DRM] %s:%d:"fmt"\n", __func__, __LINE__, ##args)
+#else
+#define DRM_ENTER(fmt, args...)
+#define DRM_DBG(fmt, args...)
+#endif
+
+static char driver_date[9];
+
+static void xocl_free_object(struct drm_gem_object *obj)
+{
+ DRM_ENTER("");
+ xocl_drm_free_bo(obj);
+}
+
+static int xocl_open(struct inode *inode, struct file *filp)
+{
+ struct xocl_drm *drm_p;
+ struct drm_file *priv;
+ struct drm_device *ddev;
+ int ret;
+
+ ret = drm_open(inode, filp);
+ if (ret)
+ return ret;
+
+ priv = filp->private_data;
+ ddev = priv->minor->dev;
+ drm_p = xocl_drvinst_open(ddev);
+ if (!drm_p)
+ return -ENXIO;
+
+ return 0;
+}
+
+static int xocl_release(struct inode *inode, struct file *filp)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *ddev = priv->minor->dev;
+ struct xocl_drm *drm_p = ddev->dev_private;
+ int ret;
+
+ ret = drm_release(inode, filp);
+ xocl_drvinst_close(drm_p);
+
+ return ret;
+}
+
+static int xocl_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int ret;
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct mm_struct *mm = current->mm;
+ struct xocl_drm *drm_p = dev->dev_private;
+ xdev_handle_t xdev = drm_p->xdev;
+ unsigned long vsize;
+ phys_addr_t res_start;
+
+ DRM_ENTER("vm pgoff %lx", vma->vm_pgoff);
+
+ /*
+ * If the page offset is > than 4G, then let GEM handle that and do what
+ * it thinks is best,we will only handle page offsets less than 4G.
+ */
+ if (likely(vma->vm_pgoff >= XOCL_FILE_PAGE_OFFSET)) {
+ ret = drm_gem_mmap(filp, vma);
+ if (ret)
+ return ret;
+ /* Clear VM_PFNMAP flag set by drm_gem_mmap()
+ * we have "struct page" for all backing pages for bo
+ */
+ vma->vm_flags &= ~VM_PFNMAP;
+ /* Clear VM_IO flag set by drm_gem_mmap()
+ * it prevents gdb from accessing mapped buffers
+ */
+ vma->vm_flags &= ~VM_IO;
+ vma->vm_flags |= VM_MIXEDMAP;
+ vma->vm_flags |= mm->def_flags;
+ vma->vm_pgoff = 0;
+
+ /* Override pgprot_writecombine() mapping setup by
+ * drm_gem_mmap()
+ * which results in very poor read performance
+ */
+ if (vma->vm_flags & (VM_READ | VM_MAYREAD))
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+ else
+ vma->vm_page_prot = pgprot_writecombine(
+ vm_get_page_prot(vma->vm_flags));
+ return ret;
+ }
+
+ if (vma->vm_pgoff != 0)
+ return -EINVAL;
+
+ vsize = vma->vm_end - vma->vm_start;
+ if (vsize > XDEV(xdev)->bar_size)
+ return -EINVAL;
+
+ DRM_DBG("MAP size %ld", vsize);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED;
+
+ res_start = pci_resource_start(XDEV(xdev)->pdev, XDEV(xdev)->bar_idx);
+ ret = io_remap_pfn_range(vma, vma->vm_start,
+ res_start >> PAGE_SHIFT,
+ vsize, vma->vm_page_prot);
+ userpf_info(xdev, "io_remap_pfn_range ret code: %d", ret);
+
+ return ret;
+}
+
+int xocl_gem_fault(struct vm_fault *vmf)
+{
+ loff_t num_pages;
+ unsigned int page_offset;
+ struct vm_area_struct *vma = vmf->vma;
+ struct drm_xocl_bo *xobj = to_xocl_bo(vma->vm_private_data);
+ int ret = 0;
+ unsigned long vmf_address = vmf->address;
+
+ page_offset = (vmf_address - vma->vm_start) >> PAGE_SHIFT;
+
+
+ if (!xobj->pages)
+ return VM_FAULT_SIGBUS;
+
+ num_pages = DIV_ROUND_UP(xobj->base.size, PAGE_SIZE);
+ if (page_offset > num_pages)
+ return VM_FAULT_SIGBUS;
+
+ if (xobj->type & XOCL_BO_P2P)
+ ret = vm_insert_page(vma, vmf_address, xobj->pages[page_offset]);
+ else
+ ret = vm_insert_page(vma, vmf_address, xobj->pages[page_offset]);
+
+ switch (ret) {
+ case -EAGAIN:
+ case 0:
+ case -ERESTARTSYS:
+ return VM_FAULT_NOPAGE;
+ case -ENOMEM:
+ return VM_FAULT_OOM;
+ default:
+ return VM_FAULT_SIGBUS;
+ }
+}
+
+static int xocl_client_open(struct drm_device *dev, struct drm_file *filp)
+{
+ struct xocl_drm *drm_p = dev->dev_private;
+ int ret = 0;
+
+ DRM_ENTER("");
+
+ /* We do not allow users to open PRIMARY node, /dev/dri/cardX node.
+ * Users should only open RENDER, /dev/dri/renderX node
+ */
+ if (drm_is_primary_client(filp))
+ return -EPERM;
+
+ if (get_live_client_size(drm_p->xdev) > XOCL_MAX_CONCURRENT_CLIENTS)
+ return -EBUSY;
+
+ ret = xocl_exec_create_client(drm_p->xdev, &filp->driver_priv);
+ if (ret)
+ goto failed;
+
+ return 0;
+
+failed:
+ return ret;
+}
+
+static void xocl_client_release(struct drm_device *dev, struct drm_file *filp)
+{
+ struct xocl_drm *drm_p = dev->dev_private;
+
+ xocl_exec_destroy_client(drm_p->xdev, &filp->driver_priv);
+}
+
+static uint xocl_poll(struct file *filp, poll_table *wait)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct xocl_drm *drm_p = dev->dev_private;
+
+ BUG_ON(!priv->driver_priv);
+
+ DRM_ENTER("");
+ return xocl_exec_poll_client(drm_p->xdev, filp, wait, priv->driver_priv);
+}
+
+static const struct drm_ioctl_desc xocl_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(XOCL_CREATE_BO, xocl_create_bo_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_USERPTR_BO, xocl_userptr_bo_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_MAP_BO, xocl_map_bo_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_SYNC_BO, xocl_sync_bo_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_INFO_BO, xocl_info_bo_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_PWRITE_BO, xocl_pwrite_bo_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_PREAD_BO, xocl_pread_bo_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_CTX, xocl_ctx_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_INFO, xocl_info_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_READ_AXLF, xocl_read_axlf_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_PWRITE_UNMGD, xocl_pwrite_unmgd_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_PREAD_UNMGD, xocl_pread_unmgd_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_USAGE_STAT, xocl_usage_stat_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_USER_INTR, xocl_user_intr_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_EXECBUF, xocl_execbuf_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_COPY_BO, xocl_copy_bo_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_HOT_RESET, xocl_hot_reset_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XOCL_RECLOCK, xocl_reclock_ioctl,
+ DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+};
+
+static long xocl_drm_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ return drm_ioctl(filp, cmd, arg);
+}
+
+static const struct file_operations xocl_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = xocl_open,
+ .mmap = xocl_mmap,
+ .poll = xocl_poll,
+ .read = drm_read,
+ .unlocked_ioctl = xocl_drm_ioctl,
+ .release = xocl_release,
+};
+
+static const struct vm_operations_struct xocl_vm_ops = {
+ .fault = xocl_gem_fault,
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static struct drm_driver mm_drm_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_PRIME |
+ DRIVER_RENDER,
+
+ .postclose = xocl_client_release,
+ .open = xocl_client_open,
+
+ .gem_free_object = xocl_free_object,
+ .gem_vm_ops = &xocl_vm_ops,
+
+ .ioctls = xocl_ioctls,
+ .num_ioctls = ARRAY_SIZE(xocl_ioctls),
+ .fops = &xocl_driver_fops,
+
+ .gem_prime_get_sg_table = xocl_gem_prime_get_sg_table,
+ .gem_prime_import_sg_table = xocl_gem_prime_import_sg_table,
+ .gem_prime_vmap = xocl_gem_prime_vmap,
+ .gem_prime_vunmap = xocl_gem_prime_vunmap,
+ .gem_prime_mmap = xocl_gem_prime_mmap,
+
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .name = XOCL_MODULE_NAME,
+ .desc = XOCL_DRIVER_DESC,
+ .date = driver_date,
+};
+
+void *xocl_drm_init(xdev_handle_t xdev_hdl)
+{
+ struct xocl_drm *drm_p = NULL;
+ struct drm_device *ddev = NULL;
+ int year, mon, day;
+ int ret = 0;
+ bool drm_registered = false;
+
+ sscanf(XRT_DRIVER_VERSION, "%d.%d.%d",
+ &mm_drm_driver.major,
+ &mm_drm_driver.minor,
+ &mm_drm_driver.patchlevel);
+
+ sscanf(xrt_build_version_date, "%d-%d-%d ", &year, &mon, &day);
+ snprintf(driver_date, sizeof(driver_date),
+ "%d%02d%02d", year, mon, day);
+
+ ddev = drm_dev_alloc(&mm_drm_driver, &XDEV(xdev_hdl)->pdev->dev);
+ if (!ddev) {
+ xocl_xdev_err(xdev_hdl, "alloc drm dev failed");
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ drm_p = xocl_drvinst_alloc(ddev->dev, sizeof(*drm_p));
+ if (!drm_p) {
+ xocl_xdev_err(xdev_hdl, "alloc drm inst failed");
+ ret = -ENOMEM;
+ goto failed;
+ }
+ drm_p->xdev = xdev_hdl;
+
+ ddev->pdev = XDEV(xdev_hdl)->pdev;
+
+ ret = drm_dev_register(ddev, 0);
+ if (ret) {
+ xocl_xdev_err(xdev_hdl, "register drm dev failed 0x%x", ret);
+ goto failed;
+ }
+ drm_registered = true;
+
+ drm_p->ddev = ddev;
+
+ mutex_init(&drm_p->mm_lock);
+ ddev->dev_private = drm_p;
+ hash_init(drm_p->mm_range);
+
+ xocl_drvinst_set_filedev(drm_p, ddev);
+ return drm_p;
+
+failed:
+ if (drm_registered)
+ drm_dev_unregister(ddev);
+//PORT4_20
+ if (ddev)
+ drm_dev_put(ddev);
+ if (drm_p)
+ xocl_drvinst_free(drm_p);
+
+ return NULL;
+}
+
+void xocl_drm_fini(struct xocl_drm *drm_p)
+{
+ xocl_cleanup_mem(drm_p);
+ drm_put_dev(drm_p->ddev);
+ mutex_destroy(&drm_p->mm_lock);
+
+ xocl_drvinst_free(drm_p);
+}
+
+void xocl_mm_get_usage_stat(struct xocl_drm *drm_p, u32 ddr,
+ struct drm_xocl_mm_stat *pstat)
+{
+ pstat->memory_usage = drm_p->mm_usage_stat[ddr] ?
+ drm_p->mm_usage_stat[ddr]->memory_usage : 0;
+ pstat->bo_count = drm_p->mm_usage_stat[ddr] ?
+ drm_p->mm_usage_stat[ddr]->bo_count : 0;
+}
+
+void xocl_mm_update_usage_stat(struct xocl_drm *drm_p, u32 ddr,
+ u64 size, int count)
+{
+ BUG_ON(!drm_p->mm_usage_stat[ddr]);
+
+ drm_p->mm_usage_stat[ddr]->memory_usage += (count > 0) ? size : -size;
+ drm_p->mm_usage_stat[ddr]->bo_count += count;
+}
+
+int xocl_mm_insert_node(struct xocl_drm *drm_p, u32 ddr,
+ struct drm_mm_node *node, u64 size)
+{
+ return drm_mm_insert_node_generic(drm_p->mm[ddr], node, size, PAGE_SIZE,
+#if defined(XOCL_DRM_FREE_MALLOC)
+ 0, 0);
+#else
+ 0, 0, 0);
+#endif
+}
+
+int xocl_check_topology(struct xocl_drm *drm_p)
+{
+ struct mem_topology *topology;
+ u16 i;
+ int err = 0;
+
+ topology = XOCL_MEM_TOPOLOGY(drm_p->xdev);
+ if (topology == NULL)
+ return 0;
+
+ for (i = 0; i < topology->m_count; i++) {
+ if (!topology->m_mem_data[i].m_used)
+ continue;
+
+ if (topology->m_mem_data[i].m_type == MEM_STREAMING)
+ continue;
+
+ if (drm_p->mm_usage_stat[i]->bo_count != 0) {
+ err = -EPERM;
+ xocl_err(drm_p->ddev->dev,
+ "The ddr %d has pre-existing buffer allocations, please exit and re-run.",
+ i);
+ }
+ }
+
+ return err;
+}
+
+uint32_t xocl_get_shared_ddr(struct xocl_drm *drm_p, struct mem_data *m_data)
+{
+ struct xocl_mm_wrapper *wrapper;
+ uint64_t start_addr = m_data->m_base_address;
+ uint64_t sz = m_data->m_size*1024;
+
+ hash_for_each_possible(drm_p->mm_range, wrapper, node, start_addr) {
+ if (!wrapper)
+ continue;
+
+ if (wrapper->start_addr == start_addr) {
+ if (wrapper->size == sz)
+ return wrapper->ddr;
+ else
+ return 0xffffffff;
+ }
+ }
+ return 0xffffffff;
+}
+
+void xocl_cleanup_mem(struct xocl_drm *drm_p)
+{
+ struct mem_topology *topology;
+ u16 i, ddr;
+ uint64_t addr;
+ struct xocl_mm_wrapper *wrapper;
+ struct hlist_node *tmp;
+
+ topology = XOCL_MEM_TOPOLOGY(drm_p->xdev);
+ if (topology) {
+ ddr = topology->m_count;
+ for (i = 0; i < ddr; i++) {
+ if (!topology->m_mem_data[i].m_used)
+ continue;
+
+ if (topology->m_mem_data[i].m_type == MEM_STREAMING)
+ continue;
+
+ xocl_info(drm_p->ddev->dev, "Taking down DDR : %d", i);
+ addr = topology->m_mem_data[i].m_base_address;
+
+ hash_for_each_possible_safe(drm_p->mm_range, wrapper,
+ tmp, node, addr) {
+ if (wrapper->ddr == i) {
+ hash_del(&wrapper->node);
+ vfree(wrapper);
+ drm_mm_takedown(drm_p->mm[i]);
+ vfree(drm_p->mm[i]);
+ vfree(drm_p->mm_usage_stat[i]);
+ }
+ }
+
+ drm_p->mm[i] = NULL;
+ drm_p->mm_usage_stat[i] = NULL;
+ }
+ }
+ vfree(drm_p->mm);
+ drm_p->mm = NULL;
+ vfree(drm_p->mm_usage_stat);
+ drm_p->mm_usage_stat = NULL;
+ vfree(drm_p->mm_p2p_off);
+ drm_p->mm_p2p_off = NULL;
+}
+
+int xocl_init_mem(struct xocl_drm *drm_p)
+{
+ size_t length = 0;
+ size_t mm_size = 0, mm_stat_size = 0;
+ size_t size = 0, wrapper_size = 0;
+ size_t ddr_bank_size;
+ struct mem_topology *topo;
+ struct mem_data *mem_data;
+ uint32_t shared;
+ struct xocl_mm_wrapper *wrapper = NULL;
+ uint64_t reserved1 = 0;
+ uint64_t reserved2 = 0;
+ uint64_t reserved_start;
+ uint64_t reserved_end;
+ int err = 0;
+ int i = -1;
+
+ if (XOCL_DSA_IS_MPSOC(drm_p->xdev)) {
+ /* TODO: This is still hardcoding.. */
+ reserved1 = 0x80000000;
+ reserved2 = 0x1000000;
+ }
+
+ topo = XOCL_MEM_TOPOLOGY(drm_p->xdev);
+ if (topo == NULL)
+ return 0;
+
+ length = topo->m_count * sizeof(struct mem_data);
+ size = topo->m_count * sizeof(void *);
+ wrapper_size = sizeof(struct xocl_mm_wrapper);
+ mm_size = sizeof(struct drm_mm);
+ mm_stat_size = sizeof(struct drm_xocl_mm_stat);
+
+ xocl_info(drm_p->ddev->dev, "Topology count = %d, data_length = %ld",
+ topo->m_count, length);
+
+ drm_p->mm = vzalloc(size);
+ drm_p->mm_usage_stat = vzalloc(size);
+ drm_p->mm_p2p_off = vzalloc(topo->m_count * sizeof(u64));
+ if (!drm_p->mm || !drm_p->mm_usage_stat || !drm_p->mm_p2p_off) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ for (i = 0; i < topo->m_count; i++) {
+ mem_data = &topo->m_mem_data[i];
+ ddr_bank_size = mem_data->m_size * 1024;
+
+ xocl_info(drm_p->ddev->dev, " Mem Index %d", i);
+ xocl_info(drm_p->ddev->dev, " Base Address:0x%llx",
+ mem_data->m_base_address);
+ xocl_info(drm_p->ddev->dev, " Size:0x%lx", ddr_bank_size);
+ xocl_info(drm_p->ddev->dev, " Type:%d", mem_data->m_type);
+ xocl_info(drm_p->ddev->dev, " Used:%d", mem_data->m_used);
+ }
+
+ /* Initialize the used banks and their sizes */
+ /* Currently only fixed sizes are supported */
+ for (i = 0; i < topo->m_count; i++) {
+ mem_data = &topo->m_mem_data[i];
+ if (!mem_data->m_used)
+ continue;
+
+ if (mem_data->m_type == MEM_STREAMING ||
+ mem_data->m_type == MEM_STREAMING_CONNECTION)
+ continue;
+
+ ddr_bank_size = mem_data->m_size * 1024;
+ xocl_info(drm_p->ddev->dev, "Allocating DDR bank%d", i);
+ xocl_info(drm_p->ddev->dev, " base_addr:0x%llx, total size:0x%lx",
+ mem_data->m_base_address, ddr_bank_size);
+
+ if (XOCL_DSA_IS_MPSOC(drm_p->xdev)) {
+ reserved_end = mem_data->m_base_address + ddr_bank_size;
+ reserved_start = reserved_end - reserved1 - reserved2;
+ xocl_info(drm_p->ddev->dev, " reserved region:0x%llx - 0x%llx",
+ reserved_start, reserved_end - 1);
+ }
+
+ shared = xocl_get_shared_ddr(drm_p, mem_data);
+ if (shared != 0xffffffff) {
+ xocl_info(drm_p->ddev->dev, "Found duplicated memory region!");
+ drm_p->mm[i] = drm_p->mm[shared];
+ drm_p->mm_usage_stat[i] = drm_p->mm_usage_stat[shared];
+ continue;
+ }
+
+ xocl_info(drm_p->ddev->dev, "Found a new memory region");
+ wrapper = vzalloc(wrapper_size);
+ drm_p->mm[i] = vzalloc(mm_size);
+ drm_p->mm_usage_stat[i] = vzalloc(mm_stat_size);
+
+ if (!drm_p->mm[i] || !drm_p->mm_usage_stat[i] || !wrapper) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ wrapper->start_addr = mem_data->m_base_address;
+ wrapper->size = mem_data->m_size*1024;
+ wrapper->mm = drm_p->mm[i];
+ wrapper->mm_usage_stat = drm_p->mm_usage_stat[i];
+ wrapper->ddr = i;
+ hash_add(drm_p->mm_range, &wrapper->node, wrapper->start_addr);
+
+ drm_mm_init(drm_p->mm[i], mem_data->m_base_address,
+ ddr_bank_size - reserved1 - reserved2);
+ drm_p->mm_p2p_off[i] = ddr_bank_size * i;
+
+ xocl_info(drm_p->ddev->dev, "drm_mm_init called");
+ }
+
+ return 0;
+
+failed:
+ vfree(wrapper);
+ if (drm_p->mm) {
+ for (; i >= 0; i--) {
+ drm_mm_takedown(drm_p->mm[i]);
+ vfree(drm_p->mm[i]);
+ vfree(drm_p->mm_usage_stat[i]);
+ }
+ vfree(drm_p->mm);
+ drm_p->mm = NULL;
+ }
+ vfree(drm_p->mm_usage_stat);
+ drm_p->mm_usage_stat = NULL;
+ vfree(drm_p->mm_p2p_off);
+ drm_p->mm_p2p_off = NULL;
+
+ return err;
+}
diff --git a/drivers/gpu/drm/xocl/userpf/xocl_drv.c b/drivers/gpu/drm/xocl/userpf/xocl_drv.c
new file mode 100644
index 000000000000..6fc57da3deab
--- /dev/null
+++ b/drivers/gpu/drm/xocl/userpf/xocl_drv.c
@@ -0,0 +1,743 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors: Lizhi.Hou@xxxxxxxxxx
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/aer.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include "../xocl_drv.h"
+#include "common.h"
+#include "../version.h"
+#include <linux/memremap.h>
+
+#ifndef PCI_EXT_CAP_ID_REBAR
+#define PCI_EXT_CAP_ID_REBAR 0x15
+#endif
+
+#ifndef PCI_REBAR_CTRL
+#define PCI_REBAR_CTRL 8 /* control register */
+#endif
+
+#ifndef PCI_REBAR_CTRL_BAR_SIZE
+#define PCI_REBAR_CTRL_BAR_SIZE 0x00001F00 /* BAR size */
+#endif
+
+#ifndef PCI_REBAR_CTRL_BAR_SHIFT
+#define PCI_REBAR_CTRL_BAR_SHIFT 8 /* shift for BAR size */
+#endif
+
+#define REBAR_FIRST_CAP 4
+
+static const struct pci_device_id pciidlist[] = XOCL_USER_PCI_IDS;
+
+struct class *xrt_class;
+
+MODULE_DEVICE_TABLE(pci, pciidlist);
+
+static int userpf_intr_config(xdev_handle_t xdev_hdl, u32 intr, bool en)
+{
+ return xocl_dma_intr_config(xdev_hdl, intr, en);
+}
+
+static int userpf_intr_register(xdev_handle_t xdev_hdl, u32 intr,
+ irq_handler_t handler, void *arg)
+{
+ return handler ?
+ xocl_dma_intr_register(xdev_hdl, intr, handler, arg, -1) :
+ xocl_dma_intr_unreg(xdev_hdl, intr);
+}
+
+struct xocl_pci_funcs userpf_pci_ops = {
+ .intr_config = userpf_intr_config,
+ .intr_register = userpf_intr_register,
+};
+
+void xocl_reset_notify(struct pci_dev *pdev, bool prepare)
+{
+ struct xocl_dev *xdev = pci_get_drvdata(pdev);
+
+ xocl_info(&pdev->dev, "PCI reset NOTIFY, prepare %d", prepare);
+
+ if (prepare) {
+ xocl_mailbox_reset(xdev, false);
+ xocl_subdev_destroy_by_id(xdev, XOCL_SUBDEV_DMA);
+ } else {
+ reset_notify_client_ctx(xdev);
+ xocl_subdev_create_by_id(xdev, XOCL_SUBDEV_DMA);
+ xocl_mailbox_reset(xdev, true);
+ xocl_exec_reset(xdev);
+ }
+}
+
+static void kill_all_clients(struct xocl_dev *xdev)
+{
+ struct list_head *ptr;
+ struct client_ctx *entry;
+ int ret;
+ int total_wait_secs = 10; // sec
+ int wait_interval = 100; // millisec
+ int retry = total_wait_secs * 1000 / wait_interval;
+
+ mutex_lock(&xdev->ctx_list_lock);
+
+ list_for_each(ptr, &xdev->ctx_list) {
+ entry = list_entry(ptr, struct client_ctx, link);
+ ret = kill_pid(entry->pid, SIGBUS, 1);
+ if (ret) {
+ userpf_err(xdev, "killing pid: %d failed. err: %d",
+ pid_nr(entry->pid), ret);
+ }
+ }
+
+ mutex_unlock(&xdev->ctx_list_lock);
+
+ while (!list_empty(&xdev->ctx_list) && retry--)
+ msleep(wait_interval);
+
+ if (!list_empty(&xdev->ctx_list))
+ userpf_err(xdev, "failed to kill all clients");
+}
+
+int64_t xocl_hot_reset(struct xocl_dev *xdev, bool force)
+{
+ bool skip = false;
+ int64_t ret = 0, mbret = 0;
+ struct mailbox_req mbreq = { MAILBOX_REQ_HOT_RESET, };
+ size_t resplen = sizeof(ret);
+
+ mutex_lock(&xdev->ctx_list_lock);
+ if (xdev->offline) {
+ skip = true;
+ } else if (!force && !list_is_singular(&xdev->ctx_list)) {
+ /* We should have one context for ourselves. */
+ BUG_ON(list_empty(&xdev->ctx_list));
+ userpf_err(xdev, "device is in use, can't reset");
+ ret = -EBUSY;
+ } else {
+ xdev->offline = true;
+ }
+ mutex_unlock(&xdev->ctx_list_lock);
+ if (ret < 0 || skip)
+ return ret;
+
+ userpf_info(xdev, "resetting device...");
+
+ if (force)
+ kill_all_clients(xdev);
+
+ xocl_reset_notify(xdev->core.pdev, true);
+ mbret = xocl_peer_request(xdev, &mbreq, sizeof(struct mailbox_req), &ret, &resplen, NULL, NULL);
+ if (mbret)
+ ret = mbret;
+ xocl_reset_notify(xdev->core.pdev, false);
+
+ mutex_lock(&xdev->ctx_list_lock);
+ xdev->offline = false;
+ mutex_unlock(&xdev->ctx_list_lock);
+
+ return ret;
+}
+
+
+int xocl_reclock(struct xocl_dev *xdev, void *data)
+{
+ int err = 0;
+ int64_t msg = -ENODEV;
+ struct mailbox_req *req = NULL;
+ size_t resplen = sizeof(msg);
+ size_t reqlen = sizeof(struct mailbox_req)+sizeof(struct drm_xocl_reclock_info);
+
+ req = kzalloc(reqlen, GFP_KERNEL);
+ req->req = MAILBOX_REQ_RECLOCK;
+ req->data_total_len = sizeof(struct drm_xocl_reclock_info);
+ memcpy(req->data, data, sizeof(struct drm_xocl_reclock_info));
+
+ err = xocl_peer_request(xdev, req, reqlen,
+ &msg, &resplen, NULL, NULL);
+
+ if (msg != 0)
+ err = -ENODEV;
+
+ kfree(req);
+ return err;
+}
+
+static void xocl_mailbox_srv(void *arg, void *data, size_t len,
+ u64 msgid, int err)
+{
+ struct xocl_dev *xdev = (struct xocl_dev *)arg;
+ struct mailbox_req *req = (struct mailbox_req *)data;
+
+ if (err != 0)
+ return;
+
+ userpf_info(xdev, "received request (%d) from peer\n", req->req);
+
+ switch (req->req) {
+ case MAILBOX_REQ_FIREWALL:
+ (void) xocl_hot_reset(xdev, true);
+ break;
+ default:
+ userpf_err(xdev, "dropped bad request (%d)\n", req->req);
+ break;
+ }
+}
+
+void get_pcie_link_info(struct xocl_dev *xdev,
+ unsigned short *link_width, unsigned short *link_speed, bool is_cap)
+{
+ u16 stat;
+ long result;
+ int pos = is_cap ? PCI_EXP_LNKCAP : PCI_EXP_LNKSTA;
+
+ result = pcie_capability_read_word(xdev->core.pdev, pos, &stat);
+ if (result) {
+ *link_width = *link_speed = 0;
+ xocl_info(&xdev->core.pdev->dev, "Read pcie capability failed");
+ return;
+ }
+ *link_width = (stat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
+ *link_speed = stat & PCI_EXP_LNKSTA_CLS;
+}
+
+void user_pci_reset_prepare(struct pci_dev *pdev)
+{
+ xocl_reset_notify(pdev, true);
+}
+
+void user_pci_reset_done(struct pci_dev *pdev)
+{
+ xocl_reset_notify(pdev, false);
+}
+
+static void xocl_dev_percpu_release(struct percpu_ref *ref)
+{
+ struct xocl_dev *xdev = container_of(ref, struct xocl_dev, ref);
+
+ complete(&xdev->cmp);
+}
+
+static void xocl_dev_percpu_exit(void *data)
+{
+ struct percpu_ref *ref = data;
+ struct xocl_dev *xdev = container_of(ref, struct xocl_dev, ref);
+
+ wait_for_completion(&xdev->cmp);
+ percpu_ref_exit(ref);
+}
+
+
+static void xocl_dev_percpu_kill(void *data)
+{
+ struct percpu_ref *ref = data;
+
+ percpu_ref_kill(ref);
+}
+
+void xocl_p2p_mem_release(struct xocl_dev *xdev, bool recov_bar_sz)
+{
+ struct pci_dev *pdev = xdev->core.pdev;
+ int p2p_bar = -1;
+
+ if (xdev->p2p_bar_addr) {
+ devres_release_group(&pdev->dev, xdev->p2p_res_grp);
+ xdev->p2p_bar_addr = NULL;
+ xdev->p2p_res_grp = NULL;
+ }
+ if (xdev->p2p_res_grp) {
+ devres_remove_group(&pdev->dev, xdev->p2p_res_grp);
+ xdev->p2p_res_grp = NULL;
+ }
+
+ if (recov_bar_sz) {
+ p2p_bar = xocl_get_p2p_bar(xdev, NULL);
+ if (p2p_bar < 0)
+ return;
+
+ xocl_pci_resize_resource(pdev, p2p_bar,
+ (XOCL_PA_SECTION_SHIFT - 20));
+
+ xocl_info(&pdev->dev, "Resize p2p bar %d to %d M ", p2p_bar,
+ (1 << XOCL_PA_SECTION_SHIFT));
+ }
+}
+
+int xocl_p2p_mem_reserve(struct xocl_dev *xdev)
+{
+ resource_size_t p2p_bar_addr;
+ resource_size_t p2p_bar_len;
+ struct resource res;
+ uint32_t p2p_bar_idx;
+ struct pci_dev *pdev = xdev->core.pdev;
+ int32_t ret;
+
+ xocl_info(&pdev->dev, "reserve p2p mem, bar %d, len %lld",
+ xdev->p2p_bar_idx, xdev->p2p_bar_len);
+
+ if (xdev->p2p_bar_idx < 0 ||
+ xdev->p2p_bar_len <= (1<<XOCL_PA_SECTION_SHIFT)) {
+ /* only p2p_bar_len > SECTION (256MB) */
+ xocl_info(&pdev->dev, "Did not find p2p BAR");
+ return 0;
+ }
+
+ p2p_bar_len = xdev->p2p_bar_len;
+ p2p_bar_idx = xdev->p2p_bar_idx;
+
+ xdev->p2p_res_grp = devres_open_group(&pdev->dev, NULL, GFP_KERNEL);
+ if (!xdev->p2p_res_grp) {
+ xocl_err(&pdev->dev, "open p2p resource group failed");
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ p2p_bar_addr = pci_resource_start(pdev, p2p_bar_idx);
+
+ res.start = p2p_bar_addr;
+ res.end = p2p_bar_addr+p2p_bar_len-1;
+ res.name = NULL;
+ res.flags = IORESOURCE_MEM;
+
+ init_completion(&xdev->cmp);
+
+ ret = percpu_ref_init(&xdev->ref, xocl_dev_percpu_release, 0,
+ GFP_KERNEL);
+ if (ret)
+ goto failed;
+
+ ret = devm_add_action_or_reset(&(pdev->dev), xocl_dev_percpu_exit,
+ &xdev->ref);
+ if (ret)
+ goto failed;
+
+ xdev->pgmap.ref = &xdev->ref;
+ memcpy(&xdev->pgmap.res, &res, sizeof(struct resource));
+ xdev->pgmap.altmap_valid = false;
+ xdev->p2p_bar_addr = devm_memremap_pages(&(pdev->dev), &xdev->pgmap);
+
+ if (!xdev->p2p_bar_addr) {
+ ret = -ENOMEM;
+ percpu_ref_kill(&xdev->ref);
+ devres_close_group(&pdev->dev, xdev->p2p_res_grp);
+ goto failed;
+ }
+
+ ret = devm_add_action_or_reset(&(pdev->dev), xocl_dev_percpu_kill,
+ &xdev->ref);
+ if (ret) {
+ percpu_ref_kill(&xdev->ref);
+ devres_close_group(&pdev->dev, xdev->p2p_res_grp);
+ goto failed;
+ }
+
+ devres_close_group(&pdev->dev, xdev->p2p_res_grp);
+
+ return 0;
+
+failed:
+ xocl_p2p_mem_release(xdev, false);
+
+ return ret;
+}
+
+static inline u64 xocl_pci_rebar_size_to_bytes(int size)
+{
+ return 1ULL << (size + 20);
+}
+
+int xocl_get_p2p_bar(struct xocl_dev *xdev, u64 *bar_size)
+{
+ struct pci_dev *dev = xdev->core.pdev;
+ int i, pos;
+ u32 cap, ctrl, size;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_REBAR);
+ if (!pos) {
+ xocl_err(&dev->dev, "did not find rebar cap");
+ return -ENOTSUPP;
+ }
+
+ pos += REBAR_FIRST_CAP;
+ for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++) {
+ pci_read_config_dword(dev, pos, &cap);
+ pci_read_config_dword(dev, pos + 4, &ctrl);
+ size = (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >>
+ PCI_REBAR_CTRL_BAR_SHIFT;
+ if (xocl_pci_rebar_size_to_bytes(size) >=
+ (1 << XOCL_PA_SECTION_SHIFT) &&
+ cap >= 0x1000) {
+ if (bar_size)
+ *bar_size = xocl_pci_rebar_size_to_bytes(size);
+ return i;
+ }
+ pos += 8;
+ }
+
+ if (bar_size)
+ *bar_size = 0;
+
+ return -1;
+}
+
+static int xocl_reassign_resources(struct pci_dev *dev, int resno)
+{
+ pci_assign_unassigned_bus_resources(dev->bus);
+
+ return 0;
+}
+
+int xocl_pci_resize_resource(struct pci_dev *dev, int resno, int size)
+{
+ struct resource *res = dev->resource + resno;
+ struct pci_dev *root;
+ struct resource *root_res;
+ u64 bar_size, req_size;
+ unsigned long flags;
+ u16 cmd;
+ int pos, ret = 0;
+ u32 ctrl, i;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_REBAR);
+ if (!pos) {
+ xocl_err(&dev->dev, "did not find rebar cap");
+ return -ENOTSUPP;
+ }
+
+ pos += resno * PCI_REBAR_CTRL;
+ pci_read_config_dword(dev, pos + PCI_REBAR_CTRL, &ctrl);
+
+ bar_size = xocl_pci_rebar_size_to_bytes(
+ (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >>
+ PCI_REBAR_CTRL_BAR_SHIFT);
+ req_size = xocl_pci_rebar_size_to_bytes(size);
+
+ xocl_info(&dev->dev, "req_size %lld, bar size %lld\n",
+ req_size, bar_size);
+ if (req_size == bar_size) {
+ xocl_info(&dev->dev, "same size, return success");
+ return -EALREADY;
+ }
+
+ xocl_get_root_dev(dev, root);
+
+ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
+ root_res = root->subordinate->resource[i];
+ root_res = (root_res) ? root_res->parent : NULL;
+ if (root_res && (root_res->flags & IORESOURCE_MEM)
+ && resource_size(root_res) > req_size)
+ break;
+ }
+
+ if (i == PCI_BRIDGE_RESOURCE_NUM) {
+ xocl_err(&dev->dev, "Not enough IO Mem space, Please check BIOS settings. ");
+ return -ENOSPC;
+ }
+ pci_release_selected_regions(dev, (1 << resno));
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ pci_write_config_word(dev, PCI_COMMAND,
+ cmd & ~PCI_COMMAND_MEMORY);
+
+ flags = res->flags;
+ if (res->parent)
+ release_resource(res);
+
+ ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+ ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
+ pci_write_config_dword(dev, pos + PCI_REBAR_CTRL, ctrl);
+
+
+ res->start = 0;
+ res->end = req_size - 1;
+
+ xocl_info(&dev->dev, "new size %lld", resource_size(res));
+ xocl_reassign_resources(dev, resno);
+ res->flags = flags;
+
+ pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
+ pci_request_selected_regions(dev, (1 << resno),
+ XOCL_MODULE_NAME);
+
+ return ret;
+}
+
+static int identify_bar(struct xocl_dev *xdev)
+{
+ struct pci_dev *pdev = xdev->core.pdev;
+ resource_size_t bar_len;
+ int i;
+
+ for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++) {
+ bar_len = pci_resource_len(pdev, i);
+ if (bar_len >= (1 << XOCL_PA_SECTION_SHIFT)) {
+ xdev->p2p_bar_idx = i;
+ xdev->p2p_bar_len = bar_len;
+ pci_request_selected_regions(pdev, 1 << i,
+ XOCL_MODULE_NAME);
+ } else if (bar_len >= 32 * 1024 * 1024) {
+ xdev->core.bar_addr = ioremap_nocache(
+ pci_resource_start(pdev, i), bar_len);
+ if (!xdev->core.bar_addr)
+ return -EIO;
+ xdev->core.bar_idx = i;
+ xdev->core.bar_size = bar_len;
+ }
+ }
+
+ return 0;
+}
+
+static void unmap_bar(struct xocl_dev *xdev)
+{
+ if (xdev->core.bar_addr) {
+ iounmap(xdev->core.bar_addr);
+ xdev->core.bar_addr = NULL;
+ }
+
+ if (xdev->p2p_bar_len)
+ pci_release_selected_regions(xdev->core.pdev,
+ 1 << xdev->p2p_bar_idx);
+}
+
+/* pci driver callbacks */
+int xocl_userpf_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct xocl_dev *xdev;
+ struct xocl_board_private *dev_info;
+ int ret;
+
+ xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
+ if (!xdev) {
+ xocl_err(&pdev->dev, "failed to alloc xocl_dev");
+ return -ENOMEM;
+ }
+
+ /* this is used for all subdevs, bind it to device earlier */
+ pci_set_drvdata(pdev, xdev);
+ dev_info = (struct xocl_board_private *)ent->driver_data;
+
+ xdev->core.pci_ops = &userpf_pci_ops;
+ xdev->core.pdev = pdev;
+ xocl_fill_dsa_priv(xdev, dev_info);
+
+ ret = identify_bar(xdev);
+ if (ret) {
+ xocl_err(&pdev->dev, "failed to identify bar");
+ goto failed_to_bar;
+ }
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ xocl_err(&pdev->dev, "failed to enable device.");
+ goto failed_to_enable;
+ }
+
+ ret = xocl_alloc_dev_minor(xdev);
+ if (ret)
+ goto failed_alloc_minor;
+
+ ret = xocl_subdev_create_all(xdev, dev_info->subdev_info,
+ dev_info->subdev_num);
+ if (ret) {
+ xocl_err(&pdev->dev, "failed to register subdevs");
+ goto failed_create_subdev;
+ }
+
+ ret = xocl_p2p_mem_reserve(xdev);
+ if (ret)
+ xocl_err(&pdev->dev, "failed to reserve p2p memory region");
+
+ ret = xocl_init_sysfs(&pdev->dev);
+ if (ret) {
+ xocl_err(&pdev->dev, "failed to init sysfs");
+ goto failed_init_sysfs;
+ }
+
+ mutex_init(&xdev->ctx_list_lock);
+ xdev->needs_reset = false;
+ atomic64_set(&xdev->total_execs, 0);
+ atomic_set(&xdev->outstanding_execs, 0);
+ INIT_LIST_HEAD(&xdev->ctx_list);
+
+ /* Launch the mailbox server. */
+ (void) xocl_peer_listen(xdev, xocl_mailbox_srv, (void *)xdev);
+
+ return 0;
+
+failed_init_sysfs:
+ xocl_p2p_mem_release(xdev, false);
+ xocl_subdev_destroy_all(xdev);
+
+failed_create_subdev:
+ xocl_free_dev_minor(xdev);
+
+failed_alloc_minor:
+ pci_disable_device(pdev);
+failed_to_enable:
+ unmap_bar(xdev);
+failed_to_bar:
+ devm_kfree(&pdev->dev, xdev);
+ pci_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+void xocl_userpf_remove(struct pci_dev *pdev)
+{
+ struct xocl_dev *xdev;
+
+ xdev = pci_get_drvdata(pdev);
+ if (!xdev) {
+ xocl_err(&pdev->dev, "driver data is NULL");
+ return;
+ }
+
+ xocl_p2p_mem_release(xdev, false);
+ xocl_subdev_destroy_all(xdev);
+
+ xocl_fini_sysfs(&pdev->dev);
+ xocl_free_dev_minor(xdev);
+
+ pci_disable_device(pdev);
+
+ unmap_bar(xdev);
+
+ mutex_destroy(&xdev->ctx_list_lock);
+
+ pci_set_drvdata(pdev, NULL);
+ devm_kfree(&pdev->dev, xdev);
+}
+
+static pci_ers_result_t user_pci_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ switch (state) {
+ case pci_channel_io_normal:
+ xocl_info(&pdev->dev, "PCI normal state error\n");
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ xocl_info(&pdev->dev, "PCI frozen state error\n");
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ xocl_info(&pdev->dev, "PCI failure state error\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ default:
+ xocl_info(&pdev->dev, "PCI unknown state (%d) error\n", state);
+ break;
+ }
+
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t user_pci_slot_reset(struct pci_dev *pdev)
+{
+ xocl_info(&pdev->dev, "PCI reset slot");
+ pci_restore_state(pdev);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void user_pci_error_resume(struct pci_dev *pdev)
+{
+ xocl_info(&pdev->dev, "PCI error resume");
+ pci_cleanup_aer_uncorrect_error_status(pdev);
+}
+
+static const struct pci_error_handlers xocl_err_handler = {
+ .error_detected = user_pci_error_detected,
+ .slot_reset = user_pci_slot_reset,
+ .resume = user_pci_error_resume,
+ .reset_prepare = user_pci_reset_prepare,
+ .reset_done = user_pci_reset_done,
+};
+
+static struct pci_driver userpf_driver = {
+ .name = XOCL_MODULE_NAME,
+ .id_table = pciidlist,
+ .probe = xocl_userpf_probe,
+ .remove = xocl_userpf_remove,
+ .err_handler = &xocl_err_handler,
+};
+
+/* INIT */
+static int (*xocl_drv_reg_funcs[])(void) __initdata = {
+ xocl_init_feature_rom,
+ xocl_init_xdma,
+ xocl_init_mb_scheduler,
+ xocl_init_mailbox,
+ xocl_init_xmc,
+ xocl_init_icap,
+ xocl_init_xvc,
+};
+
+static void (*xocl_drv_unreg_funcs[])(void) = {
+ xocl_fini_feature_rom,
+ xocl_fini_xdma,
+ xocl_fini_mb_scheduler,
+ xocl_fini_mailbox,
+ xocl_fini_xmc,
+ xocl_fini_icap,
+ xocl_fini_xvc,
+};
+
+static int __init xocl_init(void)
+{
+ int ret, i;
+
+ xrt_class = class_create(THIS_MODULE, "xrt_user");
+ if (IS_ERR(xrt_class)) {
+ ret = PTR_ERR(xrt_class);
+ goto err_class_create;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(xocl_drv_reg_funcs); ++i) {
+ ret = xocl_drv_reg_funcs[i]();
+ if (ret)
+ goto failed;
+ }
+
+ ret = pci_register_driver(&userpf_driver);
+ if (ret)
+ goto failed;
+
+ return 0;
+
+failed:
+ for (i--; i >= 0; i--)
+ xocl_drv_unreg_funcs[i]();
+
+ class_destroy(xrt_class);
+ xrt_class = NULL;
+
+err_class_create:
+ return ret;
+}
+
+static void __exit xocl_exit(void)
+{
+ int i;
+
+ pci_unregister_driver(&userpf_driver);
+
+ for (i = ARRAY_SIZE(xocl_drv_unreg_funcs) - 1; i >= 0; i--)
+ xocl_drv_unreg_funcs[i]();
+
+ class_destroy(xrt_class);
+ xrt_class = NULL;
+}
+
+module_init(xocl_init);
+module_exit(xocl_exit);
+
+MODULE_VERSION(XRT_DRIVER_VERSION);
+
+MODULE_DESCRIPTION(XOCL_DRIVER_DESC);
+MODULE_AUTHOR("Lizhi Hou <lizhi.hou@xxxxxxxxxx>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/xocl/userpf/xocl_ioctl.c b/drivers/gpu/drm/xocl/userpf/xocl_ioctl.c
new file mode 100644
index 000000000000..665ecb0e27ac
--- /dev/null
+++ b/drivers/gpu/drm/xocl/userpf/xocl_ioctl.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2018 Xilinx, Inc. All rights reserved.
+ *
+ * Authors: Sonal Santan
+ *
+ */
+
+#include <linux/version.h>
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_mm.h>
+#include <linux/eventfd.h>
+#include <linux/uuid.h>
+#include <linux/hashtable.h>
+#include "../version.h"
+#include "common.h"
+
+int xocl_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+{
+ struct drm_xocl_info *obj = data;
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ struct pci_dev *pdev = xdev->core.pdev;
+ u32 major, minor, patch;
+
+ userpf_info(xdev, "INFO IOCTL");
+
+ if (sscanf(XRT_DRIVER_VERSION, "%d.%d.%d", &major, &minor, &patch) != 3)
+ return -ENODEV;
+
+ obj->vendor = pdev->vendor;
+ obj->device = pdev->device;
+ obj->subsystem_vendor = pdev->subsystem_vendor;
+ obj->subsystem_device = pdev->subsystem_device;
+ obj->driver_version = XOCL_DRV_VER_NUM(major, minor, patch);
+ obj->pci_slot = PCI_SLOT(pdev->devfn);
+
+ return 0;
+}
+
+int xocl_execbuf_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *filp)
+{
+ struct xocl_drm *drm_p = dev->dev_private;
+ int ret = 0;
+
+ ret = xocl_exec_client_ioctl(drm_p->xdev,
+ DRM_XOCL_EXECBUF, data, filp);
+
+ return ret;
+}
+
+/*
+ * Create a context (only shared supported today) on a CU. Take a lock on xclbin if
+ * it has not been acquired before. Shared the same lock for all context requests
+ * for that process
+ */
+int xocl_ctx_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct xocl_drm *drm_p = dev->dev_private;
+ int ret = 0;
+
+ ret = xocl_exec_client_ioctl(drm_p->xdev,
+ DRM_XOCL_CTX, data, filp);
+
+ return ret;
+}
+
+int xocl_user_intr_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ struct drm_xocl_user_intr *args = data;
+ int ret = 0;
+
+ xocl_info(dev->dev, "USER INTR ioctl");
+
+ if (args->fd < 0)
+ return -EINVAL;
+
+ xocl_dma_intr_register(xdev, args->msix, NULL, NULL, args->fd);
+ xocl_dma_intr_config(xdev, args->msix, true);
+
+ return ret;
+}
+
+char *kind_to_string(enum axlf_section_kind kind)
+{
+ switch (kind) {
+ case 0: return "BITSTREAM";
+ case 1: return "CLEARING_BITSTREAM";
+ case 2: return "EMBEDDED_METADATA";
+ case 3: return "FIRMWARE";
+ case 4: return "DEBUG_DATA";
+ case 5: return "SCHED_FIRMWARE";
+ case 6: return "MEM_TOPOLOGY";
+ case 7: return "CONNECTIVITY";
+ case 8: return "IP_LAYOUT";
+ case 9: return "DEBUG_IP_LAYOUT";
+ case 10: return "DESIGN_CHECK_POINT";
+ case 11: return "CLOCK_FREQ_TOPOLOGY";
+ default: return "UNKNOWN";
+ }
+}
+
+/* should be obsoleted after mailbox implememted */
+static const struct axlf_section_header *
+get_axlf_section(const struct axlf *top, enum axlf_section_kind kind)
+{
+ int i = 0;
+
+ DRM_INFO("Finding %s section header", kind_to_string(kind));
+ for (i = 0; i < top->m_header.m_numSections; i++) {
+ if (top->m_sections[i].m_sectionKind == kind)
+ return &top->m_sections[i];
+ }
+ DRM_INFO("Did not find AXLF section %s", kind_to_string(kind));
+ return NULL;
+}
+
+static int
+xocl_check_section(const struct axlf_section_header *header, uint64_t len,
+ enum axlf_section_kind kind)
+{
+ uint64_t offset;
+ uint64_t size;
+
+ DRM_INFO("Section %s details:", kind_to_string(kind));
+ DRM_INFO(" offset = 0x%llx", header->m_sectionOffset);
+ DRM_INFO(" size = 0x%llx", header->m_sectionSize);
+
+ offset = header->m_sectionOffset;
+ size = header->m_sectionSize;
+ if (offset + size <= len)
+ return 0;
+
+ DRM_INFO("Section %s extends beyond xclbin boundary 0x%llx\n",
+ kind_to_string(kind), len);
+ return -EINVAL;
+}
+
+/* Return value: Negative for error, or the size in bytes has been copied */
+static int
+xocl_read_sect(enum axlf_section_kind kind, void **sect, struct axlf *axlf_full)
+{
+ const struct axlf_section_header *memHeader;
+ uint64_t xclbin_len;
+ uint64_t offset;
+ uint64_t size;
+ int err = 0;
+
+ memHeader = get_axlf_section(axlf_full, kind);
+ if (!memHeader)
+ return 0;
+
+ xclbin_len = axlf_full->m_header.m_length;
+ err = xocl_check_section(memHeader, xclbin_len, kind);
+ if (err)
+ return err;
+
+ offset = memHeader->m_sectionOffset;
+ size = memHeader->m_sectionSize;
+ *sect = &((char *)axlf_full)[offset];
+
+ return size;
+}
+
+/*
+ * Should be called with xdev->ctx_list_lock held
+ */
+static uint live_client_size(struct xocl_dev *xdev)
+{
+ const struct list_head *ptr;
+ const struct client_ctx *entry;
+ uint count = 0;
+
+ BUG_ON(!mutex_is_locked(&xdev->ctx_list_lock));
+
+ list_for_each(ptr, &xdev->ctx_list) {
+ entry = list_entry(ptr, struct client_ctx, link);
+ count++;
+ }
+ return count;
+}
+
+static int
+xocl_read_axlf_helper(struct xocl_drm *drm_p, struct drm_xocl_axlf *axlf_ptr)
+{
+ long err = 0;
+ struct axlf *axlf = 0;
+ struct axlf bin_obj;
+ size_t size;
+ int preserve_mem = 0;
+ struct mem_topology *new_topology = NULL, *topology;
+ struct xocl_dev *xdev = drm_p->xdev;
+ uuid_t *xclbin_id;
+
+ userpf_info(xdev, "READ_AXLF IOCTL\n");
+
+ if (!xocl_is_unified(xdev)) {
+ userpf_info(xdev, "XOCL: not unified dsa");
+ return err;
+ }
+
+ if (copy_from_user(&bin_obj, axlf_ptr->xclbin, sizeof(struct axlf)))
+ return -EFAULT;
+
+ if (memcmp(bin_obj.m_magic, "xclbin2", 8))
+ return -EINVAL;
+
+ if (xocl_xrt_version_check(xdev, &bin_obj, true))
+ return -EINVAL;
+
+ if (uuid_is_null(&bin_obj.m_header.uuid)) {
+ // Legacy xclbin, convert legacy id to new id
+ memcpy(&bin_obj.m_header.uuid, &bin_obj.m_header.m_timeStamp, 8);
+ }
+
+ xclbin_id = (uuid_t *)xocl_icap_get_data(xdev, XCLBIN_UUID);
+ if (!xclbin_id)
+ return -EINVAL;
+ /*
+ * Support for multiple processes
+ * 1. We lock &xdev->ctx_list_lock so no new contexts can be opened and no live contexts
+ * can be closed
+ * 2. If more than one context exists -- more than one clients are connected -- we cannot
+ * swap the xclbin return -EPERM
+ * 3. If no live contexts exist there may still be sumbitted exec BOs from a
+ * previous context (which was subsequently closed), hence we check for exec BO count.
+ * If exec BO are outstanding we return -EBUSY
+ */
+ if (!uuid_equal(xclbin_id, &bin_obj.m_header.uuid)) {
+ if (atomic_read(&xdev->outstanding_execs)) {
+ userpf_err(xdev, "Current xclbin is busy, can't change\n");
+ return -EBUSY;
+ }
+ }
+
+ //Ignore timestamp matching for AWS platform
+ if (!xocl_is_aws(xdev) && !xocl_verify_timestamp(xdev,
+ bin_obj.m_header.m_featureRomTimeStamp)) {
+ userpf_err(xdev, "TimeStamp of ROM did not match Xclbin\n");
+ return -EINVAL;
+ }
+
+ userpf_info(xdev, "XOCL: VBNV and TimeStamps matched\n");
+
+ if (uuid_equal(xclbin_id, &bin_obj.m_header.uuid)) {
+ userpf_info(xdev, "Skipping repopulating topology, connectivity,ip_layout data\n");
+ goto done;
+ }
+
+ //Copy from user space and proceed.
+ axlf = vmalloc(bin_obj.m_header.m_length);
+ if (!axlf) {
+ userpf_err(xdev, "Unable to create axlf\n");
+ err = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(axlf, axlf_ptr->xclbin, bin_obj.m_header.m_length)) {
+ err = -EFAULT;
+ goto done;
+ }
+
+ /* Populating MEM_TOPOLOGY sections. */
+ size = xocl_read_sect(MEM_TOPOLOGY, (void **)&new_topology, axlf);
+ if (size <= 0) {
+ if (size != 0)
+ goto done;
+ } else if (sizeof_sect(new_topology, m_mem_data) != size) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ topology = XOCL_MEM_TOPOLOGY(xdev);
+
+ /*
+ * Compare MEM_TOPOLOGY previous vs new.
+ * Ignore this and keep disable preserve_mem if not for aws.
+ */
+ if (xocl_is_aws(xdev) && (topology != NULL)) {
+ if ((size == sizeof_sect(topology, m_mem_data)) &&
+ !memcmp(new_topology, topology, size)) {
+ xocl_xdev_info(xdev, "MEM_TOPOLOGY match, preserve mem_topology.");
+ preserve_mem = 1;
+ } else {
+ xocl_xdev_info(xdev, "MEM_TOPOLOGY mismatch, do not preserve mem_topology.");
+ }
+ }
+
+ /* Switching the xclbin, make sure none of the buffers are used. */
+ if (!preserve_mem) {
+ err = xocl_check_topology(drm_p);
+ if (err)
+ goto done;
+ xocl_cleanup_mem(drm_p);
+ }
+
+ err = xocl_icap_download_axlf(xdev, axlf);
+ if (err) {
+ userpf_err(xdev, "%s Fail to download\n", __func__);
+ /*
+ * Don't just bail out here, always recreate drm mem
+ * since we have cleaned it up before download.
+ */
+ }
+
+ if (!preserve_mem) {
+ int rc = xocl_init_mem(drm_p);
+
+ if (err == 0)
+ err = rc;
+ }
+
+done:
+ if (size < 0)
+ err = size;
+ if (err)
+ userpf_err(xdev, "err: %ld\n", err);
+ else
+ userpf_info(xdev, "Loaded xclbin %pUb", xclbin_id);
+ vfree(axlf);
+ return err;
+}
+
+int xocl_read_axlf_ioctl(struct drm_device *dev,
+ void *data,
+ struct drm_file *filp)
+{
+ struct drm_xocl_axlf *axlf_obj_ptr = data;
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ struct client_ctx *client = filp->driver_priv;
+ int err = 0;
+ uuid_t *xclbin_id;
+
+ mutex_lock(&xdev->ctx_list_lock);
+ err = xocl_read_axlf_helper(drm_p, axlf_obj_ptr);
+ /*
+ * Record that user land configured this context for current device xclbin
+ * It doesn't mean that the context has a lock on the xclbin, only that
+ * when a lock is eventually acquired it can be verified to be against to
+ * be a lock on expected xclbin
+ */
+ xclbin_id = (uuid_t *)xocl_icap_get_data(xdev, XCLBIN_UUID);
+ uuid_copy(&client->xclbin_id,
+ ((err || !xclbin_id) ? &uuid_null : xclbin_id));
+ mutex_unlock(&xdev->ctx_list_lock);
+ return err;
+}
+
+uint get_live_client_size(struct xocl_dev *xdev)
+{
+ uint count;
+
+ mutex_lock(&xdev->ctx_list_lock);
+ count = live_client_size(xdev);
+ mutex_unlock(&xdev->ctx_list_lock);
+ return count;
+}
+
+void reset_notify_client_ctx(struct xocl_dev *xdev)
+{
+ xdev->needs_reset = false;
+// wmb();
+}
+
+int xocl_hot_reset_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+
+ int err = xocl_hot_reset(xdev, false);
+
+ userpf_info(xdev, "%s err: %d\n", __func__, err);
+ return err;
+}
+
+int xocl_reclock_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct xocl_drm *drm_p = dev->dev_private;
+ struct xocl_dev *xdev = drm_p->xdev;
+ int err = xocl_reclock(xdev, data);
+
+ userpf_info(xdev, "%s err: %d\n", __func__, err);
+ return err;
+}
diff --git a/drivers/gpu/drm/xocl/userpf/xocl_sysfs.c b/drivers/gpu/drm/xocl/userpf/xocl_sysfs.c
new file mode 100644
index 000000000000..fccb27906897
--- /dev/null
+++ b/drivers/gpu/drm/xocl/userpf/xocl_sysfs.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A GEM style device manager for PCIe based OpenCL accelerators.
+ *
+ * Copyright (C) 2016-2019 Xilinx, Inc. All rights reserved.
+ *
+ * Authors: Lizhi.Hou@xxxxxxxxxx
+ *
+ */
+#include "common.h"
+
+//Attributes followed by bin_attributes.
+//
+/* -Attributes -- */
+
+/* -xclbinuuid-- (supersedes xclbinid) */
+static ssize_t xclbinuuid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+ uuid_t *xclbin_id;
+
+ xclbin_id = (uuid_t *)xocl_icap_get_data(xdev, XCLBIN_UUID);
+ return sprintf(buf, "%pUb\n", xclbin_id ? xclbin_id : 0);
+}
+
+static DEVICE_ATTR_RO(xclbinuuid);
+
+/* -userbar-- */
+static ssize_t userbar_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", xdev->core.bar_idx);
+}
+
+static DEVICE_ATTR_RO(userbar);
+
+static ssize_t user_pf_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ // The existence of entry indicates user function.
+ return sprintf(buf, "%s", "");
+}
+static DEVICE_ATTR_RO(user_pf);
+
+/* -live client contects-- */
+static ssize_t kdsstat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+ int size;
+ uuid_t *xclbin_id;
+
+ xclbin_id = (uuid_t *)xocl_icap_get_data(xdev, XCLBIN_UUID);
+ size = sprintf(buf,
+ "xclbin:\t\t\t%pUb\noutstanding execs:\t%d\ntotal execs:\t\t%lld\ncontexts:\t\t%d\n",
+ xclbin_id ? xclbin_id : 0,
+ atomic_read(&xdev->outstanding_execs),
+ atomic64_read(&xdev->total_execs),
+ get_live_client_size(xdev));
+ return size;
+}
+static DEVICE_ATTR_RO(kdsstat);
+
+static ssize_t xocl_mm_stat(struct xocl_dev *xdev, char *buf, bool raw)
+{
+ int i;
+ ssize_t count = 0;
+ ssize_t size = 0;
+ size_t memory_usage = 0;
+ unsigned int bo_count = 0;
+ const char *txt_fmt = "[%s] %s@0x%012llx (%lluMB): %lluKB %dBOs\n";
+ const char *raw_fmt = "%llu %d\n";
+ struct mem_topology *topo = NULL;
+ struct drm_xocl_mm_stat stat;
+ void *drm_hdl;
+
+ drm_hdl = xocl_dma_get_drm_handle(xdev);
+ if (!drm_hdl)
+ return -EINVAL;
+
+ mutex_lock(&xdev->ctx_list_lock);
+
+ topo = XOCL_MEM_TOPOLOGY(xdev);
+ if (!topo) {
+ mutex_unlock(&xdev->ctx_list_lock);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < topo->m_count; i++) {
+ xocl_mm_get_usage_stat(drm_hdl, i, &stat);
+
+ if (raw) {
+ memory_usage = 0;
+ bo_count = 0;
+ memory_usage = stat.memory_usage;
+ bo_count = stat.bo_count;
+
+ count = sprintf(buf, raw_fmt,
+ memory_usage,
+ bo_count);
+ } else {
+ count = sprintf(buf, txt_fmt,
+ topo->m_mem_data[i].m_used ?
+ "IN-USE" : "UNUSED",
+ topo->m_mem_data[i].m_tag,
+ topo->m_mem_data[i].m_base_address,
+ topo->m_mem_data[i].m_size / 1024,
+ stat.memory_usage / 1024,
+ stat.bo_count);
+ }
+ buf += count;
+ size += count;
+ }
+ mutex_unlock(&xdev->ctx_list_lock);
+ return size;
+}
+
+/* -live memory usage-- */
+static ssize_t memstat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+
+ return xocl_mm_stat(xdev, buf, false);
+}
+static DEVICE_ATTR_RO(memstat);
+
+static ssize_t memstat_raw_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+
+ return xocl_mm_stat(xdev, buf, true);
+}
+static DEVICE_ATTR_RO(memstat_raw);
+
+static ssize_t p2p_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+ u64 size;
+
+ if (xdev->p2p_bar_addr)
+ return sprintf(buf, "1\n");
+ else if (xocl_get_p2p_bar(xdev, &size) >= 0 &&
+ size > (1 << XOCL_PA_SECTION_SHIFT))
+ return sprintf(buf, "2\n");
+
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t p2p_enable_store(struct device *dev,
+ struct device_attribute *da, const char *buf, size_t count)
+{
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+ struct pci_dev *pdev = xdev->core.pdev;
+ int ret, p2p_bar;
+ u32 enable;
+ u64 size;
+
+
+ if (kstrtou32(buf, 10, &enable) == -EINVAL || enable > 1)
+ return -EINVAL;
+
+ p2p_bar = xocl_get_p2p_bar(xdev, NULL);
+ if (p2p_bar < 0) {
+ xocl_err(&pdev->dev, "p2p bar is not configurable");
+ return -EACCES;
+ }
+
+ size = xocl_get_ddr_channel_size(xdev) *
+ xocl_get_ddr_channel_count(xdev); /* GB */
+ size = (ffs(size) == fls(size)) ? (fls(size) - 1) : fls(size);
+ size = enable ? (size + 10) : (XOCL_PA_SECTION_SHIFT - 20);
+ xocl_info(&pdev->dev, "Resize p2p bar %d to %d M ", p2p_bar,
+ (1 << size));
+ xocl_p2p_mem_release(xdev, false);
+
+ ret = xocl_pci_resize_resource(pdev, p2p_bar, size);
+ if (ret) {
+ xocl_err(&pdev->dev, "Failed to resize p2p BAR %d", ret);
+ goto failed;
+ }
+
+ xdev->p2p_bar_idx = p2p_bar;
+ xdev->p2p_bar_len = pci_resource_len(pdev, p2p_bar);
+
+ if (enable) {
+ ret = xocl_p2p_mem_reserve(xdev);
+ if (ret) {
+ xocl_err(&pdev->dev, "Failed to reserve p2p memory %d",
+ ret);
+ }
+ }
+
+ return count;
+
+failed:
+ return ret;
+
+}
+
+static DEVICE_ATTR(p2p_enable, 0644, p2p_enable_show, p2p_enable_store);
+
+static ssize_t dev_offline_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+ int val = xdev->core.offline ? 1 : 0;
+
+ return sprintf(buf, "%d\n", val);
+}
+static ssize_t dev_offline_store(struct device *dev,
+ struct device_attribute *da, const char *buf, size_t count)
+{
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+ int ret;
+ u32 offline;
+
+
+ if (kstrtou32(buf, 10, &offline) == -EINVAL || offline > 1)
+ return -EINVAL;
+
+ device_lock(dev);
+ if (offline) {
+ xocl_subdev_destroy_all(xdev);
+ xdev->core.offline = true;
+ } else {
+ ret = xocl_subdev_create_all(xdev, xdev->core.priv.subdev_info,
+ xdev->core.priv.subdev_num);
+ if (ret) {
+ xocl_err(dev, "Online subdevices failed");
+ return -EIO;
+ }
+ xdev->core.offline = false;
+ }
+ device_unlock(dev);
+
+ return count;
+}
+
+static DEVICE_ATTR(dev_offline, 0644, dev_offline_show, dev_offline_store);
+
+static ssize_t mig_calibration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0\n");
+}
+
+static DEVICE_ATTR_RO(mig_calibration);
+
+static ssize_t link_width_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned short speed, width;
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+
+ get_pcie_link_info(xdev, &width, &speed, false);
+ return sprintf(buf, "%d\n", width);
+}
+static DEVICE_ATTR_RO(link_width);
+
+static ssize_t link_speed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned short speed, width;
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+
+ get_pcie_link_info(xdev, &width, &speed, false);
+ return sprintf(buf, "%d\n", speed);
+}
+static DEVICE_ATTR_RO(link_speed);
+
+static ssize_t link_width_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned short speed, width;
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+
+ get_pcie_link_info(xdev, &width, &speed, true);
+ return sprintf(buf, "%d\n", width);
+}
+static DEVICE_ATTR_RO(link_width_max);
+
+static ssize_t link_speed_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned short speed, width;
+ struct xocl_dev *xdev = dev_get_drvdata(dev);
+
+ get_pcie_link_info(xdev, &width, &speed, true);
+ return sprintf(buf, "%d\n", speed);
+}
+static DEVICE_ATTR_RO(link_speed_max);
+/* - End attributes-- */
+
+static struct attribute *xocl_attrs[] = {
+ &dev_attr_xclbinuuid.attr,
+ &dev_attr_userbar.attr,
+ &dev_attr_kdsstat.attr,
+ &dev_attr_memstat.attr,
+ &dev_attr_memstat_raw.attr,
+ &dev_attr_user_pf.attr,
+ &dev_attr_p2p_enable.attr,
+ &dev_attr_dev_offline.attr,
+ &dev_attr_mig_calibration.attr,
+ &dev_attr_link_width.attr,
+ &dev_attr_link_speed.attr,
+ &dev_attr_link_speed_max.attr,
+ &dev_attr_link_width_max.attr,
+ NULL,
+};
+
+static struct attribute_group xocl_attr_group = {
+ .attrs = xocl_attrs,
+};
+
+//---
+int xocl_init_sysfs(struct device *dev)
+{
+ int ret;
+ struct pci_dev *rdev;
+
+ ret = sysfs_create_group(&dev->kobj, &xocl_attr_group);
+ if (ret)
+ xocl_err(dev, "create xocl attrs failed: %d", ret);
+
+ xocl_get_root_dev(to_pci_dev(dev), rdev);
+ ret = sysfs_create_link(&dev->kobj, &rdev->dev.kobj, "root_dev");
+ if (ret)
+ xocl_err(dev, "create root device link failed: %d", ret);
+
+ return ret;
+}
+
+void xocl_fini_sysfs(struct device *dev)
+{
+ sysfs_remove_link(&dev->kobj, "root_dev");
+ sysfs_remove_group(&dev->kobj, &xocl_attr_group);
+}
--
2.17.0