On Tue, 15 Sep 2026 05:53:37 -0700 Breno Leitao <[email protected]> wrote:

> 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 the span the UEFI memory map describes, which
> efi_get_ram_range() walks since the stub has no max_pfn. Bit 0 covers
> the bottom of that span, recorded in phys_base, so a machine whose RAM
> starts high does not pay for the hole below it. Memory the firmware
> hot-adds later sits outside the span and is not carried across a kexec.
> 
> One table has to serve every architecture, and what they agree on is the
> attribute: setup_e820() takes a descriptor as RAM only if it is writeback
> cacheable, and so does is_usable_memory() on arm64.
> 
> The bitmap spans from the lowest to the highest descriptor that is
> write-back cacheable or unaccepted memory, as discussed with Kiryl. That
> leaves out the MMIO apertures, which sit high enough to stretch it far
> past the RAM it needs to describe.
> 
> At one bit per 2M that is 64K per TiB, and 256M at the 4PB x86
> architectural maximum. The 2M granule is called "unit" here, and the
> table carries it so the granule can change later without breaking the
> kernels already reading it.
> 
> 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.
> 
> Signed-off-by: Breno Leitao <[email protected]>
> ---
>  drivers/firmware/efi/libstub/efi-stub-helper.c | 103 
> +++++++++++++++++++++++++
>  drivers/firmware/efi/libstub/efi-stub.c        |   1 +
>  drivers/firmware/efi/libstub/efistub.h         |   6 ++
>  drivers/firmware/efi/libstub/x86-stub.c        |   2 +
>  4 files changed, 112 insertions(+)
> 
> diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c 
> b/drivers/firmware/efi/libstub/efi-stub-helper.c
> index 48f93f7758e9e9..9c66e06c972c5a 100644
> --- a/drivers/firmware/efi/libstub/efi-stub-helper.c
> +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
> @@ -774,3 +774,106 @@ 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
> +/*
> + * Find the base and top of the memory, so, we can create the bitmap for
> + * the full range.
> + */
> +static efi_status_t efi_get_ram_range(u64 *base, u64 *top)
> +{
> +     struct efi_boot_memmap *map __free(efi_pool) = NULL;
> +     u64 ram_base = ULLONG_MAX, ram_top = 0;
> +     efi_status_t status;
> +     int i, nr_desc;
> +
> +     status = efi_get_memory_map(&map, false);
> +     if (status != EFI_SUCCESS)
> +             return status;
> +
> +     nr_desc = map->map_size / map->desc_size;
> +     for (i = 0; i < nr_desc; i++) {
> +             efi_memory_desc_t *d;
> +
> +             d = efi_memdesc_ptr((unsigned long)map->map, map->desc_size, i);
> +             if (!(d->attribute & EFI_MEMORY_WB) &&
> +                 d->type != EFI_UNACCEPTED_MEMORY)
> +                     continue;
> +             ram_base = min(ram_base, d->phys_addr);

Can this use the architecture's full RAM predicate?  On x86,
setup_e820() maps EFI_LOADER_CODE, EFI_LOADER_DATA, both boot-services
types, and EFI_CONVENTIONAL_MEMORY as E820_TYPE_RAM without requiring
EFI_MEMORY_WB.

If a non-WB descriptor is at either end of RAM, this code omits it
from the bitmap span.  efi_hwpoison_record_pfn() then rejects a
poisoned PFN there, so the next kernel can allocate the bad frame.

One possible way to preserve the x86 behavior before applying the WB rule is:

    if (IS_ENABLED(CONFIG_X86) &&
        (d->type == EFI_LOADER_CODE ||
         d->type == EFI_LOADER_DATA ||
         d->type == EFI_BOOT_SERVICES_CODE ||
         d->type == EFI_BOOT_SERVICES_DATA ||
         d->type == EFI_CONVENTIONAL_MEMORY))
            goto include;
    if (!(d->attribute & EFI_MEMORY_WB) &&
        d->type != EFI_UNACCEPTED_MEMORY)
            continue;
include:
    ram_base = min(ram_base, d->phys_addr);


> +             ram_top = max(ram_top,
> +                           d->phys_addr + d->num_pages * EFI_PAGE_SIZE);
> +     }
> +     if (!ram_top || ram_base == ULLONG_MAX)
> +             return EFI_NOT_FOUND;
> +
> +     *base = round_down(ram_base, EFI_POISON_UNIT_SIZE);
> +     *top = round_up(ram_top, EFI_POISON_UNIT_SIZE);
> +
> +     return EFI_SUCCESS;
> +}
> +
> +/* The size of the bitmap */
> +static u64 efi_poison_bitmap_size(u64 span)
> +{
> +     u64 bytes = DIV_ROUND_UP(DIV_ROUND_UP(span, EFI_POISON_UNIT_SIZE),
> +                              BITS_PER_BYTE);
> +
> +     return round_up(bytes, sizeof(unsigned long));
> +}
> +
> +static struct linux_efi_poisoned_memory *efi_poison_alloc(u64 phys_base,
> +                                                       u64 bitmap_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 = EFI_POISON_UNIT_SIZE;
> +     pm->phys_base = phys_base;
> +     pm->size = bitmap_size;
> +     memset(pm->bitmap, 0, bitmap_size);
> +
> +     return pm;
> +}
> +
> +/* This needs to be done while boot service is still active */
> +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_base, ram_top, bitmap_size;
> +     efi_status_t status;
> +
> +     /* 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_range(&ram_base, &ram_top) != EFI_SUCCESS) {
> +             efi_err("Failed to size the poisoned-memory table!\n");
> +             return;
> +     }
> +
> +     bitmap_size = efi_poison_bitmap_size(ram_top - ram_base);
> +     pm = efi_poison_alloc(ram_base, bitmap_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/efi-stub.c 
> b/drivers/firmware/efi/libstub/efi-stub.c
> index 235c9738da2d63..22a315e2814a1a 100644
> --- a/drivers/firmware/efi/libstub/efi-stub.c
> +++ b/drivers/firmware/efi/libstub/efi-stub.c
> @@ -179,6 +179,7 @@ efi_status_t efi_stub_common(efi_handle_t handle,
>                        EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP);
>  
>       install_memreserve_table();
> +     install_poisoned_memory_table();
>  
>       status = efi_boot_kernel(handle, image, image_addr, cmdline_ptr);
>  
> diff --git a/drivers/firmware/efi/libstub/efistub.h 
> b/drivers/firmware/efi/libstub/efistub.h
> index fd91fc15ec810b..44436869c4efe1 100644
> --- a/drivers/firmware/efi/libstub/efistub.h
> +++ b/drivers/firmware/efi/libstub/efistub.h
> @@ -1169,6 +1169,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);
> diff --git a/drivers/firmware/efi/libstub/x86-stub.c 
> b/drivers/firmware/efi/libstub/x86-stub.c
> index 0bae0f06b6763a..3136132b9628ab 100644
> --- a/drivers/firmware/efi/libstub/x86-stub.c
> +++ b/drivers/firmware/efi/libstub/x86-stub.c
> @@ -1024,6 +1024,8 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
>  
>       setup_unaccepted_memory();
>  
> +     install_poisoned_memory_table();
> +
>       status = exit_boot(boot_params, handle);
>       if (status != EFI_SUCCESS) {
>               efi_err("exit_boot() failed!\n");
> 
> -- 
> 2.53.0-Meta
> 
> 

Reply via email to