Hi Sonal, On Sat, Nov 28, 2020 at 04:00:39PM -0800, Sonal Santan wrote: > From: Sonal Santan <sonal.santan@xxxxxxxxxx> > > Add management physical function driver core. The driver attaches > to management physical function of Alveo devices. It instantiates > the root driver and one or more partition drivers which in turn > instantiate platform drivers. The instantiation of partition and > platform drivers is completely data driven. The driver integrates > with FPGA manager and provides xclbin download service. > > Signed-off-by: Sonal Santan <sonal.santan@xxxxxxxxxx> > --- > drivers/fpga/alveo/mgmt/xmgmt-fmgr-drv.c | 194 ++++ > drivers/fpga/alveo/mgmt/xmgmt-fmgr.h | 29 + > drivers/fpga/alveo/mgmt/xmgmt-main-impl.h | 36 + > drivers/fpga/alveo/mgmt/xmgmt-main-mailbox.c | 930 +++++++++++++++++++ > drivers/fpga/alveo/mgmt/xmgmt-main-ulp.c | 190 ++++ > drivers/fpga/alveo/mgmt/xmgmt-main.c | 843 +++++++++++++++++ > drivers/fpga/alveo/mgmt/xmgmt-root.c | 375 ++++++++ > 7 files changed, 2597 insertions(+) > create mode 100644 drivers/fpga/alveo/mgmt/xmgmt-fmgr-drv.c > create mode 100644 drivers/fpga/alveo/mgmt/xmgmt-fmgr.h > create mode 100644 drivers/fpga/alveo/mgmt/xmgmt-main-impl.h > create mode 100644 drivers/fpga/alveo/mgmt/xmgmt-main-mailbox.c > create mode 100644 drivers/fpga/alveo/mgmt/xmgmt-main-ulp.c > create mode 100644 drivers/fpga/alveo/mgmt/xmgmt-main.c > create mode 100644 drivers/fpga/alveo/mgmt/xmgmt-root.c > > diff --git a/drivers/fpga/alveo/mgmt/xmgmt-fmgr-drv.c b/drivers/fpga/alveo/mgmt/xmgmt-fmgr-drv.c > new file mode 100644 > index 000000000000..d451b5a2c291 > --- /dev/null > +++ b/drivers/fpga/alveo/mgmt/xmgmt-fmgr-drv.c > @@ -0,0 +1,194 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Xilinx Alveo Management Function Driver > + * > + * Copyright (C) 2019-2020 Xilinx, Inc. > + * Bulk of the code borrowed from XRT mgmt driver file, fmgr.c > + * > + * Authors: Sonal.Santan@xxxxxxxxxx > + */ > + > +#include <linux/cred.h> > +#include <linux/efi.h> > +#include <linux/fpga/fpga-mgr.h> > +#include <linux/platform_device.h> > +#include <linux/module.h> > +#include <linux/vmalloc.h> > + > +#include "xrt-subdev.h" > +#include "xmgmt-fmgr.h" > +#include "xrt-axigate.h" > +#include "xmgmt-main-impl.h" > + > +/* > + * Container to capture and cache full xclbin as it is passed in blocks by FPGA > + * Manager. Driver needs access to full xclbin to walk through xclbin sections. > + * FPGA Manager's .write() backend sends incremental blocks without any > + * knowledge of xclbin format forcing us to collect the blocks and stitch them > + * together here. > + */ > + > +struct xfpga_klass { Nit: xfpga_priv or xfpga_drvdata? > + const struct platform_device *pdev; > + struct axlf *blob; > + char name[64]; Nit: 64 could be a named constant ? > + size_t count; > + size_t total_count; > + struct mutex axlf_lock; > + int reader_ref; > + enum fpga_mgr_states state; > + enum xfpga_sec_level sec_level; This appears unused, do you want to add this with the code that uses it? > +}; Maybe add some kerneldoc markup? > + > +struct key *xfpga_keys; Appears unused, can you introduce this together with the code using it? > + > +static int xmgmt_pr_write_init(struct fpga_manager *mgr, > + struct fpga_image_info *info, const char *buf, size_t count) > +{ > + struct xfpga_klass *obj = mgr->priv; > + const struct axlf *bin = (const struct axlf *)buf; Nit: Reverse x-mas tree please. xxxxxx xxxx xxx x > + > + if (count < sizeof(struct axlf)) { > + obj->state = FPGA_MGR_STATE_WRITE_INIT_ERR; > + return -EINVAL; > + } > + > + if (count > bin->m_header.m_length) { > + obj->state = FPGA_MGR_STATE_WRITE_INIT_ERR; > + return -EINVAL; > + } > + > + /* Free up the previous blob */ > + vfree(obj->blob); > + obj->blob = vmalloc(bin->m_header.m_length); > + if (!obj->blob) { > + obj->state = FPGA_MGR_STATE_WRITE_INIT_ERR; > + return -ENOMEM; > + } > + > + xrt_info(obj->pdev, "Begin download of xclbin %pUb of length %lld B", > + &bin->m_header.uuid, bin->m_header.m_length); We already have framework level prints for that (admittedly somewhat less verbose). Please remove. > + > + obj->count = 0; > + obj->total_count = bin->m_header.m_length; > + obj->state = FPGA_MGR_STATE_WRITE_INIT; Does the framework state tracking not work for you? > + return 0; > +} > + > +static int xmgmt_pr_write(struct fpga_manager *mgr, > + const char *buf, size_t count) > +{ > + struct xfpga_klass *obj = mgr->priv; > + char *curr = (char *)obj->blob; > + > + if ((obj->state != FPGA_MGR_STATE_WRITE_INIT) && > + (obj->state != FPGA_MGR_STATE_WRITE)) { > + obj->state = FPGA_MGR_STATE_WRITE_ERR; > + return -EINVAL; > + } > + > + curr += obj->count; > + obj->count += count; > + > + /* > + * The xclbin buffer should not be longer than advertised in the header > + */ > + if (obj->total_count < obj->count) { > + obj->state = FPGA_MGR_STATE_WRITE_ERR; > + return -EINVAL; > + } > + > + xrt_info(obj->pdev, "Copying block of %zu B of xclbin", count); Please drop those. > + memcpy(curr, buf, count); I'm confused. Why are we just copying things around here. What picks this up afterwards? > + obj->state = FPGA_MGR_STATE_WRITE; > + return 0; > +} > + > + > +static int xmgmt_pr_write_complete(struct fpga_manager *mgr, > + struct fpga_image_info *info) > +{ > + int result = 0; > + struct xfpga_klass *obj = mgr->priv; > + > + if (obj->state != FPGA_MGR_STATE_WRITE) { > + obj->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; > + return -EINVAL; > + } > + > + /* Check if we got the complete xclbin */ > + if (obj->blob->m_header.m_length != obj->count) { > + obj->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; > + return -EINVAL; > + } > + > + result = xmgmt_ulp_download((void *)obj->pdev, obj->blob); > + > + obj->state = result ? FPGA_MGR_STATE_WRITE_COMPLETE_ERR : > + FPGA_MGR_STATE_WRITE_COMPLETE; Why the separate state tracking? > + xrt_info(obj->pdev, "Finish downloading of xclbin %pUb: %d", > + &obj->blob->m_header.uuid, result); > + vfree(obj->blob); > + obj->blob = NULL; > + obj->count = 0; > + return result; > +} > + > +static enum fpga_mgr_states xmgmt_pr_state(struct fpga_manager *mgr) > +{ > + struct xfpga_klass *obj = mgr->priv; > + > + return obj->state; > +} > + > +static const struct fpga_manager_ops xmgmt_pr_ops = { > + .initial_header_size = sizeof(struct axlf), > + .write_init = xmgmt_pr_write_init, > + .write = xmgmt_pr_write, > + .write_complete = xmgmt_pr_write_complete, > + .state = xmgmt_pr_state, > +}; > + > + > +struct fpga_manager *xmgmt_fmgr_probe(struct platform_device *pdev) > +{ > + struct fpga_manager *fmgr; > + int ret = 0; > + struct xfpga_klass *obj = vzalloc(sizeof(struct xfpga_klass)); > + > + xrt_info(pdev, "probing..."); Drop this, please. > + if (!obj) > + return ERR_PTR(-ENOMEM); > + > + snprintf(obj->name, sizeof(obj->name), "Xilinx Alveo FPGA Manager"); > + obj->state = FPGA_MGR_STATE_UNKNOWN; > + obj->pdev = pdev; > + fmgr = fpga_mgr_create(&pdev->dev, > + obj->name, > + &xmgmt_pr_ops, > + obj); I think (eyeballed) this fits on two lines? > + if (!fmgr) > + return ERR_PTR(-ENOMEM); > + > + obj->sec_level = XFPGA_SEC_NONE; Seems unused so far, please drop until it's used. > + ret = fpga_mgr_register(fmgr); > + if (ret) { > + fpga_mgr_free(fmgr); > + kfree(obj); > + return ERR_PTR(ret); > + } > + mutex_init(&obj->axlf_lock); > + return fmgr; Since this patchset will wait at least till next cycle, you might want to look into the devm_* functions for registering and creating FPGA Managers. > +} > + > +int xmgmt_fmgr_remove(struct fpga_manager *fmgr) > +{ > + struct xfpga_klass *obj = fmgr->priv; > + > + mutex_destroy(&obj->axlf_lock); > + obj->state = FPGA_MGR_STATE_UNKNOWN; > + fpga_mgr_unregister(fmgr); > + vfree(obj->blob); > + vfree(obj); > + return 0; > +} > diff --git a/drivers/fpga/alveo/mgmt/xmgmt-fmgr.h b/drivers/fpga/alveo/mgmt/xmgmt-fmgr.h > new file mode 100644 > index 000000000000..2beba649609f > --- /dev/null > +++ b/drivers/fpga/alveo/mgmt/xmgmt-fmgr.h > @@ -0,0 +1,29 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Xilinx Alveo Management Function Driver > + * > + * Copyright (C) 2019-2020 Xilinx, Inc. > + * Bulk of the code borrowed from XRT mgmt driver file, fmgr.c > + * > + * Authors: Sonal.Santan@xxxxxxxxxx > + */ > + > +#ifndef _XMGMT_FMGR_H_ > +#define _XMGMT_FMGR_H_ > + > +#include <linux/fpga/fpga-mgr.h> > +#include <linux/mutex.h> > + > +#include <linux/xrt/xclbin.h> > + > +enum xfpga_sec_level { > + XFPGA_SEC_NONE = 0, > + XFPGA_SEC_DEDICATE, > + XFPGA_SEC_SYSTEM, > + XFPGA_SEC_MAX = XFPGA_SEC_SYSTEM, > +}; > + > +struct fpga_manager *xmgmt_fmgr_probe(struct platform_device *pdev); > +int xmgmt_fmgr_remove(struct fpga_manager *fmgr); > + > +#endif > diff --git a/drivers/fpga/alveo/mgmt/xmgmt-main-impl.h b/drivers/fpga/alveo/mgmt/xmgmt-main-impl.h > new file mode 100644 > index 000000000000..c89024cb8d46 > --- /dev/null > +++ b/drivers/fpga/alveo/mgmt/xmgmt-main-impl.h > @@ -0,0 +1,36 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020 Xilinx, Inc. > + * > + * Authors: > + * Lizhi Hou <Lizhi.Hou@xxxxxxxxxx> > + * Cheng Zhen <maxz@xxxxxxxxxx> > + */ > + > +#ifndef _XMGMT_MAIN_IMPL_H_ > +#define _XMGMT_MAIN_IMPL_H_ > + > +#include "xrt-subdev.h" > +#include "xmgmt-main.h" > + > +extern struct platform_driver xmgmt_main_driver; > +extern struct xrt_subdev_endpoints xrt_mgmt_main_endpoints[]; > + > +extern int xmgmt_ulp_download(struct platform_device *pdev, const void *xclbin); > +extern int bitstream_axlf_mailbox(struct platform_device *pdev, > + const void *xclbin); > +extern int xmgmt_hot_reset(struct platform_device *pdev); > + > +/* Getting dtb for specified partition. Caller should vfree returned dtb .*/ > +extern char *xmgmt_get_dtb(struct platform_device *pdev, > + enum provider_kind kind); > +extern char *xmgmt_get_vbnv(struct platform_device *pdev); > +extern int xmgmt_get_provider_uuid(struct platform_device *pdev, > + enum provider_kind kind, uuid_t *uuid); > + > +extern void *xmgmt_pdev2mailbox(struct platform_device *pdev); > +extern void *xmgmt_mailbox_probe(struct platform_device *pdev); > +extern void xmgmt_mailbox_remove(void *handle); > +extern void xmgmt_peer_notify_state(void *handle, bool online); > + > +#endif /* _XMGMT_MAIN_IMPL_H_ */ > diff --git a/drivers/fpga/alveo/mgmt/xmgmt-main-mailbox.c b/drivers/fpga/alveo/mgmt/xmgmt-main-mailbox.c > new file mode 100644 > index 000000000000..b3d82fc3618b > --- /dev/null > +++ b/drivers/fpga/alveo/mgmt/xmgmt-main-mailbox.c > @@ -0,0 +1,930 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Xilinx Alveo FPGA MGMT PF entry point driver > + * > + * Copyright (C) 2020 Xilinx, Inc. > + * > + * Peer communication via mailbox > + * > + * Authors: > + * Cheng Zhen <maxz@xxxxxxxxxx> > + */ > + > +#include <linux/crc32c.h> > +#include <linux/xrt/mailbox_proto.h> > +#include "xmgmt-main-impl.h" > +#include "xrt-mailbox.h" > +#include "xrt-cmc.h" > +#include "xrt-metadata.h" > +#include "xrt-xclbin.h" > +#include "xrt-clock.h" > +#include "xrt-calib.h" > +#include "xrt-icap.h" > + > +struct xmgmt_mailbox { > + struct platform_device *pdev; > + struct platform_device *mailbox; > + struct mutex lock; > + void *evt_hdl; > + char *test_msg; > + bool peer_in_same_domain; > +}; > + > +#define XMGMT_MAILBOX_PRT_REQ(xmbx, send, request, sw_ch) do { \ > + const char *dir = (send) ? ">>>>>" : "<<<<<"; \ > + \ > + if ((request)->req == XCL_MAILBOX_REQ_PEER_DATA) { \ > + struct xcl_mailbox_peer_data *p = \ > + (struct xcl_mailbox_peer_data *)(request)->data;\ > + \ > + xrt_info((xmbx)->pdev, "%s(%s) %s%s", \ > + mailbox_req2name((request)->req), \ > + mailbox_group_kind2name(p->kind), \ > + dir, mailbox_chan2name(sw_ch)); \ > + } else { \ > + xrt_info((xmbx)->pdev, "%s %s%s", \ > + mailbox_req2name((request)->req), \ > + dir, mailbox_chan2name(sw_ch)); \ > + } \ > +} while (0) > +#define XMGMT_MAILBOX_PRT_REQ_SEND(xmbx, req, sw_ch) \ > + XMGMT_MAILBOX_PRT_REQ(xmbx, true, req, sw_ch) > +#define XMGMT_MAILBOX_PRT_REQ_RECV(xmbx, req, sw_ch) \ > + XMGMT_MAILBOX_PRT_REQ(xmbx, false, req, sw_ch) > +#define XMGMT_MAILBOX_PRT_RESP(xmbx, resp) \ > + xrt_info((xmbx)->pdev, "respond %ld bytes >>>>>%s", \ > + (resp)->xmip_data_size, mailbox_chan2name((resp)->xmip_sw_ch)) > + > +static inline struct xmgmt_mailbox *pdev2mbx(struct platform_device *pdev) > +{ > + return (struct xmgmt_mailbox *)xmgmt_pdev2mailbox(pdev); > +} > + > +static void xmgmt_mailbox_post(struct xmgmt_mailbox *xmbx, > + u64 msgid, bool sw_ch, void *buf, size_t len) > +{ > + int rc; > + struct xrt_mailbox_ioctl_post post = { > + .xmip_req_id = msgid, > + .xmip_sw_ch = sw_ch, > + .xmip_data = buf, > + .xmip_data_size = len > + }; > + > + BUG_ON(!mutex_is_locked(&xmbx->lock)); > + > + if (!xmbx->mailbox) { > + xrt_err(xmbx->pdev, "mailbox not available"); > + return; > + } > + > + if (msgid == 0) { > + XMGMT_MAILBOX_PRT_REQ_SEND(xmbx, > + (struct xcl_mailbox_req *)buf, sw_ch); > + } else { > + XMGMT_MAILBOX_PRT_RESP(xmbx, &post); > + } > + > + rc = xrt_subdev_ioctl(xmbx->mailbox, XRT_MAILBOX_POST, &post); > + if (rc) > + xrt_err(xmbx->pdev, "failed to post msg: %d", rc); > +} > + > +static void xmgmt_mailbox_notify(struct xmgmt_mailbox *xmbx, bool sw_ch, > + struct xcl_mailbox_req *req, size_t len) > +{ > + xmgmt_mailbox_post(xmbx, 0, sw_ch, req, len); > +} > + > +static void xmgmt_mailbox_respond(struct xmgmt_mailbox *xmbx, > + u64 msgid, bool sw_ch, void *buf, size_t len) > +{ > + mutex_lock(&xmbx->lock); > + xmgmt_mailbox_post(xmbx, msgid, sw_ch, buf, len); > + mutex_unlock(&xmbx->lock); > +} > + > +static void xmgmt_mailbox_resp_test_msg(struct xmgmt_mailbox *xmbx, > + u64 msgid, bool sw_ch) > +{ > + struct platform_device *pdev = xmbx->pdev; > + char *msg; > + > + mutex_lock(&xmbx->lock); > + > + if (xmbx->test_msg == NULL) { > + mutex_unlock(&xmbx->lock); > + xrt_err(pdev, "test msg is not set, drop request"); > + return; > + } > + msg = xmbx->test_msg; > + xmbx->test_msg = NULL; > + > + mutex_unlock(&xmbx->lock); > + > + xmgmt_mailbox_respond(xmbx, msgid, sw_ch, msg, strlen(msg) + 1); > + vfree(msg); > +} > + > +static int xmgmt_mailbox_dtb_add_prop(struct platform_device *pdev, > + char *dst_dtb, const char *ep_name, const char *regmap_name, > + const char *prop, const void *val, int size) > +{ > + int rc = xrt_md_set_prop(DEV(pdev), dst_dtb, ep_name, regmap_name, > + prop, val, size); > + > + if (rc) { > + xrt_err(pdev, "failed to set %s@(%s, %s): %d", > + ep_name, regmap_name, prop, rc); > + } > + return rc; > +} > + > +static int xmgmt_mailbox_dtb_add_vbnv(struct platform_device *pdev, char *dtb) > +{ > + int rc = 0; > + char *vbnv = xmgmt_get_vbnv(pdev); > + > + if (vbnv == NULL) { > + xrt_err(pdev, "failed to get VBNV"); > + return -ENOENT; > + } > + rc = xmgmt_mailbox_dtb_add_prop(pdev, dtb, NULL, NULL, > + PROP_VBNV, vbnv, strlen(vbnv) + 1); > + kfree(vbnv); > + return rc; > +} > + > +static int xmgmt_mailbox_dtb_copy_logic_uuid(struct platform_device *pdev, > + const char *src_dtb, char *dst_dtb) > +{ > + const void *val; > + int sz; > + int rc = xrt_md_get_prop(DEV(pdev), src_dtb, NULL, NULL, > + PROP_LOGIC_UUID, &val, &sz); > + > + if (rc) { > + xrt_err(pdev, "failed to get %s: %d", PROP_LOGIC_UUID, rc); > + return rc; > + } > + return xmgmt_mailbox_dtb_add_prop(pdev, dst_dtb, NULL, NULL, > + PROP_LOGIC_UUID, val, sz); > +} > + > +static int xmgmt_mailbox_dtb_add_vrom(struct platform_device *pdev, > + const char *src_dtb, char *dst_dtb) > +{ > + /* For compatibility for legacy xrt driver. */ > + enum FeatureBitMask { > + UNIFIED_PLATFORM = 0x0000000000000001 > + , XARE_ENBLD = 0x0000000000000002 > + , BOARD_MGMT_ENBLD = 0x0000000000000004 > + , MB_SCHEDULER = 0x0000000000000008 > + , PROM_MASK = 0x0000000000000070 > + , DEBUG_MASK = 0x000000000000FF00 > + , PEER_TO_PEER = 0x0000000000010000 > + , FBM_UUID = 0x0000000000020000 > + , HBM = 0x0000000000040000 > + , CDMA = 0x0000000000080000 > + , QDMA = 0x0000000000100000 > + , RUNTIME_CLK_SCALE = 0x0000000000200000 > + , PASSTHROUGH_VIRTUALIZATION = 0x0000000000400000 > + }; > + struct FeatureRomHeader { > + unsigned char EntryPointString[4]; > + uint8_t MajorVersion; > + uint8_t MinorVersion; > + uint32_t VivadoBuildID; > + uint32_t IPBuildID; > + uint64_t TimeSinceEpoch; > + unsigned char FPGAPartName[64]; > + unsigned char VBNVName[64]; > + uint8_t DDRChannelCount; > + uint8_t DDRChannelSize; > + uint64_t DRBaseAddress; > + uint64_t FeatureBitMap; > + unsigned char uuid[16]; > + uint8_t HBMCount; > + uint8_t HBMSize; > + uint32_t CDMABaseAddress[4]; > + } header = { 0 }; > + char *vbnv = xmgmt_get_vbnv(pdev); > + int rc; > + > + *(u32 *)header.EntryPointString = 0x786e6c78; > + > + if (vbnv) > + strncpy(header.VBNVName, vbnv, sizeof(header.VBNVName) - 1); > + kfree(vbnv); > + > + header.FeatureBitMap = UNIFIED_PLATFORM; > + rc = xrt_md_get_prop(DEV(pdev), src_dtb, > + NODE_CMC_FW_MEM, NULL, PROP_IO_OFFSET, NULL, NULL); > + if (rc == 0) > + header.FeatureBitMap |= BOARD_MGMT_ENBLD; > + rc = xrt_md_get_prop(DEV(pdev), src_dtb, > + NODE_ERT_FW_MEM, NULL, PROP_IO_OFFSET, NULL, NULL); > + if (rc == 0) > + header.FeatureBitMap |= MB_SCHEDULER; > + > + return xmgmt_mailbox_dtb_add_prop(pdev, dst_dtb, NULL, NULL, > + PROP_VROM, &header, sizeof(header)); > +} > + > +static u32 xmgmt_mailbox_dtb_user_pf(struct platform_device *pdev, > + const char *dtb, const char *epname, const char *regmap) > +{ > + const u32 *pfnump; > + int rc = xrt_md_get_prop(DEV(pdev), dtb, epname, regmap, > + PROP_PF_NUM, (const void **)&pfnump, NULL); > + > + if (rc) > + return -1; > + return be32_to_cpu(*pfnump); > +} > + > +static int xmgmt_mailbox_dtb_copy_user_endpoints(struct platform_device *pdev, > + const char *src, char *dst) > +{ > + int rc = 0; > + char *epname = NULL, *regmap = NULL; > + u32 pfnum = xmgmt_mailbox_dtb_user_pf(pdev, src, > + NODE_MAILBOX_USER, NULL); > + const u32 level = cpu_to_be32(1); > + struct device *dev = DEV(pdev); > + > + if (pfnum == (u32)-1) { > + xrt_err(pdev, "failed to get user pf num"); > + rc = -EINVAL; > + } > + > + for (xrt_md_get_next_endpoint(dev, src, NULL, NULL, &epname, ®map); > + rc == 0 && epname != NULL; > + xrt_md_get_next_endpoint(dev, src, epname, regmap, > + &epname, ®map)) { > + if (pfnum != > + xmgmt_mailbox_dtb_user_pf(pdev, src, epname, regmap)) > + continue; > + rc = xrt_md_copy_endpoint(dev, dst, src, epname, regmap, NULL); > + if (rc) { > + xrt_err(pdev, "failed to copy (%s, %s): %d", > + epname, regmap, rc); > + } else { > + rc = xrt_md_set_prop(dev, dst, epname, regmap, > + PROP_PARTITION_LEVEL, &level, sizeof(level)); > + if (rc) { > + xrt_err(pdev, > + "can't set level for (%s, %s): %d", > + epname, regmap, rc); > + } > + } > + } > + return rc; > +} > + > +static char *xmgmt_mailbox_user_dtb(struct platform_device *pdev) > +{ > + /* TODO: add support for PLP. */ > + const char *src = NULL; > + char *dst = NULL; > + struct device *dev = DEV(pdev); > + int rc = xrt_md_create(dev, &dst); > + > + if (rc || dst == NULL) > + return NULL; > + > + rc = xmgmt_mailbox_dtb_add_vbnv(pdev, dst); > + if (rc) > + goto fail; > + > + src = xmgmt_get_dtb(pdev, XMGMT_BLP); > + if (src == NULL) { > + xrt_err(pdev, "failed to get BLP dtb"); > + goto fail; > + } > + > + rc = xmgmt_mailbox_dtb_copy_logic_uuid(pdev, src, dst); > + if (rc) > + goto fail; > + > + rc = xmgmt_mailbox_dtb_add_vrom(pdev, src, dst); > + if (rc) > + goto fail; > + > + rc = xrt_md_copy_endpoint(dev, dst, src, NODE_PARTITION_INFO, > + NULL, NODE_PARTITION_INFO_BLP); > + if (rc) > + goto fail; > + > + rc = xrt_md_copy_endpoint(dev, dst, src, NODE_INTERFACES, NULL, NULL); > + if (rc) > + goto fail; > + > + rc = xmgmt_mailbox_dtb_copy_user_endpoints(pdev, src, dst); > + if (rc) > + goto fail; > + > + xrt_md_pack(dev, dst); > + vfree(src); > + return dst; > + > +fail: > + vfree(src); > + vfree(dst); > + return NULL; > +} > + > +static void xmgmt_mailbox_resp_subdev(struct xmgmt_mailbox *xmbx, > + u64 msgid, bool sw_ch, u64 offset, u64 size) > +{ > + struct platform_device *pdev = xmbx->pdev; > + char *dtb = xmgmt_mailbox_user_dtb(pdev); > + long dtbsz; > + struct xcl_subdev *hdr; > + u64 totalsz; > + > + if (dtb == NULL) > + return; > + > + dtbsz = xrt_md_size(DEV(pdev), dtb); > + totalsz = dtbsz + sizeof(*hdr) - sizeof(hdr->data); > + if (offset != 0 || totalsz > size) { > + /* Only support fetching dtb in one shot. */ > + vfree(dtb); > + xrt_err(pdev, "need %lldB, user buffer size is %lldB, dropped", > + totalsz, size); > + return; > + } > + > + hdr = vzalloc(totalsz); > + if (hdr == NULL) { > + vfree(dtb); > + return; > + } > + > + hdr->ver = 1; > + hdr->size = dtbsz; > + hdr->rtncode = XRT_MSG_SUBDEV_RTN_COMPLETE; > + (void) memcpy(hdr->data, dtb, dtbsz); > + > + xmgmt_mailbox_respond(xmbx, msgid, sw_ch, hdr, totalsz); > + > + vfree(dtb); > + vfree(hdr); > +} > + > +static void xmgmt_mailbox_resp_sensor(struct xmgmt_mailbox *xmbx, > + u64 msgid, bool sw_ch, u64 offset, u64 size) > +{ > + struct platform_device *pdev = xmbx->pdev; > + struct xcl_sensor sensors = { 0 }; > + struct platform_device *cmcpdev = xrt_subdev_get_leaf_by_id(pdev, > + XRT_SUBDEV_CMC, PLATFORM_DEVID_NONE); > + int rc; > + > + if (cmcpdev) { > + rc = xrt_subdev_ioctl(cmcpdev, XRT_CMC_READ_SENSORS, &sensors); > + (void) xrt_subdev_put_leaf(pdev, cmcpdev); > + if (rc) > + xrt_err(pdev, "can't read sensors: %d", rc); > + } > + > + xmgmt_mailbox_respond(xmbx, msgid, sw_ch, &sensors, > + min((u64)sizeof(sensors), size)); > +} > + > +static int xmgmt_mailbox_get_freq(struct xmgmt_mailbox *xmbx, > + enum CLOCK_TYPE type, u64 *freq, u64 *freq_cnter) > +{ > + struct platform_device *pdev = xmbx->pdev; > + const char *clkname = > + clock_type2epname(type) ? clock_type2epname(type) : "UNKNOWN"; > + struct platform_device *clkpdev = > + xrt_subdev_get_leaf_by_epname(pdev, clkname); > + int rc; > + struct xrt_clock_ioctl_get getfreq = { 0 }; > + > + if (clkpdev == NULL) { > + xrt_info(pdev, "%s clock is not available", clkname); > + return -ENOENT; > + } > + > + rc = xrt_subdev_ioctl(clkpdev, XRT_CLOCK_GET, &getfreq); > + (void) xrt_subdev_put_leaf(pdev, clkpdev); > + if (rc) { > + xrt_err(pdev, "can't get %s clock frequency: %d", clkname, rc); > + return rc; > + } > + > + if (freq) > + *freq = getfreq.freq; > + if (freq_cnter) > + *freq_cnter = getfreq.freq_cnter; > + return 0; > +} > + > +static int xmgmt_mailbox_get_icap_idcode(struct xmgmt_mailbox *xmbx, u64 *id) > +{ > + struct platform_device *pdev = xmbx->pdev; > + struct platform_device *icappdev = xrt_subdev_get_leaf_by_id(pdev, > + XRT_SUBDEV_ICAP, PLATFORM_DEVID_NONE); > + int rc; > + > + if (icappdev == NULL) { > + xrt_err(pdev, "can't find icap"); > + return -ENOENT; > + } > + > + rc = xrt_subdev_ioctl(icappdev, XRT_ICAP_IDCODE, id); > + (void) xrt_subdev_put_leaf(pdev, icappdev); > + if (rc) > + xrt_err(pdev, "can't get icap idcode: %d", rc); > + return rc; > +} > + > +static int xmgmt_mailbox_get_mig_calib(struct xmgmt_mailbox *xmbx, u64 *calib) > +{ > + struct platform_device *pdev = xmbx->pdev; > + struct platform_device *calibpdev = xrt_subdev_get_leaf_by_id(pdev, > + XRT_SUBDEV_CALIB, PLATFORM_DEVID_NONE); > + int rc; > + enum xrt_calib_results res; > + > + if (calibpdev == NULL) { > + xrt_err(pdev, "can't find mig calibration subdev"); > + return -ENOENT; > + } > + > + rc = xrt_subdev_ioctl(calibpdev, XRT_CALIB_RESULT, &res); > + (void) xrt_subdev_put_leaf(pdev, calibpdev); > + if (rc) { > + xrt_err(pdev, "can't get mig calibration result: %d", rc); > + } else { > + if (res == XRT_CALIB_SUCCEEDED) > + *calib = 1; > + else > + *calib = 0; > + } > + return rc; > +} > + > +static void xmgmt_mailbox_resp_icap(struct xmgmt_mailbox *xmbx, > + u64 msgid, bool sw_ch, u64 offset, u64 size) > +{ > + struct platform_device *pdev = xmbx->pdev; > + struct xcl_pr_region icap = { 0 }; > + > + (void) xmgmt_mailbox_get_freq(xmbx, > + CT_DATA, &icap.freq_data, &icap.freq_cntr_data); > + (void) xmgmt_mailbox_get_freq(xmbx, > + CT_KERNEL, &icap.freq_kernel, &icap.freq_cntr_kernel); > + (void) xmgmt_mailbox_get_freq(xmbx, > + CT_SYSTEM, &icap.freq_system, &icap.freq_cntr_system); > + (void) xmgmt_mailbox_get_icap_idcode(xmbx, &icap.idcode); > + (void) xmgmt_mailbox_get_mig_calib(xmbx, &icap.mig_calib); > + BUG_ON(sizeof(icap.uuid) != sizeof(uuid_t)); > + (void) xmgmt_get_provider_uuid(pdev, XMGMT_ULP, (uuid_t *)&icap.uuid); > + > + xmgmt_mailbox_respond(xmbx, msgid, sw_ch, &icap, > + min((u64)sizeof(icap), size)); > +} > + > +static void xmgmt_mailbox_resp_bdinfo(struct xmgmt_mailbox *xmbx, > + u64 msgid, bool sw_ch, u64 offset, u64 size) > +{ > + struct platform_device *pdev = xmbx->pdev; > + struct xcl_board_info *info = vzalloc(sizeof(*info)); > + struct platform_device *cmcpdev; > + int rc; > + > + if (info == NULL) > + return; > + > + cmcpdev = xrt_subdev_get_leaf_by_id(pdev, > + XRT_SUBDEV_CMC, PLATFORM_DEVID_NONE); > + if (cmcpdev) { > + rc = xrt_subdev_ioctl(cmcpdev, XRT_CMC_READ_BOARD_INFO, info); > + (void) xrt_subdev_put_leaf(pdev, cmcpdev); > + if (rc) > + xrt_err(pdev, "can't read board info: %d", rc); > + } > + > + xmgmt_mailbox_respond(xmbx, msgid, sw_ch, info, > + min((u64)sizeof(*info), size)); > + > + vfree(info); > +} > + > +static void xmgmt_mailbox_simple_respond(struct xmgmt_mailbox *xmbx, > + u64 msgid, bool sw_ch, int rc) > +{ > + xmgmt_mailbox_respond(xmbx, msgid, sw_ch, &rc, sizeof(rc)); > +} > + > +static void xmgmt_mailbox_resp_peer_data(struct xmgmt_mailbox *xmbx, > + struct xcl_mailbox_req *req, size_t len, u64 msgid, bool sw_ch) > +{ > + struct xcl_mailbox_peer_data *pdata = > + (struct xcl_mailbox_peer_data *)req->data; > + > + if (len < (sizeof(*req) + sizeof(*pdata) - 1)) { > + xrt_err(xmbx->pdev, "received corrupted %s, dropped", > + mailbox_req2name(req->req)); > + return; > + } > + > + switch (pdata->kind) { > + case XCL_SENSOR: > + xmgmt_mailbox_resp_sensor(xmbx, msgid, sw_ch, > + pdata->offset, pdata->size); > + break; > + case XCL_ICAP: > + xmgmt_mailbox_resp_icap(xmbx, msgid, sw_ch, > + pdata->offset, pdata->size); > + break; > + case XCL_BDINFO: > + xmgmt_mailbox_resp_bdinfo(xmbx, msgid, sw_ch, > + pdata->offset, pdata->size); > + break; > + case XCL_SUBDEV: > + xmgmt_mailbox_resp_subdev(xmbx, msgid, sw_ch, > + pdata->offset, pdata->size); > + break; > + case XCL_MIG_ECC: > + case XCL_FIREWALL: > + case XCL_DNA: /* TODO **/ > + xmgmt_mailbox_simple_respond(xmbx, msgid, sw_ch, 0); > + break; > + default: > + xrt_err(xmbx->pdev, "%s(%s) request not handled", > + mailbox_req2name(req->req), > + mailbox_group_kind2name(pdata->kind)); > + break; > + } > +} > + > +static bool xmgmt_mailbox_is_same_domain(struct xmgmt_mailbox *xmbx, > + struct xcl_mailbox_conn *mb_conn) > +{ > + uint32_t crc_chk; > + phys_addr_t paddr; > + struct platform_device *pdev = xmbx->pdev; > + > + paddr = virt_to_phys((void *)mb_conn->kaddr); > + if (paddr != (phys_addr_t)mb_conn->paddr) { > + xrt_info(pdev, "paddrs differ, user 0x%llx, mgmt 0x%llx", > + mb_conn->paddr, paddr); > + return false; > + } > + > + crc_chk = crc32c_le(~0, (void *)mb_conn->kaddr, PAGE_SIZE); > + if (crc_chk != mb_conn->crc32) { > + xrt_info(pdev, "CRCs differ, user 0x%x, mgmt 0x%x", > + mb_conn->crc32, crc_chk); > + return false; > + } > + > + return true; > +} > + > +static void xmgmt_mailbox_resp_user_probe(struct xmgmt_mailbox *xmbx, > + struct xcl_mailbox_req *req, size_t len, u64 msgid, bool sw_ch) > +{ > + struct xcl_mailbox_conn_resp *resp = vzalloc(sizeof(*resp)); > + struct xcl_mailbox_conn *conn = (struct xcl_mailbox_conn *)req->data; > + > + if (resp == NULL) > + return; > + > + if (len < (sizeof(*req) + sizeof(*conn) - 1)) { > + xrt_err(xmbx->pdev, "received corrupted %s, dropped", > + mailbox_req2name(req->req)); > + vfree(resp); > + return; > + } > + > + resp->conn_flags |= XCL_MB_PEER_READY; > + if (xmgmt_mailbox_is_same_domain(xmbx, conn)) { > + xmbx->peer_in_same_domain = true; > + resp->conn_flags |= XCL_MB_PEER_SAME_DOMAIN; > + } > + > + xmgmt_mailbox_respond(xmbx, msgid, sw_ch, resp, sizeof(*resp)); > + vfree(resp); > +} > + > +static void xmgmt_mailbox_resp_hot_reset(struct xmgmt_mailbox *xmbx, > + struct xcl_mailbox_req *req, size_t len, u64 msgid, bool sw_ch) > +{ > + int ret; > + struct platform_device *pdev = xmbx->pdev; > + > + xmgmt_mailbox_simple_respond(xmbx, msgid, sw_ch, 0); > + > + ret = xmgmt_hot_reset(pdev); > + if (ret) > + xrt_err(pdev, "failed to hot reset: %d", ret); > + else > + xmgmt_peer_notify_state(xmbx, true); > +} > + > +static void xmgmt_mailbox_resp_load_xclbin(struct xmgmt_mailbox *xmbx, > + struct xcl_mailbox_req *req, size_t len, u64 msgid, bool sw_ch) > +{ > + struct xcl_mailbox_bitstream_kaddr *kaddr = > + (struct xcl_mailbox_bitstream_kaddr *)req->data; > + void *xclbin = (void *)(uintptr_t)kaddr->addr; > + int ret = bitstream_axlf_mailbox(xmbx->pdev, xclbin); > + > + xmgmt_mailbox_simple_respond(xmbx, msgid, sw_ch, ret); > +} > + > +static void xmgmt_mailbox_listener(void *arg, void *data, size_t len, > + u64 msgid, int err, bool sw_ch) > +{ > + struct xmgmt_mailbox *xmbx = (struct xmgmt_mailbox *)arg; > + struct platform_device *pdev = xmbx->pdev; > + struct xcl_mailbox_req *req = (struct xcl_mailbox_req *)data; > + > + if (err) { > + xrt_err(pdev, "failed to receive request: %d", err); > + return; > + } > + if (len < sizeof(*req)) { > + xrt_err(pdev, "received corrupted request"); > + return; > + } > + > + XMGMT_MAILBOX_PRT_REQ_RECV(xmbx, req, sw_ch); > + switch (req->req) { > + case XCL_MAILBOX_REQ_TEST_READ: > + xmgmt_mailbox_resp_test_msg(xmbx, msgid, sw_ch); > + break; > + case XCL_MAILBOX_REQ_PEER_DATA: > + xmgmt_mailbox_resp_peer_data(xmbx, req, len, msgid, sw_ch); > + break; > + case XCL_MAILBOX_REQ_READ_P2P_BAR_ADDR: /* TODO */ > + xmgmt_mailbox_simple_respond(xmbx, msgid, sw_ch, -ENOTSUPP); > + break; > + case XCL_MAILBOX_REQ_USER_PROBE: > + xmgmt_mailbox_resp_user_probe(xmbx, req, len, msgid, sw_ch); > + break; > + case XCL_MAILBOX_REQ_HOT_RESET: > + xmgmt_mailbox_resp_hot_reset(xmbx, req, len, msgid, sw_ch); > + break; > + case XCL_MAILBOX_REQ_LOAD_XCLBIN_KADDR: > + if (xmbx->peer_in_same_domain) { > + xmgmt_mailbox_resp_load_xclbin(xmbx, > + req, len, msgid, sw_ch); > + } else { > + xrt_err(pdev, "%s not handled, not in same domain", > + mailbox_req2name(req->req)); > + } > + break; > + default: > + xrt_err(pdev, "%s(%d) request not handled", > + mailbox_req2name(req->req), req->req); > + break; > + } > +} > + > +static void xmgmt_mailbox_reg_listener(struct xmgmt_mailbox *xmbx) > +{ > + struct xrt_mailbox_ioctl_listen listen = { > + xmgmt_mailbox_listener, xmbx }; > + > + BUG_ON(!mutex_is_locked(&xmbx->lock)); > + if (!xmbx->mailbox) > + return; > + (void) xrt_subdev_ioctl(xmbx->mailbox, XRT_MAILBOX_LISTEN, &listen); > +} > + > +static void xmgmt_mailbox_unreg_listener(struct xmgmt_mailbox *xmbx) > +{ > + struct xrt_mailbox_ioctl_listen listen = { 0 }; > + > + BUG_ON(!mutex_is_locked(&xmbx->lock)); > + BUG_ON(!xmbx->mailbox); > + (void) xrt_subdev_ioctl(xmbx->mailbox, XRT_MAILBOX_LISTEN, &listen); > +} > + > +static bool xmgmt_mailbox_leaf_match(enum xrt_subdev_id id, > + struct platform_device *pdev, void *arg) > +{ > + return (id == XRT_SUBDEV_MAILBOX); > +} > + > +static int xmgmt_mailbox_event_cb(struct platform_device *pdev, > + enum xrt_events evt, void *arg) > +{ > + struct xmgmt_mailbox *xmbx = pdev2mbx(pdev); > + struct xrt_event_arg_subdev *esd = (struct xrt_event_arg_subdev *)arg; > + > + switch (evt) { > + case XRT_EVENT_POST_CREATION: > + BUG_ON(esd->xevt_subdev_id != XRT_SUBDEV_MAILBOX); > + BUG_ON(xmbx->mailbox); > + mutex_lock(&xmbx->lock); > + xmbx->mailbox = xrt_subdev_get_leaf_by_id(pdev, > + XRT_SUBDEV_MAILBOX, PLATFORM_DEVID_NONE); > + xmgmt_mailbox_reg_listener(xmbx); > + mutex_unlock(&xmbx->lock); > + break; > + case XRT_EVENT_PRE_REMOVAL: > + BUG_ON(esd->xevt_subdev_id != XRT_SUBDEV_MAILBOX); > + BUG_ON(!xmbx->mailbox); > + mutex_lock(&xmbx->lock); > + xmgmt_mailbox_unreg_listener(xmbx); > + (void) xrt_subdev_put_leaf(pdev, xmbx->mailbox); > + xmbx->mailbox = NULL; > + mutex_unlock(&xmbx->lock); > + break; > + default: > + break; > + } > + > + return XRT_EVENT_CB_CONTINUE; > +} > + > +static ssize_t xmgmt_mailbox_user_dtb_show(struct file *filp, > + struct kobject *kobj, struct bin_attribute *attr, > + char *buf, loff_t off, size_t count) > +{ > + struct device *dev = kobj_to_dev(kobj); > + struct platform_device *pdev = to_platform_device(dev); > + char *blob = NULL; > + long size; > + ssize_t ret = 0; > + > + blob = xmgmt_mailbox_user_dtb(pdev); > + if (!blob) { > + ret = -ENOENT; > + goto failed; > + } > + > + size = xrt_md_size(dev, blob); > + if (size <= 0) { > + ret = -EINVAL; > + goto failed; > + } > + > + if (off >= size) > + goto failed; > + if (off + count > size) > + count = size - off; > + memcpy(buf, blob + off, count); > + > + ret = count; > +failed: > + vfree(blob); > + return ret; > +} > + > +static struct bin_attribute meta_data_attr = { > + .attr = { > + .name = "metadata_for_user", > + .mode = 0400 > + }, > + .read = xmgmt_mailbox_user_dtb_show, > + .size = 0 > +}; > + > +static struct bin_attribute *xmgmt_mailbox_bin_attrs[] = { > + &meta_data_attr, > + NULL, > +}; > + > +int xmgmt_mailbox_get_test_msg(struct xmgmt_mailbox *xmbx, bool sw_ch, > + char *buf, size_t *len) > +{ > + int rc; > + struct platform_device *pdev = xmbx->pdev; > + struct xcl_mailbox_req req = { 0, XCL_MAILBOX_REQ_TEST_READ, }; > + struct xrt_mailbox_ioctl_request leaf_req = { > + .xmir_sw_ch = sw_ch, > + .xmir_resp_ttl = 1, > + .xmir_req = &req, > + .xmir_req_size = sizeof(req), > + .xmir_resp = buf, > + .xmir_resp_size = *len > + }; > + > + mutex_lock(&xmbx->lock); > + if (xmbx->mailbox) { > + XMGMT_MAILBOX_PRT_REQ_SEND(xmbx, &req, leaf_req.xmir_sw_ch); > + /* > + * mgmt should never send request to peer. it should send > + * either notification or response. here is the only exception > + * for debugging purpose. > + */ > + rc = xrt_subdev_ioctl(xmbx->mailbox, > + XRT_MAILBOX_REQUEST, &leaf_req); > + } else { > + rc = -ENODEV; > + xrt_err(pdev, "mailbox not available"); > + } > + mutex_unlock(&xmbx->lock); > + > + if (rc == 0) > + *len = leaf_req.xmir_resp_size; > + return rc; > +} > + > +int xmgmt_mailbox_set_test_msg(struct xmgmt_mailbox *xmbx, > + char *buf, size_t len) > +{ > + mutex_lock(&xmbx->lock); > + > + if (xmbx->test_msg) > + vfree(xmbx->test_msg); > + xmbx->test_msg = vmalloc(len); > + if (xmbx->test_msg == NULL) { > + mutex_unlock(&xmbx->lock); > + return -ENOMEM; > + } > + (void) memcpy(xmbx->test_msg, buf, len); > + > + mutex_unlock(&xmbx->lock); > + return 0; > +} > + > +static ssize_t peer_msg_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + size_t len = 4096; > + struct platform_device *pdev = to_platform_device(dev); > + struct xmgmt_mailbox *xmbx = pdev2mbx(pdev); > + int ret = xmgmt_mailbox_get_test_msg(xmbx, false, buf, &len); > + > + return ret == 0 ? len : ret; > +} > +static ssize_t peer_msg_store(struct device *dev, > + struct device_attribute *da, const char *buf, size_t count) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct xmgmt_mailbox *xmbx = pdev2mbx(pdev); > + int ret = xmgmt_mailbox_set_test_msg(xmbx, (char *)buf, count); > + > + return ret == 0 ? count : ret; > +} > +/* Message test i/f. */ > +static DEVICE_ATTR_RW(peer_msg); > + > +static struct attribute *xmgmt_mailbox_attrs[] = { > + &dev_attr_peer_msg.attr, > + NULL, > +}; > + > +static const struct attribute_group xmgmt_mailbox_attrgroup = { > + .bin_attrs = xmgmt_mailbox_bin_attrs, > + .attrs = xmgmt_mailbox_attrs, > +}; > + > +void *xmgmt_mailbox_probe(struct platform_device *pdev) > +{ > + struct xmgmt_mailbox *xmbx = > + devm_kzalloc(DEV(pdev), sizeof(*xmbx), GFP_KERNEL); > + > + if (!xmbx) > + return NULL; > + xmbx->pdev = pdev; > + mutex_init(&xmbx->lock); > + > + xmbx->evt_hdl = xrt_subdev_add_event_cb(pdev, > + xmgmt_mailbox_leaf_match, NULL, xmgmt_mailbox_event_cb); > + (void) sysfs_create_group(&DEV(pdev)->kobj, &xmgmt_mailbox_attrgroup); > + return xmbx; > +} > + > +void xmgmt_mailbox_remove(void *handle) > +{ > + struct xmgmt_mailbox *xmbx = (struct xmgmt_mailbox *)handle; > + struct platform_device *pdev = xmbx->pdev; > + > + (void) sysfs_remove_group(&DEV(pdev)->kobj, &xmgmt_mailbox_attrgroup); > + if (xmbx->evt_hdl) > + (void) xrt_subdev_remove_event_cb(pdev, xmbx->evt_hdl); > + if (xmbx->mailbox) > + (void) xrt_subdev_put_leaf(pdev, xmbx->mailbox); > + if (xmbx->test_msg) > + vfree(xmbx->test_msg); > +} > + > +void xmgmt_peer_notify_state(void *handle, bool online) > +{ > + struct xmgmt_mailbox *xmbx = (struct xmgmt_mailbox *)handle; > + struct xcl_mailbox_peer_state *st; > + struct xcl_mailbox_req *req; > + size_t reqlen = sizeof(*req) + sizeof(*st) - 1; > + > + req = vzalloc(reqlen); > + if (req == NULL) > + return; > + > + req->req = XCL_MAILBOX_REQ_MGMT_STATE; > + st = (struct xcl_mailbox_peer_state *)req->data; > + st->state_flags = online ? XCL_MB_STATE_ONLINE : XCL_MB_STATE_OFFLINE; > + mutex_lock(&xmbx->lock); > + xmgmt_mailbox_notify(xmbx, false, req, reqlen); > + mutex_unlock(&xmbx->lock); > +} > diff --git a/drivers/fpga/alveo/mgmt/xmgmt-main-ulp.c b/drivers/fpga/alveo/mgmt/xmgmt-main-ulp.c > new file mode 100644 > index 000000000000..042d86fcef41 > --- /dev/null > +++ b/drivers/fpga/alveo/mgmt/xmgmt-main-ulp.c > @@ -0,0 +1,190 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Xilinx Alveo FPGA MGMT PF entry point driver > + * > + * Copyright (C) 2020 Xilinx, Inc. > + * > + * xclbin download > + * > + * Authors: > + * Lizhi Hou <lizhi.hou@xxxxxxxxxx> > + */ > + > +#include <linux/firmware.h> > +#include <linux/uaccess.h> > +#include "xrt-xclbin.h" > +#include "xrt-metadata.h" > +#include "xrt-subdev.h" > +#include "xrt-gpio.h" > +#include "xmgmt-main.h" > +#include "xrt-icap.h" > +#include "xrt-axigate.h" > + > +static int xmgmt_download_bitstream(struct platform_device *pdev, > + const void *xclbin) > +{ > + struct platform_device *icap_leaf = NULL; > + struct XHwIcap_Bit_Header bit_header = { 0 }; Please fix the style error in struct name ... > + struct xrt_icap_ioctl_wr arg; > + char *bitstream = NULL; > + int ret; > + > + ret = xrt_xclbin_get_section(xclbin, BITSTREAM, (void **)&bitstream, > + NULL); > + if (ret || !bitstream) { > + xrt_err(pdev, "bitstream not found"); > + return -ENOENT; > + } > + ret = xrt_xclbin_parse_header(bitstream, > + DMA_HWICAP_BITFILE_BUFFER_SIZE, &bit_header); > + if (ret) { > + ret = -EINVAL; > + xrt_err(pdev, "invalid bitstream header"); > + goto done; > + } > + icap_leaf = xrt_subdev_get_leaf_by_id(pdev, XRT_SUBDEV_ICAP, > + PLATFORM_DEVID_NONE); > + if (!icap_leaf) { > + ret = -ENODEV; > + xrt_err(pdev, "icap does not exist"); > + goto done; > + } > + arg.xiiw_bit_data = bitstream + bit_header.HeaderLength; > + arg.xiiw_data_len = bit_header.BitstreamLength; > + ret = xrt_subdev_ioctl(icap_leaf, XRT_ICAP_WRITE, &arg); > + if (ret) > + xrt_err(pdev, "write bitstream failed, ret = %d", ret); > + > +done: > + if (icap_leaf) > + xrt_subdev_put_leaf(pdev, icap_leaf); > + vfree(bitstream); > + > + return ret; > +} > + > +static bool match_shell(enum xrt_subdev_id id, > + struct platform_device *pdev, void *arg) > +{ > + struct xrt_subdev_platdata *pdata = DEV_PDATA(pdev); > + const char *ulp_gate; > + int ret; > + > + if (!pdata || xrt_md_size(&pdev->dev, pdata->xsp_dtb) <= 0) > + return false; > + > + ret = xrt_md_get_epname_pointer(&pdev->dev, pdata->xsp_dtb, > + NODE_GATE_ULP, NULL, &ulp_gate); > + if (ret) > + return false; > + > + ret = xrt_md_check_uuids(&pdev->dev, pdata->xsp_dtb, arg); > + if (ret) > + return false; > + > + return true; > +} > + > +static bool match_ulp(enum xrt_subdev_id id, > + struct platform_device *pdev, void *arg) > +{ > + struct xrt_subdev_platdata *pdata = DEV_PDATA(pdev); > + const char *ulp_gate; > + int ret; > + > + if (!pdata || xrt_md_size(&pdev->dev, pdata->xsp_dtb) <= 0) > + return false; > + > + ret = xrt_md_check_uuids(&pdev->dev, pdata->xsp_dtb, arg); > + if (ret) > + return false; > + > + ret = xrt_md_get_epname_pointer(&pdev->dev, pdata->xsp_dtb, > + NODE_GATE_ULP, NULL, &ulp_gate); > + if (!ret) > + return false; > + > + return true; > +} > + > +int xmgmt_ulp_download(struct platform_device *pdev, const void *xclbin) > +{ > + struct platform_device *axigate_leaf; > + char *dtb = NULL; > + int ret = 0, part_inst; > + > + ret = xrt_xclbin_get_metadata(DEV(pdev), xclbin, &dtb); > + if (ret) { > + xrt_err(pdev, "can not get partition metadata, ret %d", ret); > + goto failed; > + } > + > + part_inst = xrt_subdev_lookup_partition(pdev, match_shell, dtb); > + if (part_inst < 0) { > + xrt_err(pdev, "not found matching plp."); > + ret = -ENODEV; > + goto failed; > + } > + > + /* > + * Find ulp partition with interface uuid from incoming xclbin, which > + * is verified before with matching plp partition. > + */ > + part_inst = xrt_subdev_lookup_partition(pdev, match_ulp, dtb); > + if (part_inst >= 0) { > + ret = xrt_subdev_destroy_partition(pdev, part_inst); > + if (ret) { > + xrt_err(pdev, "failed to destroy existing ulp, %d", > + ret); > + goto failed; > + } > + } > + > + axigate_leaf = xrt_subdev_get_leaf_by_epname(pdev, NODE_GATE_ULP); > + > + /* gate may not be exist for 0rp */ > + if (axigate_leaf) { > + ret = xrt_subdev_ioctl(axigate_leaf, XRT_AXIGATE_FREEZE, > + NULL); > + if (ret) { > + xrt_err(pdev, "can not freeze gate %s, %d", > + NODE_GATE_ULP, ret); > + xrt_subdev_put_leaf(pdev, axigate_leaf); > + goto failed; > + } > + } > + ret = xmgmt_download_bitstream(pdev, xclbin); > + if (axigate_leaf) { > + xrt_subdev_ioctl(axigate_leaf, XRT_AXIGATE_FREE, NULL); > + > + /* Do we really need this extra toggling gate before setting > + * clocks? > + * xrt_subdev_ioctl(axigate_leaf, XRT_AXIGATE_FREEZE, NULL); > + * xrt_subdev_ioctl(axigate_leaf, XRT_AXIGATE_FREE, NULL); > + */ > + > + xrt_subdev_put_leaf(pdev, axigate_leaf); > + } > + if (ret) { > + xrt_err(pdev, "bitstream download failed, ret %d", ret); > + goto failed; > + } > + ret = xrt_subdev_create_partition(pdev, dtb); > + if (ret < 0) { > + xrt_err(pdev, "failed creating partition, ret %d", ret); > + goto failed; > + } > + > + ret = xrt_subdev_wait_for_partition_bringup(pdev); > + if (ret) > + xrt_err(pdev, "partiton bringup failed, ret %d", ret); > + > + /* > + * TODO: needs to check individual subdevs to see if there > + * is any error, such as clock setting, memory bank calibration. > + */ > + > +failed: > + vfree(dtb); > + return ret; > +} > diff --git a/drivers/fpga/alveo/mgmt/xmgmt-main.c b/drivers/fpga/alveo/mgmt/xmgmt-main.c > new file mode 100644 > index 000000000000..23e68e3a4ae1 > --- /dev/null > +++ b/drivers/fpga/alveo/mgmt/xmgmt-main.c > @@ -0,0 +1,843 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Xilinx Alveo FPGA MGMT PF entry point driver > + * > + * Copyright (C) 2020 Xilinx, Inc. > + * > + * Authors: > + * Sonal Santan <sonals@xxxxxxxxxx> > + */ > + > +#include <linux/firmware.h> > +#include <linux/uaccess.h> > +#include "xrt-xclbin.h" > +#include "xrt-metadata.h" > +#include "xrt-flash.h" > +#include "xrt-subdev.h" > +#include <linux/xrt/flash_xrt_data.h> > +#include <linux/xrt/xmgmt-ioctl.h> > +#include "xrt-gpio.h" > +#include "xmgmt-main.h" > +#include "xmgmt-fmgr.h" > +#include "xrt-icap.h" > +#include "xrt-axigate.h" > +#include "xmgmt-main-impl.h" > + > +#define XMGMT_MAIN "xmgmt_main" > + > +struct xmgmt_main { > + struct platform_device *pdev; > + void *evt_hdl; > + char *firmware_blp; > + char *firmware_plp; > + char *firmware_ulp; > + bool flash_ready; > + bool gpio_ready; > + struct fpga_manager *fmgr; > + void *mailbox_hdl; > + struct mutex busy_mutex; > + > + uuid_t *blp_intf_uuids; > + u32 blp_intf_uuid_num; > +}; > + > +char *xmgmt_get_vbnv(struct platform_device *pdev) > +{ > + struct xmgmt_main *xmm = platform_get_drvdata(pdev); > + const char *vbnv; > + char *ret; > + int i; > + > + if (xmm->firmware_plp) { > + vbnv = ((struct axlf *)xmm->firmware_plp)-> > + m_header.m_platformVBNV; > + } else if (xmm->firmware_blp) { > + vbnv = ((struct axlf *)xmm->firmware_blp)-> > + m_header.m_platformVBNV; > + } else { > + return NULL; > + } > + > + ret = kstrdup(vbnv, GFP_KERNEL); > + for (i = 0; i < strlen(ret); i++) { > + if (ret[i] == ':' || ret[i] == '.') > + ret[i] = '_'; > + } > + return ret; > +} > + > +static bool xmgmt_main_leaf_match(enum xrt_subdev_id id, > + struct platform_device *pdev, void *arg) > +{ > + if (id == XRT_SUBDEV_GPIO) > + return xrt_subdev_has_epname(pdev, arg); > + else if (id == XRT_SUBDEV_QSPI) > + return true; > + > + return false; > +} > + > +static int get_dev_uuid(struct platform_device *pdev, char *uuidstr, size_t len) > +{ > + char uuid[16]; > + struct platform_device *gpio_leaf; > + struct xrt_gpio_ioctl_rw gpio_arg = { 0 }; > + int err, i, count; > + > + gpio_leaf = xrt_subdev_get_leaf_by_epname(pdev, NODE_BLP_ROM); > + if (!gpio_leaf) { > + xrt_err(pdev, "can not get %s", NODE_BLP_ROM); > + return -EINVAL; > + } > + > + gpio_arg.xgir_id = XRT_GPIO_ROM_UUID; > + gpio_arg.xgir_buf = uuid; > + gpio_arg.xgir_len = sizeof(uuid); > + gpio_arg.xgir_offset = 0; > + err = xrt_subdev_ioctl(gpio_leaf, XRT_GPIO_READ, &gpio_arg); > + xrt_subdev_put_leaf(pdev, gpio_leaf); > + if (err) { > + xrt_err(pdev, "can not get uuid: %d", err); > + return err; > + } > + > + for (count = 0, i = sizeof(uuid) - sizeof(u32); > + i >= 0 && len > count; i -= sizeof(u32)) { > + count += snprintf(uuidstr + count, len - count, > + "%08x", *(u32 *)&uuid[i]); > + } > + return 0; > +} > + > +int xmgmt_hot_reset(struct platform_device *pdev) > +{ > + int ret = xrt_subdev_broadcast_event(pdev, XRT_EVENT_PRE_HOT_RESET); > + > + if (ret) { > + xrt_err(pdev, "offline failed, hot reset is canceled"); > + return ret; > + } > + > + (void) xrt_subdev_hot_reset(pdev); > + xrt_subdev_broadcast_event(pdev, XRT_EVENT_POST_HOT_RESET); > + return 0; > +} > + > +static ssize_t reset_store(struct device *dev, > + struct device_attribute *da, const char *buf, size_t count) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + > + (void) xmgmt_hot_reset(pdev); > + return count; > +} > +static DEVICE_ATTR_WO(reset); > + > +static ssize_t VBNV_show(struct device *dev, > + struct device_attribute *da, char *buf) > +{ > + ssize_t ret; > + char *vbnv; > + struct platform_device *pdev = to_platform_device(dev); > + > + vbnv = xmgmt_get_vbnv(pdev); > + ret = sprintf(buf, "%s\n", vbnv); > + kfree(vbnv); > + return ret; > +} > +static DEVICE_ATTR_RO(VBNV); > + > +static ssize_t logic_uuids_show(struct device *dev, > + struct device_attribute *da, char *buf) > +{ > + ssize_t ret; > + char uuid[80]; > + struct platform_device *pdev = to_platform_device(dev); > + > + /* > + * Getting UUID pointed to by VSEC, > + * should be the same as logic UUID of BLP. > + * TODO: add PLP logic UUID > + */ > + ret = get_dev_uuid(pdev, uuid, sizeof(uuid)); > + if (ret) > + return ret; > + ret = sprintf(buf, "%s\n", uuid); > + return ret; > +} > +static DEVICE_ATTR_RO(logic_uuids); > + > +static inline void uuid2str(const uuid_t *uuid, char *uuidstr, size_t len) > +{ > + int i, p; > + u8 *u = (u8 *)uuid; > + > + BUG_ON(sizeof(uuid_t) * 2 + 1 > len); > + for (p = 0, i = sizeof(uuid_t) - 1; i >= 0; p++, i--) > + (void) snprintf(&uuidstr[p*2], 3, "%02x", u[i]); > +} > + > +static ssize_t interface_uuids_show(struct device *dev, > + struct device_attribute *da, char *buf) > +{ > + ssize_t ret = 0; > + struct platform_device *pdev = to_platform_device(dev); > + struct xmgmt_main *xmm = platform_get_drvdata(pdev); > + u32 i; > + > + /* > + * TODO: add PLP interface UUID > + */ > + for (i = 0; i < xmm->blp_intf_uuid_num; i++) { > + char uuidstr[80]; > + > + uuid2str(&xmm->blp_intf_uuids[i], uuidstr, sizeof(uuidstr)); > + ret += sprintf(buf + ret, "%s\n", uuidstr); > + } > + return ret; > +} > +static DEVICE_ATTR_RO(interface_uuids); > + > +static struct attribute *xmgmt_main_attrs[] = { > + &dev_attr_reset.attr, > + &dev_attr_VBNV.attr, > + &dev_attr_logic_uuids.attr, > + &dev_attr_interface_uuids.attr, > + NULL, > +}; > + > +static ssize_t ulp_image_write(struct file *filp, struct kobject *kobj, > + struct bin_attribute *attr, char *buffer, loff_t off, size_t count) > +{ > + struct xmgmt_main *xmm = > + dev_get_drvdata(container_of(kobj, struct device, kobj)); > + struct axlf *xclbin; > + ulong len; > + > + if (off == 0) { > + if (count < sizeof(*xclbin)) { > + xrt_err(xmm->pdev, "count is too small %ld", count); > + return -EINVAL; > + } > + > + if (xmm->firmware_ulp) { > + vfree(xmm->firmware_ulp); > + xmm->firmware_ulp = NULL; > + } > + xclbin = (struct axlf *)buffer; > + xmm->firmware_ulp = vmalloc(xclbin->m_header.m_length); > + if (!xmm->firmware_ulp) > + return -ENOMEM; > + } else > + xclbin = (struct axlf *)xmm->firmware_ulp; > + > + len = xclbin->m_header.m_length; > + if (off + count >= len && off < len) { > + memcpy(xmm->firmware_ulp + off, buffer, len - off); > + xmgmt_ulp_download(xmm->pdev, xmm->firmware_ulp); > + } else if (off + count < len) { > + memcpy(xmm->firmware_ulp + off, buffer, count); > + } > + > + return count; > +} > + > +static struct bin_attribute ulp_image_attr = { > + .attr = { > + .name = "ulp_image", > + .mode = 0200 > + }, > + .write = ulp_image_write, > + .size = 0 > +}; > + > +static struct bin_attribute *xmgmt_main_bin_attrs[] = { > + &ulp_image_attr, > + NULL, > +}; > + > +static const struct attribute_group xmgmt_main_attrgroup = { > + .attrs = xmgmt_main_attrs, > + .bin_attrs = xmgmt_main_bin_attrs, > +}; > + > +static int load_firmware_from_flash(struct platform_device *pdev, > + char **fw_buf, size_t *len) > +{ > + struct platform_device *flash_leaf = NULL; > + struct flash_data_header header = { 0 }; > + const size_t magiclen = sizeof(header.fdh_id_begin.fdi_magic); > + size_t flash_size = 0; > + int ret = 0; > + char *buf = NULL; > + struct flash_data_ident id = { 0 }; > + struct xrt_flash_ioctl_read frd = { 0 }; > + > + xrt_info(pdev, "try loading fw from flash"); > + > + flash_leaf = xrt_subdev_get_leaf_by_id(pdev, XRT_SUBDEV_QSPI, > + PLATFORM_DEVID_NONE); > + if (flash_leaf == NULL) { > + xrt_err(pdev, "failed to hold flash leaf"); > + return -ENODEV; > + } > + > + (void) xrt_subdev_ioctl(flash_leaf, XRT_FLASH_GET_SIZE, &flash_size); > + if (flash_size == 0) { > + xrt_err(pdev, "failed to get flash size"); > + ret = -EINVAL; > + goto done; > + } > + > + frd.xfir_buf = (char *)&header; > + frd.xfir_size = sizeof(header); > + frd.xfir_offset = flash_size - sizeof(header); > + ret = xrt_subdev_ioctl(flash_leaf, XRT_FLASH_READ, &frd); > + if (ret) { > + xrt_err(pdev, "failed to read header from flash: %d", ret); > + goto done; > + } > + > + /* Pick the end ident since header is aligned in the end of flash. */ > + id = header.fdh_id_end; > + if (strncmp(id.fdi_magic, XRT_DATA_MAGIC, magiclen)) { > + char tmp[sizeof(id.fdi_magic) + 1] = { 0 }; > + > + memcpy(tmp, id.fdi_magic, magiclen); > + xrt_info(pdev, "ignore meta data, bad magic: %s", tmp); > + ret = -ENOENT; > + goto done; > + } > + if (id.fdi_version != 0) { > + xrt_info(pdev, "flash meta data version is not supported: %d", > + id.fdi_version); > + ret = -EOPNOTSUPP; > + goto done; > + } > + > + buf = vmalloc(header.fdh_data_len); > + if (buf == NULL) { > + ret = -ENOMEM; > + goto done; > + } > + > + frd.xfir_buf = buf; > + frd.xfir_size = header.fdh_data_len; > + frd.xfir_offset = header.fdh_data_offset; > + ret = xrt_subdev_ioctl(flash_leaf, XRT_FLASH_READ, &frd); > + if (ret) { > + xrt_err(pdev, "failed to read meta data from flash: %d", ret); > + goto done; > + } else if (flash_xrt_data_get_parity32(buf, header.fdh_data_len) ^ > + header.fdh_data_parity) { > + xrt_err(pdev, "meta data is corrupted"); > + ret = -EINVAL; > + goto done; > + } > + > + xrt_info(pdev, "found meta data of %d bytes @0x%x", > + header.fdh_data_len, header.fdh_data_offset); > + *fw_buf = buf; > + *len = header.fdh_data_len; > + > +done: > + (void) xrt_subdev_put_leaf(pdev, flash_leaf); > + return ret; > +} > + > +static int load_firmware_from_disk(struct platform_device *pdev, char **fw_buf, > + size_t *len) > +{ > + char uuid[80]; > + int err = 0; > + char fw_name[256]; > + const struct firmware *fw; > + > + err = get_dev_uuid(pdev, uuid, sizeof(uuid)); > + if (err) > + return err; > + > + (void) snprintf(fw_name, > + sizeof(fw_name), "xilinx/%s/partition.xsabin", uuid); > + xrt_info(pdev, "try loading fw: %s", fw_name); > + > + err = request_firmware(&fw, fw_name, DEV(pdev)); > + if (err) > + return err; > + > + *fw_buf = vmalloc(fw->size); > + *len = fw->size; > + if (*fw_buf != NULL) > + memcpy(*fw_buf, fw->data, fw->size); > + else > + err = -ENOMEM; > + > + release_firmware(fw); > + return 0; > +} > + > +static const char *xmgmt_get_axlf_firmware(struct xmgmt_main *xmm, > + enum provider_kind kind) > +{ > + switch (kind) { > + case XMGMT_BLP: > + return xmm->firmware_blp; > + case XMGMT_PLP: > + return xmm->firmware_plp; > + case XMGMT_ULP: > + return xmm->firmware_ulp; > + default: > + xrt_err(xmm->pdev, "unknown axlf kind: %d", kind); > + return NULL; > + } > +} > + > +char *xmgmt_get_dtb(struct platform_device *pdev, enum provider_kind kind) > +{ > + struct xmgmt_main *xmm = platform_get_drvdata(pdev); > + char *dtb = NULL; > + const char *provider = xmgmt_get_axlf_firmware(xmm, kind); > + int rc; > + > + if (provider == NULL) > + return dtb; > + > + rc = xrt_xclbin_get_metadata(DEV(pdev), provider, &dtb); > + if (rc) > + xrt_err(pdev, "failed to find dtb: %d", rc); > + return dtb; > +} > + > +static const char *get_uuid_from_firmware(struct platform_device *pdev, > + const char *axlf) > +{ > + const void *uuid = NULL; > + const void *uuiddup = NULL; > + void *dtb = NULL; > + int rc; > + > + rc = xrt_xclbin_get_section(axlf, PARTITION_METADATA, &dtb, NULL); > + if (rc) > + return NULL; > + > + rc = xrt_md_get_prop(DEV(pdev), dtb, NULL, NULL, > + PROP_LOGIC_UUID, &uuid, NULL); > + if (!rc) > + uuiddup = kstrdup(uuid, GFP_KERNEL); > + vfree(dtb); > + return uuiddup; > +} > + > +static bool is_valid_firmware(struct platform_device *pdev, > + char *fw_buf, size_t fw_len) > +{ > + struct axlf *axlf = (struct axlf *)fw_buf; > + size_t axlflen = axlf->m_header.m_length; > + const char *fw_uuid; > + char dev_uuid[80]; > + int err; > + > + err = get_dev_uuid(pdev, dev_uuid, sizeof(dev_uuid)); > + if (err) > + return false; > + > + if (memcmp(fw_buf, ICAP_XCLBIN_V2, sizeof(ICAP_XCLBIN_V2)) != 0) { > + xrt_err(pdev, "unknown fw format"); > + return false; > + } > + > + if (axlflen > fw_len) { > + xrt_err(pdev, "truncated fw, length: %ld, expect: %ld", > + fw_len, axlflen); > + return false; > + } > + > + fw_uuid = get_uuid_from_firmware(pdev, fw_buf); > + if (fw_uuid == NULL || strcmp(fw_uuid, dev_uuid) != 0) { > + xrt_err(pdev, "bad fw UUID: %s, expect: %s", > + fw_uuid ? fw_uuid : "<none>", dev_uuid); > + kfree(fw_uuid); > + return false; > + } > + > + kfree(fw_uuid); > + return true; > +} > + > +int xmgmt_get_provider_uuid(struct platform_device *pdev, > + enum provider_kind kind, uuid_t *uuid) > +{ > + struct xmgmt_main *xmm = platform_get_drvdata(pdev); > + const char *fwbuf; > + const char *fw_uuid; > + int rc = -ENOENT; > + > + mutex_lock(&xmm->busy_mutex); > + > + fwbuf = xmgmt_get_axlf_firmware(xmm, kind); > + if (fwbuf == NULL) > + goto done; > + > + fw_uuid = get_uuid_from_firmware(pdev, fwbuf); > + if (fw_uuid == NULL) > + goto done; > + > + rc = xrt_md_uuid_strtoid(DEV(pdev), fw_uuid, uuid); > + kfree(fw_uuid); > + > +done: > + mutex_unlock(&xmm->busy_mutex); > + return rc; > +} > + > +static int xmgmt_create_blp(struct xmgmt_main *xmm) > +{ > + struct platform_device *pdev = xmm->pdev; > + int rc = 0; > + char *dtb = NULL; > + > + dtb = xmgmt_get_dtb(pdev, XMGMT_BLP); > + if (dtb) { > + rc = xrt_subdev_create_partition(pdev, dtb); > + if (rc < 0) > + xrt_err(pdev, "failed to create BLP: %d", rc); > + else > + rc = 0; > + > + BUG_ON(xmm->blp_intf_uuids); > + xrt_md_get_intf_uuids(&pdev->dev, dtb, > + &xmm->blp_intf_uuid_num, NULL); > + if (xmm->blp_intf_uuid_num > 0) { > + xmm->blp_intf_uuids = vzalloc(sizeof(uuid_t) * > + xmm->blp_intf_uuid_num); > + xrt_md_get_intf_uuids(&pdev->dev, dtb, > + &xmm->blp_intf_uuid_num, xmm->blp_intf_uuids); > + } > + } > + > + vfree(dtb); > + return rc; > +} > + > +static int xmgmt_main_event_cb(struct platform_device *pdev, > + enum xrt_events evt, void *arg) > +{ > + struct xmgmt_main *xmm = platform_get_drvdata(pdev); > + struct xrt_event_arg_subdev *esd = (struct xrt_event_arg_subdev *)arg; > + enum xrt_subdev_id id; > + int instance; > + size_t fwlen; > + > + switch (evt) { > + case XRT_EVENT_POST_CREATION: { > + id = esd->xevt_subdev_id; > + instance = esd->xevt_subdev_instance; > + xrt_info(pdev, "processing event %d for (%d, %d)", > + evt, id, instance); > + > + if (id == XRT_SUBDEV_GPIO) > + xmm->gpio_ready = true; > + else if (id == XRT_SUBDEV_QSPI) > + xmm->flash_ready = true; > + else > + BUG_ON(1); > + > + if (xmm->gpio_ready && xmm->flash_ready) { > + int rc; > + > + rc = load_firmware_from_disk(pdev, &xmm->firmware_blp, > + &fwlen); > + if (rc != 0) { > + rc = load_firmware_from_flash(pdev, > + &xmm->firmware_blp, &fwlen); > + } > + if (rc == 0 && is_valid_firmware(pdev, > + xmm->firmware_blp, fwlen)) > + (void) xmgmt_create_blp(xmm); > + else > + xrt_err(pdev, > + "failed to find firmware, giving up"); > + xmm->evt_hdl = NULL; > + } > + break; > + } > + case XRT_EVENT_POST_ATTACH: > + xmgmt_peer_notify_state(xmm->mailbox_hdl, true); > + break; > + case XRT_EVENT_PRE_DETACH: > + xmgmt_peer_notify_state(xmm->mailbox_hdl, false); > + break; > + default: > + xrt_info(pdev, "ignored event %d", evt); > + break; > + } > + > + return XRT_EVENT_CB_CONTINUE; > +} > + > +static int xmgmt_main_probe(struct platform_device *pdev) > +{ > + struct xmgmt_main *xmm; > + > + xrt_info(pdev, "probing..."); > + > + xmm = devm_kzalloc(DEV(pdev), sizeof(*xmm), GFP_KERNEL); > + if (!xmm) > + return -ENOMEM; > + > + xmm->pdev = pdev; > + platform_set_drvdata(pdev, xmm); > + xmm->fmgr = xmgmt_fmgr_probe(pdev); > + xmm->mailbox_hdl = xmgmt_mailbox_probe(pdev); > + mutex_init(&xmm->busy_mutex); > + > + xmm->evt_hdl = xrt_subdev_add_event_cb(pdev, > + xmgmt_main_leaf_match, NODE_BLP_ROM, xmgmt_main_event_cb); > + > + /* Ready to handle req thru sysfs nodes. */ > + if (sysfs_create_group(&DEV(pdev)->kobj, &xmgmt_main_attrgroup)) > + xrt_err(pdev, "failed to create sysfs group"); > + return 0; > +} > + > +static int xmgmt_main_remove(struct platform_device *pdev) > +{ > + struct xmgmt_main *xmm = platform_get_drvdata(pdev); > + > + /* By now, partition driver should prevent any inter-leaf call. */ > + > + xrt_info(pdev, "leaving..."); > + > + if (xmm->evt_hdl) > + (void) xrt_subdev_remove_event_cb(pdev, xmm->evt_hdl); > + vfree(xmm->blp_intf_uuids); > + vfree(xmm->firmware_blp); > + vfree(xmm->firmware_plp); > + vfree(xmm->firmware_ulp); > + (void) xmgmt_fmgr_remove(xmm->fmgr); > + xmgmt_mailbox_remove(xmm->mailbox_hdl); > + (void) sysfs_remove_group(&DEV(pdev)->kobj, &xmgmt_main_attrgroup); > + return 0; > +} > + > +static int > +xmgmt_main_leaf_ioctl(struct platform_device *pdev, u32 cmd, void *arg) > +{ > + struct xmgmt_main *xmm = platform_get_drvdata(pdev); > + int ret = 0; > + > + xrt_info(pdev, "handling IOCTL cmd: %d", cmd); > + > + switch (cmd) { > + case XRT_MGMT_MAIN_GET_AXLF_SECTION: { > + struct xrt_mgmt_main_ioctl_get_axlf_section *get = > + (struct xrt_mgmt_main_ioctl_get_axlf_section *)arg; > + const char *firmware = > + xmgmt_get_axlf_firmware(xmm, get->xmmigas_axlf_kind); > + > + if (firmware == NULL) { > + ret = -ENOENT; > + } else { > + ret = xrt_xclbin_get_section(firmware, > + get->xmmigas_section_kind, > + &get->xmmigas_section, > + &get->xmmigas_section_size); > + } > + break; > + } > + case XRT_MGMT_MAIN_GET_VBNV: { > + char **vbnv_p = (char **)arg; > + > + *vbnv_p = xmgmt_get_vbnv(pdev); > + break; > + } > + default: > + xrt_err(pdev, "unknown cmd: %d", cmd); > + ret = -EINVAL; > + break; > + } > + return ret; > +} > + > +static int xmgmt_main_open(struct inode *inode, struct file *file) > +{ > + struct platform_device *pdev = xrt_devnode_open(inode); > + > + /* Device may have gone already when we get here. */ > + if (!pdev) > + return -ENODEV; > + > + xrt_info(pdev, "opened"); > + file->private_data = platform_get_drvdata(pdev); > + return 0; > +} > + > +static int xmgmt_main_close(struct inode *inode, struct file *file) > +{ > + struct xmgmt_main *xmm = file->private_data; > + > + xrt_devnode_close(inode); > + > + xrt_info(xmm->pdev, "closed"); > + return 0; > +} > + > +static int xmgmt_bitstream_axlf_fpga_mgr(struct xmgmt_main *xmm, > + void *axlf, size_t size) > +{ > + int ret; > + struct fpga_image_info info = { 0 }; > + > + BUG_ON(!mutex_is_locked(&xmm->busy_mutex)); > + > + /* > + * Should any error happens during download, we can't trust > + * the cached xclbin any more. > + */ > + vfree(xmm->firmware_ulp); > + xmm->firmware_ulp = NULL; > + > + info.buf = (char *)axlf; > + info.count = size; > + ret = fpga_mgr_load(xmm->fmgr, &info); > + if (ret == 0) > + xmm->firmware_ulp = axlf; > + > + return ret; > +} > + > +int bitstream_axlf_mailbox(struct platform_device *pdev, const void *axlf) > +{ > + struct xmgmt_main *xmm = platform_get_drvdata(pdev); > + void *copy_buffer = NULL; > + size_t copy_buffer_size = 0; > + const struct axlf *xclbin_obj = axlf; > + int ret = 0; > + > + if (memcmp(xclbin_obj->m_magic, ICAP_XCLBIN_V2, sizeof(ICAP_XCLBIN_V2))) > + return -EINVAL; > + > + copy_buffer_size = xclbin_obj->m_header.m_length; > + if (copy_buffer_size > MAX_XCLBIN_SIZE) > + return -EINVAL; > + copy_buffer = vmalloc(copy_buffer_size); > + if (copy_buffer == NULL) > + return -ENOMEM; > + (void) memcpy(copy_buffer, axlf, copy_buffer_size); > + > + mutex_lock(&xmm->busy_mutex); > + ret = xmgmt_bitstream_axlf_fpga_mgr(xmm, copy_buffer, copy_buffer_size); > + mutex_unlock(&xmm->busy_mutex); > + if (ret) > + vfree(copy_buffer); > + return ret; > +} > + > +static int bitstream_axlf_ioctl(struct xmgmt_main *xmm, const void __user *arg) > +{ > + void *copy_buffer = NULL; > + size_t copy_buffer_size = 0; > + struct xmgmt_ioc_bitstream_axlf ioc_obj = { 0 }; > + struct axlf xclbin_obj = { {0} }; > + int ret = 0; > + > + if (copy_from_user((void *)&ioc_obj, arg, sizeof(ioc_obj))) > + return -EFAULT; > + if (copy_from_user((void *)&xclbin_obj, ioc_obj.xclbin, > + sizeof(xclbin_obj))) > + return -EFAULT; > + if (memcmp(xclbin_obj.m_magic, ICAP_XCLBIN_V2, sizeof(ICAP_XCLBIN_V2))) > + return -EINVAL; > + > + copy_buffer_size = xclbin_obj.m_header.m_length; > + if (copy_buffer_size > MAX_XCLBIN_SIZE) > + return -EINVAL; > + copy_buffer = vmalloc(copy_buffer_size); > + if (copy_buffer == NULL) > + return -ENOMEM; > + > + if (copy_from_user(copy_buffer, ioc_obj.xclbin, copy_buffer_size)) { > + vfree(copy_buffer); > + return -EFAULT; > + } > + > + ret = xmgmt_bitstream_axlf_fpga_mgr(xmm, copy_buffer, copy_buffer_size); > + if (ret) > + vfree(copy_buffer); > + > + return ret; > +} > + > +static long xmgmt_main_ioctl(struct file *filp, unsigned int cmd, > + unsigned long arg) > +{ > + long result = 0; > + struct xmgmt_main *xmm = filp->private_data; > + > + BUG_ON(!xmm); > + > + if (_IOC_TYPE(cmd) != XMGMT_IOC_MAGIC) > + return -ENOTTY; > + > + mutex_lock(&xmm->busy_mutex); > + > + xrt_info(xmm->pdev, "ioctl cmd %d, arg %ld", cmd, arg); > + switch (cmd) { > + case XMGMT_IOCICAPDOWNLOAD_AXLF: > + result = bitstream_axlf_ioctl(xmm, (const void __user *)arg); > + break; > + default: > + result = -ENOTTY; > + break; > + } > + > + mutex_unlock(&xmm->busy_mutex); > + return result; > +} > + > +void *xmgmt_pdev2mailbox(struct platform_device *pdev) > +{ > + struct xmgmt_main *xmm = platform_get_drvdata(pdev); > + > + return xmm->mailbox_hdl; > +} > + > +struct xrt_subdev_endpoints xrt_mgmt_main_endpoints[] = { > + { > + .xse_names = (struct xrt_subdev_ep_names []){ > + { .ep_name = NODE_MGMT_MAIN }, > + { NULL }, > + }, > + .xse_min_ep = 1, > + }, > + { 0 }, > +}; > + > +struct xrt_subdev_drvdata xmgmt_main_data = { > + .xsd_dev_ops = { > + .xsd_ioctl = xmgmt_main_leaf_ioctl, > + }, > + .xsd_file_ops = { > + .xsf_ops = { > + .owner = THIS_MODULE, > + .open = xmgmt_main_open, > + .release = xmgmt_main_close, > + .unlocked_ioctl = xmgmt_main_ioctl, > + }, > + .xsf_dev_name = "xmgmt", > + }, > +}; > + > +static const struct platform_device_id xmgmt_main_id_table[] = { > + { XMGMT_MAIN, (kernel_ulong_t)&xmgmt_main_data }, > + { }, > +}; > + > +struct platform_driver xmgmt_main_driver = { > + .driver = { > + .name = XMGMT_MAIN, > + }, > + .probe = xmgmt_main_probe, > + .remove = xmgmt_main_remove, > + .id_table = xmgmt_main_id_table, > +}; > diff --git a/drivers/fpga/alveo/mgmt/xmgmt-root.c b/drivers/fpga/alveo/mgmt/xmgmt-root.c > new file mode 100644 > index 000000000000..005fd5e42651 > --- /dev/null > +++ b/drivers/fpga/alveo/mgmt/xmgmt-root.c > @@ -0,0 +1,375 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Xilinx Alveo Management Function Driver > + * > + * Copyright (C) 2020 Xilinx, Inc. > + * > + * Authors: > + * Cheng Zhen <maxz@xxxxxxxxxx> > + */ > + > +#include <linux/module.h> > +#include <linux/pci.h> > +#include <linux/aer.h> > +#include <linux/vmalloc.h> > +#include <linux/delay.h> > + > +#include "xrt-root.h" > +#include "xrt-subdev.h" > +#include "xmgmt-main-impl.h" > +#include "xrt-metadata.h" > + > +#define XMGMT_MODULE_NAME "xmgmt" > +#define XMGMT_DRIVER_VERSION "4.0.0" > + > +#define XMGMT_PDEV(xm) ((xm)->pdev) > +#define XMGMT_DEV(xm) (&(XMGMT_PDEV(xm)->dev)) > +#define xmgmt_err(xm, fmt, args...) \ > + dev_err(XMGMT_DEV(xm), "%s: " fmt, __func__, ##args) > +#define xmgmt_warn(xm, fmt, args...) \ > + dev_warn(XMGMT_DEV(xm), "%s: " fmt, __func__, ##args) > +#define xmgmt_info(xm, fmt, args...) \ > + dev_info(XMGMT_DEV(xm), "%s: " fmt, __func__, ##args) > +#define xmgmt_dbg(xm, fmt, args...) \ > + dev_dbg(XMGMT_DEV(xm), "%s: " fmt, __func__, ##args) > +#define XMGMT_DEV_ID(pdev) \ > + ((pci_domain_nr(pdev->bus) << 16) | \ > + PCI_DEVID(pdev->bus->number, 0)) > + > +static struct class *xmgmt_class; > +static const struct pci_device_id xmgmt_pci_ids[] = { > + { PCI_DEVICE(0x10EE, 0xd020), }, > + { PCI_DEVICE(0x10EE, 0x5020), }, > + { 0, } > +}; > + > +struct xmgmt { > + struct pci_dev *pdev; > + void *root; > + > + /* save config for pci reset */ > + u32 saved_config[8][16]; > + bool ready; > +}; > + > +static int xmgmt_config_pci(struct xmgmt *xm) > +{ > + struct pci_dev *pdev = XMGMT_PDEV(xm); > + int rc; > + > + rc = pcim_enable_device(pdev); > + if (rc < 0) { > + xmgmt_err(xm, "failed to enable device: %d", rc); > + return rc; > + } > + > + rc = pci_enable_pcie_error_reporting(pdev); > + if (rc) > + xmgmt_warn(xm, "failed to enable AER: %d", rc); > + > + pci_set_master(pdev); > + > + rc = pcie_get_readrq(pdev); > + if (rc < 0) { > + xmgmt_err(xm, "failed to read mrrs %d", rc); > + return rc; > + } > + if (rc > 512) { > + rc = pcie_set_readrq(pdev, 512); > + if (rc) { > + xmgmt_err(xm, "failed to force mrrs %d", rc); > + return rc; > + } > + } > + > + return 0; > +} > + > +static void xmgmt_save_config_space(struct pci_dev *pdev, u32 *saved_config) > +{ > + int i; > + > + for (i = 0; i < 16; i++) > + pci_read_config_dword(pdev, i * 4, &saved_config[i]); > +} > + > +static int xmgmt_match_slot_and_save(struct device *dev, void *data) > +{ > + struct xmgmt *xm = data; > + struct pci_dev *pdev = to_pci_dev(dev); > + > + if (XMGMT_DEV_ID(pdev) == XMGMT_DEV_ID(xm->pdev)) { > + pci_cfg_access_lock(pdev); > + pci_save_state(pdev); > + xmgmt_save_config_space(pdev, > + xm->saved_config[PCI_FUNC(pdev->devfn)]); > + } > + > + return 0; > +} > + > +static void xmgmt_pci_save_config_all(struct xmgmt *xm) > +{ > + bus_for_each_dev(&pci_bus_type, NULL, xm, xmgmt_match_slot_and_save); > +} > + > +static void xmgmt_restore_config_space(struct pci_dev *pdev, u32 *config_saved) > +{ > + int i; > + u32 val; > + > + for (i = 0; i < 16; i++) { > + pci_read_config_dword(pdev, i * 4, &val); > + if (val == config_saved[i]) > + continue; > + > + pci_write_config_dword(pdev, i * 4, config_saved[i]); > + pci_read_config_dword(pdev, i * 4, &val); > + if (val != config_saved[i]) { > + dev_err(&pdev->dev, > + "restore config at %d failed", i * 4); > + } > + } > +} > + > +static int xmgmt_match_slot_and_restore(struct device *dev, void *data) > +{ > + struct xmgmt *xm = data; > + struct pci_dev *pdev = to_pci_dev(dev); > + > + if (XMGMT_DEV_ID(pdev) == XMGMT_DEV_ID(xm->pdev)) { > + xmgmt_restore_config_space(pdev, > + xm->saved_config[PCI_FUNC(pdev->devfn)]); > + > + pci_restore_state(pdev); > + pci_cfg_access_unlock(pdev); > + } > + > + return 0; > +} > + > +static void xmgmt_pci_restore_config_all(struct xmgmt *xm) > +{ > + bus_for_each_dev(&pci_bus_type, NULL, xm, xmgmt_match_slot_and_restore); > +} > + > +void xroot_hot_reset(struct pci_dev *pdev) > +{ > + struct xmgmt *xm = pci_get_drvdata(pdev); > + struct pci_bus *bus; > + u8 pci_bctl; > + u16 pci_cmd, devctl; > + int i; > + > + xmgmt_info(xm, "hot reset start"); > + > + xmgmt_pci_save_config_all(xm); > + > + pci_disable_device(pdev); > + > + bus = pdev->bus; > + > + /* > + * When flipping the SBR bit, device can fall off the bus. This is > + * usually no problem at all so long as drivers are working properly > + * after SBR. However, some systems complain bitterly when the device > + * falls off the bus. > + * The quick solution is to temporarily disable the SERR reporting of > + * switch port during SBR. > + */ > + > + pci_read_config_word(bus->self, PCI_COMMAND, &pci_cmd); > + pci_write_config_word(bus->self, PCI_COMMAND, > + (pci_cmd & ~PCI_COMMAND_SERR)); > + pcie_capability_read_word(bus->self, PCI_EXP_DEVCTL, &devctl); > + pcie_capability_write_word(bus->self, PCI_EXP_DEVCTL, > + (devctl & ~PCI_EXP_DEVCTL_FERE)); > + pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &pci_bctl); > + pci_bctl |= PCI_BRIDGE_CTL_BUS_RESET; > + pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl); > + > + msleep(100); > + pci_bctl &= ~PCI_BRIDGE_CTL_BUS_RESET; > + pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl); > + ssleep(1); > + > + pcie_capability_write_word(bus->self, PCI_EXP_DEVCTL, devctl); > + pci_write_config_word(bus->self, PCI_COMMAND, pci_cmd); > + > + pci_enable_device(pdev); > + > + for (i = 0; i < 300; i++) { > + pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); > + if (pci_cmd != 0xffff) > + break; > + msleep(20); > + } > + > + xmgmt_info(xm, "waiting for %d ms", i * 20); > + > + xmgmt_pci_restore_config_all(xm); > + > + xmgmt_config_pci(xm); > +} > + > +static int xmgmt_create_root_metadata(struct xmgmt *xm, char **root_dtb) > +{ > + char *dtb = NULL; > + int ret; > + > + ret = xrt_md_create(DEV(xm->pdev), &dtb); > + if (ret) { > + xmgmt_err(xm, "create metadata failed, ret %d", ret); > + goto failed; > + } > + > + ret = xroot_add_simple_node(xm->root, dtb, NODE_TEST); > + if (ret) > + goto failed; > + > + ret = xroot_add_vsec_node(xm->root, dtb); > + if (ret == -ENOENT) { > + /* > + * We may be dealing with a MFG board. > + * Try vsec-golden which will bring up all hard-coded leaves > + * at hard-coded offsets. > + */ > + ret = xroot_add_simple_node(xm, dtb, NODE_VSEC_GOLDEN); > + } else if (ret == 0) { > + ret = xroot_add_simple_node(xm->root, dtb, NODE_MGMT_MAIN); > + } > + if (ret) > + goto failed; > + > + *root_dtb = dtb; > + return 0; > + > +failed: > + vfree(dtb); > + return ret; > +} > + > +static ssize_t ready_show(struct device *dev, > + struct device_attribute *da, char *buf) > +{ > + struct pci_dev *pdev = to_pci_dev(dev); > + struct xmgmt *xm = pci_get_drvdata(pdev); > + > + return sprintf(buf, "%d\n", xm->ready); > +} > +static DEVICE_ATTR_RO(ready); > + > +static struct attribute *xmgmt_root_attrs[] = { > + &dev_attr_ready.attr, > + NULL > +}; > + > +static struct attribute_group xmgmt_root_attr_group = { > + .attrs = xmgmt_root_attrs, > +}; > + > +static int xmgmt_probe(struct pci_dev *pdev, const struct pci_device_id *id) > +{ > + int ret; > + struct device *dev = DEV(pdev); > + struct xmgmt *xm = devm_kzalloc(dev, sizeof(*xm), GFP_KERNEL); > + char *dtb = NULL; > + > + if (!xm) > + return -ENOMEM; > + xm->pdev = pdev; > + pci_set_drvdata(pdev, xm); > + > + ret = xmgmt_config_pci(xm); > + if (ret) > + goto failed; > + > + ret = xroot_probe(pdev, &xm->root); > + if (ret) > + goto failed; > + > + ret = xmgmt_create_root_metadata(xm, &dtb); > + if (ret) > + goto failed_metadata; > + > + ret = xroot_create_partition(xm->root, dtb); > + vfree(dtb); > + if (ret) > + xmgmt_err(xm, "failed to create root partition: %d", ret); > + > + if (!xroot_wait_for_bringup(xm->root)) > + xmgmt_err(xm, "failed to bringup all partitions"); > + else > + xm->ready = true; > + > + ret = sysfs_create_group(&pdev->dev.kobj, &xmgmt_root_attr_group); > + if (ret) { > + /* Warning instead of failing the probe. */ > + xrt_warn(pdev, "create xmgmt root attrs failed: %d", ret); > + } > + > + xroot_broadcast(xm->root, XRT_EVENT_POST_ATTACH); > + xmgmt_info(xm, "%s started successfully", XMGMT_MODULE_NAME); > + return 0; > + > +failed_metadata: > + (void) xroot_remove(xm->root); > +failed: > + pci_set_drvdata(pdev, NULL); > + return ret; > +} > + > +static void xmgmt_remove(struct pci_dev *pdev) > +{ > + struct xmgmt *xm = pci_get_drvdata(pdev); > + > + xroot_broadcast(xm->root, XRT_EVENT_PRE_DETACH); > + sysfs_remove_group(&pdev->dev.kobj, &xmgmt_root_attr_group); > + (void) xroot_remove(xm->root); > + pci_disable_pcie_error_reporting(xm->pdev); > + xmgmt_info(xm, "%s cleaned up successfully", XMGMT_MODULE_NAME); > +} > + > +static struct pci_driver xmgmt_driver = { > + .name = XMGMT_MODULE_NAME, > + .id_table = xmgmt_pci_ids, > + .probe = xmgmt_probe, > + .remove = xmgmt_remove, > +}; > + > +static int __init xmgmt_init(void) > +{ > + int res = xrt_subdev_register_external_driver(XRT_SUBDEV_MGMT_MAIN, > + &xmgmt_main_driver, xrt_mgmt_main_endpoints); > + > + if (res) > + return res; > + > + xmgmt_class = class_create(THIS_MODULE, XMGMT_MODULE_NAME); > + if (IS_ERR(xmgmt_class)) > + return PTR_ERR(xmgmt_class); > + > + res = pci_register_driver(&xmgmt_driver); > + if (res) { > + class_destroy(xmgmt_class); > + return res; > + } > + > + return 0; > +} > + > +static __exit void xmgmt_exit(void) > +{ > + pci_unregister_driver(&xmgmt_driver); > + class_destroy(xmgmt_class); > + xrt_subdev_unregister_external_driver(XRT_SUBDEV_MGMT_MAIN); > +} > + > +module_init(xmgmt_init); > +module_exit(xmgmt_exit); > + > +MODULE_DEVICE_TABLE(pci, xmgmt_pci_ids); > +MODULE_VERSION(XMGMT_DRIVER_VERSION); > +MODULE_AUTHOR("XRT Team <runtime@xxxxxxxxxx>"); > +MODULE_DESCRIPTION("Xilinx Alveo management function driver"); > +MODULE_LICENSE("GPL v2"); > -- > 2.17.1 I have not yet looked at the whole thing, but why does the FPGA Manager only copy things around? Any reason your partitions cannot be modelled as FPGA regions/Bridges? It would be helpful to split this up into smaller chunks, that make it easier to review. The FPGA Manager driver should be a separate patch, etc. - Moritz >