162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Memory subsystem initialization for Hexagon 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci#include <linux/memblock.h> 1162306a36Sopenharmony_ci#include <asm/atomic.h> 1262306a36Sopenharmony_ci#include <linux/highmem.h> 1362306a36Sopenharmony_ci#include <asm/tlb.h> 1462306a36Sopenharmony_ci#include <asm/sections.h> 1562306a36Sopenharmony_ci#include <asm/vm_mmu.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * Define a startpg just past the end of the kernel image and a lastpg 1962306a36Sopenharmony_ci * that corresponds to the end of real or simulated platform memory. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#define bootmem_startpg (PFN_UP(((unsigned long) _end) - PAGE_OFFSET + PHYS_OFFSET)) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciunsigned long bootmem_lastpg; /* Should be set by platform code */ 2462306a36Sopenharmony_ciunsigned long __phys_offset; /* physical kernel offset >> 12 */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Set as variable to limit PMD copies */ 2762306a36Sopenharmony_ciint max_kernel_seg = 0x303; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* indicate pfn's of high memory */ 3062306a36Sopenharmony_ciunsigned long highstart_pfn, highend_pfn; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Default cache attribute for newly created page tables */ 3362306a36Sopenharmony_ciunsigned long _dflt_cache_att = CACHEDEF; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * The current "generation" of kernel map, which should not roll 3762306a36Sopenharmony_ci * over until Hell freezes over. Actual bound in years needs to be 3862306a36Sopenharmony_ci * calculated to confirm. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ciDEFINE_SPINLOCK(kmap_gen_lock); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* checkpatch says don't init this to 0. */ 4362306a36Sopenharmony_ciunsigned long long kmap_generation; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * mem_init - initializes memory 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * Frees up bootmem 4962306a36Sopenharmony_ci * Fixes up more stuff for HIGHMEM 5062306a36Sopenharmony_ci * Calculates and displays memory available/used 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_civoid __init mem_init(void) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci /* No idea where this is actually declared. Seems to evade LXR. */ 5562306a36Sopenharmony_ci memblock_free_all(); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * To-Do: someone somewhere should wipe out the bootmem map 5962306a36Sopenharmony_ci * after we're done? 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * This can be moved to some more virtual-memory-specific 6462306a36Sopenharmony_ci * initialization hook at some point. Set the init_mm 6562306a36Sopenharmony_ci * descriptors "context" value to point to the initial 6662306a36Sopenharmony_ci * kernel segment table's physical address. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci init_mm.context.ptbase = __pa(init_mm.pgd); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_civoid sync_icache_dcache(pte_t pte) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci unsigned long addr; 7462306a36Sopenharmony_ci struct page *page; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci page = pte_page(pte); 7762306a36Sopenharmony_ci addr = (unsigned long) page_address(page); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci __vmcache_idsync(addr, PAGE_SIZE); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* 8362306a36Sopenharmony_ci * In order to set up page allocator "nodes", 8462306a36Sopenharmony_ci * somebody has to call free_area_init() for UMA. 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * In this mode, we only have one pg_data_t 8762306a36Sopenharmony_ci * structure: contig_mem_data. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_civoid __init paging_init(void) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci unsigned long max_zone_pfn[MAX_NR_ZONES] = {0, }; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * This is not particularly well documented anywhere, but 9562306a36Sopenharmony_ci * give ZONE_NORMAL all the memory, including the big holes 9662306a36Sopenharmony_ci * left by the kernel+bootmem_map which are already left as reserved 9762306a36Sopenharmony_ci * in the bootmem_map; free_area_init should see those bits and 9862306a36Sopenharmony_ci * adjust accordingly. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci max_zone_pfn[ZONE_NORMAL] = max_low_pfn; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci free_area_init(max_zone_pfn); /* sets up the zonelists and mem_map */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * Start of high memory area. Will probably need something more 10762306a36Sopenharmony_ci * fancy if we... get more fancy. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci high_memory = (void *)((bootmem_lastpg + 1) << PAGE_SHIFT); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#ifndef DMA_RESERVE 11362306a36Sopenharmony_ci#define DMA_RESERVE (4) 11462306a36Sopenharmony_ci#endif 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define DMA_CHUNKSIZE (1<<22) 11762306a36Sopenharmony_ci#define DMA_RESERVED_BYTES (DMA_RESERVE * DMA_CHUNKSIZE) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* 12062306a36Sopenharmony_ci * Pick out the memory size. We look for mem=size, 12162306a36Sopenharmony_ci * where size is "size[KkMm]" 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_cistatic int __init early_mem(char *p) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci unsigned long size; 12662306a36Sopenharmony_ci char *endp; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci size = memparse(p, &endp); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci bootmem_lastpg = PFN_DOWN(size); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ciearly_param("mem", early_mem); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cisize_t hexagon_coherent_pool_size = (size_t) (DMA_RESERVE << 22); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_civoid __init setup_arch_memory(void) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci /* XXX Todo: this probably should be cleaned up */ 14162306a36Sopenharmony_ci u32 *segtable = (u32 *) &swapper_pg_dir[0]; 14262306a36Sopenharmony_ci u32 *segtable_end; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * Set up boot memory allocator 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * The Gorman book also talks about these functions. 14862306a36Sopenharmony_ci * This needs to change for highmem setups. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Prior to this, bootmem_lastpg is actually mem size */ 15262306a36Sopenharmony_ci bootmem_lastpg += ARCH_PFN_OFFSET; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Memory size needs to be a multiple of 16M */ 15562306a36Sopenharmony_ci bootmem_lastpg = PFN_DOWN((bootmem_lastpg << PAGE_SHIFT) & 15662306a36Sopenharmony_ci ~((BIG_KERNEL_PAGE_SIZE) - 1)); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci memblock_add(PHYS_OFFSET, 15962306a36Sopenharmony_ci (bootmem_lastpg - ARCH_PFN_OFFSET) << PAGE_SHIFT); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Reserve kernel text/data/bss */ 16262306a36Sopenharmony_ci memblock_reserve(PHYS_OFFSET, 16362306a36Sopenharmony_ci (bootmem_startpg - ARCH_PFN_OFFSET) << PAGE_SHIFT); 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * Reserve the top DMA_RESERVE bytes of RAM for DMA (uncached) 16662306a36Sopenharmony_ci * memory allocation 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci max_low_pfn = bootmem_lastpg - PFN_DOWN(DMA_RESERVED_BYTES); 16962306a36Sopenharmony_ci min_low_pfn = ARCH_PFN_OFFSET; 17062306a36Sopenharmony_ci memblock_reserve(PFN_PHYS(max_low_pfn), DMA_RESERVED_BYTES); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci printk(KERN_INFO "bootmem_startpg: 0x%08lx\n", bootmem_startpg); 17362306a36Sopenharmony_ci printk(KERN_INFO "bootmem_lastpg: 0x%08lx\n", bootmem_lastpg); 17462306a36Sopenharmony_ci printk(KERN_INFO "min_low_pfn: 0x%08lx\n", min_low_pfn); 17562306a36Sopenharmony_ci printk(KERN_INFO "max_low_pfn: 0x%08lx\n", max_low_pfn); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * The default VM page tables (will be) populated with 17962306a36Sopenharmony_ci * VA=PA+PAGE_OFFSET mapping. We go in and invalidate entries 18062306a36Sopenharmony_ci * higher than what we have memory for. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* this is pointer arithmetic; each entry covers 4MB */ 18462306a36Sopenharmony_ci segtable = segtable + (PAGE_OFFSET >> 22); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* this actually only goes to the end of the first gig */ 18762306a36Sopenharmony_ci segtable_end = segtable + (1<<(30-22)); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * Move forward to the start of empty pages; take into account 19162306a36Sopenharmony_ci * phys_offset shift. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci segtable += (bootmem_lastpg-ARCH_PFN_OFFSET)>>(22-PAGE_SHIFT); 19562306a36Sopenharmony_ci { 19662306a36Sopenharmony_ci int i; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for (i = 1 ; i <= DMA_RESERVE ; i++) 19962306a36Sopenharmony_ci segtable[-i] = ((segtable[-i] & __HVM_PTE_PGMASK_4MB) 20062306a36Sopenharmony_ci | __HVM_PTE_R | __HVM_PTE_W | __HVM_PTE_X 20162306a36Sopenharmony_ci | __HEXAGON_C_UNC << 6 20262306a36Sopenharmony_ci | __HVM_PDE_S_4MB); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci printk(KERN_INFO "clearing segtable from %p to %p\n", segtable, 20662306a36Sopenharmony_ci segtable_end); 20762306a36Sopenharmony_ci while (segtable < (segtable_end-8)) 20862306a36Sopenharmony_ci *(segtable++) = __HVM_PDE_S_INVALID; 20962306a36Sopenharmony_ci /* stop the pointer at the device I/O 4MB page */ 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci printk(KERN_INFO "segtable = %p (should be equal to _K_io_map)\n", 21262306a36Sopenharmony_ci segtable); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#if 0 21562306a36Sopenharmony_ci /* Other half of the early device table from vm_init_segtable. */ 21662306a36Sopenharmony_ci printk(KERN_INFO "&_K_init_devicetable = 0x%08x\n", 21762306a36Sopenharmony_ci (unsigned long) _K_init_devicetable-PAGE_OFFSET); 21862306a36Sopenharmony_ci *segtable = ((u32) (unsigned long) _K_init_devicetable-PAGE_OFFSET) | 21962306a36Sopenharmony_ci __HVM_PDE_S_4KB; 22062306a36Sopenharmony_ci printk(KERN_INFO "*segtable = 0x%08x\n", *segtable); 22162306a36Sopenharmony_ci#endif 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * The bootmem allocator seemingly just lives to feed memory 22562306a36Sopenharmony_ci * to the paging system 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci printk(KERN_INFO "PAGE_SIZE=%lu\n", PAGE_SIZE); 22862306a36Sopenharmony_ci paging_init(); /* See Gorman Book, 2.3 */ 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* 23162306a36Sopenharmony_ci * At this point, the page allocator is kind of initialized, but 23262306a36Sopenharmony_ci * apparently no pages are available (just like with the bootmem 23362306a36Sopenharmony_ci * allocator), and need to be freed themselves via mem_init(), 23462306a36Sopenharmony_ci * which is called by start_kernel() later on in the process 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic const pgprot_t protection_map[16] = { 23962306a36Sopenharmony_ci [VM_NONE] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 24062306a36Sopenharmony_ci CACHEDEF), 24162306a36Sopenharmony_ci [VM_READ] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 24262306a36Sopenharmony_ci _PAGE_READ | CACHEDEF), 24362306a36Sopenharmony_ci [VM_WRITE] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 24462306a36Sopenharmony_ci CACHEDEF), 24562306a36Sopenharmony_ci [VM_WRITE | VM_READ] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 24662306a36Sopenharmony_ci _PAGE_READ | CACHEDEF), 24762306a36Sopenharmony_ci [VM_EXEC] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 24862306a36Sopenharmony_ci _PAGE_EXECUTE | CACHEDEF), 24962306a36Sopenharmony_ci [VM_EXEC | VM_READ] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 25062306a36Sopenharmony_ci _PAGE_EXECUTE | _PAGE_READ | 25162306a36Sopenharmony_ci CACHEDEF), 25262306a36Sopenharmony_ci [VM_EXEC | VM_WRITE] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 25362306a36Sopenharmony_ci _PAGE_EXECUTE | CACHEDEF), 25462306a36Sopenharmony_ci [VM_EXEC | VM_WRITE | VM_READ] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 25562306a36Sopenharmony_ci _PAGE_EXECUTE | _PAGE_READ | 25662306a36Sopenharmony_ci CACHEDEF), 25762306a36Sopenharmony_ci [VM_SHARED] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 25862306a36Sopenharmony_ci CACHEDEF), 25962306a36Sopenharmony_ci [VM_SHARED | VM_READ] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 26062306a36Sopenharmony_ci _PAGE_READ | CACHEDEF), 26162306a36Sopenharmony_ci [VM_SHARED | VM_WRITE] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 26262306a36Sopenharmony_ci _PAGE_WRITE | CACHEDEF), 26362306a36Sopenharmony_ci [VM_SHARED | VM_WRITE | VM_READ] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 26462306a36Sopenharmony_ci _PAGE_READ | _PAGE_WRITE | 26562306a36Sopenharmony_ci CACHEDEF), 26662306a36Sopenharmony_ci [VM_SHARED | VM_EXEC] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 26762306a36Sopenharmony_ci _PAGE_EXECUTE | CACHEDEF), 26862306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_READ] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 26962306a36Sopenharmony_ci _PAGE_EXECUTE | _PAGE_READ | 27062306a36Sopenharmony_ci CACHEDEF), 27162306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_WRITE] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 27262306a36Sopenharmony_ci _PAGE_EXECUTE | _PAGE_WRITE | 27362306a36Sopenharmony_ci CACHEDEF), 27462306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = __pgprot(_PAGE_PRESENT | _PAGE_USER | 27562306a36Sopenharmony_ci _PAGE_READ | _PAGE_EXECUTE | 27662306a36Sopenharmony_ci _PAGE_WRITE | CACHEDEF) 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ciDECLARE_VM_GET_PAGE_PROT 279