diff --git a/MAINTAINERS b/MAINTAINERS
index e592f489e757..9b886ef36587 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7871,6 +7871,12 @@ F:	arch/x86/platform/efi/
 F:	drivers/firmware/efi/
 F:	include/linux/efi*.h
 
+FDT FIRMWARE INTERFACE (FFI)
+M:	Yunhui Cui cuiyunhui@bytedance.com
+S:	Maintained
+F:	drivers/firmware/ffi.c
+F:	include/linux/ffi.h
+
 EXTERNAL CONNECTOR SUBSYSTEM (EXTCON)
 M:	MyungJoo Ham <myungjoo.ham@samsung.com>
 M:	Chanwoo Choi <cw00.choi@samsung.com>
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b59e3041fd62..ea0149fb4683 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -303,6 +303,17 @@ config TURRIS_MOX_RWTM
 	  other manufacturing data and also utilize the Entropy Bit Generator
 	  for hardware random number generation.
 
+config FDT_FW_INTERFACE
+       bool "An interface for passing firmware info through FDT"
+       depends on OF && OF_FLATTREE
+       default n
+       help
+         When some bootloaders do not support EFI, and the arch does not
+         support SMBIOS_ENTRY_POINT_SCAN_START, then you can enable this option
+         to support the transfer of firmware information, such as smbios tables.
+
+         Say Y here if you want to pass firmware information by FDT.
+
 source "drivers/firmware/arm_ffa/Kconfig"
 source "drivers/firmware/broadcom/Kconfig"
 source "drivers/firmware/cirrus/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 28fcddcd688f..3b8b5d0868a6 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -33,6 +33,7 @@ obj-y				+= cirrus/
 obj-y				+= meson/
 obj-$(CONFIG_GOOGLE_FIRMWARE)	+= google/
 obj-y				+= efi/
+obj-$(CONFIG_FDT_FW_INTERFACE)	+= ffi.o
 obj-y				+= imx/
 obj-y				+= psci/
 obj-y				+= smccc/
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 015c95a825d3..64c1ceffe373 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -6,6 +6,7 @@
 #include <linux/ctype.h>
 #include <linux/dmi.h>
 #include <linux/efi.h>
+#include <linux/ffi.h>
 #include <linux/memblock.h>
 #include <linux/random.h>
 #include <asm/dmi.h>
@@ -628,31 +629,56 @@ static int __init dmi_present(const u8 *buf)
 }
 
 /*
- * Check for the SMBIOS 3.0 64-bit entry point signature. Unlike the legacy
- * 32-bit entry point, there is no embedded DMI header (_DMI_) in here.
+ * According to the DMTF SMBIOS reference spec v3.0.0, it is
+ * allowed to define both the 64-bit entry point (smbios3) and
+ * the 32-bit entry point (smbios), in which case they should
+ * either both point to the same SMBIOS structure table, or the
+ * table pointed to by the 64-bit entry point should contain a
+ * superset of the table contents pointed to by the 32-bit entry
+ * point (section 5.2)
+ * This implies that the 64-bit entry point should have
+ * precedence if it is defined and supported by the OS. If we
+ * have the 64-bit entry point, but fail to decode it, fall
+ * back to the legacy one (if available)
  */
-static int __init dmi_smbios3_present(const u8 *buf)
+static int __init dmi_sacn_smbios(unsigned long smbios3, unsigned long smbios)
 {
-	if (memcmp(buf, "_SM3_", 5) == 0 &&
-	    buf[6] >= 24 && buf[6] <= 32 &&
-	    dmi_checksum(buf, buf[6])) {
-		dmi_ver = get_unaligned_be24(buf + 7);
-		dmi_num = 0;			/* No longer specified */
-		dmi_len = get_unaligned_le32(buf + 12);
-		dmi_base = get_unaligned_le64(buf + 16);
-		smbios_entry_point_size = buf[6];
-		memcpy(smbios_entry_point, buf, smbios_entry_point_size);
+	char __iomem *p;
+	char buf[32];
+	#define INVALID_TABLE_ADDR (~0UL)
 
-		if (dmi_walk_early(dmi_decode) == 0) {
-			pr_info("SMBIOS %d.%d.%d present.\n",
-				dmi_ver >> 16, (dmi_ver >> 8) & 0xFF,
-				dmi_ver & 0xFF);
-			dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string));
-			pr_info("DMI: %s\n", dmi_ids_string);
+	if (smbios3 != INVALID_TABLE_ADDR) {
+		p = dmi_early_remap(smbios3, 32);
+		if (p == NULL)
+			return -1;
+		memcpy_fromio(buf, p, 32);
+		dmi_early_unmap(p, 32);
+
+		if (!dmi_smbios3_present(buf)) {
+			dmi_available = 1;
 			return 0;
 		}
 	}
-	return 1;
+
+	if (smbios == INVALID_TABLE_ADDR)
+		return -1;
+
+	/*
+	 * This is called as a core_initcall() because it isn't
+	 * needed during early boot.  This also means we can
+	 * iounmap the space when we're done with it.
+	 */
+	p = dmi_early_remap(smbios, 32);
+	if (p == NULL)
+		return -1;
+	memcpy_fromio(buf, p, 32);
+	dmi_early_unmap(p, 32);
+
+	if (!dmi_present(buf)) {
+		dmi_available = 1;
+		return 0;
+	}
+	return -1;
 }
 
 static void __init dmi_scan_machine(void)
@@ -660,58 +686,22 @@ static void __init dmi_scan_machine(void)
 	char __iomem *p, *q;
 	char buf[32];
 
+#ifdef CONFIG_FDT_FW_INTERFACE
+	if (dmi_sacn_smbios(ffi.smbios3, ffi.smbios))
+		goto error;
+#endif
 	if (efi_enabled(EFI_CONFIG_TABLES)) {
-		/*
-		 * According to the DMTF SMBIOS reference spec v3.0.0, it is
-		 * allowed to define both the 64-bit entry point (smbios3) and
-		 * the 32-bit entry point (smbios), in which case they should
-		 * either both point to the same SMBIOS structure table, or the
-		 * table pointed to by the 64-bit entry point should contain a
-		 * superset of the table contents pointed to by the 32-bit entry
-		 * point (section 5.2)
-		 * This implies that the 64-bit entry point should have
-		 * precedence if it is defined and supported by the OS. If we
-		 * have the 64-bit entry point, but fail to decode it, fall
-		 * back to the legacy one (if available)
-		 */
-		if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) {
-			p = dmi_early_remap(efi.smbios3, 32);
-			if (p == NULL)
-				goto error;
-			memcpy_fromio(buf, p, 32);
-			dmi_early_unmap(p, 32);
-
-			if (!dmi_smbios3_present(buf)) {
-				dmi_available = 1;
-				return;
-			}
-		}
-		if (efi.smbios == EFI_INVALID_TABLE_ADDR)
+		if (dmi_sacn_smbios(efi.smbios3, efi.smbios))
 			goto error;
-
-		/* This is called as a core_initcall() because it isn't
-		 * needed during early boot.  This also means we can
-		 * iounmap the space when we're done with it.
-		 */
-		p = dmi_early_remap(efi.smbios, 32);
-		if (p == NULL)
-			goto error;
-		memcpy_fromio(buf, p, 32);
-		dmi_early_unmap(p, 32);
-
-		if (!dmi_present(buf)) {
-			dmi_available = 1;
-			return;
-		}
 	} else if (IS_ENABLED(CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK)) {
 		p = dmi_early_remap(SMBIOS_ENTRY_POINT_SCAN_START, 0x10000);
 		if (p == NULL)
 			goto error;
 
 		/*
-		 * Same logic as above, look for a 64-bit entry point
-		 * first, and if not found, fall back to 32-bit entry point.
-		 */
+		* Same logic as above, look for a 64-bit entry point
+		* first, and if not found, fall back to 32-bit entry point.
+		*/
 		memcpy_fromio(buf, p, 16);
 		for (q = p + 16; q < p + 0x10000; q += 16) {
 			memcpy_fromio(buf + 16, q, 16);
@@ -724,12 +714,12 @@ static void __init dmi_scan_machine(void)
 		}
 
 		/*
-		 * Iterate over all possible DMI header addresses q.
-		 * Maintain the 32 bytes around q in buf.  On the
-		 * first iteration, substitute zero for the
-		 * out-of-range bytes so there is no chance of falsely
-		 * detecting an SMBIOS header.
-		 */
+		* Iterate over all possible DMI header addresses q.
+		* Maintain the 32 bytes around q in buf.  On the
+		* first iteration, substitute zero for the
+		* out-of-range bytes so there is no chance of falsely
+		* detecting an SMBIOS header.
+		*/
 		memset(buf, 0, 16);
 		for (q = p; q < p + 0x10000; q += 16) {
 			memcpy_fromio(buf + 16, q, 16);
diff --git a/drivers/firmware/ffi.c b/drivers/firmware/ffi.c
new file mode 100644
index 000000000000..169802b4a7a8
--- /dev/null
+++ b/drivers/firmware/ffi.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/libfdt.h>
+#include <linux/ffi.h>
+
+#define FFI_INVALID_TABLE_ADDR	(~0UL)
+
+struct ffi __read_mostly ffi = {
+	.smbios	= FFI_INVALID_TABLE_ADDR,
+	.smbios3 = FFI_INVALID_TABLE_ADDR,
+};
+EXPORT_SYMBOL(ffi);
+
+void __init ffi_smbios_root_pointer(void)
+{
+	int cfgtbl, len;
+	fdt64_t *prop;
+
+	cfgtbl = fdt_path_offset(initial_boot_params, "/cfgtables");
+	if (cfgtbl < 0) {
+		pr_info("firmware table not found.\n");
+		return;
+	}
+	prop = fdt_getprop_w(initial_boot_params, cfgtbl, "smbios_phy_ptr", &len);
+	if (!prop || len != sizeof(u64))
+		pr_info("smbios entry point not found.\n");
+	else
+		ffi.smbios = fdt64_to_cpu(*prop);
+
+	pr_info("smbios root pointer: %lx\n", ffi.smbios);
+}
+
diff --git a/include/linux/ffi.h b/include/linux/ffi.h
new file mode 100644
index 000000000000..95298a805222
--- /dev/null
+++ b/include/linux/ffi.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_FFI_H
+#define _LINUX_FFI_H
+
+extern struct ffi {
+	unsigned long smbios;  /* SMBIOS table (32 bit entry point) */
+	unsigned long smbios3;  /* SMBIOS table (64 bit entry point) */
+	unsigned long flags;
+
+} ffi;
+
+void ffi_smbios_root_pointer(void);
+
+#endif /* _LINUX_FFI_H */
