efi/unaccepted: Make sure unaccepted table is mapped
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>
Mon, 14 Aug 2023 16:12:47 +0000 (19:12 +0300)
committerArd Biesheuvel <ardb@kernel.org>
Tue, 19 Sep 2023 16:11:36 +0000 (16:11 +0000)
Unaccepted table is now allocated from EFI_ACPI_RECLAIM_MEMORY. It
translates into E820_TYPE_ACPI, which is not added to memblock and
therefore not mapped in the direct mapping.

This causes a crash on the first touch of the table.

Use memblock_add() to make sure that the table is mapped in direct
mapping.

Align the range to the nearest page borders. Ranges smaller than page
size are not mapped.

Fixes: e7761d827e99 ("efi/unaccepted: Use ACPI reclaim memory for unaccepted memory table")
Reported-by: Hongyu Ning <hongyu.ning@intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
drivers/firmware/efi/efi.c

index 1599f11..ce20a60 100644 (file)
@@ -623,6 +623,34 @@ static __init int match_config_table(const efi_guid_t *guid,
        return 0;
 }
 
+/**
+ * reserve_unaccepted - Map and reserve unaccepted configuration table
+ * @unaccepted: Pointer to unaccepted memory table
+ *
+ * memblock_add() makes sure that the table is mapped in direct mapping. During
+ * normal boot it happens automatically because the table is allocated from
+ * usable memory. But during crashkernel boot only memory specifically reserved
+ * for crash scenario is mapped. memblock_add() forces the table to be mapped
+ * in crashkernel case.
+ *
+ * Align the range to the nearest page borders. Ranges smaller than page size
+ * are not going to be mapped.
+ *
+ * memblock_reserve() makes sure that future allocations will not touch the
+ * table.
+ */
+
+static __init void reserve_unaccepted(struct efi_unaccepted_memory *unaccepted)
+{
+       phys_addr_t start, size;
+
+       start = PAGE_ALIGN_DOWN(efi.unaccepted);
+       size = PAGE_ALIGN(sizeof(*unaccepted) + unaccepted->size);
+
+       memblock_add(start, size);
+       memblock_reserve(start, size);
+}
+
 int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
                                   int count,
                                   const efi_config_table_type_t *arch_tables)
@@ -751,11 +779,9 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
 
                unaccepted = early_memremap(efi.unaccepted, sizeof(*unaccepted));
                if (unaccepted) {
-                       unsigned long size;
 
                        if (unaccepted->version == 1) {
-                               size = sizeof(*unaccepted) + unaccepted->size;
-                               memblock_reserve(efi.unaccepted, size);
+                               reserve_unaccepted(unaccepted);
                        } else {
                                efi.unaccepted = EFI_INVALID_TABLE_ADDR;
                        }