summaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorOctavian Purdila <octavian.purdila@intel.com>2016-07-08 12:13:12 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-07-08 15:52:35 -0400
commit475fb4e8b2f4444d1d7b406ff3a7d21bc89a1e6f (patch)
tree25deca4b6de41e23d3d8abe44c6f6b94493d50f8 /drivers/firmware
parent7f24467f3b357ab6abc146c47fcedd7d57a189b6 (diff)
efi / ACPI: load SSTDs from EFI variables
This patch allows SSDTs to be loaded from EFI variables. It works by specifying the EFI variable name containing the SSDT to be loaded. All variables with the same name (regardless of the vendor GUID) will be loaded. Note that we can't use acpi_install_table and we must rely on the dynamic ACPI table loading and bus re-scanning mechanisms. That is because I2C/SPI controllers are initialized earlier then the EFI subsystems and all I2C/SPI ACPI devices are enumerated when the I2C/SPI controllers are initialized. Signed-off-by: Octavian Purdila <octavian.purdila@intel.com> Reviewed-by: Matt Fleming <matt@codeblueprint.co.uk> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/efi/efi.c96
1 files changed, 96 insertions, 0 deletions
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",