summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/acpi/ssdt-overlays.txt67
-rw-r--r--Documentation/kernel-parameters.txt7
-rw-r--r--drivers/firmware/efi/efi.c96
3 files changed, 170 insertions, 0 deletions
diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt
index 805025940248..c4b57badd0b7 100644
--- a/Documentation/acpi/ssdt-overlays.txt
+++ b/Documentation/acpi/ssdt-overlays.txt
@@ -89,3 +89,70 @@ cp ssdt.aml kernel/firmware/acpi
89# on top: 89# on top:
90find kernel | cpio -H newc --create > /boot/instrumented_initrd 90find kernel | cpio -H newc --create > /boot/instrumented_initrd
91cat /boot/initrd >>/boot/instrumented_initrd 91cat /boot/initrd >>/boot/instrumented_initrd
92
93== Loading ACPI SSDTs from EFI variables ==
94
95This is the preferred method, when EFI is supported on the platform, because it
96allows a persistent, OS independent way of storing the user defined SSDTs. There
97is also work underway to implement EFI support for loading user defined SSDTs
98and using this method will make it easier to convert to the EFI loading
99mechanism when that will arrive.
100
101In order to load SSDTs from an EFI variable the efivar_ssdt kernel command line
102parameter can be used. The argument for the option is the variable name to
103use. If there are multiple variables with the same name but with different
104vendor GUIDs, all of them will be loaded.
105
106In order to store the AML code in an EFI variable the efivarfs filesystem can be
107used. It is enabled and mounted by default in /sys/firmware/efi/efivars in all
108recent distribution.
109
110Creating a new file in /sys/firmware/efi/efivars will automatically create a new
111EFI variable. Updating a file in /sys/firmware/efi/efivars will update the EFI
112variable. Please note that the file name needs to be specially formatted as
113"Name-GUID" and that the first 4 bytes in the file (little-endian format)
114represent the attributes of the EFI variable (see EFI_VARIABLE_MASK in
115include/linux/efi.h). Writing to the file must also be done with one write
116operation.
117
118For example, you can use the following bash script to create/update an EFI
119variable with the content from a given file:
120
121#!/bin/sh -e
122
123while ! [ -z "$1" ]; do
124 case "$1" in
125 "-f") filename="$2"; shift;;
126 "-g") guid="$2"; shift;;
127 *) name="$1";;
128 esac
129 shift
130done
131
132usage()
133{
134 echo "Syntax: ${0##*/} -f filename [ -g guid ] name"
135 exit 1
136}
137
138[ -n "$name" -a -f "$filename" ] || usage
139
140EFIVARFS="/sys/firmware/efi/efivars"
141
142[ -d "$EFIVARFS" ] || exit 2
143
144if stat -tf $EFIVARFS | grep -q -v de5e81e4; then
145 mount -t efivarfs none $EFIVARFS
146fi
147
148# try to pick up an existing GUID
149[ -n "$guid" ] || guid=$(find "$EFIVARFS" -name "$name-*" | head -n1 | cut -f2- -d-)
150
151# use a randomly generated GUID
152[ -n "$guid" ] || guid="$(cat /proc/sys/kernel/random/uuid)"
153
154# efivarfs expects all of the data in one write
155tmp=$(mktemp)
156/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp
157dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp)
158rm $tmp
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 82b42c958d1c..bbfb56fe7b92 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.
1185 Address Range Mirroring feature even if your box 1185 Address Range Mirroring feature even if your box
1186 doesn't support it. 1186 doesn't support it.
1187 1187
1188 efivar_ssdt= [EFI; X86] Name of an EFI variable that contains an SSDT
1189 that is to be dynamically loaded by Linux. If there are
1190 multiple variables with the same name but with different
1191 vendor GUIDs, all of them will be loaded. See
1192 Documentation/acpi/ssdt-overlays.txt for details.
1193
1194
1188 eisa_irq_edge= [PARISC,HW] 1195 eisa_irq_edge= [PARISC,HW]
1189 See header of drivers/parisc/eisa.c. 1196 See header of drivers/parisc/eisa.c.
1190 1197
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 05509f3aaee8..8730fd475bf3 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -24,6 +24,9 @@
24#include <linux/of_fdt.h> 24#include <linux/of_fdt.h>
25#include <linux/io.h> 25#include <linux/io.h>
26#include <linux/platform_device.h> 26#include <linux/platform_device.h>
27#include <linux/slab.h>
28#include <linux/acpi.h>
29#include <linux/ucs2_string.h>
27 30
28#include <asm/early_ioremap.h> 31#include <asm/early_ioremap.h>
29 32
@@ -195,6 +198,96 @@ static void generic_ops_unregister(void)
195 efivars_unregister(&generic_efivars); 198 efivars_unregister(&generic_efivars);
196} 199}
197 200
201#if IS_ENABLED(CONFIG_ACPI)
202#define EFIVAR_SSDT_NAME_MAX 16
203static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata;
204static int __init efivar_ssdt_setup(char *str)
205{
206 if (strlen(str) < sizeof(efivar_ssdt))
207 memcpy(efivar_ssdt, str, strlen(str));
208 else
209 pr_warn("efivar_ssdt: name too long: %s\n", str);
210 return 0;
211}
212__setup("efivar_ssdt=", efivar_ssdt_setup);
213
214static __init int efivar_ssdt_iter(efi_char16_t *name, efi_guid_t vendor,
215 unsigned long name_size, void *data)
216{
217 struct efivar_entry *entry;
218 struct list_head *list = data;
219 char utf8_name[EFIVAR_SSDT_NAME_MAX];
220 int limit = min_t(unsigned long, EFIVAR_SSDT_NAME_MAX, name_size);
221
222 ucs2_as_utf8(utf8_name, name, limit - 1);
223 if (strncmp(utf8_name, efivar_ssdt, limit) != 0)
224 return 0;
225
226 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
227 if (!entry)
228 return 0;
229
230 memcpy(entry->var.VariableName, name, name_size);
231 memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t));
232
233 efivar_entry_add(entry, list);
234
235 return 0;
236}
237
238static __init int efivar_ssdt_load(void)
239{
240 LIST_HEAD(entries);
241 struct efivar_entry *entry, *aux;
242 unsigned long size;
243 void *data;
244 int ret;
245
246 ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries);
247
248 list_for_each_entry_safe(entry, aux, &entries, list) {
249 pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt,
250 &entry->var.VendorGuid);
251
252 list_del(&entry->list);
253
254 ret = efivar_entry_size(entry, &size);
255 if (ret) {
256 pr_err("failed to get var size\n");
257 goto free_entry;
258 }
259
260 data = kmalloc(size, GFP_KERNEL);
261 if (!data)
262 goto free_entry;
263
264 ret = efivar_entry_get(entry, NULL, &size, data);
265 if (ret) {
266 pr_err("failed to get var data\n");
267 goto free_data;
268 }
269
270 ret = acpi_load_table(data);
271 if (ret) {
272 pr_err("failed to load table: %d\n", ret);
273 goto free_data;
274 }
275
276 goto free_entry;
277
278free_data:
279 kfree(data);
280
281free_entry:
282 kfree(entry);
283 }
284
285 return ret;
286}
287#else
288static inline int efivar_ssdt_load(void) { return 0; }
289#endif
290
198/* 291/*
199 * We register the efi subsystem with the firmware subsystem and the 292 * We register the efi subsystem with the firmware subsystem and the
200 * efivars subsystem with the efi subsystem, if the system was booted with 293 * efivars subsystem with the efi subsystem, if the system was booted with
@@ -218,6 +311,9 @@ static int __init efisubsys_init(void)
218 if (error) 311 if (error)
219 goto err_put; 312 goto err_put;
220 313
314 if (efi_enabled(EFI_RUNTIME_SERVICES))
315 efivar_ssdt_load();
316
221 error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); 317 error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
222 if (error) { 318 if (error) {
223 pr_err("efi: Sysfs attribute export failed with error %d.\n", 319 pr_err("efi: Sysfs attribute export failed with error %d.\n",