1ad757b6aSThomas Gleixner /* 2ad757b6aSThomas Gleixner * 3ad757b6aSThomas Gleixner * Copyright (C) 1995 Linus Torvalds 4ad757b6aSThomas Gleixner * 5ad757b6aSThomas Gleixner * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 6ad757b6aSThomas Gleixner */ 7ad757b6aSThomas Gleixner 8ad757b6aSThomas Gleixner #include <linux/module.h> 9ad757b6aSThomas Gleixner #include <linux/signal.h> 10ad757b6aSThomas Gleixner #include <linux/sched.h> 11ad757b6aSThomas Gleixner #include <linux/kernel.h> 12ad757b6aSThomas Gleixner #include <linux/errno.h> 13ad757b6aSThomas Gleixner #include <linux/string.h> 14ad757b6aSThomas Gleixner #include <linux/types.h> 15ad757b6aSThomas Gleixner #include <linux/ptrace.h> 16ad757b6aSThomas Gleixner #include <linux/mman.h> 17ad757b6aSThomas Gleixner #include <linux/mm.h> 18ad757b6aSThomas Gleixner #include <linux/hugetlb.h> 19ad757b6aSThomas Gleixner #include <linux/swap.h> 20ad757b6aSThomas Gleixner #include <linux/smp.h> 21ad757b6aSThomas Gleixner #include <linux/init.h> 22ad757b6aSThomas Gleixner #include <linux/highmem.h> 23ad757b6aSThomas Gleixner #include <linux/pagemap.h> 24cfb80c9eSJeremy Fitzhardinge #include <linux/pci.h> 25ad757b6aSThomas Gleixner #include <linux/pfn.h> 26ad757b6aSThomas Gleixner #include <linux/poison.h> 27ad757b6aSThomas Gleixner #include <linux/bootmem.h> 28a9ce6bc1SYinghai Lu #include <linux/memblock.h> 29ad757b6aSThomas Gleixner #include <linux/proc_fs.h> 30ad757b6aSThomas Gleixner #include <linux/memory_hotplug.h> 31ad757b6aSThomas Gleixner #include <linux/initrd.h> 32ad757b6aSThomas Gleixner #include <linux/cpumask.h> 335a0e3ad6STejun Heo #include <linux/gfp.h> 34ad757b6aSThomas Gleixner 35f832ff18SH. Peter Anvin #include <asm/asm.h> 3646eaa670SIngo Molnar #include <asm/bios_ebda.h> 37ad757b6aSThomas Gleixner #include <asm/processor.h> 38ad757b6aSThomas Gleixner #include <asm/system.h> 39ad757b6aSThomas Gleixner #include <asm/uaccess.h> 40ad757b6aSThomas Gleixner #include <asm/pgtable.h> 41ad757b6aSThomas Gleixner #include <asm/dma.h> 42ad757b6aSThomas Gleixner #include <asm/fixmap.h> 43ad757b6aSThomas Gleixner #include <asm/e820.h> 44ad757b6aSThomas Gleixner #include <asm/apic.h> 458550eb99SIngo Molnar #include <asm/bugs.h> 46ad757b6aSThomas Gleixner #include <asm/tlb.h> 47ad757b6aSThomas Gleixner #include <asm/tlbflush.h> 48c10d1e26SAndres Salomon #include <asm/olpc_ofw.h> 49a5a19c63SJeremy Fitzhardinge #include <asm/pgalloc.h> 50ad757b6aSThomas Gleixner #include <asm/sections.h> 51ad757b6aSThomas Gleixner #include <asm/paravirt.h> 52551889a6SIan Campbell #include <asm/setup.h> 537bfeab9aSHarvey Harrison #include <asm/cacheflush.h> 542b72394eSPekka Enberg #include <asm/page_types.h> 554fcb2083SPekka Enberg #include <asm/init.h> 56ad757b6aSThomas Gleixner 57ad757b6aSThomas Gleixner unsigned long highstart_pfn, highend_pfn; 58ad757b6aSThomas Gleixner 598550eb99SIngo Molnar static noinline int do_test_wp_bit(void); 60ad757b6aSThomas Gleixner 61dc16ecf7SJeremy Fitzhardinge bool __read_mostly __vmalloc_start_set = false; 624e29684cSYinghai Lu 63d6be89adSJan Beulich static __init void *alloc_low_page(void) 644e29684cSYinghai Lu { 65d1b19426SYinghai Lu unsigned long pfn = pgt_buf_end++; 664e29684cSYinghai Lu void *adr; 674e29684cSYinghai Lu 68d1b19426SYinghai Lu if (pfn >= pgt_buf_top) 694e29684cSYinghai Lu panic("alloc_low_page: ran out of memory"); 704e29684cSYinghai Lu 714e29684cSYinghai Lu adr = __va(pfn * PAGE_SIZE); 72234bb549SJan Beulich clear_page(adr); 734e29684cSYinghai Lu return adr; 744e29684cSYinghai Lu } 754e29684cSYinghai Lu 76ad757b6aSThomas Gleixner /* 77ad757b6aSThomas Gleixner * Creates a middle page table and puts a pointer to it in the 78ad757b6aSThomas Gleixner * given global directory entry. This only returns the gd entry 79ad757b6aSThomas Gleixner * in non-PAE compilation mode, since the middle layer is folded. 80ad757b6aSThomas Gleixner */ 81ad757b6aSThomas Gleixner static pmd_t * __init one_md_table_init(pgd_t *pgd) 82ad757b6aSThomas Gleixner { 83ad757b6aSThomas Gleixner pud_t *pud; 84ad757b6aSThomas Gleixner pmd_t *pmd_table; 85ad757b6aSThomas Gleixner 86ad757b6aSThomas Gleixner #ifdef CONFIG_X86_PAE 87ad757b6aSThomas Gleixner if (!(pgd_val(*pgd) & _PAGE_PRESENT)) { 88c464573cSPekka Enberg if (after_bootmem) 893c1596efSJan Beulich pmd_table = (pmd_t *)alloc_bootmem_pages(PAGE_SIZE); 904e29684cSYinghai Lu else 91d6be89adSJan Beulich pmd_table = (pmd_t *)alloc_low_page(); 926944a9c8SJeremy Fitzhardinge paravirt_alloc_pmd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT); 93ad757b6aSThomas Gleixner set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); 94ad757b6aSThomas Gleixner pud = pud_offset(pgd, 0); 958550eb99SIngo Molnar BUG_ON(pmd_table != pmd_offset(pud, 0)); 96a376f30aSZhaolei 97a376f30aSZhaolei return pmd_table; 98ad757b6aSThomas Gleixner } 99ad757b6aSThomas Gleixner #endif 100ad757b6aSThomas Gleixner pud = pud_offset(pgd, 0); 101ad757b6aSThomas Gleixner pmd_table = pmd_offset(pud, 0); 1028550eb99SIngo Molnar 103ad757b6aSThomas Gleixner return pmd_table; 104ad757b6aSThomas Gleixner } 105ad757b6aSThomas Gleixner 106ad757b6aSThomas Gleixner /* 107ad757b6aSThomas Gleixner * Create a page table and place a pointer to it in a middle page 1088550eb99SIngo Molnar * directory entry: 109ad757b6aSThomas Gleixner */ 110ad757b6aSThomas Gleixner static pte_t * __init one_page_table_init(pmd_t *pmd) 111ad757b6aSThomas Gleixner { 112ad757b6aSThomas Gleixner if (!(pmd_val(*pmd) & _PAGE_PRESENT)) { 113509a80c4SIngo Molnar pte_t *page_table = NULL; 114509a80c4SIngo Molnar 115c464573cSPekka Enberg if (after_bootmem) { 116f8561296SVegard Nossum #if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KMEMCHECK) 117509a80c4SIngo Molnar page_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE); 118509a80c4SIngo Molnar #endif 1194e29684cSYinghai Lu if (!page_table) 120509a80c4SIngo Molnar page_table = 1213c1596efSJan Beulich (pte_t *)alloc_bootmem_pages(PAGE_SIZE); 122d6be89adSJan Beulich } else 123d6be89adSJan Beulich page_table = (pte_t *)alloc_low_page(); 124ad757b6aSThomas Gleixner 1256944a9c8SJeremy Fitzhardinge paravirt_alloc_pte(&init_mm, __pa(page_table) >> PAGE_SHIFT); 126ad757b6aSThomas Gleixner set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); 127ad757b6aSThomas Gleixner BUG_ON(page_table != pte_offset_kernel(pmd, 0)); 128ad757b6aSThomas Gleixner } 129ad757b6aSThomas Gleixner 130ad757b6aSThomas Gleixner return pte_offset_kernel(pmd, 0); 131ad757b6aSThomas Gleixner } 132ad757b6aSThomas Gleixner 133458a3e64STejun Heo pmd_t * __init populate_extra_pmd(unsigned long vaddr) 13411124411STejun Heo { 13511124411STejun Heo int pgd_idx = pgd_index(vaddr); 13611124411STejun Heo int pmd_idx = pmd_index(vaddr); 137458a3e64STejun Heo 138458a3e64STejun Heo return one_md_table_init(swapper_pg_dir + pgd_idx) + pmd_idx; 139458a3e64STejun Heo } 140458a3e64STejun Heo 141458a3e64STejun Heo pte_t * __init populate_extra_pte(unsigned long vaddr) 142458a3e64STejun Heo { 143458a3e64STejun Heo int pte_idx = pte_index(vaddr); 14411124411STejun Heo pmd_t *pmd; 14511124411STejun Heo 146458a3e64STejun Heo pmd = populate_extra_pmd(vaddr); 147458a3e64STejun Heo return one_page_table_init(pmd) + pte_idx; 14811124411STejun Heo } 14911124411STejun Heo 150a3c6018eSJan Beulich static pte_t *__init page_table_kmap_check(pte_t *pte, pmd_t *pmd, 151a3c6018eSJan Beulich unsigned long vaddr, pte_t *lastpte) 152a3c6018eSJan Beulich { 153a3c6018eSJan Beulich #ifdef CONFIG_HIGHMEM 154a3c6018eSJan Beulich /* 155a3c6018eSJan Beulich * Something (early fixmap) may already have put a pte 156a3c6018eSJan Beulich * page here, which causes the page table allocation 157a3c6018eSJan Beulich * to become nonlinear. Attempt to fix it, and if it 158a3c6018eSJan Beulich * is still nonlinear then we have to bug. 159a3c6018eSJan Beulich */ 160a3c6018eSJan Beulich int pmd_idx_kmap_begin = fix_to_virt(FIX_KMAP_END) >> PMD_SHIFT; 161a3c6018eSJan Beulich int pmd_idx_kmap_end = fix_to_virt(FIX_KMAP_BEGIN) >> PMD_SHIFT; 162a3c6018eSJan Beulich 163a3c6018eSJan Beulich if (pmd_idx_kmap_begin != pmd_idx_kmap_end 164a3c6018eSJan Beulich && (vaddr >> PMD_SHIFT) >= pmd_idx_kmap_begin 165a3c6018eSJan Beulich && (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end 166d1b19426SYinghai Lu && ((__pa(pte) >> PAGE_SHIFT) < pgt_buf_start 167d1b19426SYinghai Lu || (__pa(pte) >> PAGE_SHIFT) >= pgt_buf_end)) { 168a3c6018eSJan Beulich pte_t *newpte; 169a3c6018eSJan Beulich int i; 170a3c6018eSJan Beulich 171c464573cSPekka Enberg BUG_ON(after_bootmem); 172a3c6018eSJan Beulich newpte = alloc_low_page(); 173a3c6018eSJan Beulich for (i = 0; i < PTRS_PER_PTE; i++) 174a3c6018eSJan Beulich set_pte(newpte + i, pte[i]); 175a3c6018eSJan Beulich 176a3c6018eSJan Beulich paravirt_alloc_pte(&init_mm, __pa(newpte) >> PAGE_SHIFT); 177a3c6018eSJan Beulich set_pmd(pmd, __pmd(__pa(newpte)|_PAGE_TABLE)); 178a3c6018eSJan Beulich BUG_ON(newpte != pte_offset_kernel(pmd, 0)); 179a3c6018eSJan Beulich __flush_tlb_all(); 180a3c6018eSJan Beulich 181a3c6018eSJan Beulich paravirt_release_pte(__pa(pte) >> PAGE_SHIFT); 182a3c6018eSJan Beulich pte = newpte; 183a3c6018eSJan Beulich } 184a3c6018eSJan Beulich BUG_ON(vaddr < fix_to_virt(FIX_KMAP_BEGIN - 1) 185a3c6018eSJan Beulich && vaddr > fix_to_virt(FIX_KMAP_END) 186a3c6018eSJan Beulich && lastpte && lastpte + PTRS_PER_PTE != pte); 187a3c6018eSJan Beulich #endif 188a3c6018eSJan Beulich return pte; 189a3c6018eSJan Beulich } 190a3c6018eSJan Beulich 191ad757b6aSThomas Gleixner /* 192ad757b6aSThomas Gleixner * This function initializes a certain range of kernel virtual memory 193ad757b6aSThomas Gleixner * with new bootmem page tables, everywhere page tables are missing in 194ad757b6aSThomas Gleixner * the given range. 1958550eb99SIngo Molnar * 196ad757b6aSThomas Gleixner * NOTE: The pagetables are allocated contiguous on the physical space 197ad757b6aSThomas Gleixner * so we can cache the place of the first one and move around without 198ad757b6aSThomas Gleixner * checking the pgd every time. 199ad757b6aSThomas Gleixner */ 2008550eb99SIngo Molnar static void __init 2018550eb99SIngo Molnar page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base) 202ad757b6aSThomas Gleixner { 203ad757b6aSThomas Gleixner int pgd_idx, pmd_idx; 204ad757b6aSThomas Gleixner unsigned long vaddr; 2058550eb99SIngo Molnar pgd_t *pgd; 2068550eb99SIngo Molnar pmd_t *pmd; 207a3c6018eSJan Beulich pte_t *pte = NULL; 208ad757b6aSThomas Gleixner 209ad757b6aSThomas Gleixner vaddr = start; 210ad757b6aSThomas Gleixner pgd_idx = pgd_index(vaddr); 211ad757b6aSThomas Gleixner pmd_idx = pmd_index(vaddr); 212ad757b6aSThomas Gleixner pgd = pgd_base + pgd_idx; 213ad757b6aSThomas Gleixner 214ad757b6aSThomas Gleixner for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd++, pgd_idx++) { 215ad757b6aSThomas Gleixner pmd = one_md_table_init(pgd); 216ad757b6aSThomas Gleixner pmd = pmd + pmd_index(vaddr); 2178550eb99SIngo Molnar for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); 2188550eb99SIngo Molnar pmd++, pmd_idx++) { 219a3c6018eSJan Beulich pte = page_table_kmap_check(one_page_table_init(pmd), 220a3c6018eSJan Beulich pmd, vaddr, pte); 221ad757b6aSThomas Gleixner 222ad757b6aSThomas Gleixner vaddr += PMD_SIZE; 223ad757b6aSThomas Gleixner } 224ad757b6aSThomas Gleixner pmd_idx = 0; 225ad757b6aSThomas Gleixner } 226ad757b6aSThomas Gleixner } 227ad757b6aSThomas Gleixner 228ad757b6aSThomas Gleixner static inline int is_kernel_text(unsigned long addr) 229ad757b6aSThomas Gleixner { 2305bd5a452SMatthieu Castet if (addr >= (unsigned long)_text && addr <= (unsigned long)__init_end) 231ad757b6aSThomas Gleixner return 1; 232ad757b6aSThomas Gleixner return 0; 233ad757b6aSThomas Gleixner } 234ad757b6aSThomas Gleixner 235ad757b6aSThomas Gleixner /* 236ad757b6aSThomas Gleixner * This maps the physical memory to kernel virtual address space, a total 237ad757b6aSThomas Gleixner * of max_low_pfn pages, by creating page tables starting from address 2388550eb99SIngo Molnar * PAGE_OFFSET: 239ad757b6aSThomas Gleixner */ 240e53fb04fSPekka Enberg unsigned long __init 241e53fb04fSPekka Enberg kernel_physical_mapping_init(unsigned long start, 242e53fb04fSPekka Enberg unsigned long end, 243e53fb04fSPekka Enberg unsigned long page_size_mask) 244ad757b6aSThomas Gleixner { 245e53fb04fSPekka Enberg int use_pse = page_size_mask == (1<<PG_LEVEL_2M); 246c1fd1b43SPekka Enberg unsigned long last_map_addr = end; 247e53fb04fSPekka Enberg unsigned long start_pfn, end_pfn; 248e7179853SPekka Enberg pgd_t *pgd_base = swapper_pg_dir; 2498550eb99SIngo Molnar int pgd_idx, pmd_idx, pte_ofs; 250ad757b6aSThomas Gleixner unsigned long pfn; 251ad757b6aSThomas Gleixner pgd_t *pgd; 252ad757b6aSThomas Gleixner pmd_t *pmd; 253ad757b6aSThomas Gleixner pte_t *pte; 254a2699e47SSuresh Siddha unsigned pages_2m, pages_4k; 255a2699e47SSuresh Siddha int mapping_iter; 256a2699e47SSuresh Siddha 257e53fb04fSPekka Enberg start_pfn = start >> PAGE_SHIFT; 258e53fb04fSPekka Enberg end_pfn = end >> PAGE_SHIFT; 259e53fb04fSPekka Enberg 260a2699e47SSuresh Siddha /* 261a2699e47SSuresh Siddha * First iteration will setup identity mapping using large/small pages 262a2699e47SSuresh Siddha * based on use_pse, with other attributes same as set by 263a2699e47SSuresh Siddha * the early code in head_32.S 264a2699e47SSuresh Siddha * 265a2699e47SSuresh Siddha * Second iteration will setup the appropriate attributes (NX, GLOBAL..) 266a2699e47SSuresh Siddha * as desired for the kernel identity mapping. 267a2699e47SSuresh Siddha * 268a2699e47SSuresh Siddha * This two pass mechanism conforms to the TLB app note which says: 269a2699e47SSuresh Siddha * 270a2699e47SSuresh Siddha * "Software should not write to a paging-structure entry in a way 271a2699e47SSuresh Siddha * that would change, for any linear address, both the page size 272a2699e47SSuresh Siddha * and either the page frame or attributes." 273a2699e47SSuresh Siddha */ 274a2699e47SSuresh Siddha mapping_iter = 1; 275ad757b6aSThomas Gleixner 276a04ad82dSYinghai Lu if (!cpu_has_pse) 277a04ad82dSYinghai Lu use_pse = 0; 278a04ad82dSYinghai Lu 279a2699e47SSuresh Siddha repeat: 280a2699e47SSuresh Siddha pages_2m = pages_4k = 0; 281a04ad82dSYinghai Lu pfn = start_pfn; 282a04ad82dSYinghai Lu pgd_idx = pgd_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET); 283ad757b6aSThomas Gleixner pgd = pgd_base + pgd_idx; 284ad757b6aSThomas Gleixner for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) { 285ad757b6aSThomas Gleixner pmd = one_md_table_init(pgd); 2868550eb99SIngo Molnar 287a04ad82dSYinghai Lu if (pfn >= end_pfn) 288a04ad82dSYinghai Lu continue; 289a04ad82dSYinghai Lu #ifdef CONFIG_X86_PAE 290a04ad82dSYinghai Lu pmd_idx = pmd_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET); 291a04ad82dSYinghai Lu pmd += pmd_idx; 292a04ad82dSYinghai Lu #else 293a04ad82dSYinghai Lu pmd_idx = 0; 294a04ad82dSYinghai Lu #endif 295a04ad82dSYinghai Lu for (; pmd_idx < PTRS_PER_PMD && pfn < end_pfn; 296f3f20de8SJeremy Fitzhardinge pmd++, pmd_idx++) { 2978550eb99SIngo Molnar unsigned int addr = pfn * PAGE_SIZE + PAGE_OFFSET; 298ad757b6aSThomas Gleixner 2998550eb99SIngo Molnar /* 3008550eb99SIngo Molnar * Map with big pages if possible, otherwise 3018550eb99SIngo Molnar * create normal page tables: 3028550eb99SIngo Molnar */ 303a04ad82dSYinghai Lu if (use_pse) { 3048550eb99SIngo Molnar unsigned int addr2; 305f3f20de8SJeremy Fitzhardinge pgprot_t prot = PAGE_KERNEL_LARGE; 306a2699e47SSuresh Siddha /* 307a2699e47SSuresh Siddha * first pass will use the same initial 308a2699e47SSuresh Siddha * identity mapping attribute + _PAGE_PSE. 309a2699e47SSuresh Siddha */ 310a2699e47SSuresh Siddha pgprot_t init_prot = 311a2699e47SSuresh Siddha __pgprot(PTE_IDENT_ATTR | 312a2699e47SSuresh Siddha _PAGE_PSE); 313f3f20de8SJeremy Fitzhardinge 3148550eb99SIngo Molnar addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE + 315f3f20de8SJeremy Fitzhardinge PAGE_OFFSET + PAGE_SIZE-1; 316f3f20de8SJeremy Fitzhardinge 3178550eb99SIngo Molnar if (is_kernel_text(addr) || 3188550eb99SIngo Molnar is_kernel_text(addr2)) 319f3f20de8SJeremy Fitzhardinge prot = PAGE_KERNEL_LARGE_EXEC; 320f3f20de8SJeremy Fitzhardinge 321ce0c0e50SAndi Kleen pages_2m++; 322a2699e47SSuresh Siddha if (mapping_iter == 1) 323a2699e47SSuresh Siddha set_pmd(pmd, pfn_pmd(pfn, init_prot)); 324a2699e47SSuresh Siddha else 325f3f20de8SJeremy Fitzhardinge set_pmd(pmd, pfn_pmd(pfn, prot)); 326ad757b6aSThomas Gleixner 327ad757b6aSThomas Gleixner pfn += PTRS_PER_PTE; 3288550eb99SIngo Molnar continue; 3298550eb99SIngo Molnar } 330ad757b6aSThomas Gleixner pte = one_page_table_init(pmd); 331ad757b6aSThomas Gleixner 332a04ad82dSYinghai Lu pte_ofs = pte_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET); 333a04ad82dSYinghai Lu pte += pte_ofs; 334a04ad82dSYinghai Lu for (; pte_ofs < PTRS_PER_PTE && pfn < end_pfn; 3358550eb99SIngo Molnar pte++, pfn++, pte_ofs++, addr += PAGE_SIZE) { 336f3f20de8SJeremy Fitzhardinge pgprot_t prot = PAGE_KERNEL; 337a2699e47SSuresh Siddha /* 338a2699e47SSuresh Siddha * first pass will use the same initial 339a2699e47SSuresh Siddha * identity mapping attribute. 340a2699e47SSuresh Siddha */ 341a2699e47SSuresh Siddha pgprot_t init_prot = __pgprot(PTE_IDENT_ATTR); 342f3f20de8SJeremy Fitzhardinge 3438550eb99SIngo Molnar if (is_kernel_text(addr)) 344f3f20de8SJeremy Fitzhardinge prot = PAGE_KERNEL_EXEC; 345f3f20de8SJeremy Fitzhardinge 346ce0c0e50SAndi Kleen pages_4k++; 347c1fd1b43SPekka Enberg if (mapping_iter == 1) { 348a2699e47SSuresh Siddha set_pte(pte, pfn_pte(pfn, init_prot)); 349c1fd1b43SPekka Enberg last_map_addr = (pfn << PAGE_SHIFT) + PAGE_SIZE; 350c1fd1b43SPekka Enberg } else 351f3f20de8SJeremy Fitzhardinge set_pte(pte, pfn_pte(pfn, prot)); 352ad757b6aSThomas Gleixner } 353ad757b6aSThomas Gleixner } 354ad757b6aSThomas Gleixner } 355a2699e47SSuresh Siddha if (mapping_iter == 1) { 356a2699e47SSuresh Siddha /* 357a2699e47SSuresh Siddha * update direct mapping page count only in the first 358a2699e47SSuresh Siddha * iteration. 359a2699e47SSuresh Siddha */ 360ce0c0e50SAndi Kleen update_page_count(PG_LEVEL_2M, pages_2m); 361ce0c0e50SAndi Kleen update_page_count(PG_LEVEL_4K, pages_4k); 362a2699e47SSuresh Siddha 363a2699e47SSuresh Siddha /* 364a2699e47SSuresh Siddha * local global flush tlb, which will flush the previous 365a2699e47SSuresh Siddha * mappings present in both small and large page TLB's. 366a2699e47SSuresh Siddha */ 367a2699e47SSuresh Siddha __flush_tlb_all(); 368a2699e47SSuresh Siddha 369a2699e47SSuresh Siddha /* 370a2699e47SSuresh Siddha * Second iteration will set the actual desired PTE attributes. 371a2699e47SSuresh Siddha */ 372a2699e47SSuresh Siddha mapping_iter = 2; 373a2699e47SSuresh Siddha goto repeat; 374a2699e47SSuresh Siddha } 375c1fd1b43SPekka Enberg return last_map_addr; 376ae531c26SArjan van de Ven } 377ae531c26SArjan van de Ven 378ad757b6aSThomas Gleixner pte_t *kmap_pte; 379ad757b6aSThomas Gleixner pgprot_t kmap_prot; 380ad757b6aSThomas Gleixner 3818550eb99SIngo Molnar static inline pte_t *kmap_get_fixmap_pte(unsigned long vaddr) 3828550eb99SIngo Molnar { 3838550eb99SIngo Molnar return pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), 3848550eb99SIngo Molnar vaddr), vaddr), vaddr); 3858550eb99SIngo Molnar } 386ad757b6aSThomas Gleixner 387ad757b6aSThomas Gleixner static void __init kmap_init(void) 388ad757b6aSThomas Gleixner { 389ad757b6aSThomas Gleixner unsigned long kmap_vstart; 390ad757b6aSThomas Gleixner 3918550eb99SIngo Molnar /* 3928550eb99SIngo Molnar * Cache the first kmap pte: 3938550eb99SIngo Molnar */ 394ad757b6aSThomas Gleixner kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); 395ad757b6aSThomas Gleixner kmap_pte = kmap_get_fixmap_pte(kmap_vstart); 396ad757b6aSThomas Gleixner 397ad757b6aSThomas Gleixner kmap_prot = PAGE_KERNEL; 398ad757b6aSThomas Gleixner } 399ad757b6aSThomas Gleixner 400fd940934SKeith Packard #ifdef CONFIG_HIGHMEM 401ad757b6aSThomas Gleixner static void __init permanent_kmaps_init(pgd_t *pgd_base) 402ad757b6aSThomas Gleixner { 4038550eb99SIngo Molnar unsigned long vaddr; 404ad757b6aSThomas Gleixner pgd_t *pgd; 405ad757b6aSThomas Gleixner pud_t *pud; 406ad757b6aSThomas Gleixner pmd_t *pmd; 407ad757b6aSThomas Gleixner pte_t *pte; 408ad757b6aSThomas Gleixner 409ad757b6aSThomas Gleixner vaddr = PKMAP_BASE; 410ad757b6aSThomas Gleixner page_table_range_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base); 411ad757b6aSThomas Gleixner 412ad757b6aSThomas Gleixner pgd = swapper_pg_dir + pgd_index(vaddr); 413ad757b6aSThomas Gleixner pud = pud_offset(pgd, vaddr); 414ad757b6aSThomas Gleixner pmd = pmd_offset(pud, vaddr); 415ad757b6aSThomas Gleixner pte = pte_offset_kernel(pmd, vaddr); 416ad757b6aSThomas Gleixner pkmap_page_table = pte; 417ad757b6aSThomas Gleixner } 418ad757b6aSThomas Gleixner 419b1258ac2SMinchan Kim static void __init add_one_highpage_init(struct page *page) 420ad757b6aSThomas Gleixner { 421ad757b6aSThomas Gleixner ClearPageReserved(page); 422180c06efSJeremy Fitzhardinge init_page_count(page); 423180c06efSJeremy Fitzhardinge __free_page(page); 424180c06efSJeremy Fitzhardinge totalhigh_pages++; 425ad757b6aSThomas Gleixner } 426ad757b6aSThomas Gleixner 4271d931264SYinghai Lu void __init add_highpages_with_active_regions(int nid, 4281d931264SYinghai Lu unsigned long start_pfn, unsigned long end_pfn) 429b5bc6c0eSYinghai Lu { 4301d931264SYinghai Lu struct range *range; 4311d931264SYinghai Lu int nr_range; 4321d931264SYinghai Lu int i; 4331d931264SYinghai Lu 4341d931264SYinghai Lu nr_range = __get_free_all_memory_range(&range, nid, start_pfn, end_pfn); 4351d931264SYinghai Lu 4361d931264SYinghai Lu for (i = 0; i < nr_range; i++) { 437b5bc6c0eSYinghai Lu struct page *page; 4381d931264SYinghai Lu int node_pfn; 439b5bc6c0eSYinghai Lu 4401d931264SYinghai Lu for (node_pfn = range[i].start; node_pfn < range[i].end; 441b5bc6c0eSYinghai Lu node_pfn++) { 442b5bc6c0eSYinghai Lu if (!pfn_valid(node_pfn)) 443b5bc6c0eSYinghai Lu continue; 444b5bc6c0eSYinghai Lu page = pfn_to_page(node_pfn); 445b1258ac2SMinchan Kim add_one_highpage_init(page); 446b5bc6c0eSYinghai Lu } 447b5bc6c0eSYinghai Lu } 448ad757b6aSThomas Gleixner } 449ad757b6aSThomas Gleixner #else 450e8e32326SIngo Brueckl static inline void permanent_kmaps_init(pgd_t *pgd_base) 451e8e32326SIngo Brueckl { 452e8e32326SIngo Brueckl } 453ad757b6aSThomas Gleixner #endif /* CONFIG_HIGHMEM */ 454ad757b6aSThomas Gleixner 455ad757b6aSThomas Gleixner void __init native_pagetable_setup_start(pgd_t *base) 456ad757b6aSThomas Gleixner { 457551889a6SIan Campbell unsigned long pfn, va; 458551889a6SIan Campbell pgd_t *pgd; 459551889a6SIan Campbell pud_t *pud; 460551889a6SIan Campbell pmd_t *pmd; 461551889a6SIan Campbell pte_t *pte; 462ad757b6aSThomas Gleixner 463ad757b6aSThomas Gleixner /* 464551889a6SIan Campbell * Remove any mappings which extend past the end of physical 465551889a6SIan Campbell * memory from the boot time page table: 466ad757b6aSThomas Gleixner */ 467551889a6SIan Campbell for (pfn = max_low_pfn + 1; pfn < 1<<(32-PAGE_SHIFT); pfn++) { 468551889a6SIan Campbell va = PAGE_OFFSET + (pfn<<PAGE_SHIFT); 469551889a6SIan Campbell pgd = base + pgd_index(va); 470551889a6SIan Campbell if (!pgd_present(*pgd)) 471551889a6SIan Campbell break; 472ad757b6aSThomas Gleixner 473551889a6SIan Campbell pud = pud_offset(pgd, va); 474551889a6SIan Campbell pmd = pmd_offset(pud, va); 475551889a6SIan Campbell if (!pmd_present(*pmd)) 476551889a6SIan Campbell break; 477551889a6SIan Campbell 478551889a6SIan Campbell pte = pte_offset_kernel(pmd, va); 479551889a6SIan Campbell if (!pte_present(*pte)) 480551889a6SIan Campbell break; 481551889a6SIan Campbell 482551889a6SIan Campbell pte_clear(NULL, va, pte); 483551889a6SIan Campbell } 4846944a9c8SJeremy Fitzhardinge paravirt_alloc_pmd(&init_mm, __pa(base) >> PAGE_SHIFT); 485ad757b6aSThomas Gleixner } 486ad757b6aSThomas Gleixner 487ad757b6aSThomas Gleixner void __init native_pagetable_setup_done(pgd_t *base) 488ad757b6aSThomas Gleixner { 489ad757b6aSThomas Gleixner } 490ad757b6aSThomas Gleixner 491ad757b6aSThomas Gleixner /* 492ad757b6aSThomas Gleixner * Build a proper pagetable for the kernel mappings. Up until this 493ad757b6aSThomas Gleixner * point, we've been running on some set of pagetables constructed by 494ad757b6aSThomas Gleixner * the boot process. 495ad757b6aSThomas Gleixner * 496ad757b6aSThomas Gleixner * If we're booting on native hardware, this will be a pagetable 497551889a6SIan Campbell * constructed in arch/x86/kernel/head_32.S. The root of the 498551889a6SIan Campbell * pagetable will be swapper_pg_dir. 499ad757b6aSThomas Gleixner * 500ad757b6aSThomas Gleixner * If we're booting paravirtualized under a hypervisor, then there are 501ad757b6aSThomas Gleixner * more options: we may already be running PAE, and the pagetable may 502ad757b6aSThomas Gleixner * or may not be based in swapper_pg_dir. In any case, 503ad757b6aSThomas Gleixner * paravirt_pagetable_setup_start() will set up swapper_pg_dir 504ad757b6aSThomas Gleixner * appropriately for the rest of the initialization to work. 505ad757b6aSThomas Gleixner * 506ad757b6aSThomas Gleixner * In general, pagetable_init() assumes that the pagetable may already 507ad757b6aSThomas Gleixner * be partially populated, and so it avoids stomping on any existing 508ad757b6aSThomas Gleixner * mappings. 509ad757b6aSThomas Gleixner */ 510f765090aSPekka Enberg void __init early_ioremap_page_table_range_init(void) 511ad757b6aSThomas Gleixner { 512e7179853SPekka Enberg pgd_t *pgd_base = swapper_pg_dir; 5138550eb99SIngo Molnar unsigned long vaddr, end; 514ad757b6aSThomas Gleixner 515ad757b6aSThomas Gleixner /* 516ad757b6aSThomas Gleixner * Fixed mappings, only the page table structure has to be 517ad757b6aSThomas Gleixner * created - mappings will be set by set_fixmap(): 518ad757b6aSThomas Gleixner */ 519ad757b6aSThomas Gleixner vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; 520ad757b6aSThomas Gleixner end = (FIXADDR_TOP + PMD_SIZE - 1) & PMD_MASK; 521ad757b6aSThomas Gleixner page_table_range_init(vaddr, end, pgd_base); 522beacfaacSHuang, Ying early_ioremap_reset(); 523e7b37895SYinghai Lu } 524e7b37895SYinghai Lu 525e7b37895SYinghai Lu static void __init pagetable_init(void) 526e7b37895SYinghai Lu { 527e7b37895SYinghai Lu pgd_t *pgd_base = swapper_pg_dir; 528e7b37895SYinghai Lu 529ad757b6aSThomas Gleixner permanent_kmaps_init(pgd_base); 530ad757b6aSThomas Gleixner } 531ad757b6aSThomas Gleixner 532be43d728SJeremy Fitzhardinge pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL | _PAGE_IOMAP); 5336fdc05d4SJeremy Fitzhardinge EXPORT_SYMBOL_GPL(__supported_pte_mask); 5346fdc05d4SJeremy Fitzhardinge 53590d967e0SYinghai Lu /* user-defined highmem size */ 53690d967e0SYinghai Lu static unsigned int highmem_pages = -1; 53790d967e0SYinghai Lu 53890d967e0SYinghai Lu /* 53990d967e0SYinghai Lu * highmem=size forces highmem to be exactly 'size' bytes. 54090d967e0SYinghai Lu * This works even on boxes that have no highmem otherwise. 54190d967e0SYinghai Lu * This also works to reduce highmem size on bigger boxes. 54290d967e0SYinghai Lu */ 54390d967e0SYinghai Lu static int __init parse_highmem(char *arg) 54490d967e0SYinghai Lu { 54590d967e0SYinghai Lu if (!arg) 54690d967e0SYinghai Lu return -EINVAL; 54790d967e0SYinghai Lu 54890d967e0SYinghai Lu highmem_pages = memparse(arg, &arg) >> PAGE_SHIFT; 54990d967e0SYinghai Lu return 0; 55090d967e0SYinghai Lu } 55190d967e0SYinghai Lu early_param("highmem", parse_highmem); 55290d967e0SYinghai Lu 5534769843bSIngo Molnar #define MSG_HIGHMEM_TOO_BIG \ 5544769843bSIngo Molnar "highmem size (%luMB) is bigger than pages available (%luMB)!\n" 5554769843bSIngo Molnar 5564769843bSIngo Molnar #define MSG_LOWMEM_TOO_SMALL \ 5574769843bSIngo Molnar "highmem size (%luMB) results in <64MB lowmem, ignoring it!\n" 55890d967e0SYinghai Lu /* 5594769843bSIngo Molnar * All of RAM fits into lowmem - but if user wants highmem 5604769843bSIngo Molnar * artificially via the highmem=x boot parameter then create 5614769843bSIngo Molnar * it: 56290d967e0SYinghai Lu */ 5634769843bSIngo Molnar void __init lowmem_pfn_init(void) 56490d967e0SYinghai Lu { 565346cafecSYinghai Lu /* max_low_pfn is 0, we already have early_res support */ 56690d967e0SYinghai Lu max_low_pfn = max_pfn; 567d88316c2SIngo Molnar 5684769843bSIngo Molnar if (highmem_pages == -1) 5694769843bSIngo Molnar highmem_pages = 0; 5704769843bSIngo Molnar #ifdef CONFIG_HIGHMEM 5714769843bSIngo Molnar if (highmem_pages >= max_pfn) { 5724769843bSIngo Molnar printk(KERN_ERR MSG_HIGHMEM_TOO_BIG, 5734769843bSIngo Molnar pages_to_mb(highmem_pages), pages_to_mb(max_pfn)); 5744769843bSIngo Molnar highmem_pages = 0; 5754769843bSIngo Molnar } 5764769843bSIngo Molnar if (highmem_pages) { 5774769843bSIngo Molnar if (max_low_pfn - highmem_pages < 64*1024*1024/PAGE_SIZE) { 5784769843bSIngo Molnar printk(KERN_ERR MSG_LOWMEM_TOO_SMALL, 5794769843bSIngo Molnar pages_to_mb(highmem_pages)); 5804769843bSIngo Molnar highmem_pages = 0; 5814769843bSIngo Molnar } 5824769843bSIngo Molnar max_low_pfn -= highmem_pages; 5834769843bSIngo Molnar } 5844769843bSIngo Molnar #else 5854769843bSIngo Molnar if (highmem_pages) 5864769843bSIngo Molnar printk(KERN_ERR "ignoring highmem size on non-highmem kernel!\n"); 5874769843bSIngo Molnar #endif 5884769843bSIngo Molnar } 5894769843bSIngo Molnar 5904769843bSIngo Molnar #define MSG_HIGHMEM_TOO_SMALL \ 5914769843bSIngo Molnar "only %luMB highmem pages available, ignoring highmem size of %luMB!\n" 5924769843bSIngo Molnar 5934769843bSIngo Molnar #define MSG_HIGHMEM_TRIMMED \ 5944769843bSIngo Molnar "Warning: only 4GB will be used. Use a HIGHMEM64G enabled kernel!\n" 5954769843bSIngo Molnar /* 5964769843bSIngo Molnar * We have more RAM than fits into lowmem - we try to put it into 5974769843bSIngo Molnar * highmem, also taking the highmem=x boot parameter into account: 5984769843bSIngo Molnar */ 5994769843bSIngo Molnar void __init highmem_pfn_init(void) 6004769843bSIngo Molnar { 601d88316c2SIngo Molnar max_low_pfn = MAXMEM_PFN; 602d88316c2SIngo Molnar 60390d967e0SYinghai Lu if (highmem_pages == -1) 60490d967e0SYinghai Lu highmem_pages = max_pfn - MAXMEM_PFN; 6054769843bSIngo Molnar 60690d967e0SYinghai Lu if (highmem_pages + MAXMEM_PFN < max_pfn) 60790d967e0SYinghai Lu max_pfn = MAXMEM_PFN + highmem_pages; 6084769843bSIngo Molnar 60990d967e0SYinghai Lu if (highmem_pages + MAXMEM_PFN > max_pfn) { 6104769843bSIngo Molnar printk(KERN_WARNING MSG_HIGHMEM_TOO_SMALL, 61190d967e0SYinghai Lu pages_to_mb(max_pfn - MAXMEM_PFN), 61290d967e0SYinghai Lu pages_to_mb(highmem_pages)); 61390d967e0SYinghai Lu highmem_pages = 0; 61490d967e0SYinghai Lu } 61590d967e0SYinghai Lu #ifndef CONFIG_HIGHMEM 61690d967e0SYinghai Lu /* Maximum memory usable is what is directly addressable */ 6174769843bSIngo Molnar printk(KERN_WARNING "Warning only %ldMB will be used.\n", MAXMEM>>20); 61890d967e0SYinghai Lu if (max_pfn > MAX_NONPAE_PFN) 6194769843bSIngo Molnar printk(KERN_WARNING "Use a HIGHMEM64G enabled kernel.\n"); 62090d967e0SYinghai Lu else 62190d967e0SYinghai Lu printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n"); 62290d967e0SYinghai Lu max_pfn = MAXMEM_PFN; 62390d967e0SYinghai Lu #else /* !CONFIG_HIGHMEM */ 62490d967e0SYinghai Lu #ifndef CONFIG_HIGHMEM64G 62590d967e0SYinghai Lu if (max_pfn > MAX_NONPAE_PFN) { 62690d967e0SYinghai Lu max_pfn = MAX_NONPAE_PFN; 6274769843bSIngo Molnar printk(KERN_WARNING MSG_HIGHMEM_TRIMMED); 62890d967e0SYinghai Lu } 62990d967e0SYinghai Lu #endif /* !CONFIG_HIGHMEM64G */ 63090d967e0SYinghai Lu #endif /* !CONFIG_HIGHMEM */ 63190d967e0SYinghai Lu } 6324769843bSIngo Molnar 63390d967e0SYinghai Lu /* 63490d967e0SYinghai Lu * Determine low and high memory ranges: 63590d967e0SYinghai Lu */ 63690d967e0SYinghai Lu void __init find_low_pfn_range(void) 63790d967e0SYinghai Lu { 63890d967e0SYinghai Lu /* it could update max_pfn */ 63990d967e0SYinghai Lu 640d88316c2SIngo Molnar if (max_pfn <= MAXMEM_PFN) 6414769843bSIngo Molnar lowmem_pfn_init(); 642d88316c2SIngo Molnar else 643d88316c2SIngo Molnar highmem_pfn_init(); 64490d967e0SYinghai Lu } 64590d967e0SYinghai Lu 646b2ac82a0SYinghai Lu #ifndef CONFIG_NEED_MULTIPLE_NODES 647d8fc3afcSTejun Heo void __init initmem_init(void) 648b2ac82a0SYinghai Lu { 649b2ac82a0SYinghai Lu #ifdef CONFIG_HIGHMEM 650b2ac82a0SYinghai Lu highstart_pfn = highend_pfn = max_pfn; 651b2ac82a0SYinghai Lu if (max_pfn > max_low_pfn) 652b2ac82a0SYinghai Lu highstart_pfn = max_low_pfn; 653a9ce6bc1SYinghai Lu memblock_x86_register_active_regions(0, 0, highend_pfn); 654ed077b58SShaohua Li sparse_memory_present_with_active_regions(0); 655b2ac82a0SYinghai Lu printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", 656b2ac82a0SYinghai Lu pages_to_mb(highend_pfn - highstart_pfn)); 657b2ac82a0SYinghai Lu num_physpages = highend_pfn; 658b2ac82a0SYinghai Lu high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1; 659b2ac82a0SYinghai Lu #else 660a9ce6bc1SYinghai Lu memblock_x86_register_active_regions(0, 0, max_low_pfn); 661ed077b58SShaohua Li sparse_memory_present_with_active_regions(0); 662b2ac82a0SYinghai Lu num_physpages = max_low_pfn; 663b2ac82a0SYinghai Lu high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1; 664b2ac82a0SYinghai Lu #endif 665b2ac82a0SYinghai Lu #ifdef CONFIG_FLATMEM 666b2ac82a0SYinghai Lu max_mapnr = num_physpages; 667b2ac82a0SYinghai Lu #endif 668dc16ecf7SJeremy Fitzhardinge __vmalloc_start_set = true; 669dc16ecf7SJeremy Fitzhardinge 670b2ac82a0SYinghai Lu printk(KERN_NOTICE "%ldMB LOWMEM available.\n", 671b2ac82a0SYinghai Lu pages_to_mb(max_low_pfn)); 672b2ac82a0SYinghai Lu 673b2ac82a0SYinghai Lu setup_bootmem_allocator(); 674b2ac82a0SYinghai Lu } 675cb95a13aSYinghai Lu #endif /* !CONFIG_NEED_MULTIPLE_NODES */ 676b2ac82a0SYinghai Lu 677b2ac82a0SYinghai Lu void __init setup_bootmem_allocator(void) 678b2ac82a0SYinghai Lu { 679b2ac82a0SYinghai Lu printk(KERN_INFO " mapped low ram: 0 - %08lx\n", 680b2ac82a0SYinghai Lu max_pfn_mapped<<PAGE_SHIFT); 681fc5efe39SYinghai Lu printk(KERN_INFO " low ram: 0 - %08lx\n", max_low_pfn<<PAGE_SHIFT); 682b2ac82a0SYinghai Lu 683c464573cSPekka Enberg after_bootmem = 1; 6844e29684cSYinghai Lu } 6854e29684cSYinghai Lu 686a04ad82dSYinghai Lu /* 687ad757b6aSThomas Gleixner * paging_init() sets up the page tables - note that the first 8MB are 688ad757b6aSThomas Gleixner * already mapped by head.S. 689ad757b6aSThomas Gleixner * 690ad757b6aSThomas Gleixner * This routines also unmaps the page at virtual kernel address 0, so 691ad757b6aSThomas Gleixner * that we can trap those pesky NULL-reference errors in the kernel. 692ad757b6aSThomas Gleixner */ 693ad757b6aSThomas Gleixner void __init paging_init(void) 694ad757b6aSThomas Gleixner { 695ad757b6aSThomas Gleixner pagetable_init(); 696ad757b6aSThomas Gleixner 697ad757b6aSThomas Gleixner __flush_tlb_all(); 698ad757b6aSThomas Gleixner 699ad757b6aSThomas Gleixner kmap_init(); 70011cd0bc1SYinghai Lu 70111cd0bc1SYinghai Lu /* 70211cd0bc1SYinghai Lu * NOTE: at this point the bootmem allocator is fully available. 70311cd0bc1SYinghai Lu */ 704c10d1e26SAndres Salomon olpc_dt_build_devicetree(); 705797390d8STejun Heo sparse_memory_present_with_active_regions(MAX_NUMNODES); 70611cd0bc1SYinghai Lu sparse_init(); 70711cd0bc1SYinghai Lu zone_sizes_init(); 708ad757b6aSThomas Gleixner } 709ad757b6aSThomas Gleixner 710ad757b6aSThomas Gleixner /* 711ad757b6aSThomas Gleixner * Test if the WP bit works in supervisor mode. It isn't supported on 386's 712f7f17a67SDmitri Vorobiev * and also on some strange 486's. All 586+'s are OK. This used to involve 713f7f17a67SDmitri Vorobiev * black magic jumps to work around some nasty CPU bugs, but fortunately the 714f7f17a67SDmitri Vorobiev * switch to using exceptions got rid of all that. 715ad757b6aSThomas Gleixner */ 716ad757b6aSThomas Gleixner static void __init test_wp_bit(void) 717ad757b6aSThomas Gleixner { 718d7d119d7SIngo Molnar printk(KERN_INFO 719d7d119d7SIngo Molnar "Checking if this processor honours the WP bit even in supervisor mode..."); 720ad757b6aSThomas Gleixner 721ad757b6aSThomas Gleixner /* Any page-aligned address will do, the test is non-destructive */ 722ad757b6aSThomas Gleixner __set_fixmap(FIX_WP_TEST, __pa(&swapper_pg_dir), PAGE_READONLY); 723ad757b6aSThomas Gleixner boot_cpu_data.wp_works_ok = do_test_wp_bit(); 724ad757b6aSThomas Gleixner clear_fixmap(FIX_WP_TEST); 725ad757b6aSThomas Gleixner 726ad757b6aSThomas Gleixner if (!boot_cpu_data.wp_works_ok) { 727d7d119d7SIngo Molnar printk(KERN_CONT "No.\n"); 728ad757b6aSThomas Gleixner #ifdef CONFIG_X86_WP_WORKS_OK 729d7d119d7SIngo Molnar panic( 730d7d119d7SIngo Molnar "This kernel doesn't support CPU's with broken WP. Recompile it for a 386!"); 731ad757b6aSThomas Gleixner #endif 732ad757b6aSThomas Gleixner } else { 733d7d119d7SIngo Molnar printk(KERN_CONT "Ok.\n"); 734ad757b6aSThomas Gleixner } 735ad757b6aSThomas Gleixner } 736ad757b6aSThomas Gleixner 737ad757b6aSThomas Gleixner void __init mem_init(void) 738ad757b6aSThomas Gleixner { 739ad757b6aSThomas Gleixner int codesize, reservedpages, datasize, initsize; 740cc9f7a0cSYinghai Lu int tmp; 741ad757b6aSThomas Gleixner 742cfb80c9eSJeremy Fitzhardinge pci_iommu_alloc(); 743cfb80c9eSJeremy Fitzhardinge 744ad757b6aSThomas Gleixner #ifdef CONFIG_FLATMEM 745ad757b6aSThomas Gleixner BUG_ON(!mem_map); 746ad757b6aSThomas Gleixner #endif 747*855c743aSStanislaw Gruszka /* 748*855c743aSStanislaw Gruszka * With CONFIG_DEBUG_PAGEALLOC initialization of highmem pages has to 749*855c743aSStanislaw Gruszka * be done before free_all_bootmem(). Memblock use free low memory for 750*855c743aSStanislaw Gruszka * temporary data (see find_range_array()) and for this purpose can use 751*855c743aSStanislaw Gruszka * pages that was already passed to the buddy allocator, hence marked as 752*855c743aSStanislaw Gruszka * not accessible in the page tables when compiled with 753*855c743aSStanislaw Gruszka * CONFIG_DEBUG_PAGEALLOC. Otherwise order of initialization is not 754*855c743aSStanislaw Gruszka * important here. 755*855c743aSStanislaw Gruszka */ 756*855c743aSStanislaw Gruszka set_highmem_pages_init(); 757*855c743aSStanislaw Gruszka 758ad757b6aSThomas Gleixner /* this will put all low memory onto the freelists */ 759ad757b6aSThomas Gleixner totalram_pages += free_all_bootmem(); 760ad757b6aSThomas Gleixner 761ad757b6aSThomas Gleixner reservedpages = 0; 762ad757b6aSThomas Gleixner for (tmp = 0; tmp < max_low_pfn; tmp++) 763ad757b6aSThomas Gleixner /* 7648550eb99SIngo Molnar * Only count reserved RAM pages: 765ad757b6aSThomas Gleixner */ 766ad757b6aSThomas Gleixner if (page_is_ram(tmp) && PageReserved(pfn_to_page(tmp))) 767ad757b6aSThomas Gleixner reservedpages++; 768ad757b6aSThomas Gleixner 769ad757b6aSThomas Gleixner codesize = (unsigned long) &_etext - (unsigned long) &_text; 770ad757b6aSThomas Gleixner datasize = (unsigned long) &_edata - (unsigned long) &_etext; 771ad757b6aSThomas Gleixner initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; 772ad757b6aSThomas Gleixner 7738550eb99SIngo Molnar printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, " 7748550eb99SIngo Molnar "%dk reserved, %dk data, %dk init, %ldk highmem)\n", 775cc013a88SGeert Uytterhoeven nr_free_pages() << (PAGE_SHIFT-10), 776ad757b6aSThomas Gleixner num_physpages << (PAGE_SHIFT-10), 777ad757b6aSThomas Gleixner codesize >> 10, 778ad757b6aSThomas Gleixner reservedpages << (PAGE_SHIFT-10), 779ad757b6aSThomas Gleixner datasize >> 10, 780ad757b6aSThomas Gleixner initsize >> 10, 7814b529401SAndreas Fenkart totalhigh_pages << (PAGE_SHIFT-10)); 782ad757b6aSThomas Gleixner 783d7d119d7SIngo Molnar printk(KERN_INFO "virtual kernel memory layout:\n" 784ad757b6aSThomas Gleixner " fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n" 785ad757b6aSThomas Gleixner #ifdef CONFIG_HIGHMEM 786ad757b6aSThomas Gleixner " pkmap : 0x%08lx - 0x%08lx (%4ld kB)\n" 787ad757b6aSThomas Gleixner #endif 788ad757b6aSThomas Gleixner " vmalloc : 0x%08lx - 0x%08lx (%4ld MB)\n" 789ad757b6aSThomas Gleixner " lowmem : 0x%08lx - 0x%08lx (%4ld MB)\n" 790ad757b6aSThomas Gleixner " .init : 0x%08lx - 0x%08lx (%4ld kB)\n" 791ad757b6aSThomas Gleixner " .data : 0x%08lx - 0x%08lx (%4ld kB)\n" 792ad757b6aSThomas Gleixner " .text : 0x%08lx - 0x%08lx (%4ld kB)\n", 793ad757b6aSThomas Gleixner FIXADDR_START, FIXADDR_TOP, 794ad757b6aSThomas Gleixner (FIXADDR_TOP - FIXADDR_START) >> 10, 795ad757b6aSThomas Gleixner 796ad757b6aSThomas Gleixner #ifdef CONFIG_HIGHMEM 797ad757b6aSThomas Gleixner PKMAP_BASE, PKMAP_BASE+LAST_PKMAP*PAGE_SIZE, 798ad757b6aSThomas Gleixner (LAST_PKMAP*PAGE_SIZE) >> 10, 799ad757b6aSThomas Gleixner #endif 800ad757b6aSThomas Gleixner 801ad757b6aSThomas Gleixner VMALLOC_START, VMALLOC_END, 802ad757b6aSThomas Gleixner (VMALLOC_END - VMALLOC_START) >> 20, 803ad757b6aSThomas Gleixner 804ad757b6aSThomas Gleixner (unsigned long)__va(0), (unsigned long)high_memory, 805ad757b6aSThomas Gleixner ((unsigned long)high_memory - (unsigned long)__va(0)) >> 20, 806ad757b6aSThomas Gleixner 807ad757b6aSThomas Gleixner (unsigned long)&__init_begin, (unsigned long)&__init_end, 8088550eb99SIngo Molnar ((unsigned long)&__init_end - 8098550eb99SIngo Molnar (unsigned long)&__init_begin) >> 10, 810ad757b6aSThomas Gleixner 811ad757b6aSThomas Gleixner (unsigned long)&_etext, (unsigned long)&_edata, 812ad757b6aSThomas Gleixner ((unsigned long)&_edata - (unsigned long)&_etext) >> 10, 813ad757b6aSThomas Gleixner 814ad757b6aSThomas Gleixner (unsigned long)&_text, (unsigned long)&_etext, 815ad757b6aSThomas Gleixner ((unsigned long)&_etext - (unsigned long)&_text) >> 10); 816ad757b6aSThomas Gleixner 817beeb4195SJan Beulich /* 818beeb4195SJan Beulich * Check boundaries twice: Some fundamental inconsistencies can 819beeb4195SJan Beulich * be detected at build time already. 820beeb4195SJan Beulich */ 821beeb4195SJan Beulich #define __FIXADDR_TOP (-PAGE_SIZE) 822beeb4195SJan Beulich #ifdef CONFIG_HIGHMEM 823beeb4195SJan Beulich BUILD_BUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE > FIXADDR_START); 824beeb4195SJan Beulich BUILD_BUG_ON(VMALLOC_END > PKMAP_BASE); 825beeb4195SJan Beulich #endif 826beeb4195SJan Beulich #define high_memory (-128UL << 20) 827beeb4195SJan Beulich BUILD_BUG_ON(VMALLOC_START >= VMALLOC_END); 828beeb4195SJan Beulich #undef high_memory 829beeb4195SJan Beulich #undef __FIXADDR_TOP 830beeb4195SJan Beulich 831ad757b6aSThomas Gleixner #ifdef CONFIG_HIGHMEM 832ad757b6aSThomas Gleixner BUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE > FIXADDR_START); 833ad757b6aSThomas Gleixner BUG_ON(VMALLOC_END > PKMAP_BASE); 834ad757b6aSThomas Gleixner #endif 835beeb4195SJan Beulich BUG_ON(VMALLOC_START >= VMALLOC_END); 836ad757b6aSThomas Gleixner BUG_ON((unsigned long)high_memory > VMALLOC_START); 837ad757b6aSThomas Gleixner 838ad757b6aSThomas Gleixner if (boot_cpu_data.wp_works_ok < 0) 839ad757b6aSThomas Gleixner test_wp_bit(); 840ad757b6aSThomas Gleixner } 841ad757b6aSThomas Gleixner 842ad757b6aSThomas Gleixner #ifdef CONFIG_MEMORY_HOTPLUG 843ad757b6aSThomas Gleixner int arch_add_memory(int nid, u64 start, u64 size) 844ad757b6aSThomas Gleixner { 845ad757b6aSThomas Gleixner struct pglist_data *pgdata = NODE_DATA(nid); 846ad757b6aSThomas Gleixner struct zone *zone = pgdata->node_zones + ZONE_HIGHMEM; 847ad757b6aSThomas Gleixner unsigned long start_pfn = start >> PAGE_SHIFT; 848ad757b6aSThomas Gleixner unsigned long nr_pages = size >> PAGE_SHIFT; 849ad757b6aSThomas Gleixner 850c04fc586SGary Hade return __add_pages(nid, zone, start_pfn, nr_pages); 851ad757b6aSThomas Gleixner } 852ad757b6aSThomas Gleixner #endif 853ad757b6aSThomas Gleixner 854ad757b6aSThomas Gleixner /* 855ad757b6aSThomas Gleixner * This function cannot be __init, since exceptions don't work in that 856ad757b6aSThomas Gleixner * section. Put this after the callers, so that it cannot be inlined. 857ad757b6aSThomas Gleixner */ 8588550eb99SIngo Molnar static noinline int do_test_wp_bit(void) 859ad757b6aSThomas Gleixner { 860ad757b6aSThomas Gleixner char tmp_reg; 861ad757b6aSThomas Gleixner int flag; 862ad757b6aSThomas Gleixner 863ad757b6aSThomas Gleixner __asm__ __volatile__( 864ad757b6aSThomas Gleixner " movb %0, %1 \n" 865ad757b6aSThomas Gleixner "1: movb %1, %0 \n" 866ad757b6aSThomas Gleixner " xorl %2, %2 \n" 867ad757b6aSThomas Gleixner "2: \n" 868f832ff18SH. Peter Anvin _ASM_EXTABLE(1b,2b) 869ad757b6aSThomas Gleixner :"=m" (*(char *)fix_to_virt(FIX_WP_TEST)), 870ad757b6aSThomas Gleixner "=q" (tmp_reg), 871ad757b6aSThomas Gleixner "=r" (flag) 872ad757b6aSThomas Gleixner :"2" (1) 873ad757b6aSThomas Gleixner :"memory"); 874ad757b6aSThomas Gleixner 875ad757b6aSThomas Gleixner return flag; 876ad757b6aSThomas Gleixner } 877ad757b6aSThomas Gleixner 878ad757b6aSThomas Gleixner #ifdef CONFIG_DEBUG_RODATA 879edeed305SArjan van de Ven const int rodata_test_data = 0xC3; 880edeed305SArjan van de Ven EXPORT_SYMBOL_GPL(rodata_test_data); 881ad757b6aSThomas Gleixner 882502f6604SSuresh Siddha int kernel_set_to_readonly __read_mostly; 88316239630SSteven Rostedt 88416239630SSteven Rostedt void set_kernel_text_rw(void) 88516239630SSteven Rostedt { 88616239630SSteven Rostedt unsigned long start = PFN_ALIGN(_text); 88716239630SSteven Rostedt unsigned long size = PFN_ALIGN(_etext) - start; 88816239630SSteven Rostedt 88916239630SSteven Rostedt if (!kernel_set_to_readonly) 89016239630SSteven Rostedt return; 89116239630SSteven Rostedt 89216239630SSteven Rostedt pr_debug("Set kernel text: %lx - %lx for read write\n", 89316239630SSteven Rostedt start, start+size); 89416239630SSteven Rostedt 89516239630SSteven Rostedt set_pages_rw(virt_to_page(start), size >> PAGE_SHIFT); 89616239630SSteven Rostedt } 89716239630SSteven Rostedt 89816239630SSteven Rostedt void set_kernel_text_ro(void) 89916239630SSteven Rostedt { 90016239630SSteven Rostedt unsigned long start = PFN_ALIGN(_text); 90116239630SSteven Rostedt unsigned long size = PFN_ALIGN(_etext) - start; 90216239630SSteven Rostedt 90316239630SSteven Rostedt if (!kernel_set_to_readonly) 90416239630SSteven Rostedt return; 90516239630SSteven Rostedt 90616239630SSteven Rostedt pr_debug("Set kernel text: %lx - %lx for read only\n", 90716239630SSteven Rostedt start, start+size); 90816239630SSteven Rostedt 90916239630SSteven Rostedt set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); 91016239630SSteven Rostedt } 91116239630SSteven Rostedt 9125bd5a452SMatthieu Castet static void mark_nxdata_nx(void) 9135bd5a452SMatthieu Castet { 9145bd5a452SMatthieu Castet /* 9155bd5a452SMatthieu Castet * When this called, init has already been executed and released, 9160d2eb44fSLucas De Marchi * so everything past _etext should be NX. 9175bd5a452SMatthieu Castet */ 9185bd5a452SMatthieu Castet unsigned long start = PFN_ALIGN(_etext); 9195bd5a452SMatthieu Castet /* 9205bd5a452SMatthieu Castet * This comes from is_kernel_text upper limit. Also HPAGE where used: 9215bd5a452SMatthieu Castet */ 9225bd5a452SMatthieu Castet unsigned long size = (((unsigned long)__init_end + HPAGE_SIZE) & HPAGE_MASK) - start; 9235bd5a452SMatthieu Castet 9245bd5a452SMatthieu Castet if (__supported_pte_mask & _PAGE_NX) 9255bd5a452SMatthieu Castet printk(KERN_INFO "NX-protecting the kernel data: %luk\n", size >> 10); 9265bd5a452SMatthieu Castet set_pages_nx(virt_to_page(start), size >> PAGE_SHIFT); 9275bd5a452SMatthieu Castet } 9285bd5a452SMatthieu Castet 929ad757b6aSThomas Gleixner void mark_rodata_ro(void) 930ad757b6aSThomas Gleixner { 931ad757b6aSThomas Gleixner unsigned long start = PFN_ALIGN(_text); 932ad757b6aSThomas Gleixner unsigned long size = PFN_ALIGN(_etext) - start; 933ad757b6aSThomas Gleixner 9346d238cc4SArjan van de Ven set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); 935d7d119d7SIngo Molnar printk(KERN_INFO "Write protecting the kernel text: %luk\n", 936d7d119d7SIngo Molnar size >> 10); 9370c42f392SAndi Kleen 93816239630SSteven Rostedt kernel_set_to_readonly = 1; 93916239630SSteven Rostedt 9400c42f392SAndi Kleen #ifdef CONFIG_CPA_DEBUG 941d7d119d7SIngo Molnar printk(KERN_INFO "Testing CPA: Reverting %lx-%lx\n", 942d7d119d7SIngo Molnar start, start+size); 9436d238cc4SArjan van de Ven set_pages_rw(virt_to_page(start), size>>PAGE_SHIFT); 9440c42f392SAndi Kleen 945d7d119d7SIngo Molnar printk(KERN_INFO "Testing CPA: write protecting again\n"); 9466d238cc4SArjan van de Ven set_pages_ro(virt_to_page(start), size>>PAGE_SHIFT); 9470c42f392SAndi Kleen #endif 9488f0f996eSSteven Rostedt 949ad757b6aSThomas Gleixner start += size; 950ad757b6aSThomas Gleixner size = (unsigned long)__end_rodata - start; 9516d238cc4SArjan van de Ven set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); 952d7d119d7SIngo Molnar printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n", 953ad757b6aSThomas Gleixner size >> 10); 954edeed305SArjan van de Ven rodata_test(); 955ad757b6aSThomas Gleixner 9560c42f392SAndi Kleen #ifdef CONFIG_CPA_DEBUG 957d7d119d7SIngo Molnar printk(KERN_INFO "Testing CPA: undo %lx-%lx\n", start, start + size); 9586d238cc4SArjan van de Ven set_pages_rw(virt_to_page(start), size >> PAGE_SHIFT); 9590c42f392SAndi Kleen 960d7d119d7SIngo Molnar printk(KERN_INFO "Testing CPA: write protecting again\n"); 9616d238cc4SArjan van de Ven set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); 9620c42f392SAndi Kleen #endif 9635bd5a452SMatthieu Castet mark_nxdata_nx(); 964ad757b6aSThomas Gleixner } 965ad757b6aSThomas Gleixner #endif 966ad757b6aSThomas Gleixner 967