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 <panto@antoniou-consulting.com>
++ *
++ * 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 <linux/ctype.h>
++#include <linux/cpu.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_fdt.h>
++#include <linux/spinlock.h>
++#include <linux/sizes.h>
++#include <linux/slab.h>
++#include <linux/proc_fs.h>
++#include <linux/configfs.h>
++#include <linux/types.h>
++#include <linux/stat.h>
++#include <linux/limits.h>
++#include <linux/file.h>
++#include <linux/vmalloc.h>
++#include <linux/firmware.h>
++
++#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..33daffe
--- /dev/null
+++ b/xilinx-fpga-manager.patch
@@ -0,0 +1,676 @@
+# 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 0f4035b089a2..3aa9f5f041f6 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 <linux/dma-buf.h>
++#include <linux/dma-map-ops.h>
++#include <linux/kernel.h>
+ #include <linux/firmware.h>
+ #include <linux/fpga/fpga-mgr.h>
+ #include <linux/idr.h>
+@@ -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);
+@@ -739,6 +871,106 @@ void fpga_mgr_put(struct fpga_manager *mgr)
+ }
+ EXPORT_SYMBOL_GPL(fpga_mgr_put);
+ 
++#ifdef CONFIG_FPGA_MGR_DEBUG_FS
++#include <linux/debugfs.h>
++
++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
+@@ -788,6 +1020,9 @@ __fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *
+ 			 struct module *owner)
+ {
+ 	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;
+ 
+@@ -826,10 +1061,28 @@ __fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *
+ 	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
+@@ -843,6 +1096,28 @@ __fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *
+ 		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:
+@@ -894,6 +1169,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..d2434ed85eff 100644
+--- a/drivers/fpga/zynqmp-fpga.c
++++ b/drivers/fpga/zynqmp-fpga.c
+@@ -43,25 +43,47 @@ 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_DDR_MEM_AUTH_BITSTREAM)
++		eemi_flags |= XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_DDR;
++	else if (priv->flags & FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM)
++		eemi_flags |= XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_OCM;
++
+ 	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 e8b12ec8b060..ffd2dfdb6abd 100644
+--- a/include/linux/firmware/xlnx-zynqmp.h
++++ b/include/linux/firmware/xlnx-zynqmp.h
+@@ -83,6 +83,10 @@
+  */
+ #define XILINX_ZYNQMP_PM_FPGA_FULL	0x0U
+ #define XILINX_ZYNQMP_PM_FPGA_PARTIAL	BIT(0)
++#define XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_DDR	BIT(1)
++#define XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_OCM	BIT(2)
++#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 0d4fe068f3d8..f884d268c974 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 <linux/mutex.h>
++#include <linux/miscdevice.h>
+ #include <linux/platform_device.h>
+ 
++#define ENCRYPTED_KEY_LEN	64 /* Bytes */
++
+ struct fpga_manager;
+ struct sg_table;
+ 
+@@ -66,17 +69,29 @@ 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
++ * %FPGA_MGR_DDR_MEM_AUTH_BITSTREAM: do bitstream authentication using DDR
++ *				     memory if supported
++ * %FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM: do bitstream authentication using secure
++ *					memory if supported
+  */
+ #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 +101,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 +118,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 +177,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 +200,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,23 +215,37 @@ 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
+  * @mops_owner: module containing the mops
+  * @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;
+ 	struct module *mops_owner;
+ 	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)
+@@ -258,4 +291,6 @@ __devm_fpga_mgr_register(struct device *parent, const char *name,
+ 			 const struct fpga_manager_ops *mops, void *priv,
+ 			 struct module *owner);
+ 
++#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 d5c5eda..7ede584 100644
--- a/zynq_image.nix
+++ b/zynq_image.nix
@@ -3,6 +3,16 @@
 with lib;
 let
   customKernel = (pkgs.linux_6_6.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 = ''