0001
0002
0003
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
0020
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
0053
0054
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
0075
0076
0077
0078
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
0092 break;
0093 }
0094
0095 if (md_paddr > in_paddr || (in_paddr - md_paddr) >= md_size)
0096 continue;
0097
0098
0099
0100
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
0123
0124
0125
0126
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
0139
0140
0141
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 }