Merge remote-tracking branch 'origin/x86/boot' into x86/mm2
[linux-2.6-block.git] / arch / x86 / mm / init_32.c
index 745d66b843c84241f1d849e865dc2796a00b6f1f..b299724f6e34e9ace068a4c7f39d504a7cc5c7b4 100644 (file)
 #include <asm/page_types.h>
 #include <asm/init.h>
 
+#include "mm_internal.h"
+
 unsigned long highstart_pfn, highend_pfn;
 
 static noinline int do_test_wp_bit(void);
 
 bool __read_mostly __vmalloc_start_set = false;
 
-static __init void *alloc_low_page(void)
-{
-       unsigned long pfn = pgt_buf_end++;
-       void *adr;
-
-       if (pfn >= pgt_buf_top)
-               panic("alloc_low_page: ran out of memory");
-
-       adr = __va(pfn * PAGE_SIZE);
-       clear_page(adr);
-       return adr;
-}
-
 /*
  * Creates a middle page table and puts a pointer to it in the
  * given global directory entry. This only returns the gd entry
@@ -84,10 +73,7 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd)
 
 #ifdef CONFIG_X86_PAE
        if (!(pgd_val(*pgd) & _PAGE_PRESENT)) {
-               if (after_bootmem)
-                       pmd_table = (pmd_t *)alloc_bootmem_pages(PAGE_SIZE);
-               else
-                       pmd_table = (pmd_t *)alloc_low_page();
+               pmd_table = (pmd_t *)alloc_low_page();
                paravirt_alloc_pmd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT);
                set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT));
                pud = pud_offset(pgd, 0);
@@ -109,17 +95,7 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd)
 static pte_t * __init one_page_table_init(pmd_t *pmd)
 {
        if (!(pmd_val(*pmd) & _PAGE_PRESENT)) {
-               pte_t *page_table = NULL;
-
-               if (after_bootmem) {
-#if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KMEMCHECK)
-                       page_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);
-#endif
-                       if (!page_table)
-                               page_table =
-                               (pte_t *)alloc_bootmem_pages(PAGE_SIZE);
-               } else
-                       page_table = (pte_t *)alloc_low_page();
+               pte_t *page_table = (pte_t *)alloc_low_page();
 
                paravirt_alloc_pte(&init_mm, __pa(page_table) >> PAGE_SHIFT);
                set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));
@@ -146,8 +122,39 @@ pte_t * __init populate_extra_pte(unsigned long vaddr)
        return one_page_table_init(pmd) + pte_idx;
 }
 
+static unsigned long __init
+page_table_range_init_count(unsigned long start, unsigned long end)
+{
+       unsigned long count = 0;
+#ifdef CONFIG_HIGHMEM
+       int pmd_idx_kmap_begin = fix_to_virt(FIX_KMAP_END) >> PMD_SHIFT;
+       int pmd_idx_kmap_end = fix_to_virt(FIX_KMAP_BEGIN) >> PMD_SHIFT;
+       int pgd_idx, pmd_idx;
+       unsigned long vaddr;
+
+       if (pmd_idx_kmap_begin == pmd_idx_kmap_end)
+               return 0;
+
+       vaddr = start;
+       pgd_idx = pgd_index(vaddr);
+
+       for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd_idx++) {
+               for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end);
+                                                       pmd_idx++) {
+                       if ((vaddr >> PMD_SHIFT) >= pmd_idx_kmap_begin &&
+                           (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end)
+                               count++;
+                       vaddr += PMD_SIZE;
+               }
+               pmd_idx = 0;
+       }
+#endif
+       return count;
+}
+
 static pte_t *__init page_table_kmap_check(pte_t *pte, pmd_t *pmd,
-                                          unsigned long vaddr, pte_t *lastpte)
+                                          unsigned long vaddr, pte_t *lastpte,
+                                          void **adr)
 {
 #ifdef CONFIG_HIGHMEM
        /*
@@ -161,16 +168,15 @@ static pte_t *__init page_table_kmap_check(pte_t *pte, pmd_t *pmd,
 
        if (pmd_idx_kmap_begin != pmd_idx_kmap_end
            && (vaddr >> PMD_SHIFT) >= pmd_idx_kmap_begin
-           && (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end
-           && ((__pa(pte) >> PAGE_SHIFT) < pgt_buf_start
-               || (__pa(pte) >> PAGE_SHIFT) >= pgt_buf_end)) {
+           && (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end) {
                pte_t *newpte;
                int i;
 
                BUG_ON(after_bootmem);
-               newpte = alloc_low_page();
+               newpte = *adr;
                for (i = 0; i < PTRS_PER_PTE; i++)
                        set_pte(newpte + i, pte[i]);
+               *adr = (void *)(((unsigned long)(*adr)) + PAGE_SIZE);
 
                paravirt_alloc_pte(&init_mm, __pa(newpte) >> PAGE_SHIFT);
                set_pmd(pmd, __pmd(__pa(newpte)|_PAGE_TABLE));
@@ -204,6 +210,11 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base)
        pgd_t *pgd;
        pmd_t *pmd;
        pte_t *pte = NULL;
+       unsigned long count = page_table_range_init_count(start, end);
+       void *adr = NULL;
+
+       if (count)
+               adr = alloc_low_pages(count);
 
        vaddr = start;
        pgd_idx = pgd_index(vaddr);
@@ -216,7 +227,7 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base)
                for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end);
                                                        pmd++, pmd_idx++) {
                        pte = page_table_kmap_check(one_page_table_init(pmd),
-                                                   pmd, vaddr, pte);
+                                                   pmd, vaddr, pte, &adr);
 
                        vaddr += PMD_SIZE;
                }
@@ -310,6 +321,7 @@ repeat:
                                        __pgprot(PTE_IDENT_ATTR |
                                                 _PAGE_PSE);
 
+                               pfn &= PMD_MASK >> PAGE_SHIFT;
                                addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE +
                                        PAGE_OFFSET + PAGE_SIZE-1;
 
@@ -455,9 +467,14 @@ void __init native_pagetable_init(void)
 
        /*
         * Remove any mappings which extend past the end of physical
-        * memory from the boot time page table:
+        * memory from the boot time page table.
+        * In virtual address space, we should have at least two pages
+        * from VMALLOC_END to pkmap or fixmap according to VMALLOC_END
+        * definition. And max_low_pfn is set to VMALLOC_END physical
+        * address. If initial memory mapping is doing right job, we
+        * should have pte used near max_low_pfn or one pmd is not present.
         */
-       for (pfn = max_low_pfn + 1; pfn < 1<<(32-PAGE_SHIFT); pfn++) {
+       for (pfn = max_low_pfn; pfn < 1<<(32-PAGE_SHIFT); pfn++) {
                va = PAGE_OFFSET + (pfn<<PAGE_SHIFT);
                pgd = base + pgd_index(va);
                if (!pgd_present(*pgd))
@@ -468,10 +485,19 @@ void __init native_pagetable_init(void)
                if (!pmd_present(*pmd))
                        break;
 
+               /* should not be large page here */
+               if (pmd_large(*pmd)) {
+                       pr_warn("try to clear pte for ram above max_low_pfn: pfn: %lx pmd: %p pmd phys: %lx, but pmd is big page and is not using pte !\n",
+                               pfn, pmd, __pa(pmd));
+                       BUG_ON(1);
+               }
+
                pte = pte_offset_kernel(pmd, va);
                if (!pte_present(*pte))
                        break;
 
+               printk(KERN_DEBUG "clearing pte for ram above max_low_pfn: pfn: %lx pmd: %p pmd phys: %lx pte: %p pte phys: %lx\n",
+                               pfn, pmd, __pa(pmd), pte, __pa(pte));
                pte_clear(NULL, va, pte);
        }
        paravirt_alloc_pmd(&init_mm, __pa(base) >> PAGE_SHIFT);
@@ -550,7 +576,7 @@ early_param("highmem", parse_highmem);
  * artificially via the highmem=x boot parameter then create
  * it:
  */
-void __init lowmem_pfn_init(void)
+static void __init lowmem_pfn_init(void)
 {
        /* max_low_pfn is 0, we already have early_res support */
        max_low_pfn = max_pfn;
@@ -586,7 +612,7 @@ void __init lowmem_pfn_init(void)
  * We have more RAM than fits into lowmem - we try to put it into
  * highmem, also taking the highmem=x boot parameter into account:
  */
-void __init highmem_pfn_init(void)
+static void __init highmem_pfn_init(void)
 {
        max_low_pfn = MAXMEM_PFN;
 
@@ -669,8 +695,6 @@ void __init setup_bootmem_allocator(void)
        printk(KERN_INFO "  mapped low ram: 0 - %08lx\n",
                 max_pfn_mapped<<PAGE_SHIFT);
        printk(KERN_INFO "  low ram: 0 - %08lx\n", max_low_pfn<<PAGE_SHIFT);
-
-       after_bootmem = 1;
 }
 
 /*
@@ -753,6 +777,8 @@ void __init mem_init(void)
                if (page_is_ram(tmp) && PageReserved(pfn_to_page(tmp)))
                        reservedpages++;
 
+       after_bootmem = 1;
+
        codesize =  (unsigned long) &_etext - (unsigned long) &_text;
        datasize =  (unsigned long) &_edata - (unsigned long) &_etext;
        initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;