* [PATCH] x86/hibernate: Ignore page-zero RAM in E820 checksum
@ 2026-08-05 7:09 Matthias Goergens
2026-09-25 16:51 ` Rafael J. Wysocki (Intel)
2026-09-26 5:21 ` [PATCH v2] " Matthias Goergens
0 siblings, 2 replies; 4+ messages in thread
From: Matthias Goergens @ 2026-08-05 7:09 UTC (permalink / raw)
To: rafael, linux-pm
Cc: pavel, tglx, mingo, bp, dave.hansen, x86, hpa, yu.c.chen, jlee,
baoquan.he, dyoung, io, scardracs, moravec, kexec, linux-kernel,
Matthias Goergens
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.
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@r-ricci.it>
Signed-off-by: Matthias Goergens <matthias.goergens@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
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] x86/hibernate: Ignore page-zero RAM in E820 checksum
2026-08-05 7:09 [PATCH] x86/hibernate: Ignore page-zero RAM in E820 checksum Matthias Goergens
@ 2026-09-25 16:51 ` Rafael J. Wysocki (Intel)
2026-09-26 5:21 ` [PATCH v2] " Matthias Goergens
1 sibling, 0 replies; 4+ messages in thread
From: Rafael J. Wysocki (Intel) @ 2026-09-25 16:51 UTC (permalink / raw)
To: Matthias Goergens
Cc: rafael, linux-pm, pavel, tglx, mingo, bp, dave.hansen, x86, hpa,
yu.c.chen, jlee, baoquan.he, dyoung, io, scardracs, moravec,
kexec, linux-kernel
Sorry for the late response.
On Wed, Aug 5, 2026 at 9:09 AM Matthias Goergens
<matthias.goergens@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@r-ricci.it>
> Signed-off-by: Matthias Goergens <matthias.goergens@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
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2] x86/hibernate: Ignore page-zero RAM in E820 checksum
2026-08-05 7:09 [PATCH] x86/hibernate: Ignore page-zero RAM in E820 checksum Matthias Goergens
2026-09-25 16:51 ` Rafael J. Wysocki (Intel)
@ 2026-09-26 5:21 ` Matthias Goergens
2026-09-26 5:26 ` sashiko-bot
1 sibling, 1 reply; 4+ messages in thread
From: Matthias Goergens @ 2026-09-26 5:21 UTC (permalink / raw)
To: Rafael J . Wysocki
Cc: linux-pm, pavel, tglx, mingo, bp, dave.hansen, x86, hpa,
yu.c.chen, jlee, baoquan.he, dyoung, io, scardracs, moravec,
kexec, linux-kernel
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.
A QEMU guest showed this transition:
firmware: RAM [0-0x9fbff]
kexec_load: gap [0-0x3ff], RAM [0x400-0x9fbff]
firmware: RAM [0-0x9fbff]
The checksum covers e820_table_firmware, which trim_bios_range() leaves
alone. The nosave regions come from e820_table instead, where
trim_bios_range() has already turned conventional RAM in page zero into
reserved memory, so page zero never occurs in the image, whatever the
firmware map says about it.
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.
This is deliberately narrower than Marco Scardovi's 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.
Bump RESTORE_MAGIC, since the checksum semantics change, so that an
image written by a kernel without this change is refused by the magic
check instead of being compared under different rules.
In the same guest, with the page-zero difference kept, hibernation and
resume now work after both a direct boot and a kexec_load boot. An
image written by an unpatched kernel is refused at resume with
"Unrecognized hibernate image header format!", and the firmware-booted
kernel carries on.
Fixes: 62a03defeabd ("PM / hibernate: Verify the consistent of e820 memory map by md5 digest")
Reported-by: Roberto Ricci <io@r-ricci.it>
Signed-off-by: Matthias Goergens <matthias.goergens@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/
---
Changes in v2:
- Bump RESTORE_MAGIC, as Rafael asked:
https://lore.kernel.org/all/CAJZ5v0izV3g1mSKJtKMcc=TfeUjJofSXCX92y4XX55Lam28qOg@mail.gmail.com/
- Say which E820 table the checksum covers and why page zero still
cannot be in the image; drop the paragraph on cross-version images,
which the magic now covers, and report that such an image is refused.
- Rebased on 6812ce4e4379 ("Merge tag 'drm-fixes-2026-09-26' of
https://gitlab.freedesktop.org/drm/kernel").
arch/x86/power/hibernate.c | 51 +++++++++++++++++++++++++++++++++-----
1 file changed, 45 insertions(+), 6 deletions(-)
diff --git a/arch/x86/power/hibernate.c b/arch/x86/power/hibernate.c
index a2294c1649f6..dbb340fb24ad 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,18 +89,38 @@ 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
-#define RESTORE_MAGIC 0x23456789ABCDEF02UL
+#define RESTORE_MAGIC 0x23456789ABCDEF03UL
#else
-#define RESTORE_MAGIC 0x12345679UL
+#define RESTORE_MAGIC 0x1234567AUL
#endif
/**
base-commit: 6812ce4e4379ffc99c52401ec28f0d7ffbc36206
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] x86/hibernate: Ignore page-zero RAM in E820 checksum
2026-09-26 5:21 ` [PATCH v2] " Matthias Goergens
@ 2026-09-26 5:26 ` sashiko-bot
0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-09-26 5:26 UTC (permalink / raw)
To: Matthias Goergens, Rafael J . Wysocki
Cc: scardracs, linux-kernel, mingo, tglx, dyoung, io, x86, linux-pm,
yu.c.chen, kexec, baoquan.he, hpa, moravec, jlee, dave.hansen,
bp, pavel
> 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.
> A QEMU guest showed this transition:
>
> firmware: RAM [0-0x9fbff]
> kexec_load: gap [0-0x3ff], RAM [0x400-0x9fbff]
> firmware: RAM [0-0x9fbff]
>
> The checksum covers e820_table_firmware, which trim_bios_range() leaves
> [ ... ]
> kernel carries on.
>
> Fixes: 62a03defeabd ("PM / hibernate: Verify the consistent of e820 memory map by md5 digest")
> Reported-by: Roberto Ricci <io@r-ricci.it>
> Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260926052117.3613637-1-matthias.goergens@gmail.com?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-26 5:26 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-05 7:09 [PATCH] x86/hibernate: Ignore page-zero RAM in E820 checksum Matthias Goergens
2026-09-25 16:51 ` Rafael J. Wysocki (Intel)
2026-09-26 5:21 ` [PATCH v2] " Matthias Goergens
2026-09-26 5:26 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®