[PATCH] x86-64: Fix harmless off by one in e820 code
[pandora-kernel.git] / arch / x86_64 / kernel / e820.c
index 657003e..eb7929e 100644 (file)
@@ -2,6 +2,12 @@
  * Handle the memory map.
  * The functions here do the job until bootmem takes over.
  * $Id: e820.c,v 1.4 2002/09/19 19:25:32 ak Exp $
+ *
+ *  Getting sanitize_e820_map() in sync with i386 version by applying change:
+ *  -  Provisions for empty E820 memory regions (reported by certain BIOSes).
+ *     Alex Achenbach <xela@slit.de>, December 2002.
+ *  Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+ *
  */
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -10,6 +16,7 @@
 #include <linux/bootmem.h>
 #include <linux/ioport.h>
 #include <linux/string.h>
+#include <linux/kexec.h>
 #include <asm/page.h>
 #include <asm/e820.h>
 #include <asm/proto.h>
@@ -78,7 +85,7 @@ int __init e820_mapped(unsigned long start, unsigned long end, unsigned type)
                struct e820entry *ei = &e820.map[i]; 
                if (type && ei->type != type) 
                        continue;
-               if (ei->addr >= end || ei->addr + ei->size < start) 
+               if (ei->addr >= end || ei->addr + ei->size <= start)
                        continue; 
                return 1; 
        } 
@@ -124,7 +131,7 @@ void __init e820_bootmem_free(pg_data_t *pgdat, unsigned long start,unsigned lon
 
                if (ei->type != E820_RAM || 
                    ei->addr+ei->size <= start || 
-                   ei->addr > end)
+                   ei->addr >= end)
                        continue;
 
                addr = round_up(ei->addr, PAGE_SIZE);
@@ -178,6 +185,40 @@ unsigned long __init e820_end_of_ram(void)
 }
 
 /* 
+ * Compute how much memory is missing in a range.
+ * Unlike the other functions in this file the arguments are in page numbers.
+ */
+unsigned long __init
+e820_hole_size(unsigned long start_pfn, unsigned long end_pfn)
+{
+       unsigned long ram = 0;
+       unsigned long start = start_pfn << PAGE_SHIFT;
+       unsigned long end = end_pfn << PAGE_SHIFT;
+       int i;
+       for (i = 0; i < e820.nr_map; i++) {
+               struct e820entry *ei = &e820.map[i];
+               unsigned long last, addr;
+
+               if (ei->type != E820_RAM ||
+                   ei->addr+ei->size <= start ||
+                   ei->addr >= end)
+                       continue;
+
+               addr = round_up(ei->addr, PAGE_SIZE);
+               if (addr < start)
+                       addr = start;
+
+               last = round_down(ei->addr + ei->size, PAGE_SIZE);
+               if (last >= end)
+                       last = end;
+
+               if (last > addr)
+                       ram += last - addr;
+       }
+       return ((end - start) - ram) >> PAGE_SHIFT;
+}
+
+/*
  * Mark e820 reserved areas as busy for the resource manager.
  */
 void __init e820_reserve_resources(void)
@@ -185,8 +226,6 @@ void __init e820_reserve_resources(void)
        int i;
        for (i = 0; i < e820.nr_map; i++) {
                struct resource *res;
-               if (e820.map[i].addr + e820.map[i].size > 0x100000000ULL)
-                       continue;
                res = alloc_bootmem_low(sizeof(struct resource));
                switch (e820.map[i].type) {
                case E820_RAM:  res->name = "System RAM"; break;
@@ -206,6 +245,9 @@ void __init e820_reserve_resources(void)
                         */
                        request_resource(res, &code_resource);
                        request_resource(res, &data_resource);
+#ifdef CONFIG_KEXEC
+                       request_resource(res, &crashk_res);
+#endif
                }
        }
 }
@@ -277,7 +319,7 @@ static int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map)
        int chgidx, still_changing;
        int overlap_entries;
        int new_bios_entry;
-       int old_nr, new_nr;
+       int old_nr, new_nr, chg_nr;
        int i;
 
        /*
@@ -331,20 +373,24 @@ static int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map)
        for (i=0; i < 2*old_nr; i++)
                change_point[i] = &change_point_list[i];
 
-       /* record all known change-points (starting and ending addresses) */
+       /* record all known change-points (starting and ending addresses),
+          omitting those that are for empty memory regions */
        chgidx = 0;
        for (i=0; i < old_nr; i++)      {
-               change_point[chgidx]->addr = biosmap[i].addr;
-               change_point[chgidx++]->pbios = &biosmap[i];
-               change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size;
-               change_point[chgidx++]->pbios = &biosmap[i];
+               if (biosmap[i].size != 0) {
+                       change_point[chgidx]->addr = biosmap[i].addr;
+                       change_point[chgidx++]->pbios = &biosmap[i];
+                       change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size;
+                       change_point[chgidx++]->pbios = &biosmap[i];
+               }
        }
+       chg_nr = chgidx;
 
        /* sort change-point list by memory addresses (low -> high) */
        still_changing = 1;
        while (still_changing)  {
                still_changing = 0;
-               for (i=1; i < 2*old_nr; i++)  {
+               for (i=1; i < chg_nr; i++)  {
                        /* if <current_addr> > <last_addr>, swap */
                        /* or, if current=<start_addr> & last=<end_addr>, swap */
                        if ((change_point[i]->addr < change_point[i-1]->addr) ||
@@ -367,7 +413,7 @@ static int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map)
        last_type = 0;           /* start with undefined memory type */
        last_addr = 0;           /* start with 0 as last starting address */
        /* loop through change-points, determining affect on the new bios map */
-       for (chgidx=0; chgidx < 2*old_nr; chgidx++)
+       for (chgidx=0; chgidx < chg_nr; chgidx++)
        {
                /* keep track of all overlapping bios entries */
                if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr)
@@ -511,3 +557,62 @@ void __init parse_memopt(char *p, char **from)
        end_user_pfn >>= PAGE_SHIFT;    
 } 
 
+unsigned long pci_mem_start = 0xaeedbabe;
+
+/*
+ * Search for the biggest gap in the low 32 bits of the e820
+ * memory space.  We pass this space to PCI to assign MMIO resources
+ * for hotplug or unconfigured devices in.
+ * Hopefully the BIOS let enough space left.
+ */
+__init void e820_setup_gap(void)
+{
+       unsigned long gapstart, gapsize, round;
+       unsigned long last;
+       int i;
+       int found = 0;
+
+       last = 0x100000000ull;
+       gapstart = 0x10000000;
+       gapsize = 0x400000;
+       i = e820.nr_map;
+       while (--i >= 0) {
+               unsigned long long start = e820.map[i].addr;
+               unsigned long long end = start + e820.map[i].size;
+
+               /*
+                * Since "last" is at most 4GB, we know we'll
+                * fit in 32 bits if this condition is true
+                */
+               if (last > end) {
+                       unsigned long gap = last - end;
+
+                       if (gap > gapsize) {
+                               gapsize = gap;
+                               gapstart = end;
+                               found = 1;
+                       }
+               }
+               if (start < last)
+                       last = start;
+       }
+
+       if (!found) {
+               gapstart = (end_pfn << PAGE_SHIFT) + 1024*1024;
+               printk(KERN_ERR "PCI: Warning: Cannot find a gap in the 32bit address range\n"
+                      KERN_ERR "PCI: Unassigned devices with 32bit resource registers may break!\n");
+       }
+
+       /*
+        * See how much we want to round up: start off with
+        * rounding to the next 1MB area.
+        */
+       round = 0x100000;
+       while ((gapsize >> 4) > round)
+               round += round;
+       /* Fun with two's complement */
+       pci_mem_start = (gapstart + round) & -round;
+
+       printk(KERN_INFO "Allocating PCI resources starting at %lx (gap: %lx:%lx)\n",
+               pci_mem_start, gapstart, gapsize);
+}