summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/firmware/efi/capsule-loader.c2
-rw-r--r--drivers/firmware/efi/efi-bgrt.c7
-rw-r--r--drivers/firmware/efi/efi.c31
-rw-r--r--drivers/firmware/efi/libstub/fdt.c12
-rw-r--r--drivers/firmware/efi/memattr.c37
-rw-r--r--include/linux/efi.h21
-rw-r--r--include/linux/memblock.h1
-rw-r--r--mm/memblock.c15
8 files changed, 68 insertions, 58 deletions
diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c
index 2c628a127091..8e8f81f0a5a0 100644
--- a/drivers/firmware/efi/capsule-loader.c
+++ b/drivers/firmware/efi/capsule-loader.c
@@ -67,7 +67,7 @@ int __efi_capsule_setup_info(struct capsule_info *cap_info)
cap_info->pages = temp_page;
temp_page = krealloc(cap_info->phys,
- pages_needed * sizeof(phys_addr_t *),
+ pages_needed * sizeof(phys_addr_t),
GFP_KERNEL | __GFP_ZERO);
if (!temp_page)
return -ENOMEM;
diff --git a/drivers/firmware/efi/efi-bgrt.c b/drivers/firmware/efi/efi-bgrt.c
index 6aafdb67dbca..1da451582812 100644
--- a/drivers/firmware/efi/efi-bgrt.c
+++ b/drivers/firmware/efi/efi-bgrt.c
@@ -29,11 +29,12 @@ void __init efi_bgrt_init(struct acpi_table_header *table)
void *image;
struct bmp_header bmp_header;
struct acpi_table_bgrt *bgrt = &bgrt_tab;
+ int mem_type;
if (acpi_disabled)
return;
- if (!efi_enabled(EFI_MEMMAP))
+ if (!efi_enabled(EFI_MEMMAP) && !efi_enabled(EFI_PARAVIRT))
return;
if (table->length < sizeof(bgrt_tab)) {
@@ -62,7 +63,9 @@ void __init efi_bgrt_init(struct acpi_table_header *table)
goto out;
}
- if (efi_mem_type(bgrt->image_address) != EFI_BOOT_SERVICES_DATA) {
+ mem_type = efi_mem_type(bgrt->image_address);
+ if (mem_type != EFI_BOOT_SERVICES_DATA &&
+ mem_type != EFI_ACPI_RECLAIM_MEMORY) {
pr_notice("Ignoring BGRT: invalid image address\n");
goto out;
}
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 3dab284a7754..d04be38f1750 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -600,7 +600,9 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size)
return;
if (!memblock_is_region_reserved(addr, size))
- memblock_reserve(addr, size);
+ memblock_reserve_kern(addr, size);
+ else
+ memblock_reserved_mark_kern(addr, size);
/*
* Some architectures (x86) reserve all boot services ranges
@@ -983,18 +985,12 @@ char * __init efi_md_typeattr_format(char *buf, size_t size,
*/
u64 efi_mem_attributes(unsigned long phys_addr)
{
- efi_memory_desc_t *md;
+ efi_memory_desc_t md;
- if (!efi_enabled(EFI_MEMMAP))
+ if (efi_mem_desc_lookup(phys_addr, &md))
return 0;
- for_each_efi_memory_desc(md) {
- if ((md->phys_addr <= phys_addr) &&
- (phys_addr < (md->phys_addr +
- (md->num_pages << EFI_PAGE_SHIFT))))
- return md->attribute;
- }
- return 0;
+ return md.attribute;
}
/*
@@ -1007,18 +1003,15 @@ u64 efi_mem_attributes(unsigned long phys_addr)
*/
int efi_mem_type(unsigned long phys_addr)
{
- const efi_memory_desc_t *md;
+ efi_memory_desc_t md;
- if (!efi_enabled(EFI_MEMMAP))
+ if (!efi_enabled(EFI_MEMMAP) && !efi_enabled(EFI_PARAVIRT))
return -ENOTSUPP;
- for_each_efi_memory_desc(md) {
- if ((md->phys_addr <= phys_addr) &&
- (phys_addr < (md->phys_addr +
- (md->num_pages << EFI_PAGE_SHIFT))))
- return md->type;
- }
- return -EINVAL;
+ if (efi_mem_desc_lookup(phys_addr, &md))
+ return -EINVAL;
+
+ return md.type;
}
int efi_status_to_err(efi_status_t status)
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
index 6a337f1f8787..23b3543d3041 100644
--- a/drivers/firmware/efi/libstub/fdt.c
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -32,8 +32,8 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
{
int node, num_rsv;
int status;
- u32 fdt_val32;
- u64 fdt_val64;
+ fdt32_t fdt_val32;
+ fdt64_t fdt_val64;
/* Do some checks on provided FDT, if it exists: */
if (orig_fdt) {
@@ -100,13 +100,13 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
if (status)
goto fdt_set_fail;
- fdt_val64 = U64_MAX; /* placeholder */
+ fdt_val64 = cpu_to_fdt64(U64_MAX); /* placeholder */
status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-start", fdt_val64);
if (status)
goto fdt_set_fail;
- fdt_val32 = U32_MAX; /* placeholder */
+ fdt_val32 = cpu_to_fdt32(U32_MAX); /* placeholder */
status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-size", fdt_val32);
if (status)
@@ -147,8 +147,8 @@ fdt_set_fail:
static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
{
int node = fdt_path_offset(fdt, "/chosen");
- u64 fdt_val64;
- u32 fdt_val32;
+ fdt64_t fdt_val64;
+ fdt32_t fdt_val32;
int err;
if (node < 0)
diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c
index e727cc5909cb..5edbffd4e4b7 100644
--- a/drivers/firmware/efi/memattr.c
+++ b/drivers/firmware/efi/memattr.c
@@ -22,7 +22,6 @@ unsigned long __ro_after_init efi_mem_attr_table = EFI_INVALID_TABLE_ADDR;
void __init efi_memattr_init(void)
{
efi_memory_attributes_table_t *tbl;
- unsigned long size;
if (efi_mem_attr_table == EFI_INVALID_TABLE_ADDR)
return;
@@ -40,22 +39,42 @@ void __init efi_memattr_init(void)
goto unmap;
}
+ /*
+ * The EFI memory attributes table descriptors might potentially be
+ * smaller than those used by the EFI memory map, as long as they can
+ * fit a efi_memory_desc_t. However, a larger descriptor size makes no
+ * sense, and might be an indication that the table is corrupted.
+ *
+ * The only exception is kexec_load(), where the EFI memory map is
+ * reconstructed by user space, and may use a smaller descriptor size
+ * than the original. Given that, ignoring this companion table is
+ * still the right thing to do here, but don't complain too loudly when
+ * this happens.
+ */
+ if (tbl->desc_size < sizeof(efi_memory_desc_t) ||
+ tbl->desc_size > efi.memmap.desc_size) {
+ pr_warn("Unexpected EFI Memory Attributes descriptor size %u (expected: %lu)\n",
+ tbl->desc_size, efi.memmap.desc_size);
+ goto unmap;
+ }
/*
- * Sanity check: the Memory Attributes Table contains up to 3 entries
- * for each entry of type EfiRuntimeServicesCode in the EFI memory map.
- * So if the size of the table exceeds 3x the size of the entire EFI
- * memory map, there is clearly something wrong, and the table should
- * just be ignored altogether.
+ * Sanity check: the Memory Attributes Table contains multiple entries
+ * for each EFI runtime services code or data region in the EFI memory
+ * map, each with the permission attributes that may be applied when
+ * mapping the region. There is no upper bound for the number of
+ * entries, as it could conceivably contain more entries than the EFI
+ * memory map itself. So pick an arbitrary limit of 64k, which is
+ * ludicrously high. This prevents a corrupted table from eating all
+ * system RAM.
*/
- size = tbl->num_entries * tbl->desc_size;
- if (size > 3 * efi.memmap.nr_map * efi.memmap.desc_size) {
+ if (tbl->num_entries > SZ_64K) {
pr_warn(FW_BUG "Corrupted EFI Memory Attributes Table detected! (version == %u, desc_size == %u, num_entries == %u)\n",
tbl->version, tbl->desc_size, tbl->num_entries);
goto unmap;
}
- tbl_size = sizeof(*tbl) + size;
+ tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size;
memblock_reserve(efi_mem_attr_table, tbl_size);
set_bit(EFI_MEM_ATTR, &efi.flags);
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 664898d09ff5..72e76ec54641 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -832,27 +832,6 @@ extern int __init parse_efi_signature_list(
const void *data, size_t size,
efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *));
-/**
- * efi_range_is_wc - check the WC bit on an address range
- * @start: starting kvirt address
- * @len: length of range
- *
- * Consult the EFI memory map and make sure it's ok to set this range WC.
- * Returns true or false.
- */
-static inline int efi_range_is_wc(unsigned long start, unsigned long len)
-{
- unsigned long i;
-
- for (i = 0; i < len; i += (1UL << EFI_PAGE_SHIFT)) {
- unsigned long paddr = __pa(start + i);
- if (!(efi_mem_attributes(paddr) & EFI_MEMORY_WC))
- return 0;
- }
- /* The range checked out */
- return 1;
-}
-
/*
* We play games with efi_enabled so that the compiler will, if
* possible, remove EFI-related code altogether.
diff --git a/include/linux/memblock.h b/include/linux/memblock.h
index 6ec5e9ac0699..9eac4f268359 100644
--- a/include/linux/memblock.h
+++ b/include/linux/memblock.h
@@ -155,6 +155,7 @@ int memblock_mark_mirror(phys_addr_t base, phys_addr_t size);
int memblock_mark_nomap(phys_addr_t base, phys_addr_t size);
int memblock_clear_nomap(phys_addr_t base, phys_addr_t size);
int memblock_reserved_mark_noinit(phys_addr_t base, phys_addr_t size);
+int memblock_reserved_mark_kern(phys_addr_t base, phys_addr_t size);
int memblock_mark_kho_scratch(phys_addr_t base, phys_addr_t size);
int memblock_clear_kho_scratch(phys_addr_t base, phys_addr_t size);
diff --git a/mm/memblock.c b/mm/memblock.c
index b3ddfdec7a80..2505ce8b319c 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -1116,6 +1116,21 @@ int __init_memblock memblock_reserved_mark_noinit(phys_addr_t base, phys_addr_t
}
/**
+ * memblock_reserved_mark_kern - Mark a reserved memory region with flag
+ * MEMBLOCK_RSRV_KERN
+ *
+ * @base: the base phys addr of the region
+ * @size: the size of the region
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int __init_memblock memblock_reserved_mark_kern(phys_addr_t base, phys_addr_t size)
+{
+ return memblock_setclr_flag(&memblock.reserved, base, size, 1,
+ MEMBLOCK_RSRV_KERN);
+}
+
+/**
* memblock_mark_kho_scratch - Mark a memory region as MEMBLOCK_KHO_SCRATCH.
* @base: the base phys addr of the region
* @size: the size of the region