Merge tag 'arm64-perf' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
[linux-2.6-block.git] / arch / arm / kernel / efi.c
index ff8a9d8acfaca727517c76bbefe4bbdd4755f73a..9f43ba012d1077ef553617bff01cf002fba70613 100644 (file)
 #include <asm/mach/map.h>
 #include <asm/mmu_context.h>
 
+static int __init set_permissions(pte_t *ptep, pgtable_t token,
+                                 unsigned long addr, void *data)
+{
+       efi_memory_desc_t *md = data;
+       pte_t pte = *ptep;
+
+       if (md->attribute & EFI_MEMORY_RO)
+               pte = set_pte_bit(pte, __pgprot(L_PTE_RDONLY));
+       if (md->attribute & EFI_MEMORY_XP)
+               pte = set_pte_bit(pte, __pgprot(L_PTE_XN));
+       set_pte_ext(ptep, pte, PTE_EXT_NG);
+       return 0;
+}
+
+int __init efi_set_mapping_permissions(struct mm_struct *mm,
+                                      efi_memory_desc_t *md)
+{
+       unsigned long base, size;
+
+       base = md->virt_addr;
+       size = md->num_pages << EFI_PAGE_SHIFT;
+
+       /*
+        * We can only use apply_to_page_range() if we can guarantee that the
+        * entire region was mapped using pages. This should be the case if the
+        * region does not cover any naturally aligned SECTION_SIZE sized
+        * blocks.
+        */
+       if (round_down(base + size, SECTION_SIZE) <
+           round_up(base, SECTION_SIZE) + SECTION_SIZE)
+               return apply_to_page_range(mm, base, size, set_permissions, md);
+
+       return 0;
+}
+
 int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
 {
        struct map_desc desc = {
@@ -34,5 +69,11 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
                desc.type = MT_DEVICE;
 
        create_mapping_late(mm, &desc, true);
+
+       /*
+        * If stricter permissions were specified, apply them now.
+        */
+       if (md->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP))
+               return efi_set_mapping_permissions(mm, md);
        return 0;
 }