nix-servo/not-os-patches/pr-33.patch

1093 lines
31 KiB
Diff
Raw Normal View History

2024-03-14 13:03:09 +08:00
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..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 <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);
+@@ -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 <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
+@@ -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 <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,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 = ''