[PATCH] x86/hibernate: Ignore page-zero RAM in E820 checksum

Rafael J. Wysocki (Intel) rafael at kernel.org
Fri Sep 25 09:51:06 PDT 2026


Sorry for the late response.

On Wed, Aug 5, 2026 at 9:09 AM Matthias Goergens
<matthias.goergens at gmail.com> wrote:
>
> The legacy kexec_load path reconstructs the E820 map exported through
> sysfs.  kexec-tools leaves the first 1 KiB unavailable for the real-mode
> transition, so a kernel entered through kexec_load can see conventional
> RAM starting at 0x400.  A subsequent firmware boot reports the same RAM
> range starting at zero.
>
> The hibernation E820 checksum compares those byte representations and
> rejects the image, even though the maps agree from page one onwards.  The
> reproducer observed this exact transition:
>
>   firmware:     RAM [0-0x9fbff]
>   kexec_load:   gap [0-0x3ff], RAM [0x400-0x9fbff]
>   firmware:     RAM [0-0x9fbff]
>
> Common x86 setup already converts conventional RAM in page zero to
> reserved memory in trim_bios_range() before registering hibernation
> nosave regions.  Page zero therefore cannot occur in the image.
>
> Canonicalise only the conventional-RAM portion below PAGE_SIZE before
> calculating the checksum.  Preserve RESERVED, ACPI, NVS, UNUSABLE, PMEM
> and all other E820 types so that changes to exceptional mappings remain
> detectable.  Maps without conventional RAM intersecting page zero retain
> the previous checksum byte stream.
>
> This is deliberately narrower than the June proposal to checksum only RAM
> and its opt-in relaxed_memmap successor.  Rafael noted that ignoring non-RAM
> changes could hide moved ACPI or UEFI regions still used by the resumed
> kernel.  This patch preserves every non-RAM entry and ignores only RAM within
> page zero, which common setup already reserves and excludes from the image.
>
> Changing the checksum semantics means an image made by an unpatched
> kernel can fail to resume under a patched kernel, or vice versa, when its
> raw map contains page-zero RAM.  Such cross-version attempts remain
> fail-closed; normal same-kernel hibernation is unaffected.

So this should update RESTORE_MAGIC in arch/x86/power/hibernate.c to
indicate the protocol change.

> With the reproduced raw-map difference retained, both the direct-boot
> control and legacy kexec_load hibernation/resume tests passed.  The test
> kernel also completed a clean full bzImage build.
>
> Fixes: 62a03defeabd ("PM / hibernate: Verify the consistent of e820 memory map by md5 digest")
> Reported-by: Roberto Ricci <io at r-ricci.it>
> Signed-off-by: Matthias Goergens <matthias.goergens at gmail.com>
> Link: https://lore.kernel.org/all/Z-hYWc9LtBU1Yhtg@desktop0a/
> Link: https://lists.openwall.net/linux-kernel/2025/04/04/1372
> Link: https://lore.kernel.org/all/CAJZ5v0jmOj0WBtMTvbnaD+2b0bTFowA=JWrqRzaaCYpHpai1Nw@mail.gmail.com/
> Link: https://lore.kernel.org/all/20260623165724.10753-1-scardracs@disroot.org/
> ---
>  arch/x86/power/hibernate.c | 47 ++++++++++++++++++++++++++++++++++----
>  1 file changed, 43 insertions(+), 4 deletions(-)
>
> diff --git a/arch/x86/power/hibernate.c b/arch/x86/power/hibernate.c
> index a2294c1649f65..ec53c970c92e6 100644
> --- a/arch/x86/power/hibernate.c
> +++ b/arch/x86/power/hibernate.c
> @@ -63,6 +63,25 @@ struct restore_data_record {
>         unsigned long e820_checksum;
>  };
>
> +static bool trim_e820_page_zero_ram(struct e820_entry *entry)
> +{
> +       u64 lowmem_size;
> +
> +       /*
> +        * Page zero is BIOS-owned and registered as nosave.  Boot loaders may
> +        * therefore omit part of its conventional RAM entry without changing
> +        * any memory available to the image.  Preserve all other E820 types.
> +        */
> +       if (entry->type != E820_TYPE_RAM || entry->addr >= PAGE_SIZE)
> +               return true;
> +
> +       lowmem_size = min_t(u64, entry->size, PAGE_SIZE - entry->addr);
> +       entry->addr += lowmem_size;
> +       entry->size -= lowmem_size;
> +
> +       return entry->size;
> +}
> +
>  /**
>   * compute_e820_crc32 - calculate crc32 of a given e820 table
>   *
> @@ -70,12 +89,32 @@ struct restore_data_record {
>   *
>   * Return: the resulting checksum
>   */
> -static inline u32 compute_e820_crc32(struct e820_table *table)
> +static u32 compute_e820_crc32(struct e820_table *table)
>  {
> -       int size = offsetof(struct e820_table, entries) +
> -               sizeof(struct e820_entry) * table->nr_entries;
> +       struct e820_entry entry;
> +       u32 crc = ~0;
> +       u32 nr_entries = 0;
> +       u32 i;
> +
> +       for (i = 0; i < table->nr_entries; i++) {
> +               entry = table->entries[i];
> +               if (trim_e820_page_zero_ram(&entry))
> +                       nr_entries++;
> +       }
> +
> +       crc = crc32_le(crc, (unsigned char const *)&nr_entries,
> +                      sizeof(nr_entries));
> +
> +       for (i = 0; i < table->nr_entries; i++) {
> +               entry = table->entries[i];
> +               if (!trim_e820_page_zero_ram(&entry))
> +                       continue;
> +
> +               crc = crc32_le(crc, (unsigned char const *)&entry,
> +                              sizeof(entry));
> +       }
>
> -       return ~crc32_le(~0, (unsigned char const *)table, size);
> +       return ~crc;
>  }
>
>  #ifdef CONFIG_X86_64
> --
> 2.55.0
>



More information about the kexec mailing list