diff --git a/drivers/fpga/xrt/include/xroot.h b/drivers/fpga/xrt/include/xroot.h
new file mode 100644
index 000000000000..6ea1b64b3ae3
--- /dev/null
+++ b/drivers/fpga/xrt/include/xroot.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ *	Cheng Zhen <maxz@xilinx.com>
+ */
+
+#ifndef _XRT_ROOT_H_
+#define _XRT_ROOT_H_
+
+struct xroot_range {
+	__be32 child_addr[3];
+	__be32 parent_addr[2];
+	__be32 child_size[2];
+};
+
+struct xroot_info {
+	u32 addr;
+	int num_range;
+	struct xroot_range *ranges;
+};
+
+int xroot_probe(struct device *dev, struct xroot_info *info, void **root);
+void xroot_remove(void *root);
+
+int xroot_create_group(void *xr, void *dtb);
+void xroot_destroy_group(void *xr, u32 grp_id);
+
+#endif	/* _XRT_ROOT_H_ */
diff --git a/drivers/fpga/xrt/lib/Makefile b/drivers/fpga/xrt/lib/Makefile
index f67bb19ef20a..fd2af2cbd1da 100644
--- a/drivers/fpga/xrt/lib/Makefile
+++ b/drivers/fpga/xrt/lib/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_FPGA_XRT_LIB) += xrt-lib.o
 
 xrt-lib-objs :=			\
 	lib-drv.o		\
+	xroot.o			\
 	xrt-bus.dtb.o
 
 ccflags-y := -I$(FULL_XRT_PATH)/include
diff --git a/drivers/fpga/xrt/lib/xroot.c b/drivers/fpga/xrt/lib/xroot.c
new file mode 100644
index 000000000000..34489a0cc10b
--- /dev/null
+++ b/drivers/fpga/xrt/lib/xroot.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo FPGA Root Functions
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ *	Cheng Zhen <maxz@xilinx.com>
+ *	Lizhi Hou <lizhih@xilinx.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/xrt/xdevice.h>
+#include "xroot.h"
+
+#define xroot_err(xr, fmt, args...) dev_err((xr)->dev, "%s: " fmt, __func__, ##args)
+#define xroot_warn(xr, fmt, args...) dev_warn((xr)->dev, "%s: " fmt, __func__, ##args)
+#define xroot_info(xr, fmt, args...) dev_info((xr)->dev, "%s: " fmt, __func__, ##args)
+#define xroot_dbg(xr, fmt, args...) dev_dbg((xr)->dev, "%s: " fmt, __func__, ##args)
+
+struct xroot {
+	struct device *dev;
+	struct list_head groups;
+	u32 addr;
+	struct xroot_range *ranges;
+	int num_range;
+	struct ida grp_ida;
+};
+
+struct xroot_group {
+	struct list_head node;
+	struct xroot *xr;
+	struct xrt_device *grp_dev;
+	struct property ranges;
+	struct of_changeset chgset;
+	bool chgset_applied;
+	void *dn_mem;
+	char *name;
+	int id;
+};
+
+#define XRT_GROUP	"xrt-group"
+#define MAX_GRP_NAME_LEN	64
+
+static void xroot_cleanup_group(struct xroot_group *grp)
+{
+	if (grp->grp_dev)
+		xrt_device_unregister(grp->grp_dev);
+	if (grp->chgset_applied)
+		of_changeset_revert(&grp->chgset);
+	of_changeset_destroy(&grp->chgset);
+
+	if (grp->id >= 0)
+		ida_free(&grp->xr->grp_ida, grp->id);
+	kfree(grp->dn_mem);
+	kfree(grp->name);
+}
+
+void xroot_destroy_group(void *root, u32 grp_id)
+{
+	struct xroot *xr = root;
+	struct xroot_group *grp;
+
+	list_for_each_entry(grp, &xr->groups, node) {
+		if (grp->id == grp_id)
+			break;
+	}
+	if (list_entry_is_head(grp, &xr->groups, node))
+		return;
+
+	list_del(&grp->node);
+
+	xroot_cleanup_group(grp);
+	kfree(grp);
+}
+EXPORT_SYMBOL_GPL(xroot_destroy_group);
+
+/*
+ * Create XRT group device.
+ *
+ * Unflatten the device tree blob attached to the group and
+ * overlay the device nodes under /xrt-bus. Then create group device
+ * and link it to device node.
+ */
+int xroot_create_group(void *root, void *dtb)
+{
+	struct device_node *dn, *bus, *grp_dn;
+	struct xroot *xr = root;
+	struct xroot_group *grp;
+	int ret;
+
+	grp = kzalloc(sizeof(*grp), GFP_KERNEL);
+	if (!grp)
+		return -ENOMEM;
+
+	bus = of_find_node_by_path("/xrt-bus");
+	if (!bus) {
+		kfree(grp);
+		return -EINVAL;
+	}
+	grp->xr = xr;
+	of_changeset_init(&grp->chgset);
+
+	ret = ida_alloc(&xr->grp_ida, GFP_KERNEL);
+	if (ret < 0)
+		goto failed;
+
+	grp->id = ret;
+
+	grp->name = kzalloc(MAX_GRP_NAME_LEN, GFP_KERNEL);
+	if (!grp->name) {
+		ret = -ENOMEM;
+		goto failed;
+	}
+	snprintf(grp->name, MAX_GRP_NAME_LEN, "%s@%x,%x", XRT_GROUP, xr->addr, grp->id);
+
+	grp->dn_mem = of_fdt_unflatten_tree(dtb, NULL, &grp_dn);
+	if (!grp->dn_mem) {
+		ret = -EINVAL;
+		goto failed;
+	}
+
+	of_node_get(grp_dn);
+	grp_dn->full_name = grp->name;
+	grp_dn->parent = bus;
+	for (dn = grp_dn; dn; dn = of_find_all_nodes(dn))
+		of_changeset_attach_node(&grp->chgset, dn);
+
+	grp->ranges.name = "ranges";
+	grp->ranges.length = xr->num_range * sizeof(*xr->ranges);
+	grp->ranges.value = xr->ranges;
+	ret = of_changeset_add_property(&grp->chgset, grp_dn, &grp->ranges);
+	if (ret)
+		goto failed;
+
+	ret = of_changeset_apply(&grp->chgset);
+	if (ret)
+		goto failed;
+	grp->chgset_applied = true;
+
+	of_node_put(bus);
+	bus = NULL;
+
+	grp->grp_dev = xrt_device_register(xr->dev, grp_dn, NULL, 0, NULL, 0);
+	if (!grp->grp_dev) {
+		ret = -EFAULT;
+		goto failed;
+	}
+
+	if (device_attach(&grp->grp_dev->dev) != 1) {
+		ret = -EFAULT;
+		xroot_err(xr, "failed to attach");
+		goto failed;
+	}
+
+	list_add(&grp->node, &xr->groups);
+
+	return grp->id;
+
+failed:
+	if (bus)
+		of_node_put(bus);
+	xroot_cleanup_group(grp);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(xroot_create_group);
+
+int xroot_probe(struct device *dev, struct xroot_info *info, void **root)
+{
+	struct xroot *xr = NULL;
+
+	dev_info(dev, "%s: probing...", __func__);
+
+	xr = devm_kzalloc(dev, sizeof(*xr), GFP_KERNEL);
+	if (!xr)
+		return -ENOMEM;
+
+	xr->dev = dev;
+	INIT_LIST_HEAD(&xr->groups);
+
+	xr->addr = info->addr;
+	xr->num_range = info->num_range;
+	xr->ranges = devm_kzalloc(dev, sizeof(*info->ranges) * info->num_range, GFP_KERNEL);
+	if (!xr->ranges)
+		return -ENOMEM;
+
+	memcpy(&xr->ranges, info->ranges, sizeof(*info->ranges) * info->num_range);
+	ida_init(&xr->grp_ida);
+
+	*root = xr;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(xroot_probe);
+
+void xroot_remove(void *root)
+{
+	struct xroot *xr = (struct xroot *)root;
+	struct xroot_group *grp, *tmp;
+
+	xroot_info(xr, "leaving...");
+	list_for_each_entry_safe(grp, tmp, &xr->groups, node) {
+		list_del(&grp->node);
+		xroot_cleanup_group(grp);
+		kfree(grp);
+	}
+}
+EXPORT_SYMBOL_GPL(xroot_remove);
