Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/pci-2.6
[pandora-kernel.git] / arch / x86 / kernel / e820_64.c
index 8e73211..cbd42e5 100644 (file)
@@ -27,6 +27,7 @@
 #include <asm/setup.h>
 #include <asm/sections.h>
 #include <asm/kdebug.h>
+#include <asm/trampoline.h>
 
 struct e820map e820;
 
@@ -36,69 +37,118 @@ struct e820map e820;
 unsigned long end_pfn;
 
 /*
- * end_pfn only includes RAM, while end_pfn_map includes all e820 entries.
- * The direct mapping extends to end_pfn_map, so that we can directly access
+ * end_pfn only includes RAM, while max_pfn_mapped includes all e820 entries.
+ * The direct mapping extends to max_pfn_mapped, so that we can directly access
  * apertures, ACPI and other tables without having to play with fixmaps.
  */
-unsigned long end_pfn_map;
+unsigned long max_pfn_mapped;
 
 /*
  * Last pfn which the user wants to use.
  */
 static unsigned long __initdata end_user_pfn = MAXMEM>>PAGE_SHIFT;
 
-/* Check for some hardcoded bad areas that early boot is not allowed to touch */
-static inline int bad_addr(unsigned long *addrp, unsigned long size)
-{
-       unsigned long addr = *addrp, last = addr + size;
+/*
+ * Early reserved memory areas.
+ */
+#define MAX_EARLY_RES 20
+
+struct early_res {
+       unsigned long start, end;
+       char name[16];
+};
+static struct early_res early_res[MAX_EARLY_RES] __initdata = {
+       { 0, PAGE_SIZE, "BIOS data page" },                     /* BIOS data page */
+#ifdef CONFIG_X86_TRAMPOLINE
+       { TRAMPOLINE_BASE, TRAMPOLINE_BASE + 2 * PAGE_SIZE, "TRAMPOLINE" },
+#endif
+       {}
+};
 
-       /* various gunk below that needed for SMP startup */
-       if (addr < 0x8000) {
-               *addrp = PAGE_ALIGN(0x8000);
-               return 1;
+void __init reserve_early(unsigned long start, unsigned long end, char *name)
+{
+       int i;
+       struct early_res *r;
+       for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
+               r = &early_res[i];
+               if (end > r->start && start < r->end)
+                       panic("Overlapping early reservations %lx-%lx %s to %lx-%lx %s\n",
+                             start, end - 1, name?name:"", r->start, r->end - 1, r->name);
        }
+       if (i >= MAX_EARLY_RES)
+               panic("Too many early reservations");
+       r = &early_res[i];
+       r->start = start;
+       r->end = end;
+       if (name)
+               strncpy(r->name, name, sizeof(r->name) - 1);
+}
 
-       /* direct mapping tables of the kernel */
-       if (last >= table_start<<PAGE_SHIFT && addr < table_end<<PAGE_SHIFT) {
-               *addrp = PAGE_ALIGN(table_end << PAGE_SHIFT);
-               return 1;
+void __init early_res_to_bootmem(void)
+{
+       int i;
+       for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
+               struct early_res *r = &early_res[i];
+               printk(KERN_INFO "early res: %d [%lx-%lx] %s\n", i,
+                       r->start, r->end - 1, r->name);
+               reserve_bootmem_generic(r->start, r->end - r->start);
        }
+}
 
-       /* initrd */
-#ifdef CONFIG_BLK_DEV_INITRD
-       if (boot_params.hdr.type_of_loader && boot_params.hdr.ramdisk_image) {
-               unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
-               unsigned long ramdisk_size  = boot_params.hdr.ramdisk_size;
-               unsigned long ramdisk_end   = ramdisk_image+ramdisk_size;
-
-               if (last >= ramdisk_image && addr < ramdisk_end) {
-                       *addrp = PAGE_ALIGN(ramdisk_end);
-                       return 1;
+/* Check for already reserved areas */
+static inline int __init
+bad_addr(unsigned long *addrp, unsigned long size, unsigned long align)
+{
+       int i;
+       unsigned long addr = *addrp, last;
+       int changed = 0;
+again:
+       last = addr + size;
+       for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
+               struct early_res *r = &early_res[i];
+               if (last >= r->start && addr < r->end) {
+                       *addrp = addr = round_up(r->end, align);
+                       changed = 1;
+                       goto again;
                }
        }
-#endif
-       /* kernel code */
-       if (last >= __pa_symbol(&_text) && addr < __pa_symbol(&_end)) {
-               *addrp = PAGE_ALIGN(__pa_symbol(&_end));
-               return 1;
-       }
+       return changed;
+}
 
-       if (last >= ebda_addr && addr < ebda_addr + ebda_size) {
-               *addrp = PAGE_ALIGN(ebda_addr + ebda_size);
-               return 1;
+/* Check for already reserved areas */
+static inline int __init
+bad_addr_size(unsigned long *addrp, unsigned long *sizep, unsigned long align)
+{
+       int i;
+       unsigned long addr = *addrp, last;
+       unsigned long size = *sizep;
+       int changed = 0;
+again:
+       last = addr + size;
+       for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
+               struct early_res *r = &early_res[i];
+               if (last > r->start && addr < r->start) {
+                       size = r->start - addr;
+                       changed = 1;
+                       goto again;
+               }
+               if (last > r->end && addr < r->end) {
+                       addr = round_up(r->end, align);
+                       size = last - addr;
+                       changed = 1;
+                       goto again;
+               }
+               if (last <= r->end && addr >= r->start) {
+                       (*sizep)++;
+                       return 0;
+               }
        }
-
-#ifdef CONFIG_NUMA
-       /* NUMA memory to node map */
-       if (last >= nodemap_addr && addr < nodemap_addr + nodemap_size) {
-               *addrp = nodemap_addr + nodemap_size;
-               return 1;
+       if (changed) {
+               *addrp = addr;
+               *sizep = size;
        }
-#endif
-       /* XXX ramdisk image here? */
-       return 0;
+       return changed;
 }
-
 /*
  * This function checks if any part of the range <start,end> is mapped
  * with type.
@@ -157,27 +207,30 @@ int __init e820_all_mapped(unsigned long start, unsigned long end,
 }
 
 /*
- * Find a free area in a specific range.
+ * Find a free area with specified alignment in a specific range.
  */
 unsigned long __init find_e820_area(unsigned long start, unsigned long end,
-                                   unsigned size)
+                                   unsigned long size, unsigned long align)
 {
        int i;
 
        for (i = 0; i < e820.nr_map; i++) {
                struct e820entry *ei = &e820.map[i];
-               unsigned long addr = ei->addr, last;
+               unsigned long addr, last;
+               unsigned long ei_last;
 
                if (ei->type != E820_RAM)
                        continue;
+               addr = round_up(ei->addr, align);
+               ei_last = ei->addr + ei->size;
                if (addr < start)
-                       addr = start;
-               if (addr > ei->addr + ei->size)
+                       addr = round_up(start, align);
+               if (addr >= ei_last)
                        continue;
-               while (bad_addr(&addr, size) && addr+size <= ei->addr+ei->size)
+               while (bad_addr(&addr, size, align) && addr+size <= ei_last)
                        ;
-               last = PAGE_ALIGN(addr) + size;
-               if (last > ei->addr + ei->size)
+               last = addr + size;
+               if (last > ei_last)
                        continue;
                if (last > end)
                        continue;
@@ -186,6 +239,40 @@ unsigned long __init find_e820_area(unsigned long start, unsigned long end,
        return -1UL;
 }
 
+/*
+ * Find next free range after *start
+ */
+unsigned long __init find_e820_area_size(unsigned long start,
+                                        unsigned long *sizep,
+                                        unsigned long align)
+{
+       int i;
+
+       for (i = 0; i < e820.nr_map; i++) {
+               struct e820entry *ei = &e820.map[i];
+               unsigned long addr, last;
+               unsigned long ei_last;
+
+               if (ei->type != E820_RAM)
+                       continue;
+               addr = round_up(ei->addr, align);
+               ei_last = ei->addr + ei->size;
+               if (addr < start)
+                       addr = round_up(start, align);
+               if (addr >= ei_last)
+                       continue;
+               *sizep = ei_last - addr;
+               while (bad_addr_size(&addr, sizep, align) &&
+                       addr + *sizep <= ei_last)
+                       ;
+               last = addr + *sizep;
+               if (last > ei_last)
+                       continue;
+               return addr;
+       }
+       return -1UL;
+
+}
 /*
  * Find the highest page frame number we have available
  */
@@ -195,29 +282,29 @@ unsigned long __init e820_end_of_ram(void)
 
        end_pfn = find_max_pfn_with_active_regions();
 
-       if (end_pfn > end_pfn_map)
-               end_pfn_map = end_pfn;
-       if (end_pfn_map > MAXMEM>>PAGE_SHIFT)
-               end_pfn_map = MAXMEM>>PAGE_SHIFT;
+       if (end_pfn > max_pfn_mapped)
+               max_pfn_mapped = end_pfn;
+       if (max_pfn_mapped > MAXMEM>>PAGE_SHIFT)
+               max_pfn_mapped = MAXMEM>>PAGE_SHIFT;
        if (end_pfn > end_user_pfn)
                end_pfn = end_user_pfn;
-       if (end_pfn > end_pfn_map)
-               end_pfn = end_pfn_map;
+       if (end_pfn > max_pfn_mapped)
+               end_pfn = max_pfn_mapped;
 
-       printk(KERN_INFO "end_pfn_map = %lu\n", end_pfn_map);
+       printk(KERN_INFO "max_pfn_mapped = %lu\n", max_pfn_mapped);
        return end_pfn;
 }
 
 /*
  * Mark e820 reserved areas as busy for the resource manager.
  */
-void __init e820_reserve_resources(struct resource *code_resource,
-               struct resource *data_resource, struct resource *bss_resource)
+void __init e820_reserve_resources(void)
 {
        int i;
+       struct resource *res;
+
+       res = alloc_bootmem_low(sizeof(struct resource) * e820.nr_map);
        for (i = 0; i < e820.nr_map; i++) {
-               struct resource *res;
-               res = alloc_bootmem_low(sizeof(struct resource));
                switch (e820.map[i].type) {
                case E820_RAM:  res->name = "System RAM"; break;
                case E820_ACPI: res->name = "ACPI Tables"; break;
@@ -227,21 +314,8 @@ void __init e820_reserve_resources(struct resource *code_resource,
                res->start = e820.map[i].addr;
                res->end = res->start + e820.map[i].size - 1;
                res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-               request_resource(&iomem_resource, res);
-               if (e820.map[i].type == E820_RAM) {
-                       /*
-                        * We don't know which RAM region contains kernel data,
-                        * so we try it repeatedly and let the resource manager
-                        * test it.
-                        */
-                       request_resource(res, code_resource);
-                       request_resource(res, data_resource);
-                       request_resource(res, bss_resource);
-#ifdef CONFIG_KEXEC
-                       if (crashk_res.start != crashk_res.end)
-                               request_resource(res, &crashk_res);
-#endif
-               }
+               insert_resource(&iomem_resource, res);
+               res++;
        }
 }
 
@@ -293,9 +367,9 @@ static int __init e820_find_active_region(const struct e820entry *ei,
        if (*ei_startpfn >= *ei_endpfn)
                return 0;
 
-       /* Check if end_pfn_map should be updated */
-       if (ei->type != E820_RAM && *ei_endpfn > end_pfn_map)
-               end_pfn_map = *ei_endpfn;
+       /* Check if max_pfn_mapped should be updated */
+       if (ei->type != E820_RAM && *ei_endpfn > max_pfn_mapped)
+               max_pfn_mapped = *ei_endpfn;
 
        /* Skip if map is outside the node */
        if (ei->type != E820_RAM || *ei_endpfn <= start_pfn ||
@@ -618,10 +692,10 @@ static int __init copy_e820_map(struct e820entry *biosmap, int nr_map)
                return -1;
 
        do {
-               unsigned long start = biosmap->addr;
-               unsigned long size = biosmap->size;
-               unsigned long end = start + size;
-               unsigned long type = biosmap->type;
+               u64 start = biosmap->addr;
+               u64 size = biosmap->size;
+               u64 end = start + size;
+               u32 type = biosmap->type;
 
                /* Overflow in 64 bits? Ignore the memory map. */
                if (start > end)
@@ -638,8 +712,10 @@ static void early_panic(char *msg)
        panic(msg);
 }
 
-void __init setup_memory_region(void)
+/* We're not void only for x86 32-bit compat */
+char * __init machine_specific_memory_setup(void)
 {
+       char *who = "BIOS-e820";
        /*
         * Try to copy the BIOS-supplied E820-map.
         *
@@ -650,7 +726,10 @@ void __init setup_memory_region(void)
        if (copy_e820_map(boot_params.e820_map, boot_params.e820_entries) < 0)
                early_panic("Cannot find a valid memory map");
        printk(KERN_INFO "BIOS-provided physical RAM map:\n");
-       e820_print_map("BIOS-e820");
+       e820_print_map(who);
+
+       /* In case someone cares... */
+       return who;
 }
 
 static int __init parse_memopt(char *p)
@@ -681,7 +760,7 @@ static int __init parse_memmap_opt(char *p)
                saved_max_pfn = e820_end_of_ram();
                remove_all_active_ranges();
 #endif
-               end_pfn_map = 0;
+               max_pfn_mapped = 0;
                e820.nr_map = 0;
                userdef = 1;
                return 0;
@@ -723,6 +802,45 @@ void __init finish_e820_parsing(void)
        }
 }
 
+void __init update_memory_range(u64 start, u64 size, unsigned old_type,
+                               unsigned new_type)
+{
+       int i;
+
+       BUG_ON(old_type == new_type);
+
+       for (i = 0; i < e820.nr_map; i++) {
+               struct e820entry *ei = &e820.map[i];
+               u64 final_start, final_end;
+               if (ei->type != old_type)
+                       continue;
+               /* totally covered? */
+               if (ei->addr >= start && ei->size <= size) {
+                       ei->type = new_type;
+                       continue;
+               }
+               /* partially covered */
+               final_start = max(start, ei->addr);
+               final_end = min(start + size, ei->addr + ei->size);
+               if (final_start >= final_end)
+                       continue;
+               add_memory_region(final_start, final_end - final_start,
+                                        new_type);
+       }
+}
+
+void __init update_e820(void)
+{
+       u8 nr_map;
+
+       nr_map = e820.nr_map;
+       if (sanitize_e820_map(e820.map, &nr_map))
+               return;
+       e820.nr_map = nr_map;
+       printk(KERN_INFO "modified physical RAM map:\n");
+       e820_print_map("modified");
+}
+
 unsigned long pci_mem_start = 0xaeedbabe;
 EXPORT_SYMBOL(pci_mem_start);