]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
x86/boot/e820: Introduce the bootloader provided e820_table_firmware[] table
authorChen Yu <yu.c.chen@intel.com>
Sun, 2 Jul 2017 17:07:32 +0000 (01:07 +0800)
committerIngo Molnar <mingo@kernel.org>
Wed, 5 Jul 2017 08:09:02 +0000 (10:09 +0200)
Add the real e820_tabel_firmware[] that will not be modified by the kernel
or the EFI boot stub under any circumstance.

In addition to that modify the code so that e820_table_firmwarep[] is
exposed via sysfs to represent the real firmware memory layout,
rather than exposing the e820_table_kexec[] table.

This fixes a hibernation bug/warning, which uses e820_table_kexec[] to check
RAM layout consistency across hibernation/resume:

  The suspend kernel:
  [    0.000000] e820: update [mem 0x76671018-0x76679457] usable ==> usable

  The resume kernel:
  [    0.000000] e820: update [mem 0x7666f018-0x76677457] usable ==> usable
  ...
  [   15.752088] PM: Using 3 thread(s) for decompression.
  [   15.752088] PM: Loading and decompressing image data (471870 pages)...
  [   15.764971] Hibernate inconsistent memory map detected!
  [   15.770833] PM: Image mismatch: architecture specific data

Actually it is safe to restore these pages because E820_TYPE_RAM and
E820_TYPE_RESERVED_KERN are treated the same during hibernation, so
the original e820 table provided by the bootloader is used for
hibernation MD5 fingerprint checking.

The side effect is that, this newly introduced variable might increase the
kernel size at compile time.

Suggested-by: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Chen Yu <yu.c.chen@intel.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Len Brown <lenb@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Xunlei Pang <xlpang@redhat.com>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/include/asm/e820/api.h
arch/x86/kernel/e820.c
arch/x86/power/hibernate_64.c

index a688095421ab7ef7a93d48b8d888526ef0bb5efd..a504adc661a4954ca0d1c5ed04149fece0e042a3 100644 (file)
@@ -5,6 +5,7 @@
 
 extern struct e820_table *e820_table;
 extern struct e820_table *e820_table_kexec;
+extern struct e820_table *e820_table_firmware;
 
 extern unsigned long pci_mem_start;
 
index 591019031e236221b4691970f858a17cb281f281..532da61d605ccc2271067fdf67bac83c454616aa 100644 (file)
 #include <asm/setup.h>
 
 /*
- * We organize the E820 table into two main data structures:
+ * We organize the E820 table into three main data structures:
  *
- * - 'e820_table_kexec': the original firmware version passed to us by the
- *   bootloader - not modified by the kernel. We use this to:
+ * - 'e820_table_firmware': the original firmware version passed to us by the
+ *   bootloader - not modified by the kernel. It is composed of two parts:
+ *   the first 128 E820 memory entries in boot_params.e820_table and the remaining
+ *   (if any) entries of the SETUP_E820_EXT nodes. We use this to:
  *
  *       - inform the user about the firmware's notion of memory layout
  *         via /sys/firmware/memmap
  *       - the hibernation code uses it to generate a kernel-independent MD5
  *         fingerprint of the physical memory layout of a system.
  *
+ * - 'e820_table_kexec': a slightly modified (by the kernel) firmware version
+ *   passed to us by the bootloader - the major difference between
+ *   e820_table_firmware[] and this one is that, the latter marks the setup_data
+ *   list created by the EFI boot stub as reserved, so that kexec can reuse the
+ *   setup_data information in the second kernel. Besides, e820_table_kexec[]
+ *   might also be modified by the kexec itself to fake a mptable.
+ *   We use this to:
+ *
  *       - kexec, which is a bootloader in disguise, uses the original E820
  *         layout to pass to the kexec-ed kernel. This way the original kernel
  *         can have a restricted E820 map while the kexec()-ed kexec-kernel
  */
 static struct e820_table e820_table_init               __initdata;
 static struct e820_table e820_table_kexec_init         __initdata;
+static struct e820_table e820_table_firmware_init      __initdata;
 
 struct e820_table *e820_table __refdata                        = &e820_table_init;
 struct e820_table *e820_table_kexec __refdata          = &e820_table_kexec_init;
+struct e820_table *e820_table_firmware __refdata       = &e820_table_firmware_init;
 
 /* For PCI or other memory-mapped resources */
 unsigned long pci_mem_start = 0xaeedbabe;
@@ -648,6 +660,12 @@ __init void e820__reallocate_tables(void)
        BUG_ON(!n);
        memcpy(n, e820_table_kexec, size);
        e820_table_kexec = n;
+
+       size = offsetof(struct e820_table, entries) + sizeof(struct e820_entry)*e820_table_firmware->nr_entries;
+       n = kmalloc(size, GFP_KERNEL);
+       BUG_ON(!n);
+       memcpy(n, e820_table_firmware, size);
+       e820_table_firmware = n;
 }
 
 /*
@@ -670,6 +688,7 @@ void __init e820__memory_setup_extended(u64 phys_addr, u32 data_len)
        e820__update_table(e820_table);
 
        memcpy(e820_table_kexec, e820_table, sizeof(*e820_table_kexec));
+       memcpy(e820_table_firmware, e820_table, sizeof(*e820_table_firmware));
 
        early_memunmap(sdata, data_len);
        pr_info("e820: extended physical RAM map:\n");
@@ -1064,8 +1083,9 @@ void __init e820__reserve_resources(void)
                res++;
        }
 
-       for (i = 0; i < e820_table_kexec->nr_entries; i++) {
-               struct e820_entry *entry = e820_table_kexec->entries + i;
+       /* Expose the bootloader-provided memory layout to the sysfs. */
+       for (i = 0; i < e820_table_firmware->nr_entries; i++) {
+               struct e820_entry *entry = e820_table_firmware->entries + i;
 
                firmware_map_add_early(entry->addr, entry->addr + entry->size, e820_type_to_string(entry));
        }
@@ -1178,6 +1198,7 @@ void __init e820__memory_setup(void)
        who = x86_init.resources.memory_setup();
 
        memcpy(e820_table_kexec, e820_table, sizeof(*e820_table_kexec));
+       memcpy(e820_table_firmware, e820_table, sizeof(*e820_table_firmware));
 
        pr_info("e820: BIOS-provided physical RAM map:\n");
        e820__print_table(who);
index 3ba161a08d6e26517d356b286337b54d3b924802..e3e62c8a8e70ee6ec6aed3856ba913dbe15523f6 100644 (file)
@@ -251,7 +251,7 @@ static int get_e820_md5(struct e820_table *table, void *buf)
 
 static void hibernation_e820_save(void *buf)
 {
-       get_e820_md5(e820_table_kexec, buf);
+       get_e820_md5(e820_table_firmware, buf);
 }
 
 static bool hibernation_e820_mismatch(void *buf)
@@ -264,7 +264,7 @@ static bool hibernation_e820_mismatch(void *buf)
        if (!memcmp(result, buf, MD5_DIGEST_SIZE))
                return false;
 
-       ret = get_e820_md5(e820_table_kexec, result);
+       ret = get_e820_md5(e820_table_firmware, result);
        if (ret)
                return true;