powerpc/fsl_booke: make sure PAGE_OFFSET map to memstart_addr for relocatable kernel
[pandora-kernel.git] / arch / powerpc / mm / fsl_booke_mmu.c
index ca956c8..95deb9f 100644 (file)
@@ -231,7 +231,7 @@ void __init adjust_total_lowmem(void)
 
        i = switch_to_as1();
        __max_low_memory = map_mem_in_cams(ram, CONFIG_LOWMEM_CAM_NUM);
-       restore_to_as0(i);
+       restore_to_as0(i, 0, 0);
 
        pr_info("Memory CAM mapping: ");
        for (i = 0; i < tlbcam_index - 1; i++)
@@ -252,17 +252,25 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base,
 }
 
 #ifdef CONFIG_RELOCATABLE
-notrace void __init relocate_init(phys_addr_t start)
+int __initdata is_second_reloc;
+notrace void __init relocate_init(u64 dt_ptr, phys_addr_t start)
 {
        unsigned long base = KERNELBASE;
 
+       kernstart_addr = start;
+       if (is_second_reloc) {
+               virt_phys_offset = PAGE_OFFSET - memstart_addr;
+               return;
+       }
+
        /*
         * Relocatable kernel support based on processing of dynamic
-        * relocation entries.
-        * Compute the virt_phys_offset :
+        * relocation entries. Before we get the real memstart_addr,
+        * We will compute the virt_phys_offset like this:
         * virt_phys_offset = stext.run - kernstart_addr
         *
-        * stext.run = (KERNELBASE & ~0x3ffffff) + (kernstart_addr & 0x3ffffff)
+        * stext.run = (KERNELBASE & ~0x3ffffff) +
+        *                              (kernstart_addr & 0x3ffffff)
         * When we relocate, we have :
         *
         *      (kernstart_addr & 0x3ffffff) = (stext.run & 0x3ffffff)
@@ -272,10 +280,32 @@ notrace void __init relocate_init(phys_addr_t start)
         *                              (kernstart_addr & ~0x3ffffff)
         *
         */
-       kernstart_addr = start;
        start &= ~0x3ffffff;
        base &= ~0x3ffffff;
        virt_phys_offset = base - start;
+       early_get_first_memblock_info(__va(dt_ptr), NULL);
+       /*
+        * We now get the memstart_addr, then we should check if this
+        * address is the same as what the PAGE_OFFSET map to now. If
+        * not we have to change the map of PAGE_OFFSET to memstart_addr
+        * and do a second relocation.
+        */
+       if (start != memstart_addr) {
+               int n;
+               long offset = start - memstart_addr;
+
+               is_second_reloc = 1;
+               n = switch_to_as1();
+               /* map a 64M area for the second relocation */
+               if (memstart_addr > start)
+                       map_mem_in_cams(0x4000000, CONFIG_LOWMEM_CAM_NUM);
+               else
+                       map_mem_in_cams_addr(start, PAGE_OFFSET + offset,
+                                       0x4000000, CONFIG_LOWMEM_CAM_NUM);
+               restore_to_as0(n, offset, __va(dt_ptr));
+               /* We should never reach here */
+               panic("Relocation error");
+       }
 }
 #endif
 #endif