162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (C) 2007-2008 Michal Simek <monstr@monstr.eu> 362306a36Sopenharmony_ci * Copyright (C) 2006 Atmark Techno, Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 662306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 762306a36Sopenharmony_ci * for more details. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 1162306a36Sopenharmony_ci#include <linux/memblock.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> /* mem_init */ 1562306a36Sopenharmony_ci#include <linux/initrd.h> 1662306a36Sopenharmony_ci#include <linux/of_fdt.h> 1762306a36Sopenharmony_ci#include <linux/pagemap.h> 1862306a36Sopenharmony_ci#include <linux/pfn.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/swap.h> 2162306a36Sopenharmony_ci#include <linux/export.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/page.h> 2462306a36Sopenharmony_ci#include <asm/mmu_context.h> 2562306a36Sopenharmony_ci#include <asm/pgalloc.h> 2662306a36Sopenharmony_ci#include <asm/sections.h> 2762306a36Sopenharmony_ci#include <asm/tlb.h> 2862306a36Sopenharmony_ci#include <asm/fixmap.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Use for MMU and noMMU because of PCI generic code */ 3162306a36Sopenharmony_ciint mem_init_done; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cichar *klimit = _end; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * Initialize the bootmem system and give it all the memory we 3762306a36Sopenharmony_ci * have available. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ciunsigned long memory_start; 4062306a36Sopenharmony_ciEXPORT_SYMBOL(memory_start); 4162306a36Sopenharmony_ciunsigned long memory_size; 4262306a36Sopenharmony_ciEXPORT_SYMBOL(memory_size); 4362306a36Sopenharmony_ciunsigned long lowmem_size; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciEXPORT_SYMBOL(min_low_pfn); 4662306a36Sopenharmony_ciEXPORT_SYMBOL(max_low_pfn); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM 4962306a36Sopenharmony_cistatic void __init highmem_init(void) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci pr_debug("%x\n", (u32)PKMAP_BASE); 5262306a36Sopenharmony_ci map_page(PKMAP_BASE, 0, 0); /* XXX gross */ 5362306a36Sopenharmony_ci pkmap_page_table = virt_to_kpte(PKMAP_BASE); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void __meminit highmem_setup(void) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci unsigned long pfn; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci for (pfn = max_low_pfn; pfn < max_pfn; ++pfn) { 6162306a36Sopenharmony_ci struct page *page = pfn_to_page(pfn); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* FIXME not sure about */ 6462306a36Sopenharmony_ci if (!memblock_is_reserved(pfn << PAGE_SHIFT)) 6562306a36Sopenharmony_ci free_highmem_page(page); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci#endif /* CONFIG_HIGHMEM */ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * paging_init() sets up the page tables - in fact we've already done this. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic void __init paging_init(void) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci unsigned long zones_size[MAX_NR_ZONES]; 7662306a36Sopenharmony_ci int idx; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* Setup fixmaps */ 7962306a36Sopenharmony_ci for (idx = 0; idx < __end_of_fixed_addresses; idx++) 8062306a36Sopenharmony_ci clear_fixmap(idx); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Clean every zones */ 8362306a36Sopenharmony_ci memset(zones_size, 0, sizeof(zones_size)); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM 8662306a36Sopenharmony_ci highmem_init(); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci zones_size[ZONE_DMA] = max_low_pfn; 8962306a36Sopenharmony_ci zones_size[ZONE_HIGHMEM] = max_pfn; 9062306a36Sopenharmony_ci#else 9162306a36Sopenharmony_ci zones_size[ZONE_DMA] = max_pfn; 9262306a36Sopenharmony_ci#endif 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* We don't have holes in memory map */ 9562306a36Sopenharmony_ci free_area_init(zones_size); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_civoid __init setup_memory(void) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci /* 10162306a36Sopenharmony_ci * Kernel: 10262306a36Sopenharmony_ci * start: base phys address of kernel - page align 10362306a36Sopenharmony_ci * end: base phys address of kernel - page align 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * min_low_pfn - the first page (mm/bootmem.c - node_boot_start) 10662306a36Sopenharmony_ci * max_low_pfn 10762306a36Sopenharmony_ci * max_mapnr - the first unused page (mm/bootmem.c - node_low_pfn) 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* memory start is from the kernel end (aligned) to higher addr */ 11162306a36Sopenharmony_ci min_low_pfn = memory_start >> PAGE_SHIFT; /* minimum for allocation */ 11262306a36Sopenharmony_ci /* RAM is assumed contiguous */ 11362306a36Sopenharmony_ci max_mapnr = memory_size >> PAGE_SHIFT; 11462306a36Sopenharmony_ci max_low_pfn = ((u64)memory_start + (u64)lowmem_size) >> PAGE_SHIFT; 11562306a36Sopenharmony_ci max_pfn = ((u64)memory_start + (u64)memory_size) >> PAGE_SHIFT; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci pr_info("%s: max_mapnr: %#lx\n", __func__, max_mapnr); 11862306a36Sopenharmony_ci pr_info("%s: min_low_pfn: %#lx\n", __func__, min_low_pfn); 11962306a36Sopenharmony_ci pr_info("%s: max_low_pfn: %#lx\n", __func__, max_low_pfn); 12062306a36Sopenharmony_ci pr_info("%s: max_pfn: %#lx\n", __func__, max_pfn); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci paging_init(); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_civoid __init mem_init(void) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci high_memory = (void *)__va(memory_start + lowmem_size - 1); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* this will put all memory onto the freelists */ 13062306a36Sopenharmony_ci memblock_free_all(); 13162306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM 13262306a36Sopenharmony_ci highmem_setup(); 13362306a36Sopenharmony_ci#endif 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci mem_init_done = 1; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciint page_is_ram(unsigned long pfn) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci return pfn < max_low_pfn; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* 14462306a36Sopenharmony_ci * Check for command-line options that affect what MMU_init will do. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistatic void mm_cmdline_setup(void) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned long maxmem = 0; 14962306a36Sopenharmony_ci char *p = cmd_line; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Look for mem= option on command line */ 15262306a36Sopenharmony_ci p = strstr(cmd_line, "mem="); 15362306a36Sopenharmony_ci if (p) { 15462306a36Sopenharmony_ci p += 4; 15562306a36Sopenharmony_ci maxmem = memparse(p, &p); 15662306a36Sopenharmony_ci if (maxmem && memory_size > maxmem) { 15762306a36Sopenharmony_ci memory_size = maxmem; 15862306a36Sopenharmony_ci memblock.memory.regions[0].size = memory_size; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * MMU_init_hw does the chip-specific initialization of the MMU hardware. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic void __init mmu_init_hw(void) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * The Zone Protection Register (ZPR) defines how protection will 17062306a36Sopenharmony_ci * be applied to every page which is a member of a given zone. At 17162306a36Sopenharmony_ci * present, we utilize only two of the zones. 17262306a36Sopenharmony_ci * The zone index bits (of ZSEL) in the PTE are used for software 17362306a36Sopenharmony_ci * indicators, except the LSB. For user access, zone 1 is used, 17462306a36Sopenharmony_ci * for kernel access, zone 0 is used. We set all but zone 1 17562306a36Sopenharmony_ci * to zero, allowing only kernel access as indicated in the PTE. 17662306a36Sopenharmony_ci * For zone 1, we set a 01 binary (a value of 10 will not work) 17762306a36Sopenharmony_ci * to allow user access as indicated in the PTE. This also allows 17862306a36Sopenharmony_ci * kernel access as indicated in the PTE. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci __asm__ __volatile__ ("ori r11, r0, 0x10000000;" \ 18162306a36Sopenharmony_ci "mts rzpr, r11;" 18262306a36Sopenharmony_ci : : : "r11"); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * MMU_init sets up the basic memory mappings for the kernel, 18762306a36Sopenharmony_ci * including both RAM and possibly some I/O regions, 18862306a36Sopenharmony_ci * and sets up the page tables and the MMU hardware ready to go. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* called from head.S */ 19262306a36Sopenharmony_ciasmlinkage void __init mmu_init(void) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci unsigned int kstart, ksize; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!memblock.reserved.cnt) { 19762306a36Sopenharmony_ci pr_emerg("Error memory count\n"); 19862306a36Sopenharmony_ci machine_restart(NULL); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if ((u32) memblock.memory.regions[0].size < 0x400000) { 20262306a36Sopenharmony_ci pr_emerg("Memory must be greater than 4MB\n"); 20362306a36Sopenharmony_ci machine_restart(NULL); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if ((u32) memblock.memory.regions[0].size < kernel_tlb) { 20762306a36Sopenharmony_ci pr_emerg("Kernel size is greater than memory node\n"); 20862306a36Sopenharmony_ci machine_restart(NULL); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Find main memory where the kernel is */ 21262306a36Sopenharmony_ci memory_start = (u32) memblock.memory.regions[0].base; 21362306a36Sopenharmony_ci lowmem_size = memory_size = (u32) memblock.memory.regions[0].size; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (lowmem_size > CONFIG_LOWMEM_SIZE) { 21662306a36Sopenharmony_ci lowmem_size = CONFIG_LOWMEM_SIZE; 21762306a36Sopenharmony_ci#ifndef CONFIG_HIGHMEM 21862306a36Sopenharmony_ci memory_size = lowmem_size; 21962306a36Sopenharmony_ci#endif 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci mm_cmdline_setup(); /* FIXME parse args from command line - not used */ 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Map out the kernel text/data/bss from the available physical 22662306a36Sopenharmony_ci * memory. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci kstart = __pa(CONFIG_KERNEL_START); /* kernel start */ 22962306a36Sopenharmony_ci /* kernel size */ 23062306a36Sopenharmony_ci ksize = PAGE_ALIGN(((u32)_end - (u32)CONFIG_KERNEL_START)); 23162306a36Sopenharmony_ci memblock_reserve(kstart, ksize); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#if defined(CONFIG_BLK_DEV_INITRD) 23462306a36Sopenharmony_ci /* Remove the init RAM disk from the available memory. */ 23562306a36Sopenharmony_ci if (initrd_start) { 23662306a36Sopenharmony_ci unsigned long size; 23762306a36Sopenharmony_ci size = initrd_end - initrd_start; 23862306a36Sopenharmony_ci memblock_reserve(__virt_to_phys(initrd_start), size); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci#endif /* CONFIG_BLK_DEV_INITRD */ 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Initialize the MMU hardware */ 24362306a36Sopenharmony_ci mmu_init_hw(); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Map in all of RAM starting at CONFIG_KERNEL_START */ 24662306a36Sopenharmony_ci mapin_ram(); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Extend vmalloc and ioremap area as big as possible */ 24962306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM 25062306a36Sopenharmony_ci ioremap_base = ioremap_bot = PKMAP_BASE; 25162306a36Sopenharmony_ci#else 25262306a36Sopenharmony_ci ioremap_base = ioremap_bot = FIXADDR_START; 25362306a36Sopenharmony_ci#endif 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Initialize the context management stuff */ 25662306a36Sopenharmony_ci mmu_context_init(); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Shortly after that, the entire linear mapping will be available */ 25962306a36Sopenharmony_ci /* This will also cause that unflatten device tree will be allocated 26062306a36Sopenharmony_ci * inside 768MB limit */ 26162306a36Sopenharmony_ci memblock_set_current_limit(memory_start + lowmem_size - 1); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci parse_early_param(); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci early_init_fdt_scan_reserved_mem(); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* CMA initialization */ 26862306a36Sopenharmony_ci dma_contiguous_reserve(memory_start + lowmem_size - 1); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci memblock_dump_all(); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic const pgprot_t protection_map[16] = { 27462306a36Sopenharmony_ci [VM_NONE] = PAGE_NONE, 27562306a36Sopenharmony_ci [VM_READ] = PAGE_READONLY_X, 27662306a36Sopenharmony_ci [VM_WRITE] = PAGE_COPY, 27762306a36Sopenharmony_ci [VM_WRITE | VM_READ] = PAGE_COPY_X, 27862306a36Sopenharmony_ci [VM_EXEC] = PAGE_READONLY, 27962306a36Sopenharmony_ci [VM_EXEC | VM_READ] = PAGE_READONLY_X, 28062306a36Sopenharmony_ci [VM_EXEC | VM_WRITE] = PAGE_COPY, 28162306a36Sopenharmony_ci [VM_EXEC | VM_WRITE | VM_READ] = PAGE_COPY_X, 28262306a36Sopenharmony_ci [VM_SHARED] = PAGE_NONE, 28362306a36Sopenharmony_ci [VM_SHARED | VM_READ] = PAGE_READONLY_X, 28462306a36Sopenharmony_ci [VM_SHARED | VM_WRITE] = PAGE_SHARED, 28562306a36Sopenharmony_ci [VM_SHARED | VM_WRITE | VM_READ] = PAGE_SHARED_X, 28662306a36Sopenharmony_ci [VM_SHARED | VM_EXEC] = PAGE_READONLY, 28762306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_READ] = PAGE_READONLY_X, 28862306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_SHARED, 28962306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED_X 29062306a36Sopenharmony_ci}; 29162306a36Sopenharmony_ciDECLARE_VM_GET_PAGE_PROT 292