110f0d2f5SArd Biesheuvel /* 210f0d2f5SArd Biesheuvel * Copyright (C) 2016 Linaro Ltd. <ard.biesheuvel@linaro.org> 310f0d2f5SArd Biesheuvel * 410f0d2f5SArd Biesheuvel * This program is free software; you can redistribute it and/or modify 510f0d2f5SArd Biesheuvel * it under the terms of the GNU General Public License version 2 as 610f0d2f5SArd Biesheuvel * published by the Free Software Foundation. 710f0d2f5SArd Biesheuvel */ 810f0d2f5SArd Biesheuvel 910f0d2f5SArd Biesheuvel #define pr_fmt(fmt) "efi: memattr: " fmt 1010f0d2f5SArd Biesheuvel 1110f0d2f5SArd Biesheuvel #include <linux/efi.h> 1210f0d2f5SArd Biesheuvel #include <linux/init.h> 1310f0d2f5SArd Biesheuvel #include <linux/io.h> 1410f0d2f5SArd Biesheuvel #include <linux/memblock.h> 1510f0d2f5SArd Biesheuvel 1610f0d2f5SArd Biesheuvel #include <asm/early_ioremap.h> 1710f0d2f5SArd Biesheuvel 1810f0d2f5SArd Biesheuvel static int __initdata tbl_size; 1910f0d2f5SArd Biesheuvel 2010f0d2f5SArd Biesheuvel /* 2110f0d2f5SArd Biesheuvel * Reserve the memory associated with the Memory Attributes configuration 2210f0d2f5SArd Biesheuvel * table, if it exists. 2310f0d2f5SArd Biesheuvel */ 2410f0d2f5SArd Biesheuvel int __init efi_memattr_init(void) 2510f0d2f5SArd Biesheuvel { 2610f0d2f5SArd Biesheuvel efi_memory_attributes_table_t *tbl; 2710f0d2f5SArd Biesheuvel 2810f0d2f5SArd Biesheuvel if (efi.mem_attr_table == EFI_INVALID_TABLE_ADDR) 2910f0d2f5SArd Biesheuvel return 0; 3010f0d2f5SArd Biesheuvel 3110f0d2f5SArd Biesheuvel tbl = early_memremap(efi.mem_attr_table, sizeof(*tbl)); 3210f0d2f5SArd Biesheuvel if (!tbl) { 3310f0d2f5SArd Biesheuvel pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n", 3410f0d2f5SArd Biesheuvel efi.mem_attr_table); 3510f0d2f5SArd Biesheuvel return -ENOMEM; 3610f0d2f5SArd Biesheuvel } 3710f0d2f5SArd Biesheuvel 3810f0d2f5SArd Biesheuvel if (tbl->version > 1) { 3910f0d2f5SArd Biesheuvel pr_warn("Unexpected EFI Memory Attributes table version %d\n", 4010f0d2f5SArd Biesheuvel tbl->version); 4110f0d2f5SArd Biesheuvel goto unmap; 4210f0d2f5SArd Biesheuvel } 4310f0d2f5SArd Biesheuvel 4410f0d2f5SArd Biesheuvel tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size; 4510f0d2f5SArd Biesheuvel memblock_reserve(efi.mem_attr_table, tbl_size); 46a19ebf59SSai Praneeth set_bit(EFI_MEM_ATTR, &efi.flags); 4710f0d2f5SArd Biesheuvel 4810f0d2f5SArd Biesheuvel unmap: 4910f0d2f5SArd Biesheuvel early_memunmap(tbl, sizeof(*tbl)); 5010f0d2f5SArd Biesheuvel return 0; 5110f0d2f5SArd Biesheuvel } 5210f0d2f5SArd Biesheuvel 5310f0d2f5SArd Biesheuvel /* 5410f0d2f5SArd Biesheuvel * Returns a copy @out of the UEFI memory descriptor @in if it is covered 5510f0d2f5SArd Biesheuvel * entirely by a UEFI memory map entry with matching attributes. The virtual 5610f0d2f5SArd Biesheuvel * address of @out is set according to the matching entry that was found. 5710f0d2f5SArd Biesheuvel */ 5810f0d2f5SArd Biesheuvel static bool entry_is_valid(const efi_memory_desc_t *in, efi_memory_desc_t *out) 5910f0d2f5SArd Biesheuvel { 6010f0d2f5SArd Biesheuvel u64 in_paddr = in->phys_addr; 6110f0d2f5SArd Biesheuvel u64 in_size = in->num_pages << EFI_PAGE_SHIFT; 6210f0d2f5SArd Biesheuvel efi_memory_desc_t *md; 6310f0d2f5SArd Biesheuvel 6410f0d2f5SArd Biesheuvel *out = *in; 6510f0d2f5SArd Biesheuvel 6610f0d2f5SArd Biesheuvel if (in->type != EFI_RUNTIME_SERVICES_CODE && 6710f0d2f5SArd Biesheuvel in->type != EFI_RUNTIME_SERVICES_DATA) { 6810f0d2f5SArd Biesheuvel pr_warn("Entry type should be RuntimeServiceCode/Data\n"); 6910f0d2f5SArd Biesheuvel return false; 7010f0d2f5SArd Biesheuvel } 7110f0d2f5SArd Biesheuvel 7210f0d2f5SArd Biesheuvel if (!(in->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP))) { 7310f0d2f5SArd Biesheuvel pr_warn("Entry attributes invalid: RO and XP bits both cleared\n"); 7410f0d2f5SArd Biesheuvel return false; 7510f0d2f5SArd Biesheuvel } 7610f0d2f5SArd Biesheuvel 7710f0d2f5SArd Biesheuvel if (PAGE_SIZE > EFI_PAGE_SIZE && 7810f0d2f5SArd Biesheuvel (!PAGE_ALIGNED(in->phys_addr) || 7910f0d2f5SArd Biesheuvel !PAGE_ALIGNED(in->num_pages << EFI_PAGE_SHIFT))) { 8010f0d2f5SArd Biesheuvel /* 8110f0d2f5SArd Biesheuvel * Since arm64 may execute with page sizes of up to 64 KB, the 8210f0d2f5SArd Biesheuvel * UEFI spec mandates that RuntimeServices memory regions must 8310f0d2f5SArd Biesheuvel * be 64 KB aligned. We need to validate this here since we will 8410f0d2f5SArd Biesheuvel * not be able to tighten permissions on such regions without 8510f0d2f5SArd Biesheuvel * affecting adjacent regions. 8610f0d2f5SArd Biesheuvel */ 8710f0d2f5SArd Biesheuvel pr_warn("Entry address region misaligned\n"); 8810f0d2f5SArd Biesheuvel return false; 8910f0d2f5SArd Biesheuvel } 9010f0d2f5SArd Biesheuvel 9110f0d2f5SArd Biesheuvel for_each_efi_memory_desc(md) { 9210f0d2f5SArd Biesheuvel u64 md_paddr = md->phys_addr; 9310f0d2f5SArd Biesheuvel u64 md_size = md->num_pages << EFI_PAGE_SHIFT; 9410f0d2f5SArd Biesheuvel 9510f0d2f5SArd Biesheuvel if (!(md->attribute & EFI_MEMORY_RUNTIME)) 9610f0d2f5SArd Biesheuvel continue; 975de0fef0SArd Biesheuvel if (md->virt_addr == 0 && md->phys_addr != 0) { 9810f0d2f5SArd Biesheuvel /* no virtual mapping has been installed by the stub */ 9910f0d2f5SArd Biesheuvel break; 10010f0d2f5SArd Biesheuvel } 10110f0d2f5SArd Biesheuvel 10210f0d2f5SArd Biesheuvel if (md_paddr > in_paddr || (in_paddr - md_paddr) >= md_size) 10310f0d2f5SArd Biesheuvel continue; 10410f0d2f5SArd Biesheuvel 10510f0d2f5SArd Biesheuvel /* 10610f0d2f5SArd Biesheuvel * This entry covers the start of @in, check whether 10710f0d2f5SArd Biesheuvel * it covers the end as well. 10810f0d2f5SArd Biesheuvel */ 10910f0d2f5SArd Biesheuvel if (md_paddr + md_size < in_paddr + in_size) { 11010f0d2f5SArd Biesheuvel pr_warn("Entry covers multiple EFI memory map regions\n"); 11110f0d2f5SArd Biesheuvel return false; 11210f0d2f5SArd Biesheuvel } 11310f0d2f5SArd Biesheuvel 11410f0d2f5SArd Biesheuvel if (md->type != in->type) { 11510f0d2f5SArd Biesheuvel pr_warn("Entry type deviates from EFI memory map region type\n"); 11610f0d2f5SArd Biesheuvel return false; 11710f0d2f5SArd Biesheuvel } 11810f0d2f5SArd Biesheuvel 11910f0d2f5SArd Biesheuvel out->virt_addr = in_paddr + (md->virt_addr - md_paddr); 12010f0d2f5SArd Biesheuvel 12110f0d2f5SArd Biesheuvel return true; 12210f0d2f5SArd Biesheuvel } 12310f0d2f5SArd Biesheuvel 12410f0d2f5SArd Biesheuvel pr_warn("No matching entry found in the EFI memory map\n"); 12510f0d2f5SArd Biesheuvel return false; 12610f0d2f5SArd Biesheuvel } 12710f0d2f5SArd Biesheuvel 12810f0d2f5SArd Biesheuvel /* 12910f0d2f5SArd Biesheuvel * To be called after the EFI page tables have been populated. If a memory 13010f0d2f5SArd Biesheuvel * attributes table is available, its contents will be used to update the 13110f0d2f5SArd Biesheuvel * mappings with tightened permissions as described by the table. 13210f0d2f5SArd Biesheuvel * This requires the UEFI memory map to have already been populated with 13310f0d2f5SArd Biesheuvel * virtual addresses. 13410f0d2f5SArd Biesheuvel */ 13510f0d2f5SArd Biesheuvel int __init efi_memattr_apply_permissions(struct mm_struct *mm, 13610f0d2f5SArd Biesheuvel efi_memattr_perm_setter fn) 13710f0d2f5SArd Biesheuvel { 13810f0d2f5SArd Biesheuvel efi_memory_attributes_table_t *tbl; 13910f0d2f5SArd Biesheuvel int i, ret; 14010f0d2f5SArd Biesheuvel 14110f0d2f5SArd Biesheuvel if (tbl_size <= sizeof(*tbl)) 14210f0d2f5SArd Biesheuvel return 0; 14310f0d2f5SArd Biesheuvel 14410f0d2f5SArd Biesheuvel /* 14510f0d2f5SArd Biesheuvel * We need the EFI memory map to be setup so we can use it to 14610f0d2f5SArd Biesheuvel * lookup the virtual addresses of all entries in the of EFI 14710f0d2f5SArd Biesheuvel * Memory Attributes table. If it isn't available, this 14810f0d2f5SArd Biesheuvel * function should not be called. 14910f0d2f5SArd Biesheuvel */ 15010f0d2f5SArd Biesheuvel if (WARN_ON(!efi_enabled(EFI_MEMMAP))) 15110f0d2f5SArd Biesheuvel return 0; 15210f0d2f5SArd Biesheuvel 15310f0d2f5SArd Biesheuvel tbl = memremap(efi.mem_attr_table, tbl_size, MEMREMAP_WB); 15410f0d2f5SArd Biesheuvel if (!tbl) { 15510f0d2f5SArd Biesheuvel pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n", 15610f0d2f5SArd Biesheuvel efi.mem_attr_table); 15710f0d2f5SArd Biesheuvel return -ENOMEM; 15810f0d2f5SArd Biesheuvel } 15910f0d2f5SArd Biesheuvel 16010f0d2f5SArd Biesheuvel if (efi_enabled(EFI_DBG)) 16110f0d2f5SArd Biesheuvel pr_info("Processing EFI Memory Attributes table:\n"); 16210f0d2f5SArd Biesheuvel 16310f0d2f5SArd Biesheuvel for (i = ret = 0; ret == 0 && i < tbl->num_entries; i++) { 16410f0d2f5SArd Biesheuvel efi_memory_desc_t md; 16510f0d2f5SArd Biesheuvel unsigned long size; 16610f0d2f5SArd Biesheuvel bool valid; 16710f0d2f5SArd Biesheuvel char buf[64]; 16810f0d2f5SArd Biesheuvel 16910f0d2f5SArd Biesheuvel valid = entry_is_valid((void *)tbl->entry + i * tbl->desc_size, 17010f0d2f5SArd Biesheuvel &md); 17110f0d2f5SArd Biesheuvel size = md.num_pages << EFI_PAGE_SHIFT; 17210f0d2f5SArd Biesheuvel if (efi_enabled(EFI_DBG) || !valid) 17310f0d2f5SArd Biesheuvel pr_info("%s 0x%012llx-0x%012llx %s\n", 17410f0d2f5SArd Biesheuvel valid ? "" : "!", md.phys_addr, 17510f0d2f5SArd Biesheuvel md.phys_addr + size - 1, 17610f0d2f5SArd Biesheuvel efi_md_typeattr_format(buf, sizeof(buf), &md)); 17710f0d2f5SArd Biesheuvel 17818141e89SSai Praneeth if (valid) { 17910f0d2f5SArd Biesheuvel ret = fn(mm, &md); 18018141e89SSai Praneeth if (ret) 18118141e89SSai Praneeth pr_err("Error updating mappings, skipping subsequent md's\n"); 18218141e89SSai Praneeth } 18310f0d2f5SArd Biesheuvel } 18410f0d2f5SArd Biesheuvel memunmap(tbl); 18510f0d2f5SArd Biesheuvel return ret; 18610f0d2f5SArd Biesheuvel } 187