diff --git a/flake.nix b/flake.nix index c191b67..45cbf69 100644 --- a/flake.nix +++ b/flake.nix @@ -23,6 +23,7 @@ ./not-os-patches/pr-29.patch ./not-os-patches/pr-30.patch ./not-os-patches/pr-31.patch + ./not-os-patches/pr-33.patch ]; }; diff --git a/not-os-patches/pr-33.patch b/not-os-patches/pr-33.patch new file mode 100644 index 0000000..de989a1 --- /dev/null +++ b/not-os-patches/pr-33.patch @@ -0,0 +1,1092 @@ +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 e2e3871..2decd54 100644 +--- a/zynq_image.nix ++++ b/zynq_image.nix +@@ -18,6 +18,16 @@ let + }; + }; + customKernel = (crosspkgs.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 +@@ -32,6 +42,7 @@ let + OF_FPGA_REGION y + FPGA_MGR_ZYNQ_FPGA y + OF_OVERLAY y ++ OF_CONFIGFS y + ''; + }).overrideAttrs (oa: { + postInstall = ''