Merge tag 'xfs-for-linus-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / drivers / firmware / efi / efi.c
index 3a69ed5ecfcb5f1254eaf1b0c3fb53cbdbcc61fb..8730fd475bf3d78d658bd979d2122af3f8c7ddc3 100644 (file)
@@ -24,6 +24,9 @@
 #include <linux/of_fdt.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/ucs2_string.h>
 
 #include <asm/early_ioremap.h>
 
@@ -43,6 +46,7 @@ struct efi __read_mostly efi = {
        .config_table           = EFI_INVALID_TABLE_ADDR,
        .esrt                   = EFI_INVALID_TABLE_ADDR,
        .properties_table       = EFI_INVALID_TABLE_ADDR,
+       .mem_attr_table         = EFI_INVALID_TABLE_ADDR,
 };
 EXPORT_SYMBOL(efi);
 
@@ -194,6 +198,96 @@ 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] __initdata;
+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 __init int efivar_ssdt_iter(efi_char16_t *name, efi_guid_t vendor,
+                                  unsigned long name_size, void *data)
+{
+       struct efivar_entry *entry;
+       struct list_head *list = data;
+       char utf8_name[EFIVAR_SSDT_NAME_MAX];
+       int limit = min_t(unsigned long, EFIVAR_SSDT_NAME_MAX, name_size);
+
+       ucs2_as_utf8(utf8_name, name, limit - 1);
+       if (strncmp(utf8_name, efivar_ssdt, limit) != 0)
+               return 0;
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return 0;
+
+       memcpy(entry->var.VariableName, name, name_size);
+       memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t));
+
+       efivar_entry_add(entry, list);
+
+       return 0;
+}
+
+static __init int efivar_ssdt_load(void)
+{
+       LIST_HEAD(entries);
+       struct efivar_entry *entry, *aux;
+       unsigned long size;
+       void *data;
+       int ret;
+
+       ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries);
+
+       list_for_each_entry_safe(entry, aux, &entries, list) {
+               pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt,
+                       &entry->var.VendorGuid);
+
+               list_del(&entry->list);
+
+               ret = efivar_entry_size(entry, &size);
+               if (ret) {
+                       pr_err("failed to get var size\n");
+                       goto free_entry;
+               }
+
+               data = kmalloc(size, GFP_KERNEL);
+               if (!data)
+                       goto free_entry;
+
+               ret = efivar_entry_get(entry, NULL, &size, data);
+               if (ret) {
+                       pr_err("failed to get var data\n");
+                       goto free_data;
+               }
+
+               ret = acpi_load_table(data);
+               if (ret) {
+                       pr_err("failed to load table: %d\n", ret);
+                       goto free_data;
+               }
+
+               goto free_entry;
+
+free_data:
+               kfree(data);
+
+free_entry:
+               kfree(entry);
+       }
+
+       return ret;
+}
+#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
@@ -217,6 +311,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",
@@ -256,7 +353,7 @@ subsys_initcall(efisubsys_init);
  */
 int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
 {
-       struct efi_memory_map *map = efi.memmap;
+       struct efi_memory_map *map = &efi.memmap;
        phys_addr_t p, e;
 
        if (!efi_enabled(EFI_MEMMAP)) {
@@ -338,6 +435,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
        {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
        {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
        {EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table},
+       {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table},
        {NULL_GUID, NULL, NULL},
 };
 
@@ -351,8 +449,9 @@ static __init int match_config_table(efi_guid_t *guid,
                for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
                        if (!efi_guidcmp(*guid, table_types[i].guid)) {
                                *(table_types[i].ptr) = table;
-                               pr_cont(" %s=0x%lx ",
-                                       table_types[i].name, table);
+                               if (table_types[i].name)
+                                       pr_cont(" %s=0x%lx ",
+                                               table_types[i].name, table);
                                return 1;
                        }
                }
@@ -620,16 +719,12 @@ char * __init efi_md_typeattr_format(char *buf, size_t size,
  */
 u64 __weak efi_mem_attributes(unsigned long phys_addr)
 {
-       struct efi_memory_map *map;
        efi_memory_desc_t *md;
-       void *p;
 
        if (!efi_enabled(EFI_MEMMAP))
                return 0;
 
-       map = efi.memmap;
-       for (p = map->map; p < map->map_end; p += map->desc_size) {
-               md = p;
+       for_each_efi_memory_desc(md) {
                if ((md->phys_addr <= phys_addr) &&
                    (phys_addr < (md->phys_addr +
                    (md->num_pages << EFI_PAGE_SHIFT))))
@@ -637,3 +732,36 @@ u64 __weak efi_mem_attributes(unsigned long phys_addr)
        }
        return 0;
 }
+
+int efi_status_to_err(efi_status_t status)
+{
+       int err;
+
+       switch (status) {
+       case EFI_SUCCESS:
+               err = 0;
+               break;
+       case EFI_INVALID_PARAMETER:
+               err = -EINVAL;
+               break;
+       case EFI_OUT_OF_RESOURCES:
+               err = -ENOSPC;
+               break;
+       case EFI_DEVICE_ERROR:
+               err = -EIO;
+               break;
+       case EFI_WRITE_PROTECTED:
+               err = -EROFS;
+               break;
+       case EFI_SECURITY_VIOLATION:
+               err = -EACCES;
+               break;
+       case EFI_NOT_FOUND:
+               err = -ENOENT;
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+       return err;
+}