Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
0004  */
0005 
0006 #define pr_fmt(fmt) "efi: memattr: " fmt
0007 
0008 #include <linux/efi.h>
0009 #include <linux/init.h>
0010 #include <linux/io.h>
0011 #include <linux/memblock.h>
0012 
0013 #include <asm/early_ioremap.h>
0014 
0015 static int __initdata tbl_size;
0016 unsigned long __ro_after_init efi_mem_attr_table = EFI_INVALID_TABLE_ADDR;
0017 
0018 /*
0019  * Reserve the memory associated with the Memory Attributes configuration
0020  * table, if it exists.
0021  */
0022 int __init efi_memattr_init(void)
0023 {
0024     efi_memory_attributes_table_t *tbl;
0025 
0026     if (efi_mem_attr_table == EFI_INVALID_TABLE_ADDR)
0027         return 0;
0028 
0029     tbl = early_memremap(efi_mem_attr_table, sizeof(*tbl));
0030     if (!tbl) {
0031         pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
0032                efi_mem_attr_table);
0033         return -ENOMEM;
0034     }
0035 
0036     if (tbl->version > 1) {
0037         pr_warn("Unexpected EFI Memory Attributes table version %d\n",
0038             tbl->version);
0039         goto unmap;
0040     }
0041 
0042     tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size;
0043     memblock_reserve(efi_mem_attr_table, tbl_size);
0044     set_bit(EFI_MEM_ATTR, &efi.flags);
0045 
0046 unmap:
0047     early_memunmap(tbl, sizeof(*tbl));
0048     return 0;
0049 }
0050 
0051 /*
0052  * Returns a copy @out of the UEFI memory descriptor @in if it is covered
0053  * entirely by a UEFI memory map entry with matching attributes. The virtual
0054  * address of @out is set according to the matching entry that was found.
0055  */
0056 static bool entry_is_valid(const efi_memory_desc_t *in, efi_memory_desc_t *out)
0057 {
0058     u64 in_paddr = in->phys_addr;
0059     u64 in_size = in->num_pages << EFI_PAGE_SHIFT;
0060     efi_memory_desc_t *md;
0061 
0062     *out = *in;
0063 
0064     if (in->type != EFI_RUNTIME_SERVICES_CODE &&
0065         in->type != EFI_RUNTIME_SERVICES_DATA) {
0066         pr_warn("Entry type should be RuntimeServiceCode/Data\n");
0067         return false;
0068     }
0069 
0070     if (PAGE_SIZE > EFI_PAGE_SIZE &&
0071         (!PAGE_ALIGNED(in->phys_addr) ||
0072          !PAGE_ALIGNED(in->num_pages << EFI_PAGE_SHIFT))) {
0073         /*
0074          * Since arm64 may execute with page sizes of up to 64 KB, the
0075          * UEFI spec mandates that RuntimeServices memory regions must
0076          * be 64 KB aligned. We need to validate this here since we will
0077          * not be able to tighten permissions on such regions without
0078          * affecting adjacent regions.
0079          */
0080         pr_warn("Entry address region misaligned\n");
0081         return false;
0082     }
0083 
0084     for_each_efi_memory_desc(md) {
0085         u64 md_paddr = md->phys_addr;
0086         u64 md_size = md->num_pages << EFI_PAGE_SHIFT;
0087 
0088         if (!(md->attribute & EFI_MEMORY_RUNTIME))
0089             continue;
0090         if (md->virt_addr == 0 && md->phys_addr != 0) {
0091             /* no virtual mapping has been installed by the stub */
0092             break;
0093         }
0094 
0095         if (md_paddr > in_paddr || (in_paddr - md_paddr) >= md_size)
0096             continue;
0097 
0098         /*
0099          * This entry covers the start of @in, check whether
0100          * it covers the end as well.
0101          */
0102         if (md_paddr + md_size < in_paddr + in_size) {
0103             pr_warn("Entry covers multiple EFI memory map regions\n");
0104             return false;
0105         }
0106 
0107         if (md->type != in->type) {
0108             pr_warn("Entry type deviates from EFI memory map region type\n");
0109             return false;
0110         }
0111 
0112         out->virt_addr = in_paddr + (md->virt_addr - md_paddr);
0113 
0114         return true;
0115     }
0116 
0117     pr_warn("No matching entry found in the EFI memory map\n");
0118     return false;
0119 }
0120 
0121 /*
0122  * To be called after the EFI page tables have been populated. If a memory
0123  * attributes table is available, its contents will be used to update the
0124  * mappings with tightened permissions as described by the table.
0125  * This requires the UEFI memory map to have already been populated with
0126  * virtual addresses.
0127  */
0128 int __init efi_memattr_apply_permissions(struct mm_struct *mm,
0129                      efi_memattr_perm_setter fn)
0130 {
0131     efi_memory_attributes_table_t *tbl;
0132     int i, ret;
0133 
0134     if (tbl_size <= sizeof(*tbl))
0135         return 0;
0136 
0137     /*
0138      * We need the EFI memory map to be setup so we can use it to
0139      * lookup the virtual addresses of all entries in the  of EFI
0140      * Memory Attributes table. If it isn't available, this
0141      * function should not be called.
0142      */
0143     if (WARN_ON(!efi_enabled(EFI_MEMMAP)))
0144         return 0;
0145 
0146     tbl = memremap(efi_mem_attr_table, tbl_size, MEMREMAP_WB);
0147     if (!tbl) {
0148         pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
0149                efi_mem_attr_table);
0150         return -ENOMEM;
0151     }
0152 
0153     if (efi_enabled(EFI_DBG))
0154         pr_info("Processing EFI Memory Attributes table:\n");
0155 
0156     for (i = ret = 0; ret == 0 && i < tbl->num_entries; i++) {
0157         efi_memory_desc_t md;
0158         unsigned long size;
0159         bool valid;
0160         char buf[64];
0161 
0162         valid = entry_is_valid((void *)tbl->entry + i * tbl->desc_size,
0163                        &md);
0164         size = md.num_pages << EFI_PAGE_SHIFT;
0165         if (efi_enabled(EFI_DBG) || !valid)
0166             pr_info("%s 0x%012llx-0x%012llx %s\n",
0167                 valid ? "" : "!", md.phys_addr,
0168                 md.phys_addr + size - 1,
0169                 efi_md_typeattr_format(buf, sizeof(buf), &md));
0170 
0171         if (valid) {
0172             ret = fn(mm, &md);
0173             if (ret)
0174                 pr_err("Error updating mappings, skipping subsequent md's\n");
0175         }
0176     }
0177     memunmap(tbl);
0178     return ret;
0179 }