x86: reserve_early end-of-conventional-memory to 1MB, 64-bit
[sfrench/cifs-2.6.git] / arch / x86 / kernel / head64.c
index 38f32e798a99913ff2b1995159e771559e990164..b684552347df8e80e2e62beec3c40417a551fbfb 100644 (file)
@@ -49,33 +49,42 @@ static void __init copy_bootdata(char *real_mode_data)
        }
 }
 
-#define EBDA_ADDR_POINTER 0x40E
+#define BIOS_EBDA_SEGMENT 0x40E
+#define BIOS_LOWMEM_KILOBYTES 0x413
 
+/*
+ * The BIOS places the EBDA/XBDA at the top of conventional
+ * memory, and usually decreases the reported amount of
+ * conventional memory (int 0x12) too.
+ */
 static __init void reserve_ebda(void)
 {
-       unsigned ebda_addr, ebda_size;
+       unsigned int lowmem, ebda_addr;
 
-       /*
-        * there is a real-mode segmented pointer pointing to the
-        * 4K EBDA area at 0x40E
-        */
-       ebda_addr = *(unsigned short *)__va(EBDA_ADDR_POINTER);
+       /* end of low (conventional) memory */
+       lowmem = *(unsigned short *)__va(BIOS_LOWMEM_KILOBYTES);
+       lowmem <<= 10;
+
+       /* start of EBDA area */
+       ebda_addr = *(unsigned short *)__va(BIOS_EBDA_SEGMENT);
        ebda_addr <<= 4;
 
-       if (!ebda_addr)
-               return;
+       /* Fixup: bios puts an EBDA in the top 64K segment */
+       /* of conventional memory, but does not adjust lowmem. */
+       if ((lowmem - ebda_addr) <= 0x10000)
+               lowmem = ebda_addr;
 
-       ebda_size = *(unsigned short *)__va(ebda_addr);
+       /* Fixup: bios does not report an EBDA at all. */
+       /* Some old Dells seem to need 4k anyhow (bugzilla 2990) */
+       if ((ebda_addr == 0) && (lowmem >= 0x9f000))
+               lowmem = 0x9f000;
 
-       /* Round EBDA up to pages */
-       if (ebda_size == 0)
-               ebda_size = 1;
-       ebda_size <<= 10;
-       ebda_size = round_up(ebda_size + (ebda_addr & ~PAGE_MASK), PAGE_SIZE);
-       if (ebda_size > 64*1024)
-               ebda_size = 64*1024;
+       /* Paranoia: should never happen, but... */
+       if (lowmem >= 0x100000)
+               lowmem = 0xa0000;
 
-       reserve_early(ebda_addr, ebda_addr + ebda_size, "EBDA");
+       /* reserve all memory between lowmem and the 1MB mark */
+       reserve_early(lowmem, 0x100000, "BIOS reserved");
 }
 
 void __init x86_64_start_kernel(char * real_mode_data)