170d64ceaSPaul Mackerras /* 270d64ceaSPaul Mackerras * PowerPC version 370d64ceaSPaul Mackerras * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) 470d64ceaSPaul Mackerras * 570d64ceaSPaul Mackerras * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) 670d64ceaSPaul Mackerras * and Cort Dougan (PReP) (cort@cs.nmt.edu) 770d64ceaSPaul Mackerras * Copyright (C) 1996 Paul Mackerras 870d64ceaSPaul Mackerras * 970d64ceaSPaul Mackerras * Derived from "arch/i386/mm/init.c" 1070d64ceaSPaul Mackerras * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds 1170d64ceaSPaul Mackerras * 1270d64ceaSPaul Mackerras * Dave Engebretsen <engebret@us.ibm.com> 1370d64ceaSPaul Mackerras * Rework for PPC64 port. 1470d64ceaSPaul Mackerras * 1570d64ceaSPaul Mackerras * This program is free software; you can redistribute it and/or 1670d64ceaSPaul Mackerras * modify it under the terms of the GNU General Public License 1770d64ceaSPaul Mackerras * as published by the Free Software Foundation; either version 1870d64ceaSPaul Mackerras * 2 of the License, or (at your option) any later version. 1970d64ceaSPaul Mackerras * 2070d64ceaSPaul Mackerras */ 2170d64ceaSPaul Mackerras 22cec08e7aSBenjamin Herrenschmidt #undef DEBUG 23cec08e7aSBenjamin Herrenschmidt 2470d64ceaSPaul Mackerras #include <linux/signal.h> 2570d64ceaSPaul Mackerras #include <linux/sched.h> 2670d64ceaSPaul Mackerras #include <linux/kernel.h> 2770d64ceaSPaul Mackerras #include <linux/errno.h> 2870d64ceaSPaul Mackerras #include <linux/string.h> 2970d64ceaSPaul Mackerras #include <linux/types.h> 3070d64ceaSPaul Mackerras #include <linux/mman.h> 3170d64ceaSPaul Mackerras #include <linux/mm.h> 3270d64ceaSPaul Mackerras #include <linux/swap.h> 3370d64ceaSPaul Mackerras #include <linux/stddef.h> 3470d64ceaSPaul Mackerras #include <linux/vmalloc.h> 3570d64ceaSPaul Mackerras #include <linux/init.h> 3670d64ceaSPaul Mackerras #include <linux/delay.h> 3770d64ceaSPaul Mackerras #include <linux/highmem.h> 3870d64ceaSPaul Mackerras #include <linux/idr.h> 3970d64ceaSPaul Mackerras #include <linux/nodemask.h> 4070d64ceaSPaul Mackerras #include <linux/module.h> 41c9cf5528SRandy Dunlap #include <linux/poison.h> 4295f72d1eSYinghai Lu #include <linux/memblock.h> 43a4fe3ce7SDavid Gibson #include <linux/hugetlb.h> 445a0e3ad6STejun Heo #include <linux/slab.h> 4518569c1fSPaul Mackerras #include <linux/of_fdt.h> 4618569c1fSPaul Mackerras #include <linux/libfdt.h> 4770d64ceaSPaul Mackerras 4870d64ceaSPaul Mackerras #include <asm/pgalloc.h> 4970d64ceaSPaul Mackerras #include <asm/page.h> 5070d64ceaSPaul Mackerras #include <asm/prom.h> 5170d64ceaSPaul Mackerras #include <asm/rtas.h> 5270d64ceaSPaul Mackerras #include <asm/io.h> 5370d64ceaSPaul Mackerras #include <asm/mmu_context.h> 5470d64ceaSPaul Mackerras #include <asm/pgtable.h> 5570d64ceaSPaul Mackerras #include <asm/mmu.h> 567c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 5770d64ceaSPaul Mackerras #include <asm/smp.h> 5870d64ceaSPaul Mackerras #include <asm/machdep.h> 5970d64ceaSPaul Mackerras #include <asm/tlb.h> 6070d64ceaSPaul Mackerras #include <asm/eeh.h> 6170d64ceaSPaul Mackerras #include <asm/processor.h> 6270d64ceaSPaul Mackerras #include <asm/mmzone.h> 6370d64ceaSPaul Mackerras #include <asm/cputable.h> 6470d64ceaSPaul Mackerras #include <asm/sections.h> 6570d64ceaSPaul Mackerras #include <asm/iommu.h> 6670d64ceaSPaul Mackerras #include <asm/vdso.h> 67800fc3eeSDavid Gibson 68800fc3eeSDavid Gibson #include "mmu_decl.h" 6970d64ceaSPaul Mackerras 7094491685SBenjamin Herrenschmidt #ifdef CONFIG_PPC_STD_MMU_64 71dd1842a2SAneesh Kumar K.V #if H_PGTABLE_RANGE > USER_VSID_RANGE 7270d64ceaSPaul Mackerras #warning Limited user VSID range means pagetable space is wasted 7370d64ceaSPaul Mackerras #endif 7494491685SBenjamin Herrenschmidt #endif /* CONFIG_PPC_STD_MMU_64 */ 7570d64ceaSPaul Mackerras 7637dd2badSKumar Gala phys_addr_t memstart_addr = ~0; 7779c3095fSSonny Rao EXPORT_SYMBOL_GPL(memstart_addr); 7837dd2badSKumar Gala phys_addr_t kernstart_addr; 7979c3095fSSonny Rao EXPORT_SYMBOL_GPL(kernstart_addr); 80d7917ba7SKumar Gala 81d29eff7bSAndy Whitcroft #ifdef CONFIG_SPARSEMEM_VMEMMAP 82d29eff7bSAndy Whitcroft /* 83d29eff7bSAndy Whitcroft * Given an address within the vmemmap, determine the pfn of the page that 84d29eff7bSAndy Whitcroft * represents the start of the section it is within. Note that we have to 85d29eff7bSAndy Whitcroft * do this by hand as the proffered address may not be correctly aligned. 86d29eff7bSAndy Whitcroft * Subtraction of non-aligned pointers produces undefined results. 87d29eff7bSAndy Whitcroft */ 8809de9ff8SMichael Ellerman static unsigned long __meminit vmemmap_section_start(unsigned long page) 89d29eff7bSAndy Whitcroft { 90d29eff7bSAndy Whitcroft unsigned long offset = page - ((unsigned long)(vmemmap)); 91d29eff7bSAndy Whitcroft 92d29eff7bSAndy Whitcroft /* Return the pfn of the start of the section. */ 93d29eff7bSAndy Whitcroft return (offset / sizeof(struct page)) & PAGE_SECTION_MASK; 94d29eff7bSAndy Whitcroft } 95d29eff7bSAndy Whitcroft 96d29eff7bSAndy Whitcroft /* 97d29eff7bSAndy Whitcroft * Check if this vmemmap page is already initialised. If any section 98d29eff7bSAndy Whitcroft * which overlaps this vmemmap page is initialised then this page is 99d29eff7bSAndy Whitcroft * initialised already. 100d29eff7bSAndy Whitcroft */ 10109de9ff8SMichael Ellerman static int __meminit vmemmap_populated(unsigned long start, int page_size) 102d29eff7bSAndy Whitcroft { 103d29eff7bSAndy Whitcroft unsigned long end = start + page_size; 10416a05bffSLi Zhong start = (unsigned long)(pfn_to_page(vmemmap_section_start(start))); 105d29eff7bSAndy Whitcroft 106d29eff7bSAndy Whitcroft for (; start < end; start += (PAGES_PER_SECTION * sizeof(struct page))) 10716a05bffSLi Zhong if (pfn_valid(page_to_pfn((struct page *)start))) 108d29eff7bSAndy Whitcroft return 1; 109d29eff7bSAndy Whitcroft 110d29eff7bSAndy Whitcroft return 0; 111d29eff7bSAndy Whitcroft } 112d29eff7bSAndy Whitcroft 113*39e46751SAnshuman Khandual /* 114*39e46751SAnshuman Khandual * vmemmap virtual address space management does not have a traditonal page 115*39e46751SAnshuman Khandual * table to track which virtual struct pages are backed by physical mapping. 116*39e46751SAnshuman Khandual * The virtual to physical mappings are tracked in a simple linked list 117*39e46751SAnshuman Khandual * format. 'vmemmap_list' maintains the entire vmemmap physical mapping at 118*39e46751SAnshuman Khandual * all times where as the 'next' list maintains the available 119*39e46751SAnshuman Khandual * vmemmap_backing structures which have been deleted from the 120*39e46751SAnshuman Khandual * 'vmemmap_global' list during system runtime (memory hotplug remove 121*39e46751SAnshuman Khandual * operation). The freed 'vmemmap_backing' structures are reused later when 122*39e46751SAnshuman Khandual * new requests come in without allocating fresh memory. This pointer also 123*39e46751SAnshuman Khandual * tracks the allocated 'vmemmap_backing' structures as we allocate one 124*39e46751SAnshuman Khandual * full page memory at a time when we dont have any. 125*39e46751SAnshuman Khandual */ 12691eea67cSMark Nelson struct vmemmap_backing *vmemmap_list; 127bd8cb03dSLi Zhong static struct vmemmap_backing *next; 128*39e46751SAnshuman Khandual 129*39e46751SAnshuman Khandual /* 130*39e46751SAnshuman Khandual * The same pointer 'next' tracks individual chunks inside the allocated 131*39e46751SAnshuman Khandual * full page during the boot time and again tracks the freeed nodes during 132*39e46751SAnshuman Khandual * runtime. It is racy but it does not happen as they are separated by the 133*39e46751SAnshuman Khandual * boot process. Will create problem if some how we have memory hotplug 134*39e46751SAnshuman Khandual * operation during boot !! 135*39e46751SAnshuman Khandual */ 136bd8cb03dSLi Zhong static int num_left; 137bd8cb03dSLi Zhong static int num_freed; 13891eea67cSMark Nelson 13991eea67cSMark Nelson static __meminit struct vmemmap_backing * vmemmap_list_alloc(int node) 14091eea67cSMark Nelson { 141bd8cb03dSLi Zhong struct vmemmap_backing *vmem_back; 142bd8cb03dSLi Zhong /* get from freed entries first */ 143bd8cb03dSLi Zhong if (num_freed) { 144bd8cb03dSLi Zhong num_freed--; 145bd8cb03dSLi Zhong vmem_back = next; 146bd8cb03dSLi Zhong next = next->list; 147bd8cb03dSLi Zhong 148bd8cb03dSLi Zhong return vmem_back; 149bd8cb03dSLi Zhong } 15091eea67cSMark Nelson 15191eea67cSMark Nelson /* allocate a page when required and hand out chunks */ 152bd8cb03dSLi Zhong if (!num_left) { 15391eea67cSMark Nelson next = vmemmap_alloc_block(PAGE_SIZE, node); 15491eea67cSMark Nelson if (unlikely(!next)) { 15591eea67cSMark Nelson WARN_ON(1); 15691eea67cSMark Nelson return NULL; 15791eea67cSMark Nelson } 15891eea67cSMark Nelson num_left = PAGE_SIZE / sizeof(struct vmemmap_backing); 15991eea67cSMark Nelson } 16091eea67cSMark Nelson 16191eea67cSMark Nelson num_left--; 16291eea67cSMark Nelson 16391eea67cSMark Nelson return next++; 16491eea67cSMark Nelson } 16591eea67cSMark Nelson 16691eea67cSMark Nelson static __meminit void vmemmap_list_populate(unsigned long phys, 16791eea67cSMark Nelson unsigned long start, 16891eea67cSMark Nelson int node) 16991eea67cSMark Nelson { 17091eea67cSMark Nelson struct vmemmap_backing *vmem_back; 17191eea67cSMark Nelson 17291eea67cSMark Nelson vmem_back = vmemmap_list_alloc(node); 17391eea67cSMark Nelson if (unlikely(!vmem_back)) { 17491eea67cSMark Nelson WARN_ON(1); 17591eea67cSMark Nelson return; 17691eea67cSMark Nelson } 17791eea67cSMark Nelson 17891eea67cSMark Nelson vmem_back->phys = phys; 17991eea67cSMark Nelson vmem_back->virt_addr = start; 18091eea67cSMark Nelson vmem_back->list = vmemmap_list; 18191eea67cSMark Nelson 18291eea67cSMark Nelson vmemmap_list = vmem_back; 18391eea67cSMark Nelson } 18491eea67cSMark Nelson 18571b0bfe4SLi Zhong int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) 18671b0bfe4SLi Zhong { 18771b0bfe4SLi Zhong unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift; 18871b0bfe4SLi Zhong 18971b0bfe4SLi Zhong /* Align to the page size of the linear mapping. */ 19071b0bfe4SLi Zhong start = _ALIGN_DOWN(start, page_size); 19171b0bfe4SLi Zhong 19271b0bfe4SLi Zhong pr_debug("vmemmap_populate %lx..%lx, node %d\n", start, end, node); 19371b0bfe4SLi Zhong 19471b0bfe4SLi Zhong for (; start < end; start += page_size) { 19571b0bfe4SLi Zhong void *p; 1961dace6c6SDavid Gibson int rc; 19771b0bfe4SLi Zhong 19871b0bfe4SLi Zhong if (vmemmap_populated(start, page_size)) 19971b0bfe4SLi Zhong continue; 20071b0bfe4SLi Zhong 20171b0bfe4SLi Zhong p = vmemmap_alloc_block(page_size, node); 20271b0bfe4SLi Zhong if (!p) 20371b0bfe4SLi Zhong return -ENOMEM; 20471b0bfe4SLi Zhong 20571b0bfe4SLi Zhong vmemmap_list_populate(__pa(p), start, node); 20671b0bfe4SLi Zhong 20771b0bfe4SLi Zhong pr_debug(" * %016lx..%016lx allocated at %p\n", 20871b0bfe4SLi Zhong start, start + page_size, p); 20971b0bfe4SLi Zhong 2101dace6c6SDavid Gibson rc = vmemmap_create_mapping(start, page_size, __pa(p)); 2111dace6c6SDavid Gibson if (rc < 0) { 2121dace6c6SDavid Gibson pr_warning( 2131dace6c6SDavid Gibson "vmemmap_populate: Unable to create vmemmap mapping: %d\n", 2141dace6c6SDavid Gibson rc); 2151dace6c6SDavid Gibson return -EFAULT; 2161dace6c6SDavid Gibson } 21771b0bfe4SLi Zhong } 21871b0bfe4SLi Zhong 21971b0bfe4SLi Zhong return 0; 22071b0bfe4SLi Zhong } 22171b0bfe4SLi Zhong 22271b0bfe4SLi Zhong #ifdef CONFIG_MEMORY_HOTPLUG 223bd8cb03dSLi Zhong static unsigned long vmemmap_list_free(unsigned long start) 224bd8cb03dSLi Zhong { 225bd8cb03dSLi Zhong struct vmemmap_backing *vmem_back, *vmem_back_prev; 226bd8cb03dSLi Zhong 227bd8cb03dSLi Zhong vmem_back_prev = vmem_back = vmemmap_list; 228bd8cb03dSLi Zhong 229bd8cb03dSLi Zhong /* look for it with prev pointer recorded */ 230bd8cb03dSLi Zhong for (; vmem_back; vmem_back = vmem_back->list) { 231bd8cb03dSLi Zhong if (vmem_back->virt_addr == start) 232bd8cb03dSLi Zhong break; 233bd8cb03dSLi Zhong vmem_back_prev = vmem_back; 234bd8cb03dSLi Zhong } 235bd8cb03dSLi Zhong 236bd8cb03dSLi Zhong if (unlikely(!vmem_back)) { 237bd8cb03dSLi Zhong WARN_ON(1); 238bd8cb03dSLi Zhong return 0; 239bd8cb03dSLi Zhong } 240bd8cb03dSLi Zhong 241bd8cb03dSLi Zhong /* remove it from vmemmap_list */ 242bd8cb03dSLi Zhong if (vmem_back == vmemmap_list) /* remove head */ 243bd8cb03dSLi Zhong vmemmap_list = vmem_back->list; 244bd8cb03dSLi Zhong else 245bd8cb03dSLi Zhong vmem_back_prev->list = vmem_back->list; 246bd8cb03dSLi Zhong 247bd8cb03dSLi Zhong /* next point to this freed entry */ 248bd8cb03dSLi Zhong vmem_back->list = next; 249bd8cb03dSLi Zhong next = vmem_back; 250bd8cb03dSLi Zhong num_freed++; 251bd8cb03dSLi Zhong 252bd8cb03dSLi Zhong return vmem_back->phys; 253bd8cb03dSLi Zhong } 254bd8cb03dSLi Zhong 25571b0bfe4SLi Zhong void __ref vmemmap_free(unsigned long start, unsigned long end) 256d29eff7bSAndy Whitcroft { 257cec08e7aSBenjamin Herrenschmidt unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift; 258d29eff7bSAndy Whitcroft 259d29eff7bSAndy Whitcroft start = _ALIGN_DOWN(start, page_size); 260d29eff7bSAndy Whitcroft 26171b0bfe4SLi Zhong pr_debug("vmemmap_free %lx...%lx\n", start, end); 26232a74949SBenjamin Herrenschmidt 263d29eff7bSAndy Whitcroft for (; start < end; start += page_size) { 26471b0bfe4SLi Zhong unsigned long addr; 265d29eff7bSAndy Whitcroft 26671b0bfe4SLi Zhong /* 26771b0bfe4SLi Zhong * the section has already be marked as invalid, so 26871b0bfe4SLi Zhong * vmemmap_populated() true means some other sections still 26971b0bfe4SLi Zhong * in this page, so skip it. 27071b0bfe4SLi Zhong */ 271d29eff7bSAndy Whitcroft if (vmemmap_populated(start, page_size)) 272d29eff7bSAndy Whitcroft continue; 273d29eff7bSAndy Whitcroft 27471b0bfe4SLi Zhong addr = vmemmap_list_free(start); 27571b0bfe4SLi Zhong if (addr) { 27671b0bfe4SLi Zhong struct page *page = pfn_to_page(addr >> PAGE_SHIFT); 277d29eff7bSAndy Whitcroft 27871b0bfe4SLi Zhong if (PageReserved(page)) { 27971b0bfe4SLi Zhong /* allocated from bootmem */ 28071b0bfe4SLi Zhong if (page_size < PAGE_SIZE) { 28171b0bfe4SLi Zhong /* 28271b0bfe4SLi Zhong * this shouldn't happen, but if it is 28371b0bfe4SLi Zhong * the case, leave the memory there 28471b0bfe4SLi Zhong */ 28571b0bfe4SLi Zhong WARN_ON_ONCE(1); 28671b0bfe4SLi Zhong } else { 28771b0bfe4SLi Zhong unsigned int nr_pages = 28871b0bfe4SLi Zhong 1 << get_order(page_size); 28971b0bfe4SLi Zhong while (nr_pages--) 29071b0bfe4SLi Zhong free_reserved_page(page++); 291d29eff7bSAndy Whitcroft } 29271b0bfe4SLi Zhong } else 29371b0bfe4SLi Zhong free_pages((unsigned long)(__va(addr)), 29471b0bfe4SLi Zhong get_order(page_size)); 295d29eff7bSAndy Whitcroft 29671b0bfe4SLi Zhong vmemmap_remove_mapping(start, page_size); 297d29eff7bSAndy Whitcroft } 2980197518cSTang Chen } 29971b0bfe4SLi Zhong } 30071b0bfe4SLi Zhong #endif 301f7e3334aSNathan Fontenot void register_page_bootmem_memmap(unsigned long section_nr, 302f7e3334aSNathan Fontenot struct page *start_page, unsigned long size) 303f7e3334aSNathan Fontenot { 304f7e3334aSNathan Fontenot } 305cd3db0c4SBenjamin Herrenschmidt 3068e0861faSAlexey Kardashevskiy /* 3078e0861faSAlexey Kardashevskiy * We do not have access to the sparsemem vmemmap, so we fallback to 3088e0861faSAlexey Kardashevskiy * walking the list of sparsemem blocks which we already maintain for 3098e0861faSAlexey Kardashevskiy * the sake of crashdump. In the long run, we might want to maintain 3108e0861faSAlexey Kardashevskiy * a tree if performance of that linear walk becomes a problem. 3118e0861faSAlexey Kardashevskiy * 3128e0861faSAlexey Kardashevskiy * realmode_pfn_to_page functions can fail due to: 3138e0861faSAlexey Kardashevskiy * 1) As real sparsemem blocks do not lay in RAM continously (they 3148e0861faSAlexey Kardashevskiy * are in virtual address space which is not available in the real mode), 3158e0861faSAlexey Kardashevskiy * the requested page struct can be split between blocks so get_page/put_page 3168e0861faSAlexey Kardashevskiy * may fail. 3178e0861faSAlexey Kardashevskiy * 2) When huge pages are used, the get_page/put_page API will fail 3188e0861faSAlexey Kardashevskiy * in real mode as the linked addresses in the page struct are virtual 3198e0861faSAlexey Kardashevskiy * too. 3208e0861faSAlexey Kardashevskiy */ 3218e0861faSAlexey Kardashevskiy struct page *realmode_pfn_to_page(unsigned long pfn) 3228e0861faSAlexey Kardashevskiy { 3238e0861faSAlexey Kardashevskiy struct vmemmap_backing *vmem_back; 3248e0861faSAlexey Kardashevskiy struct page *page; 3258e0861faSAlexey Kardashevskiy unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift; 3268e0861faSAlexey Kardashevskiy unsigned long pg_va = (unsigned long) pfn_to_page(pfn); 3278e0861faSAlexey Kardashevskiy 3288e0861faSAlexey Kardashevskiy for (vmem_back = vmemmap_list; vmem_back; vmem_back = vmem_back->list) { 3298e0861faSAlexey Kardashevskiy if (pg_va < vmem_back->virt_addr) 3308e0861faSAlexey Kardashevskiy continue; 3318e0861faSAlexey Kardashevskiy 332bd8cb03dSLi Zhong /* After vmemmap_list entry free is possible, need check all */ 333bd8cb03dSLi Zhong if ((pg_va + sizeof(struct page)) <= 334bd8cb03dSLi Zhong (vmem_back->virt_addr + page_size)) { 3358e0861faSAlexey Kardashevskiy page = (struct page *) (vmem_back->phys + pg_va - 3368e0861faSAlexey Kardashevskiy vmem_back->virt_addr); 3378e0861faSAlexey Kardashevskiy return page; 3388e0861faSAlexey Kardashevskiy } 339bd8cb03dSLi Zhong } 3408e0861faSAlexey Kardashevskiy 341bd8cb03dSLi Zhong /* Probably that page struct is split between real pages */ 3428e0861faSAlexey Kardashevskiy return NULL; 3438e0861faSAlexey Kardashevskiy } 3448e0861faSAlexey Kardashevskiy EXPORT_SYMBOL_GPL(realmode_pfn_to_page); 3458e0861faSAlexey Kardashevskiy 3468e0861faSAlexey Kardashevskiy #elif defined(CONFIG_FLATMEM) 3478e0861faSAlexey Kardashevskiy 3488e0861faSAlexey Kardashevskiy struct page *realmode_pfn_to_page(unsigned long pfn) 3498e0861faSAlexey Kardashevskiy { 3508e0861faSAlexey Kardashevskiy struct page *page = pfn_to_page(pfn); 3518e0861faSAlexey Kardashevskiy return page; 3528e0861faSAlexey Kardashevskiy } 3538e0861faSAlexey Kardashevskiy EXPORT_SYMBOL_GPL(realmode_pfn_to_page); 3548e0861faSAlexey Kardashevskiy 3558e0861faSAlexey Kardashevskiy #endif /* CONFIG_SPARSEMEM_VMEMMAP/CONFIG_FLATMEM */ 3561a01dc87SMichael Ellerman 3571a01dc87SMichael Ellerman #ifdef CONFIG_PPC_STD_MMU_64 358c610ec60SMichael Ellerman static bool disable_radix; 359c610ec60SMichael Ellerman static int __init parse_disable_radix(char *p) 360c610ec60SMichael Ellerman { 361c610ec60SMichael Ellerman disable_radix = true; 362c610ec60SMichael Ellerman return 0; 363c610ec60SMichael Ellerman } 364c610ec60SMichael Ellerman early_param("disable_radix", parse_disable_radix); 365c610ec60SMichael Ellerman 36618569c1fSPaul Mackerras /* 367cc3d2940SPaul Mackerras * If we're running under a hypervisor, we need to check the contents of 368cc3d2940SPaul Mackerras * /chosen/ibm,architecture-vec-5 to see if the hypervisor is willing to do 369cc3d2940SPaul Mackerras * radix. If not, we clear the radix feature bit so we fall back to hash. 37018569c1fSPaul Mackerras */ 37118569c1fSPaul Mackerras static void early_check_vec5(void) 37218569c1fSPaul Mackerras { 37318569c1fSPaul Mackerras unsigned long root, chosen; 37418569c1fSPaul Mackerras int size; 37518569c1fSPaul Mackerras const u8 *vec5; 376014d02cbSSuraj Jitindar Singh u8 mmu_supported; 37718569c1fSPaul Mackerras 37818569c1fSPaul Mackerras root = of_get_flat_dt_root(); 37918569c1fSPaul Mackerras chosen = of_get_flat_dt_subnode_by_name(root, "chosen"); 380014d02cbSSuraj Jitindar Singh if (chosen == -FDT_ERR_NOTFOUND) { 38118569c1fSPaul Mackerras cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX; 382014d02cbSSuraj Jitindar Singh return; 383014d02cbSSuraj Jitindar Singh } 384014d02cbSSuraj Jitindar Singh vec5 = of_get_flat_dt_prop(chosen, "ibm,architecture-vec-5", &size); 385014d02cbSSuraj Jitindar Singh if (!vec5) { 386014d02cbSSuraj Jitindar Singh cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX; 387014d02cbSSuraj Jitindar Singh return; 388014d02cbSSuraj Jitindar Singh } 389014d02cbSSuraj Jitindar Singh if (size <= OV5_INDX(OV5_MMU_SUPPORT)) { 390014d02cbSSuraj Jitindar Singh cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX; 391014d02cbSSuraj Jitindar Singh return; 392014d02cbSSuraj Jitindar Singh } 393014d02cbSSuraj Jitindar Singh 394014d02cbSSuraj Jitindar Singh /* Check for supported configuration */ 395014d02cbSSuraj Jitindar Singh mmu_supported = vec5[OV5_INDX(OV5_MMU_SUPPORT)] & 396014d02cbSSuraj Jitindar Singh OV5_FEAT(OV5_MMU_SUPPORT); 397014d02cbSSuraj Jitindar Singh if (mmu_supported == OV5_FEAT(OV5_MMU_RADIX)) { 398014d02cbSSuraj Jitindar Singh /* Hypervisor only supports radix - check enabled && GTSE */ 399014d02cbSSuraj Jitindar Singh if (!early_radix_enabled()) { 400014d02cbSSuraj Jitindar Singh pr_warn("WARNING: Ignoring cmdline option disable_radix\n"); 401014d02cbSSuraj Jitindar Singh } 402014d02cbSSuraj Jitindar Singh if (!(vec5[OV5_INDX(OV5_RADIX_GTSE)] & 403014d02cbSSuraj Jitindar Singh OV5_FEAT(OV5_RADIX_GTSE))) { 404014d02cbSSuraj Jitindar Singh pr_warn("WARNING: Hypervisor doesn't support RADIX with GTSE\n"); 405014d02cbSSuraj Jitindar Singh } 406014d02cbSSuraj Jitindar Singh /* Do radix anyway - the hypervisor said we had to */ 407014d02cbSSuraj Jitindar Singh cur_cpu_spec->mmu_features |= MMU_FTR_TYPE_RADIX; 408014d02cbSSuraj Jitindar Singh } else if (mmu_supported == OV5_FEAT(OV5_MMU_HASH)) { 409014d02cbSSuraj Jitindar Singh /* Hypervisor only supports hash - disable radix */ 410014d02cbSSuraj Jitindar Singh cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX; 411014d02cbSSuraj Jitindar Singh } 41218569c1fSPaul Mackerras } 41318569c1fSPaul Mackerras 4141a01dc87SMichael Ellerman void __init mmu_early_init_devtree(void) 4151a01dc87SMichael Ellerman { 416c610ec60SMichael Ellerman /* Disable radix mode based on kernel command line. */ 417fc36a903SPaul Mackerras if (disable_radix) 4185a25b6f5SAneesh Kumar K.V cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX; 419bacf9cf8SMichael Ellerman 42018569c1fSPaul Mackerras /* 42118569c1fSPaul Mackerras * Check /chosen/ibm,architecture-vec-5 if running as a guest. 42218569c1fSPaul Mackerras * When running bare-metal, we can use radix if we like 42318569c1fSPaul Mackerras * even though the ibm,architecture-vec-5 property created by 42418569c1fSPaul Mackerras * skiboot doesn't have the necessary bits set. 42518569c1fSPaul Mackerras */ 426014d02cbSSuraj Jitindar Singh if (!(mfmsr() & MSR_HV)) 42718569c1fSPaul Mackerras early_check_vec5(); 42818569c1fSPaul Mackerras 429b8f1b4f8SAneesh Kumar K.V if (early_radix_enabled()) 4302537b09cSMichael Ellerman radix__early_init_devtree(); 4312537b09cSMichael Ellerman else 432bacf9cf8SMichael Ellerman hash__early_init_devtree(); 4331a01dc87SMichael Ellerman } 4341a01dc87SMichael Ellerman #endif /* CONFIG_PPC_STD_MMU_64 */ 435