x86: handle BIOSes which terminate e820 with CF=1 and no SMAP
authorH. Peter Anvin <hpa@zytor.com>
Wed, 13 Feb 2008 19:16:46 +0000 (11:16 -0800)
committerIngo Molnar <mingo@elte.hu>
Tue, 26 Feb 2008 11:55:52 +0000 (12:55 +0100)
The proper way to terminate the e820 chain is with %ebx == 0 on the
last legitimate memory block.  However, several BIOSes don't do that
and instead return error (CF = 1) when trying to read off the end of
the list.  For this error return, %eax doesn't necessarily return the
SMAP signature -- correctly so, since %ah should contain an error code
in this case.

To deal with some particularly broken BIOSes, we clear the entire e820
chain if the SMAP signature is missing in the middle, indicating a
plain insane e820 implementation.  However, we need to make the test
for CF = 1 before the SMAP check.

This fixes at least one HP laptop (nc6400) for which none of the
memory-probing methods (e820, e801, 88) functioned fully according to
spec.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/boot/memory.c

index 378353956b5dfc86469b66419b9680e1770a7410..e77d89f9e8aa23c13751bee268718e8ed7ee54a7 100644 (file)
@@ -37,6 +37,12 @@ static int detect_memory_e820(void)
                      "=m" (*desc)
                    : "D" (desc), "d" (SMAP), "a" (0xe820));
 
+               /* BIOSes which terminate the chain with CF = 1 as opposed
+                  to %ebx = 0 don't always report the SMAP signature on
+                  the final, failing, probe. */
+               if (err)
+                       break;
+
                /* Some BIOSes stop returning SMAP in the middle of
                   the search loop.  We don't know exactly how the BIOS
                   screwed up the map at that point, we might have a
@@ -47,9 +53,6 @@ static int detect_memory_e820(void)
                        break;
                }
 
-               if (err)
-                       break;
-
                count++;
                desc++;
        } while (next && count < E820MAX);