A EFI config table can only be installed while boot services are still up, so the stub has to create it; the running kernel can only flip bits in a table that already exists. Size the bitmap from efi_get_ram_top(): bit N covers unit N counting from address 0, up to the top of usable RAM, so the table tracks max_pfn. Memory the firmware hot-adds later sits above it and is not carried across a kexec. At one bit per 2M that is 64K per TiB. The 2M granule is called "unit" here. Cap the bitmap at 1M to make sure this doesn't get too big. The next kernel reads unit_size back out of the table, so a wider unit only costs precision: more memory withheld per poisoned frame, nothing lost. Allocate it as EFI_ACPI_RECLAIM_MEMORY so the next kernel does not take it for free RAM, and install it empty. A table installed by an earlier boot rides the system table across kexec and is reused as-is. Nothing calls it yet; the stub entry paths pick it up next. Signed-off-by: Breno Leitao --- drivers/firmware/efi/libstub/efi-stub-helper.c | 89 ++++++++++++++++++++++++++ drivers/firmware/efi/libstub/efistub.h | 6 ++ 2 files changed, 95 insertions(+) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index f27f2e1f00199..7258ecdf1f7c2 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -774,3 +774,92 @@ void efi_remap_image(unsigned long image_base, unsigned alloc_size, efi_warn("Failed to remap data region non-executable\n"); } } + +#ifdef CONFIG_EFI_POISONED_MEMORY +/* Cap on the bitmap; a span too wide to fit gets a coarser unit instead. */ +#define EFI_POISON_MAX_TABLE_SIZE SZ_1M +#define EFI_POISON_MAX_UNIT_SIZE SZ_1G + +/* + * Bitmap geometry for a given top of RAM. The bitmap starts at address 0, so + * bit N covers unit N. + * In the following code, a "unit" is granule of physical address space that one + * bitmap bit covers. + */ +static u32 efi_poison_geometry(u64 ram_top, u64 *bitmap_size) +{ + u64 nr_units = DIV_ROUND_UP(ram_top, EFI_POISON_UNIT_SIZE); + u32 unit_size = EFI_POISON_UNIT_SIZE; + + while (DIV_ROUND_UP(nr_units, BITS_PER_BYTE) > EFI_POISON_MAX_TABLE_SIZE && + unit_size < EFI_POISON_MAX_UNIT_SIZE) { + nr_units = DIV_ROUND_UP(nr_units, 2); + unit_size *= 2; + } + + *bitmap_size = DIV_ROUND_UP(nr_units, BITS_PER_BYTE); + return unit_size; +} + +/* ACPI reclaim memory, so the next kernel does not treat it as free RAM. */ +static struct linux_efi_poisoned_memory *efi_poison_alloc(u64 bitmap_size, + u32 unit_size) +{ + struct linux_efi_poisoned_memory *pm; + efi_status_t status; + + status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, + sizeof(*pm) + bitmap_size, (void **)&pm); + if (status != EFI_SUCCESS) + return NULL; + + pm->version = 1; + pm->unit_size = unit_size; + pm->phys_base = 0; + pm->size = bitmap_size; + memset(pm->bitmap, 0, bitmap_size); + + return pm; +} + +/* + * Allocate and install the poisoned-memory bitmap while boot services are + * available + */ +void install_poisoned_memory_table(void) +{ + efi_guid_t poisoned_memory_table_guid = LINUX_EFI_POISONED_MEMORY_TABLE_GUID; + struct linux_efi_poisoned_memory *pm; + u64 ram_top, bitmap_size; + efi_status_t status; + u32 unit_size; + + /* A table installed by an earlier boot rides the system table across kexec. */ + pm = get_efi_config_table(poisoned_memory_table_guid); + if (pm) { + if (pm->version != 1) + efi_err("Unknown version of poisoned-memory table\n"); + return; + } + + if (efi_get_ram_top(&ram_top) != EFI_SUCCESS) { + efi_err("Failed to size the poisoned-memory table!\n"); + return; + } + + unit_size = efi_poison_geometry(ram_top, &bitmap_size); + + pm = efi_poison_alloc(bitmap_size, unit_size); + if (!pm) { + efi_err("Failed to allocate poisoned-memory table!\n"); + return; + } + + status = efi_bs_call(install_configuration_table, + &poisoned_memory_table_guid, pm); + if (status != EFI_SUCCESS) { + efi_bs_call(free_pool, pm); + efi_err("Failed to install poisoned-memory config table!\n"); + } +} +#endif diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 77ba576779aca..b5e19ba50ddae 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -1171,6 +1171,12 @@ efi_enable_reset_attack_mitigation(void) { } void efi_retrieve_eventlog(void); +#ifdef CONFIG_EFI_POISONED_MEMORY +void install_poisoned_memory_table(void); +#else +static inline void install_poisoned_memory_table(void) { } +#endif + struct sysfb_display_info *alloc_primary_display(void); struct sysfb_display_info *__alloc_primary_display(void); void free_primary_display(struct sysfb_display_info *dpy); -- 2.53.0-Meta