diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index cf6ee7ba6350..d1110358fd45 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -19,6 +19,8 @@ obj-y	+= kexec.o
 endif
 endif
 
+obj-$(CONFIG_KUNIT) += of_kunit.o
+
 DTC_FLAGS_kunit += -@
 obj-$(CONFIG_OF_KUNIT) += kunit.dtbo.o
 obj-$(CONFIG_OF_KUNIT_TEST) += of_test.o
diff --git a/drivers/of/of_kunit.c b/drivers/of/of_kunit.c
new file mode 100644
index 000000000000..59757145cbe6
--- /dev/null
+++ b/drivers/of/of_kunit.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test managed device tree APIs
+ */
+
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+
+#include <kunit/of.h>
+#include <kunit/test.h>
+#include <kunit/resource.h>
+
+struct of_overlay_fdt_apply_kunit_params {
+	void *overlay_fdt;
+	u32 overlay_fdt_size;
+	int *ovcs_id;
+};
+
+static int of_overlay_fdt_apply_kunit_init(struct kunit_resource *res, void *context)
+{
+	struct of_overlay_fdt_apply_kunit_params *params = context;
+	int ret;
+
+	ret = of_overlay_fdt_apply(params->overlay_fdt, params->overlay_fdt_size, params->ovcs_id);
+	if (ret)
+		return ret;
+
+	res->data = (void *)(uintptr_t)(*params->ovcs_id);
+
+	return 0;
+}
+
+static void of_overlay_fdt_apply_kunit_exit(struct kunit_resource *res)
+{
+	int ovcs_id = (uintptr_t)res->data;
+
+	of_overlay_remove(&ovcs_id);
+}
+
+/**
+ * of_overlay_fdt_apply_kunit() - Test managed of_overlay_fdt_apply()
+ * @test: test context
+ * @overlay_fdt: device tree overlay to apply
+ * @overlay_fdt_size: size in bytes of @overlay_fdt
+ * @ovcs_id: identifier of overlay, used to remove the overlay
+ *
+ * Just like `of_overlay_fdt_apply(...)`, except the overlay is managed by the
+ * test case and is automatically removed with `of_overlay_remove(...)` after
+ * the test case concludes.
+ *
+ * Returns: 0 on success, negative errno on failure.
+ */
+int of_overlay_fdt_apply_kunit(struct kunit *test, void *overlay_fdt,
+			       u32 overlay_fdt_size, int *ovcs_id)
+{
+	struct of_overlay_fdt_apply_kunit_params params = {
+		.overlay_fdt = overlay_fdt,
+		.overlay_fdt_size = overlay_fdt_size,
+		.ovcs_id = ovcs_id,
+	};
+
+	if (!IS_ENABLED(CONFIG_OF_OVERLAY))
+		kunit_skip(test, "requires CONFIG_OF_OVERLAY");
+
+	if (!kunit_alloc_resource(test,
+				  of_overlay_fdt_apply_kunit_init,
+				  of_overlay_fdt_apply_kunit_exit,
+				  GFP_KERNEL, &params))
+		return -ENOMEM;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_overlay_fdt_apply_kunit);
+
+/**
+ * __of_overlay_apply_kunit() - Test managed of_overlay_fdt_apply() variant
+ * @test: test context
+ * @overlay_begin: start address of overlay to apply
+ * @overlay_end: end address of overlay to apply
+ *
+ * Similar to `of_overlay_fdt_apply(...)`, except the overlay is managed by the
+ * test case and is automatically removed with `of_overlay_remove(...)` after
+ * the test case concludes.
+ *
+ * Returns: 0 on success, negative errno on failure.
+ */
+int __of_overlay_apply_kunit(struct kunit *test, u8 *overlay_begin,
+			     const u8 *overlay_end)
+{
+	int unused;
+
+	return of_overlay_fdt_apply_kunit(test, overlay_begin,
+					  overlay_end - overlay_begin,
+					  &unused);
+}
+EXPORT_SYMBOL_GPL(__of_overlay_apply_kunit);
+
+static void of_node_put_kunit_exit(struct kunit_resource *res)
+{
+	struct device_node *node = res->data;
+
+	of_node_put(node);
+}
+
+/**
+ * of_node_put_kunit() - Test managed of_node_put()
+ * @test: test context
+ * @node: node to pass to `of_node_put()`
+ *
+ * Just like `of_node_put(...)`, except the node is managed by the test case
+ * and is automatically put with `of_node_put(...)` after the test case
+ * concludes.
+ *
+ * Returns: 0 on success, negative errno on failure.
+ */
+void of_node_put_kunit(struct kunit *test, struct device_node *node)
+{
+	if (!kunit_alloc_resource(test, NULL, of_node_put_kunit_exit,
+				  GFP_KERNEL, node))
+		KUNIT_FAIL(test,
+			   "Can't allocate a kunit resource to put of_node\n");
+}
+EXPORT_SYMBOL_GPL(of_node_put_kunit);
diff --git a/include/kunit/of.h b/include/kunit/of.h
new file mode 100644
index 000000000000..00c43c6e3e67
--- /dev/null
+++ b/include/kunit/of.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _KUNIT_OF_H
+#define _KUNIT_OF_H
+
+#include <kunit/test.h>
+
+struct device_node;
+
+#ifdef CONFIG_OF
+
+int of_overlay_fdt_apply_kunit(struct kunit *test, void *overlay_fdt,
+			       u32 overlay_fdt_size, int *ovcs_id);
+int __of_overlay_apply_kunit(struct kunit *test, u8 *overlay_begin,
+			     const u8 *overlay_end);
+
+void of_node_put_kunit(struct kunit *test, struct device_node *node);
+
+#else
+
+static inline int
+of_overlay_fdt_apply_kunit(struct kunit *test, void *overlay_fdt,
+			   u32 overlay_fdt_size, int *ovcs_id)
+{
+	kunit_skip(test, "requires CONFIG_OF");
+	return -EINVAL;
+}
+
+static inline int
+__of_overlay_apply_kunit(struct kunit *test, u8 *overlay_begin,
+		         const u8 *overlay_end)
+{
+	kunit_skip(test, "requires CONFIG_OF");
+	return -EINVAL;
+}
+
+static inline
+void of_node_put_kunit(struct kunit *test, struct device_node *node)
+{
+	kunit_skip(test, "requires CONFIG_OF");
+}
+
+#endif /* !CONFIG_OF */
+
+/**
+ * of_overlay_apply_kunit() - Test managed of_overlay_fdt_apply() for built-in overlays
+ * @test: test context
+ * @overlay_name: name of overlay to apply
+ *
+ * This macro is used to apply a device tree overlay built with the
+ * cmd_dt_S_dtbo rule in scripts/Makefile.lib that has been compiled into the
+ * kernel image or KUnit test module. The overlay is automatically removed when
+ * the test is finished.
+ *
+ * Unit tests that need device tree nodes should compile an overlay file with
+ * `@overlay_name`.dtbo.o in their Makefile along with their unit test and then
+ * load the overlay during their test. The @overlay_name matches the filename
+ * of the overlay. If CONFIG_OF_OVERLAY is not enabled, the @test will be
+ * skipped.
+ *
+ * .. code-block:: none
+ *
+ *	obj-$(CONFIG_OF_OVERLAY_KUNIT_TEST) += overlay_test.o kunit_overlay_test.dtbo.o
+ *
+ * .. code-block:: c
+ *
+ *	static void of_overlay_kunit_of_overlay_apply(struct kunit *test)
+ *	{
+ *		struct device_node *np;
+ *
+ *		KUNIT_ASSERT_EQ(test, 0,
+ *				of_overlay_apply_kunit(test, kunit_overlay_test));
+ *
+ *		np = of_find_node_by_name(NULL, "test-kunit");
+ *		KUNIT_EXPECT_NOT_ERR_OR_NULL(test, np);
+ *		of_node_put(np);
+ *	}
+ *
+ * Returns: 0 on success, negative errno on failure.
+ */
+#define of_overlay_apply_kunit(test, overlay_name)		\
+({								\
+	extern uint8_t __dtbo_##overlay_name##_begin[];		\
+	extern uint8_t __dtbo_##overlay_name##_end[];		\
+								\
+	__of_overlay_apply_kunit((test),			\
+			__dtbo_##overlay_name##_begin,		\
+			__dtbo_##overlay_name##_end);		\
+})
+
+#endif
