root/drivers/firmware/efi/memattr.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. efi_memattr_init
  2. entry_is_valid
  3. efi_memattr_apply_permissions

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (C) 2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
   4  */
   5 
   6 #define pr_fmt(fmt)     "efi: memattr: " fmt
   7 
   8 #include <linux/efi.h>
   9 #include <linux/init.h>
  10 #include <linux/io.h>
  11 #include <linux/memblock.h>
  12 
  13 #include <asm/early_ioremap.h>
  14 
  15 static int __initdata tbl_size;
  16 
  17 /*
  18  * Reserve the memory associated with the Memory Attributes configuration
  19  * table, if it exists.
  20  */
  21 int __init efi_memattr_init(void)
  22 {
  23         efi_memory_attributes_table_t *tbl;
  24 
  25         if (efi.mem_attr_table == EFI_INVALID_TABLE_ADDR)
  26                 return 0;
  27 
  28         tbl = early_memremap(efi.mem_attr_table, sizeof(*tbl));
  29         if (!tbl) {
  30                 pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
  31                        efi.mem_attr_table);
  32                 return -ENOMEM;
  33         }
  34 
  35         if (tbl->version > 1) {
  36                 pr_warn("Unexpected EFI Memory Attributes table version %d\n",
  37                         tbl->version);
  38                 goto unmap;
  39         }
  40 
  41         tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size;
  42         memblock_reserve(efi.mem_attr_table, tbl_size);
  43         set_bit(EFI_MEM_ATTR, &efi.flags);
  44 
  45 unmap:
  46         early_memunmap(tbl, sizeof(*tbl));
  47         return 0;
  48 }
  49 
  50 /*
  51  * Returns a copy @out of the UEFI memory descriptor @in if it is covered
  52  * entirely by a UEFI memory map entry with matching attributes. The virtual
  53  * address of @out is set according to the matching entry that was found.
  54  */
  55 static bool entry_is_valid(const efi_memory_desc_t *in, efi_memory_desc_t *out)
  56 {
  57         u64 in_paddr = in->phys_addr;
  58         u64 in_size = in->num_pages << EFI_PAGE_SHIFT;
  59         efi_memory_desc_t *md;
  60 
  61         *out = *in;
  62 
  63         if (in->type != EFI_RUNTIME_SERVICES_CODE &&
  64             in->type != EFI_RUNTIME_SERVICES_DATA) {
  65                 pr_warn("Entry type should be RuntimeServiceCode/Data\n");
  66                 return false;
  67         }
  68 
  69         if (!(in->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP))) {
  70                 pr_warn("Entry attributes invalid: RO and XP bits both cleared\n");
  71                 return false;
  72         }
  73 
  74         if (PAGE_SIZE > EFI_PAGE_SIZE &&
  75             (!PAGE_ALIGNED(in->phys_addr) ||
  76              !PAGE_ALIGNED(in->num_pages << EFI_PAGE_SHIFT))) {
  77                 /*
  78                  * Since arm64 may execute with page sizes of up to 64 KB, the
  79                  * UEFI spec mandates that RuntimeServices memory regions must
  80                  * be 64 KB aligned. We need to validate this here since we will
  81                  * not be able to tighten permissions on such regions without
  82                  * affecting adjacent regions.
  83                  */
  84                 pr_warn("Entry address region misaligned\n");
  85                 return false;
  86         }
  87 
  88         for_each_efi_memory_desc(md) {
  89                 u64 md_paddr = md->phys_addr;
  90                 u64 md_size = md->num_pages << EFI_PAGE_SHIFT;
  91 
  92                 if (!(md->attribute & EFI_MEMORY_RUNTIME))
  93                         continue;
  94                 if (md->virt_addr == 0 && md->phys_addr != 0) {
  95                         /* no virtual mapping has been installed by the stub */
  96                         break;
  97                 }
  98 
  99                 if (md_paddr > in_paddr || (in_paddr - md_paddr) >= md_size)
 100                         continue;
 101 
 102                 /*
 103                  * This entry covers the start of @in, check whether
 104                  * it covers the end as well.
 105                  */
 106                 if (md_paddr + md_size < in_paddr + in_size) {
 107                         pr_warn("Entry covers multiple EFI memory map regions\n");
 108                         return false;
 109                 }
 110 
 111                 if (md->type != in->type) {
 112                         pr_warn("Entry type deviates from EFI memory map region type\n");
 113                         return false;
 114                 }
 115 
 116                 out->virt_addr = in_paddr + (md->virt_addr - md_paddr);
 117 
 118                 return true;
 119         }
 120 
 121         pr_warn("No matching entry found in the EFI memory map\n");
 122         return false;
 123 }
 124 
 125 /*
 126  * To be called after the EFI page tables have been populated. If a memory
 127  * attributes table is available, its contents will be used to update the
 128  * mappings with tightened permissions as described by the table.
 129  * This requires the UEFI memory map to have already been populated with
 130  * virtual addresses.
 131  */
 132 int __init efi_memattr_apply_permissions(struct mm_struct *mm,
 133                                          efi_memattr_perm_setter fn)
 134 {
 135         efi_memory_attributes_table_t *tbl;
 136         int i, ret;
 137 
 138         if (tbl_size <= sizeof(*tbl))
 139                 return 0;
 140 
 141         /*
 142          * We need the EFI memory map to be setup so we can use it to
 143          * lookup the virtual addresses of all entries in the  of EFI
 144          * Memory Attributes table. If it isn't available, this
 145          * function should not be called.
 146          */
 147         if (WARN_ON(!efi_enabled(EFI_MEMMAP)))
 148                 return 0;
 149 
 150         tbl = memremap(efi.mem_attr_table, tbl_size, MEMREMAP_WB);
 151         if (!tbl) {
 152                 pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
 153                        efi.mem_attr_table);
 154                 return -ENOMEM;
 155         }
 156 
 157         if (efi_enabled(EFI_DBG))
 158                 pr_info("Processing EFI Memory Attributes table:\n");
 159 
 160         for (i = ret = 0; ret == 0 && i < tbl->num_entries; i++) {
 161                 efi_memory_desc_t md;
 162                 unsigned long size;
 163                 bool valid;
 164                 char buf[64];
 165 
 166                 valid = entry_is_valid((void *)tbl->entry + i * tbl->desc_size,
 167                                        &md);
 168                 size = md.num_pages << EFI_PAGE_SHIFT;
 169                 if (efi_enabled(EFI_DBG) || !valid)
 170                         pr_info("%s 0x%012llx-0x%012llx %s\n",
 171                                 valid ? "" : "!", md.phys_addr,
 172                                 md.phys_addr + size - 1,
 173                                 efi_md_typeattr_format(buf, sizeof(buf), &md));
 174 
 175                 if (valid) {
 176                         ret = fn(mm, &md);
 177                         if (ret)
 178                                 pr_err("Error updating mappings, skipping subsequent md's\n");
 179                 }
 180         }
 181         memunmap(tbl);
 182         return ret;
 183 }

/* [<][>][^][v][top][bottom][index][help] */