diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt
index 8050259..c4b57ba 100644
--- a/Documentation/acpi/ssdt-overlays.txt
+++ b/Documentation/acpi/ssdt-overlays.txt
@@ -89,3 +89,70 @@ cp ssdt.aml kernel/firmware/acpi
 # on top:
 find kernel | cpio -H newc --create > /boot/instrumented_initrd
 cat /boot/initrd >>/boot/instrumented_initrd
+
+== Loading ACPI SSDTs from EFI variables ==
+
+This is the preferred method, when EFI is supported on the platform, because it
+allows a persistent, OS independent way of storing the user defined SSDTs. There
+is also work underway to implement EFI support for loading user defined SSDTs
+and using this method will make it easier to convert to the EFI loading
+mechanism when that will arrive.
+
+In order to load SSDTs from an EFI variable the efivar_ssdt kernel command line
+parameter can be used. The argument for the option is the variable name to
+use. If there are multiple variables with the same name but with different
+vendor GUIDs, all of them will be loaded.
+
+In order to store the AML code in an EFI variable the efivarfs filesystem can be
+used. It is enabled and mounted by default in /sys/firmware/efi/efivars in all
+recent distribution.
+
+Creating a new file in /sys/firmware/efi/efivars will automatically create a new
+EFI variable. Updating a file in /sys/firmware/efi/efivars will update the EFI
+variable. Please note that the file name needs to be specially formatted as
+"Name-GUID" and that the first 4 bytes in the file (little-endian format)
+represent the attributes of the EFI variable (see EFI_VARIABLE_MASK in
+include/linux/efi.h). Writing to the file must also be done with one write
+operation.
+
+For example, you can use the following bash script to create/update an EFI
+variable with the content from a given file:
+
+#!/bin/sh -e
+
+while ! [ -z "$1" ]; do
+        case "$1" in
+        "-f") filename="$2"; shift;;
+        "-g") guid="$2"; shift;;
+        *) name="$1";;
+        esac
+        shift
+done
+
+usage()
+{
+        echo "Syntax: ${0##*/} -f filename [ -g guid ] name"
+        exit 1
+}
+
+[ -n "$name" -a -f "$filename" ] || usage
+
+EFIVARFS="/sys/firmware/efi/efivars"
+
+[ -d "$EFIVARFS" ] || exit 2
+
+if stat -tf $EFIVARFS | grep -q -v de5e81e4; then
+        mount -t efivarfs none $EFIVARFS
+fi
+
+# try to pick up an existing GUID
+[ -n "$guid" ] || guid=$(find "$EFIVARFS" -name "$name-*" | head -n1 | cut -f2- -d-)
+
+# use a randomly generated GUID
+[ -n "$guid" ] || guid="$(cat /proc/sys/kernel/random/uuid)"
+
+# efivarfs expects all of the data in one write
+tmp=$(mktemp)
+/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp
+dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp)
+rm $tmp
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 82b42c9..bbfb56f 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1185,6 +1185,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 			Address Range Mirroring feature even if your box
 			doesn't support it.
 
+	efivar_ssdt=	[EFI; X86] Name of an EFI variable that contains an SSDT
+			that is to be dynamically loaded by Linux. If there are
+			multiple variables with the same name but with different
+			vendor GUIDs, all of them will be loaded. See
+			Documentation/acpi/ssdt-overlays.txt for details.
+
+
 	eisa_irq_edge=	[PARISC,HW]
 			See header of drivers/parisc/eisa.c.
 
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 05509f3..b68cb8d 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -24,6 +24,8 @@
 #include <linux/of_fdt.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
 
 #include <asm/early_ioremap.h>
 
@@ -195,6 +197,107 @@ static void generic_ops_unregister(void)
 	efivars_unregister(&generic_efivars);
 }
 
+#if IS_ENABLED(CONFIG_ACPI)
+#define EFIVAR_SSDT_NAME_MAX	16
+static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX];
+static int __init efivar_ssdt_setup(char *str)
+{
+	if (strlen(str) < sizeof(efivar_ssdt))
+		memcpy(efivar_ssdt, str, strlen(str));
+	else
+		pr_warn("efivar_ssdt: name too long: %s\n", str);
+	return 0;
+}
+__setup("efivar_ssdt=", efivar_ssdt_setup);
+
+static LIST_HEAD(efivar_ssdts);
+
+static inline void pr_efivar_name(efi_char16_t *name16)
+{
+	char name[EFIVAR_SSDT_NAME_MAX];
+	int i;
+
+	for (i = 0; i < EFIVAR_SSDT_NAME_MAX - 1; i++)
+		name[i] = name16[i] & 0xFF;
+	name[i] = 0;
+	pr_cont("%s", name);
+}
+
+static __init int efivar_acpi_iter(efi_char16_t *name, efi_guid_t vendor,
+				   unsigned long name_size, void *data)
+{
+	int i;
+	int str_len = name_size / sizeof(efi_char16_t);
+	struct efivar_entry *entry;
+
+	if (str_len != strlen(efivar_ssdt) + 1)
+		return 0;
+
+	for (i = 0; i < str_len; i++)
+		if ((name[i] & 0xFF) != efivar_ssdt[i])
+			return 0;
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	memcpy(entry->var.VariableName, name, name_size);
+	memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t));
+
+	efivar_entry_add(entry, &efivar_ssdts);
+
+	return 0;
+}
+
+static __init int efivar_ssdt_load(void)
+{
+	struct efivar_entry *i;
+	int err;
+
+	err = efivar_init(efivar_acpi_iter, NULL, true, &efivar_ssdts);
+	if (err) {
+		pr_err("%s: efivar_init failed: %d\n", __func__, err);
+		return err;
+	}
+
+	list_for_each_entry(i, &efivar_ssdts, list) {
+		void *data;
+		unsigned long size;
+
+		pr_info("loading SSDT from EFI variable ");
+		pr_efivar_name(i->var.VariableName); pr_cont("\n");
+
+		err = efivar_entry_size(i, &size);
+		if (err) {
+			pr_err("failed to get size\n");
+			continue;
+		}
+
+		data = kmalloc(size, GFP_KERNEL);
+		if (!data)
+			continue;
+
+		err = efivar_entry_get(i, NULL, &size, data);
+		if (err) {
+			pr_err("failed to get data\n");
+			kfree(data);
+			continue;
+		}
+
+		err = acpi_load_table(data);
+		if (err) {
+			pr_err("failed to load table: %d\n", err);
+			kfree(data);
+			continue;
+		}
+	}
+
+	return 0;
+}
+#else
+static inline int efivar_ssdt_load(void) { return 0; }
+#endif
+
 /*
  * We register the efi subsystem with the firmware subsystem and the
  * efivars subsystem with the efi subsystem, if the system was booted with
@@ -218,6 +321,9 @@ static int __init efisubsys_init(void)
 	if (error)
 		goto err_put;
 
+	if (efi_enabled(EFI_RUNTIME_SERVICES))
+		efivar_ssdt_load();
+
 	error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
 	if (error) {
 		pr_err("efi: Sysfs attribute export failed with error %d.\n",
