x86/efi: Add support for EFI_MEMORY_ATTRIBUTES_TABLE
authorSai Praneeth <sai.praneeth.prakhya@intel.com>
Tue, 31 Jan 2017 13:21:37 +0000 (13:21 +0000)
committerIngo Molnar <mingo@kernel.org>
Wed, 1 Feb 2017 07:45:44 +0000 (08:45 +0100)
UEFI v2.6 introduces EFI_MEMORY_ATTRIBUTES_TABLE which describes memory
protections that may be applied to the EFI Runtime code and data regions by
the kernel. This enables the kernel to map these regions more strictly thereby
increasing security.

Presently, the only valid bits for the attribute field of a memory descriptor
are EFI_MEMORY_RO and EFI_MEMORY_XP, hence use these bits to update the
mappings in efi_pgd.

The UEFI specification recommends to use this feature instead of
EFI_PROPERTIES_TABLE and hence while updating EFI mappings we first
check for EFI_MEMORY_ATTRIBUTES_TABLE and if it's present we update
the mappings according to this table and hence disregarding
EFI_PROPERTIES_TABLE even if it's published by the firmware. We consider
EFI_PROPERTIES_TABLE only when EFI_MEMORY_ATTRIBUTES_TABLE is absent.

Signed-off-by: Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>
Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: Lee, Chun-Yi <jlee@suse.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Shankar <ravi.v.shankar@intel.com>
Cc: Ricardo Neri <ricardo.neri@intel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-efi@vger.kernel.org
Link: http://lkml.kernel.org/r/1485868902-20401-6-git-send-email-ard.biesheuvel@linaro.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/platform/efi/efi_64.c
drivers/firmware/efi/memattr.c

index 2f25a363068cf9723e8b418e8c1942a6d3ca4029..a4695da42d77b39cec3b65083c7b7115b1faa32d 100644 (file)
@@ -414,10 +414,44 @@ void __init parse_efi_setup(u64 phys_addr, u32 data_len)
        efi_setup = phys_addr + sizeof(struct setup_data);
 }
 
-void __init efi_runtime_update_mappings(void)
+static int __init efi_update_mappings(efi_memory_desc_t *md, unsigned long pf)
 {
        unsigned long pfn;
        pgd_t *pgd = efi_pgd;
+       int err1, err2;
+
+       /* Update the 1:1 mapping */
+       pfn = md->phys_addr >> PAGE_SHIFT;
+       err1 = kernel_map_pages_in_pgd(pgd, pfn, md->phys_addr, md->num_pages, pf);
+       if (err1) {
+               pr_err("Error while updating 1:1 mapping PA 0x%llx -> VA 0x%llx!\n",
+                          md->phys_addr, md->virt_addr);
+       }
+
+       err2 = kernel_map_pages_in_pgd(pgd, pfn, md->virt_addr, md->num_pages, pf);
+       if (err2) {
+               pr_err("Error while updating VA mapping PA 0x%llx -> VA 0x%llx!\n",
+                          md->phys_addr, md->virt_addr);
+       }
+
+       return err1 || err2;
+}
+
+static int __init efi_update_mem_attr(struct mm_struct *mm, efi_memory_desc_t *md)
+{
+       unsigned long pf = 0;
+
+       if (md->attribute & EFI_MEMORY_XP)
+               pf |= _PAGE_NX;
+
+       if (!(md->attribute & EFI_MEMORY_RO))
+               pf |= _PAGE_RW;
+
+       return efi_update_mappings(md, pf);
+}
+
+void __init efi_runtime_update_mappings(void)
+{
        efi_memory_desc_t *md;
 
        if (efi_enabled(EFI_OLD_MEMMAP)) {
@@ -426,6 +460,24 @@ void __init efi_runtime_update_mappings(void)
                return;
        }
 
+       /*
+        * Use the EFI Memory Attribute Table for mapping permissions if it
+        * exists, since it is intended to supersede EFI_PROPERTIES_TABLE.
+        */
+       if (efi_enabled(EFI_MEM_ATTR)) {
+               efi_memattr_apply_permissions(NULL, efi_update_mem_attr);
+               return;
+       }
+
+       /*
+        * EFI_MEMORY_ATTRIBUTES_TABLE is intended to replace
+        * EFI_PROPERTIES_TABLE. So, use EFI_PROPERTIES_TABLE to update
+        * permissions only if EFI_MEMORY_ATTRIBUTES_TABLE is not
+        * published by the firmware. Even if we find a buggy implementation of
+        * EFI_MEMORY_ATTRIBUTES_TABLE, don't fall back to
+        * EFI_PROPERTIES_TABLE, because of the same reason.
+        */
+
        if (!efi_enabled(EFI_NX_PE_DATA))
                return;
 
@@ -446,15 +498,7 @@ void __init efi_runtime_update_mappings(void)
                        (md->type != EFI_RUNTIME_SERVICES_CODE))
                        pf |= _PAGE_RW;
 
-               /* Update the 1:1 mapping */
-               pfn = md->phys_addr >> PAGE_SHIFT;
-               if (kernel_map_pages_in_pgd(pgd, pfn, md->phys_addr, md->num_pages, pf))
-                       pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n",
-                                  md->phys_addr, md->virt_addr);
-
-               if (kernel_map_pages_in_pgd(pgd, pfn, md->virt_addr, md->num_pages, pf))
-                       pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n",
-                                  md->phys_addr, md->virt_addr);
+               efi_update_mappings(md, pf);
        }
 }
 
index 4021974605070bed12969894f702720a5c163e5b..8986757eafafa29dbf287b9e45b4de592cbc9570 100644 (file)
@@ -175,8 +175,11 @@ int __init efi_memattr_apply_permissions(struct mm_struct *mm,
                                md.phys_addr + size - 1,
                                efi_md_typeattr_format(buf, sizeof(buf), &md));
 
-               if (valid)
+               if (valid) {
                        ret = fn(mm, &md);
+                       if (ret)
+                               pr_err("Error updating mappings, skipping subsequent md's\n");
+               }
        }
        memunmap(tbl);
        return ret;