x86/boot: Reorganize and clean up the BIOS area reservation code
authorIngo Molnar <mingo@kernel.org>
Thu, 21 Jul 2016 07:53:52 +0000 (09:53 +0200)
committerIngo Molnar <mingo@kernel.org>
Thu, 21 Jul 2016 08:11:57 +0000 (10:11 +0200)
So the reserve_ebda_region() code has accumulated a number of
problems over the years that make it really difficult to read
and understand:

- The calculation of 'lowmem' and 'ebda_addr' is an unnecessarily
  interleaved mess of first lowmem, then ebda_addr, then lowmem tweaks...

- 'lowmem' here means 'super low mem' - i.e. 16-bit addressable memory. In other
  parts of the x86 code 'lowmem' means 32-bit addressable memory... This makes it
  super confusing to read.

- It does not help at all that we have various memory range markers, half of which
  are 'start of range', half of which are 'end of range' - but this crucial
  property is not obvious in the naming at all ... gave me a headache trying to
  understand all this.

- Also, the 'ebda_addr' name sucks: it highlights that it's an address (which is
  obvious, all values here are addresses!), while it does not highlight that it's
  the _start_ of the EBDA region ...

- 'BIOS_LOWMEM_KILOBYTES' says a lot of things, except that this is the only value
  that is a pointer to a value, not a memory range address!

- The function name itself is a misnomer: it says 'reserve_ebda_region()' while
  its main purpose is to reserve all the firmware ROM typically between 640K and
  1MB, while the 'EBDA' part is only a small part of that ...

- Likewise, the paravirt quirk flag name 'ebda_search' is misleading as well: this
  too should be about whether to reserve firmware areas in the paravirt case.

- In fact thinking about this as 'end of RAM' is confusing: what this function
  *really* wants to reserve is firmware data and code areas! Once the thinking is
  inverted from a mixed 'ram' and 'reserved firmware area' notion to a pure
  'reserved area' notion everything becomes a lot clearer.

To improve all this rewrite the whole code (without changing the logic):

- Firstly invert the naming from 'lowmem end' to 'BIOS reserved area start'
  and propagate this concept through all the variable names and constants.

BIOS_RAM_SIZE_KB_PTR // was: BIOS_LOWMEM_KILOBYTES

BIOS_START_MIN // was: INSANE_CUTOFF

ebda_start // was: ebda_addr
bios_start // was: lowmem

BIOS_START_MAX // was: LOWMEM_CAP

- Then clean up the name of the function itself by renaming it
  to reserve_bios_regions() and renaming the ::ebda_search paravirt
  flag to ::reserve_bios_regions.

- Fix up all the comments (fix typos), harmonize and simplify their
  formulation and remove comments that become unnecessary due to
  the much better naming all around.

Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/include/asm/bios_ebda.h
arch/x86/include/asm/x86_init.h
arch/x86/kernel/ebda.c
arch/x86/kernel/head32.c
arch/x86/kernel/head64.c
arch/x86/kernel/platform-quirks.c

index 2b00c776f223af3e05e35a7c53096e77f724e892..4b7b8e71607eaf8f800770f4dfc3b0ce9d5f675b 100644 (file)
@@ -17,7 +17,7 @@ static inline unsigned int get_bios_ebda(void)
        return address; /* 0 means none */
 }
 
-void reserve_ebda_region(void);
+void reserve_bios_regions(void);
 
 #ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
 /*
index 4dcdf74dfed8606ebd0a948bd733a4d721c0d1b8..c519c052700a705b581c6f9a48576d5172c91267 100644 (file)
@@ -168,14 +168,14 @@ struct x86_legacy_devices {
  * struct x86_legacy_features - legacy x86 features
  *
  * @rtc: this device has a CMOS real-time clock present
- * @ebda_search: it's safe to search for the EBDA signature in the hardware's
+ * @reserve_bios_regions: it's safe to search for the EBDA signature in the hardware's
  *     low RAM
  * @devices: legacy x86 devices, refer to struct x86_legacy_devices
  *     documentation for further details.
  */
 struct x86_legacy_features {
        int rtc;
-       int ebda_search;
+       int reserve_bios_regions;
        struct x86_legacy_devices devices;
 };
 
index afe65dffee80b80bda7dc1f69072fc23d21472b3..6219eef20e2e7c8b33609bf9a895466cba5ca11c 100644 (file)
 #include <asm/bios_ebda.h>
 
 /*
+ * This function reserves all conventional PC system BIOS related
+ * firmware memory areas (some of which are data, some of which
+ * are code), that must not be used by the kernel as available
+ * RAM.
+ *
  * The BIOS places the EBDA/XBDA at the top of conventional
  * memory, and usually decreases the reported amount of
- * conventional memory (int 0x12) too. This also contains a
- * workaround for Dell systems that neglect to reserve EBDA.
- * The same workaround also avoids a problem with the AMD768MPX
- * chipset: reserve a page before VGA to prevent PCI prefetch
- * into it (errata #56). Usually the page is reserved anyways,
- * unless you have no PS/2 mouse plugged in.
+ * conventional memory (int 0x12) too.
+ *
+ * This means that as a first approximation on most systems we can
+ * guess the reserved BIOS area by looking at the low BIOS RAM size
+ * value and assume that everything above that value (up to 1MB) is
+ * reserved.
+ *
+ * But life in firmware country is not that simple:
+ *
+ * - This code also contains a quirk for Dell systems that neglect
+ *   to reserve the EBDA area in the 'RAM size' value ...
+ *
+ * - The same quirk also avoids a problem with the AMD768MPX
+ *   chipset: reserve a page before VGA to prevent PCI prefetch
+ *   into it (errata #56). (Usually the page is reserved anyways,
+ *   unless you have no PS/2 mouse plugged in.)
+ *
+ * - Plus paravirt systems don't have a reliable value in the
+ *   'BIOS RAM size' pointer we can rely on, so we must quirk
+ *   them too.
+ *
+ * Due to those various problems this function is deliberately
+ * very conservative and tries to err on the side of reserving
+ * too much, to not risk reserving too little.
+ *
+ * Losing a small amount of memory in the bottom megabyte is
+ * rarely a problem, as long as we have enough memory to install
+ * the SMP bootup trampoline which *must* be in this area.
  *
- * This functions is deliberately very conservative.  Losing
- * memory in the bottom megabyte is rarely a problem, as long
- * as we have enough memory to install the trampoline.  Using
- * memory that is in use by the BIOS or by some DMA device
- * the BIOS didn't shut down *is* a big problem.
+ * Using memory that is in use by the BIOS or by some DMA device
+ * the BIOS didn't shut down *is* a big problem to the kernel,
+ * obviously.
  */
 
-#define BIOS_LOWMEM_KILOBYTES  0x413
-#define LOWMEM_CAP             0x9f000U        /* Absolute maximum */
-#define INSANE_CUTOFF          0x20000U        /* Less than this = insane */
+#define BIOS_RAM_SIZE_KB_PTR   0x413
 
-void __init reserve_ebda_region(void)
+#define BIOS_START_MIN         0x20000U        /* 128K, less than this is insane */
+#define BIOS_START_MAX         0x9f000U        /* 640K, absolute maximum */
+
+void __init reserve_bios_regions(void)
 {
-       unsigned int lowmem, ebda_addr;
+       unsigned int bios_start, ebda_start;
 
        /*
-        * To determine the position of the EBDA and the
-        * end of conventional memory, we need to look at
-        * the BIOS data area. In a paravirtual environment
-        * that area is absent. We'll just have to assume
-        * that the paravirt case can handle memory setup
-        * correctly, without our help.
+        * NOTE: In a paravirtual environment the BIOS reserved
+        * area is absent. We'll just have to assume that the
+        * paravirt case can handle memory setup correctly,
+        * without our help.
         */
-       if (!x86_platform.legacy.ebda_search)
+       if (!x86_platform.legacy.reserve_bios_regions)
                return;
 
-       /* end of low (conventional) memory */
-       lowmem = *(unsigned short *)__va(BIOS_LOWMEM_KILOBYTES);
-       lowmem <<= 10;
-
-       /* start of EBDA area */
-       ebda_addr = get_bios_ebda();
+       /* Get the start address of the EBDA page: */
+       ebda_start = get_bios_ebda();
 
        /*
-        * Note: some old Dells seem to need 4k EBDA without
-        * reporting so, so just consider the memory above 0x9f000
-        * to be off limits (bugzilla 2990).
+        * Quirk: some old Dells seem to have a 4k EBDA without
+        * reporting so in their BIOS RAM size value, so just
+        * consider the memory above 640K to be off limits
+        * (bugzilla 2990).
+        *
+        * We detect this case by filtering for nonsensical EBDA
+        * addresses below 128K, where we can assume that they
+        * are bogus and bump it up to a fixed 640K value:
         */
+       if (ebda_start < BIOS_START_MIN)
+               ebda_start = BIOS_START_MAX;
 
-       /* If the EBDA address is below 128K, assume it is bogus */
-       if (ebda_addr < INSANE_CUTOFF)
-               ebda_addr = LOWMEM_CAP;
+       /*
+        * BIOS RAM size is encoded in kilobytes, convert it
+        * to bytes to get a first guess at where the BIOS
+        * firmware area starts:
+        */
+       bios_start = *(unsigned short *)__va(BIOS_RAM_SIZE_KB_PTR);
+       bios_start <<= 10;
 
-       /* If lowmem is less than 128K, assume it is bogus */
-       if (lowmem < INSANE_CUTOFF)
-               lowmem = LOWMEM_CAP;
+       /*
+        * If bios_start is less than 128K, assume it is bogus
+        * and bump it up to 640K:
+        */
+       if (bios_start < BIOS_START_MIN)
+               bios_start = BIOS_START_MAX;
 
-       /* Use the lower of the lowmem and EBDA markers as the cutoff */
-       lowmem = min(lowmem, ebda_addr);
-       lowmem = min(lowmem, LOWMEM_CAP); /* Absolute cap */
+       /*
+        * Use the lower of the bios_start and ebda_start
+        * as the starting point, but don't allow it to
+        * go beyond 640K:
+        */
+       bios_start = min(bios_start, ebda_start);
+       bios_start = min(bios_start, BIOS_START_MAX);
 
-       /* reserve all memory between lowmem and the 1MB mark */
-       memblock_reserve(lowmem, 0x100000 - lowmem);
+       /* Reserve all memory between bios_start and the 1MB mark: */
+       memblock_reserve(bios_start, 0x100000 - bios_start);
 }
index d784bb547a9dd64f64ab0b9c429d7d0bff20df9d..2dda0bc4576ebf7a6940056e621d0bdb4850aa79 100644 (file)
@@ -26,7 +26,7 @@ static void __init i386_default_early_setup(void)
        x86_init.resources.reserve_resources = i386_reserve_resources;
        x86_init.mpparse.setup_ioapic_ids = setup_ioapic_ids_from_mpc;
 
-       reserve_ebda_region();
+       reserve_bios_regions();
 }
 
 asmlinkage __visible void __init i386_start_kernel(void)
index b72fb0b71dd1f84c7e9e8141281a82e758be9909..99d48e7d2974c64fe7b3fe9c1dd59e7f3b189cbb 100644 (file)
@@ -183,7 +183,7 @@ void __init x86_64_start_reservations(char *real_mode_data)
                copy_bootdata(__va(real_mode_data));
 
        x86_early_init_platform_quirks();
-       reserve_ebda_region();
+       reserve_bios_regions();
 
        switch (boot_params.hdr.hardware_subarch) {
        case X86_SUBARCH_INTEL_MID:
index b2f8a33b36ff4e6fb3beef130af93717f10baf11..24a50301f1504a3560066e25ea9fa0ddc0d8fdaa 100644 (file)
@@ -7,12 +7,12 @@
 void __init x86_early_init_platform_quirks(void)
 {
        x86_platform.legacy.rtc = 1;
-       x86_platform.legacy.ebda_search = 0;
+       x86_platform.legacy.reserve_bios_regions = 0;
        x86_platform.legacy.devices.pnpbios = 1;
 
        switch (boot_params.hdr.hardware_subarch) {
        case X86_SUBARCH_PC:
-               x86_platform.legacy.ebda_search = 1;
+               x86_platform.legacy.reserve_bios_regions = 1;
                break;
        case X86_SUBARCH_XEN:
        case X86_SUBARCH_LGUEST: