diff --git a/xilinx-configfs-overlays.patch b/xilinx-configfs-overlays.patch new file mode 100644 index 0000000..53c5349 --- /dev/null +++ b/xilinx-configfs-overlays.patch @@ -0,0 +1,387 @@ +# Add the Device Tree Overlay ConfigFS interface for PL programming +# diff from Xilinx/linux-xilinx/tree/xlnx_rebase_v6.6_LTS commit ID: 7e42c87 +# See: https://github.com/Xilinx/linux-xlnx/commit/7e42c87c489b702795794e480fb5483897ff9e91 + +diff --git a/Documentation/devicetree/configfs-overlays.txt b/Documentation/devicetree/configfs-overlays.txt +new file mode 100644 +index 00000000000000..5fa43e0643072c +--- /dev/null ++++ b/Documentation/devicetree/configfs-overlays.txt +@@ -0,0 +1,31 @@ ++Howto use the configfs overlay interface. ++ ++A device-tree configfs entry is created in /config/device-tree/overlays ++and and it is manipulated using standard file system I/O. ++Note that this is a debug level interface, for use by developers and ++not necessarily something accessed by normal users due to the ++security implications of having direct access to the kernel's device tree. ++ ++* To create an overlay you mkdir the directory: ++ ++ # mkdir /config/device-tree/overlays/foo ++ ++* Either you echo the overlay firmware file to the path property file. ++ ++ # echo foo.dtbo >/config/device-tree/overlays/foo/path ++ ++* Or you cat the contents of the overlay to the dtbo file ++ ++ # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo ++ ++The overlay file will be applied, and devices will be created/destroyed ++as required. ++ ++To remove it simply rmdir the directory. ++ ++ # rmdir /config/device-tree/overlays/foo ++ ++The rationalle of the dual interface (firmware & direct copy) is that each is ++better suited to different use patterns. The firmware interface is what's ++intended to be used by hardware managers in the kernel, while the copy interface ++make sense for developers (since it avoids problems with namespaces). +diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig +index da9826accb1b5d..4c7af532589d0c 100644 +--- a/drivers/of/Kconfig ++++ b/drivers/of/Kconfig +@@ -102,4 +102,15 @@ config OF_OVERLAY + config OF_NUMA + bool + ++config OF_CONFIGFS ++ bool "Device Tree Overlay ConfigFS interface" ++ select CONFIGFS_FS ++ depends on OF_OVERLAY ++ help ++ Select this option to enable simple user-space driven DT overlay ++ interface to support device tree manipulated at runtime. ++ Say Y here to include this support. ++ ++ If unsure, say N. ++ + endif # OF +diff --git a/drivers/of/Makefile b/drivers/of/Makefile +index eff624854575c5..61bd05f08ca1b8 100644 +--- a/drivers/of/Makefile ++++ b/drivers/of/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-y = base.o cpu.o device.o module.o platform.o property.o + obj-$(CONFIG_OF_KOBJ) += kobj.o ++obj-$(CONFIG_OF_CONFIGFS) += configfs.o + obj-$(CONFIG_OF_DYNAMIC) += dynamic.o + obj-$(CONFIG_OF_FLATTREE) += fdt.o + obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o +diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c +new file mode 100644 +index 00000000000000..3839f14dc8938e +--- /dev/null ++++ b/drivers/of/configfs.c +@@ -0,0 +1,294 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Configfs entries for device-tree ++ * ++ * Copyright (C) 2013 - Pantelis Antoniou ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "of_private.h" ++ ++struct cfs_overlay_item { ++ struct config_item item; ++ ++ char path[PATH_MAX]; ++ ++ const struct firmware *fw; ++ struct device_node *overlay; ++ int ov_id; ++ ++ void *dtbo; ++ int dtbo_size; ++ ++ void *mem; ++}; ++ ++static DEFINE_MUTEX(overlay_lock); ++ ++static int create_overlay(struct cfs_overlay_item *overlay, void *blob) ++{ ++ int err; ++ ++ /* FIXME */ ++ err = of_overlay_fdt_apply(blob, overlay->dtbo_size, &overlay->ov_id, NULL); ++ if (err < 0) { ++ pr_err("%s: Failed to create overlay (err=%d)\n", ++ __func__, err); ++ return err; ++ } ++ ++ return err; ++} ++ ++static inline struct cfs_overlay_item ++ *to_cfs_overlay_item(struct config_item *item) ++{ ++ return item ? container_of(item, struct cfs_overlay_item, item) : NULL; ++} ++ ++static ssize_t cfs_overlay_item_path_show(struct config_item *item, char *page) ++{ ++ return sprintf(page, "%s\n", to_cfs_overlay_item(item)->path); ++} ++ ++static ssize_t cfs_overlay_item_path_store(struct config_item *item, ++ const char *page, size_t count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ const char *p = page; ++ char *s; ++ int err; ++ ++ /* if it's set do not allow changes */ ++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) ++ return -EPERM; ++ ++ /* copy to path buffer (and make sure it's always zero terminated */ ++ count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); ++ overlay->path[sizeof(overlay->path) - 1] = '\0'; ++ ++ /* strip trailing newlines */ ++ s = overlay->path + strlen(overlay->path); ++ while (s > overlay->path && *--s == '\n') ++ *s = '\0'; ++ ++ pr_debug("%s: path is '%s'\n", __func__, overlay->path); ++ ++ err = request_firmware(&overlay->fw, overlay->path, NULL); ++ if (err != 0) ++ goto out_err; ++ ++ overlay->dtbo_size = overlay->fw->size; ++ err = create_overlay(overlay, (void *)overlay->fw->data); ++ if (err < 0) ++ goto out_err; ++ ++ return count; ++ ++out_err: ++ ++ release_firmware(overlay->fw); ++ overlay->fw = NULL; ++ ++ overlay->path[0] = '\0'; ++ ++ return count; ++} ++ ++static ssize_t cfs_overlay_item_status_show(struct config_item *item, ++ char *page) ++{ ++ return sprintf(page, "%s\n", to_cfs_overlay_item(item)->ov_id >= 0 ? ++ "applied" : "unapplied"); ++} ++ ++CONFIGFS_ATTR(cfs_overlay_item_, path); ++CONFIGFS_ATTR_RO(cfs_overlay_item_, status); ++ ++static struct configfs_attribute *cfs_overlay_attrs[] = { ++ &cfs_overlay_item_attr_path, ++ &cfs_overlay_item_attr_status, ++ NULL, ++}; ++ ++ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, ++ void *buf, size_t max_count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ pr_debug("%s: buf=%p max_count=%zu\n", __func__, buf, max_count); ++ ++ if (!overlay->dtbo) ++ return 0; ++ ++ /* copy if buffer provided */ ++ if (buf) { ++ /* the buffer must be large enough */ ++ if (overlay->dtbo_size > max_count) ++ return -ENOSPC; ++ ++ memcpy(buf, overlay->dtbo, overlay->dtbo_size); ++ } ++ ++ return overlay->dtbo_size; ++} ++ ++ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, ++ const void *buf, size_t count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ int err; ++ ++ /* if it's set do not allow changes */ ++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) ++ return -EPERM; ++ ++ /* copy the contents */ ++ overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); ++ if (!overlay->dtbo) ++ return -ENOMEM; ++ ++ overlay->dtbo_size = count; ++ ++ err = create_overlay(overlay, overlay->dtbo); ++ if (err < 0) ++ goto out_err; ++ ++ return count; ++ ++out_err: ++ kfree(overlay->dtbo); ++ overlay->dtbo = NULL; ++ overlay->dtbo_size = 0; ++ ++ return err; ++} ++ ++CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); ++ ++static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { ++ &cfs_overlay_item_attr_dtbo, ++ NULL, ++}; ++ ++static void cfs_overlay_release(struct config_item *item) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ if (overlay->ov_id >= 0) ++ of_overlay_remove(&overlay->ov_id); ++ if (overlay->fw) ++ release_firmware(overlay->fw); ++ /* kfree with NULL is safe */ ++ kfree(overlay->dtbo); ++ kfree(overlay->mem); ++ kfree(overlay); ++} ++ ++static struct configfs_item_operations cfs_overlay_item_ops = { ++ .release = cfs_overlay_release, ++}; ++ ++static struct config_item_type cfs_overlay_type = { ++ .ct_item_ops = &cfs_overlay_item_ops, ++ .ct_attrs = cfs_overlay_attrs, ++ .ct_bin_attrs = cfs_overlay_bin_attrs, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct config_item ++ *cfs_overlay_group_make_item(struct config_group *group, ++ const char *name) ++{ ++ struct cfs_overlay_item *overlay; ++ ++ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); ++ if (!overlay) ++ return ERR_PTR(-ENOMEM); ++ overlay->ov_id = -1; ++ config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); ++ ++ return &overlay->item; ++} ++ ++static void cfs_overlay_group_drop_item(struct config_group *group, ++ struct config_item *item) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ config_item_put(&overlay->item); ++} ++ ++static struct configfs_group_operations overlays_ops = { ++ .make_item = cfs_overlay_group_make_item, ++ .drop_item = cfs_overlay_group_drop_item, ++}; ++ ++static struct config_item_type overlays_type = { ++ .ct_group_ops = &overlays_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct configfs_group_operations of_cfs_ops = { ++ /* empty - we don't allow anything to be created */ ++}; ++ ++static struct config_item_type of_cfs_type = { ++ .ct_group_ops = &of_cfs_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++struct config_group of_cfs_overlay_group; ++ ++static struct configfs_subsystem of_cfs_subsys = { ++ .su_group = { ++ .cg_item = { ++ .ci_namebuf = "device-tree", ++ .ci_type = &of_cfs_type, ++ }, ++ }, ++ .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), ++}; ++ ++static int __init of_cfs_init(void) ++{ ++ int ret; ++ ++ pr_info("%s\n", __func__); ++ ++ config_group_init(&of_cfs_subsys.su_group); ++ config_group_init_type_name(&of_cfs_overlay_group, "overlays", ++ &overlays_type); ++ configfs_add_default_group(&of_cfs_overlay_group, ++ &of_cfs_subsys.su_group); ++ ++ ret = configfs_register_subsystem(&of_cfs_subsys); ++ if (ret != 0) { ++ pr_err("%s: failed to register subsys\n", __func__); ++ goto out; ++ } ++ pr_info("%s: OK\n", __func__); ++out: ++ return ret; ++} ++late_initcall(of_cfs_init); +diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c +index a9a292d6d59b26..aee4c4edd5ee9d 100644 +--- a/drivers/of/overlay.c ++++ b/drivers/of/overlay.c +@@ -1245,6 +1245,9 @@ int of_overlay_remove(int *ovcs_id) + if (!ret) + ret = ret_tmp; + ++ /* Wait for completion of call_rcu()'s */ ++ rcu_barrier(); ++ + free_overlay_changeset(ovcs); + + err_unlock: \ No newline at end of file diff --git a/xilinx-fpga-manager.patch b/xilinx-fpga-manager.patch new file mode 100644 index 0000000..59aa585 --- /dev/null +++ b/xilinx-fpga-manager.patch @@ -0,0 +1,663 @@ +# Enable user-space interface for PL programming via Linux FPGA manager +# diff cherry-picked from Xilinx/linux-xilinx/tree/xlnx_rebase_v6.6_LTS +# commit IDs: e61c0a9, 0a38712, dc67651, 89a24e3, 8d224b1, 2a9c05f, 4e94580 +# https://github.com/Xilinx/linux-xlnx/commits/xlnx_rebase_v6.6_LTS/drivers/fpga/fpga-mgr.c + +diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig +index 2f689ac4ba3a..67eb50451af2 100644 +--- a/drivers/fpga/Kconfig ++++ b/drivers/fpga/Kconfig +@@ -12,6 +12,16 @@ menuconfig FPGA + + if FPGA + ++config FPGA_MGR_DEBUG_FS ++ tristate "FPGA debug fs" ++ select DEBUG_FS ++ help ++ Say Y here if you want to expose a DebugFS interface for the ++ FPGA Manager Framework. FPGA manager DebugFS provides a user ++ interface to read the fpga specific configuration information. ++ ++ If unsure, say N. ++ + config FPGA_MGR_SOCFPGA + tristate "Altera SOCFPGA FPGA Manager" + depends on ARCH_INTEL_SOCFPGA || COMPILE_TEST +diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c +index 06651389c592..6e8c45974f28 100644 +--- a/drivers/fpga/fpga-mgr.c ++++ b/drivers/fpga/fpga-mgr.c +@@ -8,6 +8,9 @@ + * With code from the mailing list: + * Copyright (C) 2013 Xilinx, Inc. + */ ++#include ++#include ++#include + #include + #include + #include +@@ -61,12 +64,14 @@ static inline int fpga_mgr_write_complete(struct fpga_manager *mgr, + { + int ret = 0; + ++ mgr->err = 0; + mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE; + if (mgr->mops->write_complete) + ret = mgr->mops->write_complete(mgr, info); + if (ret) { + dev_err(&mgr->dev, "Error after writing image data to FPGA\n"); + mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; ++ mgr->err = ret; + return ret; + } + mgr->state = FPGA_MGR_STATE_OPERATING; +@@ -272,6 +277,7 @@ static int fpga_mgr_write_init_buf(struct fpga_manager *mgr, + size_t header_size = info->header_size; + int ret; + ++ mgr->err = 0; + mgr->state = FPGA_MGR_STATE_WRITE_INIT; + + if (header_size > count) +@@ -284,6 +290,7 @@ static int fpga_mgr_write_init_buf(struct fpga_manager *mgr, + if (ret) { + dev_err(&mgr->dev, "Error preparing FPGA for writing\n"); + mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR; ++ mgr->err = ret; + return ret; + } + +@@ -364,11 +371,15 @@ static int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, + { + int ret; + ++ if (info->flags & FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM) ++ memcpy(info->key, mgr->key, ENCRYPTED_KEY_LEN); ++ + ret = fpga_mgr_prepare_sg(mgr, info, sgt); + if (ret) + return ret; + + /* Write the FPGA image to the FPGA. */ ++ mgr->err = 0; + mgr->state = FPGA_MGR_STATE_WRITE; + if (mgr->mops->write_sg) { + ret = fpga_mgr_write_sg(mgr, sgt); +@@ -405,6 +416,7 @@ static int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, + if (ret) { + dev_err(&mgr->dev, "Error while writing image data to FPGA\n"); + mgr->state = FPGA_MGR_STATE_WRITE_ERR; ++ mgr->err = ret; + return ret; + } + +@@ -436,11 +448,13 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr, + /* + * Write the FPGA image to the FPGA. + */ ++ mgr->err = 0; + mgr->state = FPGA_MGR_STATE_WRITE; + ret = fpga_mgr_write(mgr, buf, count); + if (ret) { + dev_err(&mgr->dev, "Error while writing image data to FPGA\n"); + mgr->state = FPGA_MGR_STATE_WRITE_ERR; ++ mgr->err = ret; + return ret; + } + +@@ -519,6 +533,39 @@ static int fpga_mgr_buf_load(struct fpga_manager *mgr, + return rc; + } + ++static int fpga_dmabuf_load(struct fpga_manager *mgr, ++ struct fpga_image_info *info) ++{ ++ struct dma_buf_attachment *attach; ++ struct sg_table *sgt; ++ int ret; ++ ++ /* create attachment for dmabuf with the user device */ ++ attach = dma_buf_attach(mgr->dmabuf, &mgr->dev); ++ if (IS_ERR(attach)) { ++ pr_err("failed to attach dmabuf\n"); ++ ret = PTR_ERR(attach); ++ goto fail_put; ++ } ++ ++ sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); ++ if (IS_ERR(sgt)) { ++ ret = PTR_ERR(sgt); ++ goto fail_detach; ++ } ++ ++ info->sgt = sgt; ++ ret = fpga_mgr_buf_load_sg(mgr, info, info->sgt); ++ dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); ++ ++fail_detach: ++ dma_buf_detach(mgr->dmabuf, attach); ++fail_put: ++ dma_buf_put(mgr->dmabuf); ++ ++ return ret; ++} ++ + /** + * fpga_mgr_firmware_load - request firmware and load to fpga + * @mgr: fpga manager +@@ -543,11 +590,17 @@ static int fpga_mgr_firmware_load(struct fpga_manager *mgr, + + dev_info(dev, "writing %s to %s\n", image_name, mgr->name); + ++ mgr->err = 0; + mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ; + ++ /* flags indicates whether to do full or partial reconfiguration */ ++ info->flags = mgr->flags; ++ memcpy(info->key, mgr->key, ENCRYPTED_KEY_LEN); ++ + ret = request_firmware(&fw, image_name, dev); + if (ret) { + mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ_ERR; ++ mgr->err = ret; + dev_err(dev, "Error requesting firmware %s\n", image_name); + return ret; + } +@@ -573,6 +626,8 @@ int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info) + { + info->header_size = mgr->mops->initial_header_size; + ++ if (mgr->flags & FPGA_MGR_CONFIG_DMA_BUF) ++ return fpga_dmabuf_load(mgr, info); + if (info->sgt) + return fpga_mgr_buf_load_sg(mgr, info, info->sgt); + if (info->buf && info->count) +@@ -626,6 +681,9 @@ static ssize_t state_show(struct device *dev, + { + struct fpga_manager *mgr = to_fpga_manager(dev); + ++ if (mgr->err) ++ return sprintf(buf, "%s: 0x%x\n", state_str[mgr->state], mgr->err); ++ + return sprintf(buf, "%s\n", state_str[mgr->state]); + } + +@@ -652,14 +710,88 @@ static ssize_t status_show(struct device *dev, + return len; + } + ++static ssize_t firmware_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fpga_manager *mgr = to_fpga_manager(dev); ++ unsigned int len; ++ char image_name[NAME_MAX]; ++ int ret; ++ ++ /* struct with information about the FPGA image to program. */ ++ struct fpga_image_info info = {0}; ++ ++ info.header_size = mgr->mops->initial_header_size; ++ ++ /* lose terminating \n */ ++ strcpy(image_name, buf); ++ len = strlen(image_name); ++ if (image_name[len - 1] == '\n') ++ image_name[len - 1] = 0; ++ ++ ret = fpga_mgr_firmware_load(mgr, &info, image_name); ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++static ssize_t key_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fpga_manager *mgr = to_fpga_manager(dev); ++ ++ return snprintf(buf, ENCRYPTED_KEY_LEN + 1, "%s\n", mgr->key); ++} ++ ++static ssize_t key_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fpga_manager *mgr = to_fpga_manager(dev); ++ ++ memcpy(mgr->key, buf, count); ++ ++ return count; ++} ++ ++static ssize_t flags_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fpga_manager *mgr = to_fpga_manager(dev); ++ ++ return sprintf(buf, "%lx\n", mgr->flags); ++} ++ ++static ssize_t flags_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fpga_manager *mgr = to_fpga_manager(dev); ++ int ret; ++ ++ ret = kstrtol(buf, 16, &mgr->flags); ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ + static DEVICE_ATTR_RO(name); + static DEVICE_ATTR_RO(state); + static DEVICE_ATTR_RO(status); ++static DEVICE_ATTR_WO(firmware); ++static DEVICE_ATTR_RW(flags); ++static DEVICE_ATTR_RW(key); + + static struct attribute *fpga_mgr_attrs[] = { + &dev_attr_name.attr, + &dev_attr_state.attr, + &dev_attr_status.attr, ++ &dev_attr_firmware.attr, ++ &dev_attr_flags.attr, ++ &dev_attr_key.attr, + NULL, + }; + ATTRIBUTE_GROUPS(fpga_mgr); +@@ -732,6 +864,106 @@ void fpga_mgr_put(struct fpga_manager *mgr) + } + EXPORT_SYMBOL_GPL(fpga_mgr_put); + ++#ifdef CONFIG_FPGA_MGR_DEBUG_FS ++#include ++ ++static int fpga_mgr_read(struct seq_file *s, void *data) ++{ ++ struct fpga_manager *mgr = (struct fpga_manager *)s->private; ++ int ret = 0; ++ ++ if (!mgr->mops->read) ++ return -ENOENT; ++ ++ if (!mutex_trylock(&mgr->ref_mutex)) ++ return -EBUSY; ++ ++ if (mgr->state != FPGA_MGR_STATE_OPERATING) { ++ ret = -EPERM; ++ goto err_unlock; ++ } ++ ++ /* Read the FPGA configuration data from the fabric */ ++ ret = mgr->mops->read(mgr, s); ++ if (ret) ++ dev_err(&mgr->dev, "Error while reading configuration data from FPGA\n"); ++ ++err_unlock: ++ mutex_unlock(&mgr->ref_mutex); ++ ++ return ret; ++} ++ ++static int fpga_mgr_read_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, fpga_mgr_read, inode->i_private); ++} ++ ++static const struct file_operations fpga_mgr_ops_image = { ++ .owner = THIS_MODULE, ++ .open = fpga_mgr_read_open, ++ .read = seq_read, ++}; ++#endif ++ ++static int fpga_dmabuf_fd_get(struct file *file, char __user *argp) ++{ ++ struct fpga_manager *mgr = (struct fpga_manager *)(file->private_data); ++ int buffd; ++ ++ if (copy_from_user(&buffd, argp, sizeof(buffd))) ++ return -EFAULT; ++ ++ mgr->dmabuf = dma_buf_get(buffd); ++ if (IS_ERR_OR_NULL(mgr->dmabuf)) ++ return -EINVAL; ++ ++ mgr->flags = FPGA_MGR_CONFIG_DMA_BUF; ++ ++ return 0; ++} ++ ++static int fpga_device_open(struct inode *inode, struct file *file) ++{ ++ struct miscdevice *miscdev = file->private_data; ++ struct fpga_manager *mgr = container_of(miscdev, ++ struct fpga_manager, miscdev); ++ ++ file->private_data = mgr; ++ ++ return 0; ++} ++ ++static int fpga_device_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static long fpga_device_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ char __user *argp = (char __user *)arg; ++ int err; ++ ++ switch (cmd) { ++ case FPGA_IOCTL_LOAD_DMA_BUFF: ++ err = fpga_dmabuf_fd_get(file, argp); ++ break; ++ default: ++ err = -ENOTTY; ++ } ++ ++ return err; ++} ++ ++static const struct file_operations fpga_fops = { ++ .owner = THIS_MODULE, ++ .open = fpga_device_open, ++ .release = fpga_device_release, ++ .unlocked_ioctl = fpga_device_ioctl, ++ .compat_ioctl = fpga_device_ioctl, ++}; ++ + /** + * fpga_mgr_lock - Lock FPGA manager for exclusive use + * @mgr: fpga manager +@@ -779,6 +1011,9 @@ struct fpga_manager * + fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info) + { + const struct fpga_manager_ops *mops = info->mops; ++#ifdef CONFIG_FPGA_MGR_DEBUG_FS ++ struct dentry *d, *parent_dir; ++#endif + struct fpga_manager *mgr; + int id, ret; + +@@ -815,10 +1050,28 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in + mgr->dev.of_node = parent->of_node; + mgr->dev.id = id; + ++ /* Make device dma capable by inheriting from parent's */ ++ set_dma_ops(&mgr->dev, get_dma_ops(parent)); ++ ret = dma_coerce_mask_and_coherent(&mgr->dev, dma_get_mask(parent)); ++ if (ret) { ++ dev_warn(parent, ++ "Failed to set DMA mask %llx. Trying to continue... %x\n", ++ dma_get_mask(parent), ret); ++ } ++ + ret = dev_set_name(&mgr->dev, "fpga%d", id); + if (ret) + goto error_device; + ++ mgr->miscdev.minor = MISC_DYNAMIC_MINOR; ++ mgr->miscdev.name = kobject_name(&mgr->dev.kobj); ++ mgr->miscdev.fops = &fpga_fops; ++ ret = misc_register(&mgr->miscdev); ++ if (ret) { ++ pr_err("fpga: failed to register misc device.\n"); ++ goto error_device; ++ } ++ + /* + * Initialize framework state by requesting low level driver read state + * from device. FPGA may be in reset mode or may have been programmed +@@ -832,6 +1085,28 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in + return ERR_PTR(ret); + } + ++#ifdef CONFIG_FPGA_MGR_DEBUG_FS ++ mgr->dir = debugfs_create_dir("fpga", NULL); ++ if (!mgr->dir) ++ goto error_device; ++ ++ parent_dir = mgr->dir; ++ d = debugfs_create_dir(mgr->dev.kobj.name, parent_dir); ++ if (!d) { ++ debugfs_remove_recursive(parent_dir); ++ goto error_device; ++ } ++ ++ parent_dir = d; ++ d = debugfs_create_file("image", 0644, parent_dir, mgr, ++ &fpga_mgr_ops_image); ++ if (!d) { ++ debugfs_remove_recursive(mgr->dir); ++ goto error_device; ++ } ++#endif ++ dev_info(&mgr->dev, "%s registered\n", mgr->name); ++ + return mgr; + + error_device: +@@ -882,6 +1157,10 @@ void fpga_mgr_unregister(struct fpga_manager *mgr) + { + dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name); + ++#ifdef CONFIG_FPGA_MGR_DEBUG_FS ++ debugfs_remove_recursive(mgr->dir); ++#endif ++ + /* + * If the low level driver provides a method for putting fpga into + * a desired state upon unregister, do it. +diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c +index a6affd83f275..48ac7617296e 100644 +--- a/drivers/fpga/of-fpga-region.c ++++ b/drivers/fpga/of-fpga-region.c +@@ -230,6 +230,16 @@ of_fpga_region_parse_ov(struct fpga_region *region, + if (of_property_read_bool(overlay, "encrypted-fpga-config")) + info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM; + ++ if (of_property_read_bool(overlay, "userkey-encrypted-fpga-config")) ++ info->flags |= FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM; ++ ++ if (of_property_read_bool(overlay, "ddrmem-authenticated-fpga-config")) ++ info->flags |= FPGA_MGR_DDR_MEM_AUTH_BITSTREAM; ++ ++ if (of_property_read_bool(overlay, ++ "securemem-authenticated-fpga-config")) ++ info->flags |= FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM; ++ + if (!of_property_read_string(overlay, "firmware-name", + &firmware_name)) { + info->firmware_name = devm_kstrdup(dev, firmware_name, +diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c +index f3434e2c487b..db923746cac5 100644 +--- a/drivers/fpga/zynqmp-fpga.c ++++ b/drivers/fpga/zynqmp-fpga.c +@@ -43,25 +43,42 @@ static int zynqmp_fpga_ops_write(struct fpga_manager *mgr, + struct zynqmp_fpga_priv *priv; + dma_addr_t dma_addr; + u32 eemi_flags = 0; ++ size_t dma_size; + char *kbuf; + int ret; + + priv = mgr->priv; + +- kbuf = dma_alloc_coherent(priv->dev, size, &dma_addr, GFP_KERNEL); ++ if (priv->flags & FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM) ++ dma_size = size + ENCRYPTED_KEY_LEN; ++ else ++ dma_size = size; ++ ++ kbuf = dma_alloc_coherent(priv->dev, dma_size, &dma_addr, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + memcpy(kbuf, buf, size); + ++ if (priv->flags & FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM) { ++ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_USERKEY; ++ memcpy(kbuf + size, mgr->key, ENCRYPTED_KEY_LEN); ++ } else if (priv->flags & FPGA_MGR_ENCRYPTED_BITSTREAM) { ++ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_DEVKEY; ++ } ++ + wmb(); /* ensure all writes are done before initiate FW call */ + + if (priv->flags & FPGA_MGR_PARTIAL_RECONFIG) + eemi_flags |= XILINX_ZYNQMP_PM_FPGA_PARTIAL; + +- ret = zynqmp_pm_fpga_load(dma_addr, size, eemi_flags); ++ if (priv->flags & FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM) ++ ret = zynqmp_pm_fpga_load(dma_addr, dma_addr + size, ++ eemi_flags); ++ else ++ ret = zynqmp_pm_fpga_load(dma_addr, size, eemi_flags); + +- dma_free_coherent(priv->dev, size, kbuf, dma_addr); ++ dma_free_coherent(priv->dev, dma_size, kbuf, dma_addr); + + return ret; + } +diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h +index 9dda7d9898ff..edaf77160746 100644 +--- a/include/linux/firmware/xlnx-zynqmp.h ++++ b/include/linux/firmware/xlnx-zynqmp.h +@@ -70,6 +70,8 @@ + */ + #define XILINX_ZYNQMP_PM_FPGA_FULL 0x0U + #define XILINX_ZYNQMP_PM_FPGA_PARTIAL BIT(0) ++#define XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_USERKEY BIT(3) ++#define XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_DEVKEY BIT(4) + + /* FPGA Status Reg */ + #define XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET 7U +diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h +index 54f63459efd6..c96a4405f909 100644 +--- a/include/linux/fpga/fpga-mgr.h ++++ b/include/linux/fpga/fpga-mgr.h +@@ -9,8 +9,11 @@ + #define _LINUX_FPGA_MGR_H + + #include ++#include + #include + ++#define ENCRYPTED_KEY_LEN 64 /* Bytes */ ++ + struct fpga_manager; + struct sg_table; + +@@ -66,17 +69,25 @@ enum fpga_mgr_states { + * + * %FPGA_MGR_EXTERNAL_CONFIG: FPGA has been configured prior to Linux booting + * +- * %FPGA_MGR_ENCRYPTED_BITSTREAM: indicates bitstream is encrypted ++ * %FPGA_MGR_ENCRYPTED_BITSTREAM: indicates bitstream is encrypted with ++ * device key + * + * %FPGA_MGR_BITSTREAM_LSB_FIRST: SPI bitstream bit order is LSB first + * + * %FPGA_MGR_COMPRESSED_BITSTREAM: FPGA bitstream is compressed ++ * ++ * %FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM: indicates bitstream is encrypted with ++ * user key + */ + #define FPGA_MGR_PARTIAL_RECONFIG BIT(0) + #define FPGA_MGR_EXTERNAL_CONFIG BIT(1) + #define FPGA_MGR_ENCRYPTED_BITSTREAM BIT(2) + #define FPGA_MGR_BITSTREAM_LSB_FIRST BIT(3) + #define FPGA_MGR_COMPRESSED_BITSTREAM BIT(4) ++#define FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM BIT(5) ++#define FPGA_MGR_DDR_MEM_AUTH_BITSTREAM BIT(6) ++#define FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM BIT(7) ++#define FPGA_MGR_CONFIG_DMA_BUF BIT(5) + + /** + * struct fpga_image_info - information specific to an FPGA image +@@ -86,6 +97,7 @@ enum fpga_mgr_states { + * @config_complete_timeout_us: maximum time for FPGA to switch to operating + * status in the write_complete op. + * @firmware_name: name of FPGA image firmware file ++ * @key: key value useful for Encrypted Bitstream loading to read the userkey + * @sgt: scatter/gather table containing FPGA image + * @buf: contiguous buffer containing FPGA image + * @count: size of buf +@@ -102,6 +114,7 @@ struct fpga_image_info { + u32 disable_timeout_us; + u32 config_complete_timeout_us; + char *firmware_name; ++ char key[ENCRYPTED_KEY_LEN]; + struct sg_table *sgt; + const char *buf; + size_t count; +@@ -160,6 +173,7 @@ struct fpga_manager_info { + * @write: write count bytes of configuration data to the FPGA + * @write_sg: write the scatter list of configuration data to the FPGA + * @write_complete: set FPGA to operating state after writing is done ++ * @read: optional: read FPGA configuration information + * @fpga_remove: optional: Set FPGA into a specific state during driver remove + * @groups: optional attribute groups. + * +@@ -182,6 +196,7 @@ struct fpga_manager_ops { + int (*write_sg)(struct fpga_manager *mgr, struct sg_table *sgt); + int (*write_complete)(struct fpga_manager *mgr, + struct fpga_image_info *info); ++ int (*read)(struct fpga_manager *mgr, struct seq_file *s); + void (*fpga_remove)(struct fpga_manager *mgr); + const struct attribute_group **groups; + }; +@@ -196,21 +211,35 @@ struct fpga_manager_ops { + /** + * struct fpga_manager - fpga manager structure + * @name: name of low level fpga manager ++ * @flags: flags determines the type of Bitstream ++ * @key: key value useful for Encrypted Bitstream loading to read the userkey + * @dev: fpga manager device ++ * @dmabuf: shared dma buffer + * @ref_mutex: only allows one reference to fpga manager ++ * @miscdev: information about character device node + * @state: state of fpga manager + * @compat_id: FPGA manager id for compatibility check. + * @mops: pointer to struct of fpga manager ops + * @priv: low level driver private date ++ * @err: low level driver error code ++ * @dir: debugfs image directory + */ + struct fpga_manager { + const char *name; ++ unsigned long flags; ++ char key[ENCRYPTED_KEY_LEN + 1]; + struct device dev; ++ struct dma_buf *dmabuf; + struct mutex ref_mutex; ++ struct miscdevice miscdev; + enum fpga_mgr_states state; + struct fpga_compat_id *compat_id; + const struct fpga_manager_ops *mops; + void *priv; ++ int err; ++#ifdef CONFIG_FPGA_MGR_DEBUG_FS ++ struct dentry *dir; ++#endif + }; + + #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev) +@@ -244,4 +273,6 @@ struct fpga_manager * + devm_fpga_mgr_register(struct device *parent, const char *name, + const struct fpga_manager_ops *mops, void *priv); + ++#define FPGA_IOCTL_LOAD_DMA_BUFF _IOWR('R', 1, __u32) ++ + #endif /*_LINUX_FPGA_MGR_H */ diff --git a/zynq_image.nix b/zynq_image.nix index 9d1621e..012e50c 100644 --- a/zynq_image.nix +++ b/zynq_image.nix @@ -3,6 +3,16 @@ with lib; let customKernel = (pkgs.linux.override { + kernelPatches = [ + ({ + name = "xilinx-configfs-overlays"; + patch = ./xilinx-configfs-overlays.patch; + }) + ({ + name = "xilinx-fpga-manager"; + patch = ./xilinx-fpga-manager.patch; + }) + ]; extraConfig = '' OVERLAY_FS y MEDIA_SUPPORT n @@ -17,6 +27,7 @@ let OF_FPGA_REGION y FPGA_MGR_ZYNQ_FPGA y OF_OVERLAY y + OF_CONFIGFS y ''; }).overrideAttrs (oa: { postInstall = ''