162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/parisc/mm/init.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1995 Linus Torvalds 662306a36Sopenharmony_ci * Copyright 1999 SuSE GmbH 762306a36Sopenharmony_ci * changed by Philipp Rumpf 862306a36Sopenharmony_ci * Copyright 1999 Philipp Rumpf (prumpf@tux.org) 962306a36Sopenharmony_ci * Copyright 2004 Randolph Chung (tausq@debian.org) 1062306a36Sopenharmony_ci * Copyright 2006-2007 Helge Deller (deller@gmx.de) 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/memblock.h> 1862306a36Sopenharmony_ci#include <linux/gfp.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/initrd.h> 2262306a36Sopenharmony_ci#include <linux/swap.h> 2362306a36Sopenharmony_ci#include <linux/unistd.h> 2462306a36Sopenharmony_ci#include <linux/nodemask.h> /* for node_online_map */ 2562306a36Sopenharmony_ci#include <linux/pagemap.h> /* for release_pages */ 2662306a36Sopenharmony_ci#include <linux/compat.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <asm/pgalloc.h> 2962306a36Sopenharmony_ci#include <asm/tlb.h> 3062306a36Sopenharmony_ci#include <asm/pdc_chassis.h> 3162306a36Sopenharmony_ci#include <asm/mmzone.h> 3262306a36Sopenharmony_ci#include <asm/sections.h> 3362306a36Sopenharmony_ci#include <asm/msgbuf.h> 3462306a36Sopenharmony_ci#include <asm/sparsemem.h> 3562306a36Sopenharmony_ci#include <asm/asm-offsets.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciextern int data_start; 3862306a36Sopenharmony_ciextern void parisc_kernel_start(void); /* Kernel entry point in head.S */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 4162306a36Sopenharmony_cipmd_t pmd0[PTRS_PER_PMD] __section(".data..vm0.pmd") __attribute__ ((aligned(PAGE_SIZE))); 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cipgd_t swapper_pg_dir[PTRS_PER_PGD] __section(".data..vm0.pgd") __attribute__ ((aligned(PAGE_SIZE))); 4562306a36Sopenharmony_cipte_t pg0[PT_INITIAL * PTRS_PER_PTE] __section(".data..vm0.pte") __attribute__ ((aligned(PAGE_SIZE))); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct resource data_resource = { 4862306a36Sopenharmony_ci .name = "Kernel data", 4962306a36Sopenharmony_ci .flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct resource code_resource = { 5362306a36Sopenharmony_ci .name = "Kernel code", 5462306a36Sopenharmony_ci .flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM, 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct resource pdcdata_resource = { 5862306a36Sopenharmony_ci .name = "PDC data (Page Zero)", 5962306a36Sopenharmony_ci .start = 0, 6062306a36Sopenharmony_ci .end = 0x9ff, 6162306a36Sopenharmony_ci .flags = IORESOURCE_BUSY | IORESOURCE_MEM, 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct resource sysram_resources[MAX_PHYSMEM_RANGES] __ro_after_init; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* The following array is initialized from the firmware specific 6762306a36Sopenharmony_ci * information retrieved in kernel/inventory.c. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciphysmem_range_t pmem_ranges[MAX_PHYSMEM_RANGES] __initdata; 7162306a36Sopenharmony_ciint npmem_ranges __initdata; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#ifdef CONFIG_64BIT 7462306a36Sopenharmony_ci#define MAX_MEM (1UL << MAX_PHYSMEM_BITS) 7562306a36Sopenharmony_ci#else /* !CONFIG_64BIT */ 7662306a36Sopenharmony_ci#define MAX_MEM (3584U*1024U*1024U) 7762306a36Sopenharmony_ci#endif /* !CONFIG_64BIT */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic unsigned long mem_limit __read_mostly = MAX_MEM; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void __init mem_limit_func(void) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci char *cp, *end; 8462306a36Sopenharmony_ci unsigned long limit; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* We need this before __setup() functions are called */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci limit = MAX_MEM; 8962306a36Sopenharmony_ci for (cp = boot_command_line; *cp; ) { 9062306a36Sopenharmony_ci if (memcmp(cp, "mem=", 4) == 0) { 9162306a36Sopenharmony_ci cp += 4; 9262306a36Sopenharmony_ci limit = memparse(cp, &end); 9362306a36Sopenharmony_ci if (end != cp) 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci cp = end; 9662306a36Sopenharmony_ci } else { 9762306a36Sopenharmony_ci while (*cp != ' ' && *cp) 9862306a36Sopenharmony_ci ++cp; 9962306a36Sopenharmony_ci while (*cp == ' ') 10062306a36Sopenharmony_ci ++cp; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (limit < mem_limit) 10562306a36Sopenharmony_ci mem_limit = limit; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define MAX_GAP (0x40000000UL >> PAGE_SHIFT) 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void __init setup_bootmem(void) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci unsigned long mem_max; 11362306a36Sopenharmony_ci#ifndef CONFIG_SPARSEMEM 11462306a36Sopenharmony_ci physmem_range_t pmem_holes[MAX_PHYSMEM_RANGES - 1]; 11562306a36Sopenharmony_ci int npmem_holes; 11662306a36Sopenharmony_ci#endif 11762306a36Sopenharmony_ci int i, sysram_resource_count; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci disable_sr_hashing(); /* Turn off space register hashing */ 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * Sort the ranges. Since the number of ranges is typically 12362306a36Sopenharmony_ci * small, and performance is not an issue here, just do 12462306a36Sopenharmony_ci * a simple insertion sort. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci for (i = 1; i < npmem_ranges; i++) { 12862306a36Sopenharmony_ci int j; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci for (j = i; j > 0; j--) { 13162306a36Sopenharmony_ci if (pmem_ranges[j-1].start_pfn < 13262306a36Sopenharmony_ci pmem_ranges[j].start_pfn) { 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci swap(pmem_ranges[j-1], pmem_ranges[j]); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#ifndef CONFIG_SPARSEMEM 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * Throw out ranges that are too far apart (controlled by 14362306a36Sopenharmony_ci * MAX_GAP). 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci for (i = 1; i < npmem_ranges; i++) { 14762306a36Sopenharmony_ci if (pmem_ranges[i].start_pfn - 14862306a36Sopenharmony_ci (pmem_ranges[i-1].start_pfn + 14962306a36Sopenharmony_ci pmem_ranges[i-1].pages) > MAX_GAP) { 15062306a36Sopenharmony_ci npmem_ranges = i; 15162306a36Sopenharmony_ci printk("Large gap in memory detected (%ld pages). " 15262306a36Sopenharmony_ci "Consider turning on CONFIG_SPARSEMEM\n", 15362306a36Sopenharmony_ci pmem_ranges[i].start_pfn - 15462306a36Sopenharmony_ci (pmem_ranges[i-1].start_pfn + 15562306a36Sopenharmony_ci pmem_ranges[i-1].pages)); 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci#endif 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Print the memory ranges */ 16262306a36Sopenharmony_ci pr_info("Memory Ranges:\n"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci for (i = 0; i < npmem_ranges; i++) { 16562306a36Sopenharmony_ci struct resource *res = &sysram_resources[i]; 16662306a36Sopenharmony_ci unsigned long start; 16762306a36Sopenharmony_ci unsigned long size; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci size = (pmem_ranges[i].pages << PAGE_SHIFT); 17062306a36Sopenharmony_ci start = (pmem_ranges[i].start_pfn << PAGE_SHIFT); 17162306a36Sopenharmony_ci pr_info("%2d) Start 0x%016lx End 0x%016lx Size %6ld MB\n", 17262306a36Sopenharmony_ci i, start, start + (size - 1), size >> 20); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* request memory resource */ 17562306a36Sopenharmony_ci res->name = "System RAM"; 17662306a36Sopenharmony_ci res->start = start; 17762306a36Sopenharmony_ci res->end = start + size - 1; 17862306a36Sopenharmony_ci res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; 17962306a36Sopenharmony_ci request_resource(&iomem_resource, res); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci sysram_resource_count = npmem_ranges; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * For 32 bit kernels we limit the amount of memory we can 18662306a36Sopenharmony_ci * support, in order to preserve enough kernel address space 18762306a36Sopenharmony_ci * for other purposes. For 64 bit kernels we don't normally 18862306a36Sopenharmony_ci * limit the memory, but this mechanism can be used to 18962306a36Sopenharmony_ci * artificially limit the amount of memory (and it is written 19062306a36Sopenharmony_ci * to work with multiple memory ranges). 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci mem_limit_func(); /* check for "mem=" argument */ 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci mem_max = 0; 19662306a36Sopenharmony_ci for (i = 0; i < npmem_ranges; i++) { 19762306a36Sopenharmony_ci unsigned long rsize; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci rsize = pmem_ranges[i].pages << PAGE_SHIFT; 20062306a36Sopenharmony_ci if ((mem_max + rsize) > mem_limit) { 20162306a36Sopenharmony_ci printk(KERN_WARNING "Memory truncated to %ld MB\n", mem_limit >> 20); 20262306a36Sopenharmony_ci if (mem_max == mem_limit) 20362306a36Sopenharmony_ci npmem_ranges = i; 20462306a36Sopenharmony_ci else { 20562306a36Sopenharmony_ci pmem_ranges[i].pages = (mem_limit >> PAGE_SHIFT) 20662306a36Sopenharmony_ci - (mem_max >> PAGE_SHIFT); 20762306a36Sopenharmony_ci npmem_ranges = i + 1; 20862306a36Sopenharmony_ci mem_max = mem_limit; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci mem_max += rsize; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci printk(KERN_INFO "Total Memory: %ld MB\n",mem_max >> 20); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci#ifndef CONFIG_SPARSEMEM 21862306a36Sopenharmony_ci /* Merge the ranges, keeping track of the holes */ 21962306a36Sopenharmony_ci { 22062306a36Sopenharmony_ci unsigned long end_pfn; 22162306a36Sopenharmony_ci unsigned long hole_pages; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci npmem_holes = 0; 22462306a36Sopenharmony_ci end_pfn = pmem_ranges[0].start_pfn + pmem_ranges[0].pages; 22562306a36Sopenharmony_ci for (i = 1; i < npmem_ranges; i++) { 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci hole_pages = pmem_ranges[i].start_pfn - end_pfn; 22862306a36Sopenharmony_ci if (hole_pages) { 22962306a36Sopenharmony_ci pmem_holes[npmem_holes].start_pfn = end_pfn; 23062306a36Sopenharmony_ci pmem_holes[npmem_holes++].pages = hole_pages; 23162306a36Sopenharmony_ci end_pfn += hole_pages; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci end_pfn += pmem_ranges[i].pages; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci pmem_ranges[0].pages = end_pfn - pmem_ranges[0].start_pfn; 23762306a36Sopenharmony_ci npmem_ranges = 1; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci#endif 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* 24262306a36Sopenharmony_ci * Initialize and free the full range of memory in each range. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci max_pfn = 0; 24662306a36Sopenharmony_ci for (i = 0; i < npmem_ranges; i++) { 24762306a36Sopenharmony_ci unsigned long start_pfn; 24862306a36Sopenharmony_ci unsigned long npages; 24962306a36Sopenharmony_ci unsigned long start; 25062306a36Sopenharmony_ci unsigned long size; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci start_pfn = pmem_ranges[i].start_pfn; 25362306a36Sopenharmony_ci npages = pmem_ranges[i].pages; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci start = start_pfn << PAGE_SHIFT; 25662306a36Sopenharmony_ci size = npages << PAGE_SHIFT; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* add system RAM memblock */ 25962306a36Sopenharmony_ci memblock_add(start, size); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if ((start_pfn + npages) > max_pfn) 26262306a36Sopenharmony_ci max_pfn = start_pfn + npages; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * We can't use memblock top-down allocations because we only 26762306a36Sopenharmony_ci * created the initial mapping up to KERNEL_INITIAL_SIZE in 26862306a36Sopenharmony_ci * the assembly bootup code. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci memblock_set_bottom_up(true); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* IOMMU is always used to access "high mem" on those boxes 27362306a36Sopenharmony_ci * that can support enough mem that a PCI device couldn't 27462306a36Sopenharmony_ci * directly DMA to any physical addresses. 27562306a36Sopenharmony_ci * ISA DMA support will need to revisit this. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci max_low_pfn = max_pfn; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* reserve PAGE0 pdc memory, kernel text/data/bss & bootmap */ 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci#define PDC_CONSOLE_IO_IODC_SIZE 32768 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci memblock_reserve(0UL, (unsigned long)(PAGE0->mem_free + 28462306a36Sopenharmony_ci PDC_CONSOLE_IO_IODC_SIZE)); 28562306a36Sopenharmony_ci memblock_reserve(__pa(KERNEL_BINARY_TEXT_START), 28662306a36Sopenharmony_ci (unsigned long)(_end - KERNEL_BINARY_TEXT_START)); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci#ifndef CONFIG_SPARSEMEM 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* reserve the holes */ 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci for (i = 0; i < npmem_holes; i++) { 29362306a36Sopenharmony_ci memblock_reserve((pmem_holes[i].start_pfn << PAGE_SHIFT), 29462306a36Sopenharmony_ci (pmem_holes[i].pages << PAGE_SHIFT)); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci#endif 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 29962306a36Sopenharmony_ci if (initrd_start) { 30062306a36Sopenharmony_ci printk(KERN_INFO "initrd: %08lx-%08lx\n", initrd_start, initrd_end); 30162306a36Sopenharmony_ci if (__pa(initrd_start) < mem_max) { 30262306a36Sopenharmony_ci unsigned long initrd_reserve; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (__pa(initrd_end) > mem_max) { 30562306a36Sopenharmony_ci initrd_reserve = mem_max - __pa(initrd_start); 30662306a36Sopenharmony_ci } else { 30762306a36Sopenharmony_ci initrd_reserve = initrd_end - initrd_start; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci initrd_below_start_ok = 1; 31062306a36Sopenharmony_ci printk(KERN_INFO "initrd: reserving %08lx-%08lx (mem_max %08lx)\n", __pa(initrd_start), __pa(initrd_start) + initrd_reserve, mem_max); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci memblock_reserve(__pa(initrd_start), initrd_reserve); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci#endif 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci data_resource.start = virt_to_phys(&data_start); 31862306a36Sopenharmony_ci data_resource.end = virt_to_phys(_end) - 1; 31962306a36Sopenharmony_ci code_resource.start = virt_to_phys(_text); 32062306a36Sopenharmony_ci code_resource.end = virt_to_phys(&data_start)-1; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* We don't know which region the kernel will be in, so try 32362306a36Sopenharmony_ci * all of them. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci for (i = 0; i < sysram_resource_count; i++) { 32662306a36Sopenharmony_ci struct resource *res = &sysram_resources[i]; 32762306a36Sopenharmony_ci request_resource(res, &code_resource); 32862306a36Sopenharmony_ci request_resource(res, &data_resource); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci request_resource(&sysram_resources[0], &pdcdata_resource); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Initialize Page Deallocation Table (PDT) and check for bad memory. */ 33362306a36Sopenharmony_ci pdc_pdt_init(); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci memblock_allow_resize(); 33662306a36Sopenharmony_ci memblock_dump_all(); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic bool kernel_set_to_readonly; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic void __ref map_pages(unsigned long start_vaddr, 34262306a36Sopenharmony_ci unsigned long start_paddr, unsigned long size, 34362306a36Sopenharmony_ci pgprot_t pgprot, int force) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci pmd_t *pmd; 34662306a36Sopenharmony_ci pte_t *pg_table; 34762306a36Sopenharmony_ci unsigned long end_paddr; 34862306a36Sopenharmony_ci unsigned long start_pmd; 34962306a36Sopenharmony_ci unsigned long start_pte; 35062306a36Sopenharmony_ci unsigned long tmp1; 35162306a36Sopenharmony_ci unsigned long tmp2; 35262306a36Sopenharmony_ci unsigned long address; 35362306a36Sopenharmony_ci unsigned long vaddr; 35462306a36Sopenharmony_ci unsigned long ro_start; 35562306a36Sopenharmony_ci unsigned long ro_end; 35662306a36Sopenharmony_ci unsigned long kernel_start, kernel_end; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ro_start = __pa((unsigned long)_text); 35962306a36Sopenharmony_ci ro_end = __pa((unsigned long)&data_start); 36062306a36Sopenharmony_ci kernel_start = __pa((unsigned long)&__init_begin); 36162306a36Sopenharmony_ci kernel_end = __pa((unsigned long)&_end); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci end_paddr = start_paddr + size; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* for 2-level configuration PTRS_PER_PMD is 0 so start_pmd will be 0 */ 36662306a36Sopenharmony_ci start_pmd = ((start_vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1)); 36762306a36Sopenharmony_ci start_pte = ((start_vaddr >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci address = start_paddr; 37062306a36Sopenharmony_ci vaddr = start_vaddr; 37162306a36Sopenharmony_ci while (address < end_paddr) { 37262306a36Sopenharmony_ci pgd_t *pgd = pgd_offset_k(vaddr); 37362306a36Sopenharmony_ci p4d_t *p4d = p4d_offset(pgd, vaddr); 37462306a36Sopenharmony_ci pud_t *pud = pud_offset(p4d, vaddr); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 37762306a36Sopenharmony_ci if (pud_none(*pud)) { 37862306a36Sopenharmony_ci pmd = memblock_alloc(PAGE_SIZE << PMD_TABLE_ORDER, 37962306a36Sopenharmony_ci PAGE_SIZE << PMD_TABLE_ORDER); 38062306a36Sopenharmony_ci if (!pmd) 38162306a36Sopenharmony_ci panic("pmd allocation failed.\n"); 38262306a36Sopenharmony_ci pud_populate(NULL, pud, pmd); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci#endif 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci pmd = pmd_offset(pud, vaddr); 38762306a36Sopenharmony_ci for (tmp1 = start_pmd; tmp1 < PTRS_PER_PMD; tmp1++, pmd++) { 38862306a36Sopenharmony_ci if (pmd_none(*pmd)) { 38962306a36Sopenharmony_ci pg_table = memblock_alloc(PAGE_SIZE, PAGE_SIZE); 39062306a36Sopenharmony_ci if (!pg_table) 39162306a36Sopenharmony_ci panic("page table allocation failed\n"); 39262306a36Sopenharmony_ci pmd_populate_kernel(NULL, pmd, pg_table); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci pg_table = pte_offset_kernel(pmd, vaddr); 39662306a36Sopenharmony_ci for (tmp2 = start_pte; tmp2 < PTRS_PER_PTE; tmp2++, pg_table++) { 39762306a36Sopenharmony_ci pte_t pte; 39862306a36Sopenharmony_ci pgprot_t prot; 39962306a36Sopenharmony_ci bool huge = false; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (force) { 40262306a36Sopenharmony_ci prot = pgprot; 40362306a36Sopenharmony_ci } else if (address < kernel_start || address >= kernel_end) { 40462306a36Sopenharmony_ci /* outside kernel memory */ 40562306a36Sopenharmony_ci prot = PAGE_KERNEL; 40662306a36Sopenharmony_ci } else if (!kernel_set_to_readonly) { 40762306a36Sopenharmony_ci /* still initializing, allow writing to RO memory */ 40862306a36Sopenharmony_ci prot = PAGE_KERNEL_RWX; 40962306a36Sopenharmony_ci huge = true; 41062306a36Sopenharmony_ci } else if (address >= ro_start) { 41162306a36Sopenharmony_ci /* Code (ro) and Data areas */ 41262306a36Sopenharmony_ci prot = (address < ro_end) ? 41362306a36Sopenharmony_ci PAGE_KERNEL_EXEC : PAGE_KERNEL; 41462306a36Sopenharmony_ci huge = true; 41562306a36Sopenharmony_ci } else { 41662306a36Sopenharmony_ci prot = PAGE_KERNEL; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci pte = __mk_pte(address, prot); 42062306a36Sopenharmony_ci if (huge) 42162306a36Sopenharmony_ci pte = pte_mkhuge(pte); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (address >= end_paddr) 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci set_pte(pg_table, pte); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci address += PAGE_SIZE; 42962306a36Sopenharmony_ci vaddr += PAGE_SIZE; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci start_pte = 0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (address >= end_paddr) 43462306a36Sopenharmony_ci break; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci start_pmd = 0; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_civoid __init set_kernel_text_rw(int enable_read_write) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci unsigned long start = (unsigned long) __init_begin; 44362306a36Sopenharmony_ci unsigned long end = (unsigned long) &data_start; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci map_pages(start, __pa(start), end-start, 44662306a36Sopenharmony_ci PAGE_KERNEL_RWX, enable_read_write ? 1:0); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* force the kernel to see the new page table entries */ 44962306a36Sopenharmony_ci flush_cache_all(); 45062306a36Sopenharmony_ci flush_tlb_all(); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_civoid free_initmem(void) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci unsigned long init_begin = (unsigned long)__init_begin; 45662306a36Sopenharmony_ci unsigned long init_end = (unsigned long)__init_end; 45762306a36Sopenharmony_ci unsigned long kernel_end = (unsigned long)&_end; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* Remap kernel text and data, but do not touch init section yet. */ 46062306a36Sopenharmony_ci kernel_set_to_readonly = true; 46162306a36Sopenharmony_ci map_pages(init_end, __pa(init_end), kernel_end - init_end, 46262306a36Sopenharmony_ci PAGE_KERNEL, 0); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* The init text pages are marked R-X. We have to 46562306a36Sopenharmony_ci * flush the icache and mark them RW- 46662306a36Sopenharmony_ci * 46762306a36Sopenharmony_ci * Do a dummy remap of the data section first (the data 46862306a36Sopenharmony_ci * section is already PAGE_KERNEL) to pull in the TLB entries 46962306a36Sopenharmony_ci * for map_kernel */ 47062306a36Sopenharmony_ci map_pages(init_begin, __pa(init_begin), init_end - init_begin, 47162306a36Sopenharmony_ci PAGE_KERNEL_RWX, 1); 47262306a36Sopenharmony_ci /* now remap at PAGE_KERNEL since the TLB is pre-primed to execute 47362306a36Sopenharmony_ci * map_pages */ 47462306a36Sopenharmony_ci map_pages(init_begin, __pa(init_begin), init_end - init_begin, 47562306a36Sopenharmony_ci PAGE_KERNEL, 1); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* force the kernel to see the new TLB entries */ 47862306a36Sopenharmony_ci __flush_tlb_range(0, init_begin, kernel_end); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* finally dump all the instructions which were cached, since the 48162306a36Sopenharmony_ci * pages are no-longer executable */ 48262306a36Sopenharmony_ci flush_icache_range(init_begin, init_end); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci free_initmem_default(POISON_FREE_INITMEM); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* set up a new led state on systems shipped LED State panel */ 48762306a36Sopenharmony_ci pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BCOMPLETE); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci#ifdef CONFIG_STRICT_KERNEL_RWX 49262306a36Sopenharmony_civoid mark_rodata_ro(void) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci /* rodata memory was already mapped with KERNEL_RO access rights by 49562306a36Sopenharmony_ci pagetable_init() and map_pages(). No need to do additional stuff here */ 49662306a36Sopenharmony_ci unsigned long roai_size = __end_ro_after_init - __start_ro_after_init; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci pr_info("Write protected read-only-after-init data: %luk\n", roai_size >> 10); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci#endif 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/* 50462306a36Sopenharmony_ci * Just an arbitrary offset to serve as a "hole" between mapping areas 50562306a36Sopenharmony_ci * (between top of physical memory and a potential pcxl dma mapping 50662306a36Sopenharmony_ci * area, and below the vmalloc mapping area). 50762306a36Sopenharmony_ci * 50862306a36Sopenharmony_ci * The current 32K value just means that there will be a 32K "hole" 50962306a36Sopenharmony_ci * between mapping areas. That means that any out-of-bounds memory 51062306a36Sopenharmony_ci * accesses will hopefully be caught. The vmalloc() routines leaves 51162306a36Sopenharmony_ci * a hole of 4kB between each vmalloced area for the same reason. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Leave room for gateway page expansion */ 51562306a36Sopenharmony_ci#if KERNEL_MAP_START < GATEWAY_PAGE_SIZE 51662306a36Sopenharmony_ci#error KERNEL_MAP_START is in gateway reserved region 51762306a36Sopenharmony_ci#endif 51862306a36Sopenharmony_ci#define MAP_START (KERNEL_MAP_START) 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci#define VM_MAP_OFFSET (32*1024) 52162306a36Sopenharmony_ci#define SET_MAP_OFFSET(x) ((void *)(((unsigned long)(x) + VM_MAP_OFFSET) \ 52262306a36Sopenharmony_ci & ~(VM_MAP_OFFSET-1))) 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_civoid *parisc_vmalloc_start __ro_after_init; 52562306a36Sopenharmony_ciEXPORT_SYMBOL(parisc_vmalloc_start); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_civoid __init mem_init(void) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci /* Do sanity checks on IPC (compat) structures */ 53062306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct ipc64_perm) != 48); 53162306a36Sopenharmony_ci#ifndef CONFIG_64BIT 53262306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct semid64_ds) != 80); 53362306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct msqid64_ds) != 104); 53462306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct shmid64_ds) != 104); 53562306a36Sopenharmony_ci#endif 53662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 53762306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct compat_ipc64_perm) != sizeof(struct ipc64_perm)); 53862306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct compat_semid64_ds) != 80); 53962306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct compat_msqid64_ds) != 104); 54062306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct compat_shmid64_ds) != 104); 54162306a36Sopenharmony_ci#endif 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* Do sanity checks on page table constants */ 54462306a36Sopenharmony_ci BUILD_BUG_ON(PTE_ENTRY_SIZE != sizeof(pte_t)); 54562306a36Sopenharmony_ci BUILD_BUG_ON(PMD_ENTRY_SIZE != sizeof(pmd_t)); 54662306a36Sopenharmony_ci BUILD_BUG_ON(PGD_ENTRY_SIZE != sizeof(pgd_t)); 54762306a36Sopenharmony_ci BUILD_BUG_ON(PAGE_SHIFT + BITS_PER_PTE + BITS_PER_PMD + BITS_PER_PGD 54862306a36Sopenharmony_ci > BITS_PER_LONG); 54962306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 55062306a36Sopenharmony_ci BUILD_BUG_ON(PT_INITIAL > PTRS_PER_PMD); 55162306a36Sopenharmony_ci#else 55262306a36Sopenharmony_ci BUILD_BUG_ON(PT_INITIAL > PTRS_PER_PGD); 55362306a36Sopenharmony_ci#endif 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci#ifdef CONFIG_64BIT 55662306a36Sopenharmony_ci /* avoid ldil_%L() asm statements to sign-extend into upper 32-bits */ 55762306a36Sopenharmony_ci BUILD_BUG_ON(__PAGE_OFFSET >= 0x80000000); 55862306a36Sopenharmony_ci BUILD_BUG_ON(TMPALIAS_MAP_START >= 0x80000000); 55962306a36Sopenharmony_ci#endif 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci high_memory = __va((max_pfn << PAGE_SHIFT)); 56262306a36Sopenharmony_ci set_max_mapnr(max_low_pfn); 56362306a36Sopenharmony_ci memblock_free_all(); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci#ifdef CONFIG_PA11 56662306a36Sopenharmony_ci if (boot_cpu_data.cpu_type == pcxl2 || boot_cpu_data.cpu_type == pcxl) { 56762306a36Sopenharmony_ci pcxl_dma_start = (unsigned long)SET_MAP_OFFSET(MAP_START); 56862306a36Sopenharmony_ci parisc_vmalloc_start = SET_MAP_OFFSET(pcxl_dma_start 56962306a36Sopenharmony_ci + PCXL_DMA_MAP_SIZE); 57062306a36Sopenharmony_ci } else 57162306a36Sopenharmony_ci#endif 57262306a36Sopenharmony_ci parisc_vmalloc_start = SET_MAP_OFFSET(MAP_START); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci#if 0 57562306a36Sopenharmony_ci /* 57662306a36Sopenharmony_ci * Do not expose the virtual kernel memory layout to userspace. 57762306a36Sopenharmony_ci * But keep code for debugging purposes. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci printk("virtual kernel memory layout:\n" 58062306a36Sopenharmony_ci " vmalloc : 0x%px - 0x%px (%4ld MB)\n" 58162306a36Sopenharmony_ci " fixmap : 0x%px - 0x%px (%4ld kB)\n" 58262306a36Sopenharmony_ci " memory : 0x%px - 0x%px (%4ld MB)\n" 58362306a36Sopenharmony_ci " .init : 0x%px - 0x%px (%4ld kB)\n" 58462306a36Sopenharmony_ci " .data : 0x%px - 0x%px (%4ld kB)\n" 58562306a36Sopenharmony_ci " .text : 0x%px - 0x%px (%4ld kB)\n", 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci (void*)VMALLOC_START, (void*)VMALLOC_END, 58862306a36Sopenharmony_ci (VMALLOC_END - VMALLOC_START) >> 20, 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci (void *)FIXMAP_START, (void *)(FIXMAP_START + FIXMAP_SIZE), 59162306a36Sopenharmony_ci (unsigned long)(FIXMAP_SIZE / 1024), 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci __va(0), high_memory, 59462306a36Sopenharmony_ci ((unsigned long)high_memory - (unsigned long)__va(0)) >> 20, 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci __init_begin, __init_end, 59762306a36Sopenharmony_ci ((unsigned long)__init_end - (unsigned long)__init_begin) >> 10, 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci _etext, _edata, 60062306a36Sopenharmony_ci ((unsigned long)_edata - (unsigned long)_etext) >> 10, 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci _text, _etext, 60362306a36Sopenharmony_ci ((unsigned long)_etext - (unsigned long)_text) >> 10); 60462306a36Sopenharmony_ci#endif 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ciunsigned long *empty_zero_page __ro_after_init; 60862306a36Sopenharmony_ciEXPORT_SYMBOL(empty_zero_page); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci/* 61162306a36Sopenharmony_ci * pagetable_init() sets up the page tables 61262306a36Sopenharmony_ci * 61362306a36Sopenharmony_ci * Note that gateway_init() places the Linux gateway page at page 0. 61462306a36Sopenharmony_ci * Since gateway pages cannot be dereferenced this has the desirable 61562306a36Sopenharmony_ci * side effect of trapping those pesky NULL-reference errors in the 61662306a36Sopenharmony_ci * kernel. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_cistatic void __init pagetable_init(void) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci int range; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* Map each physical memory range to its kernel vaddr */ 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci for (range = 0; range < npmem_ranges; range++) { 62562306a36Sopenharmony_ci unsigned long start_paddr; 62662306a36Sopenharmony_ci unsigned long size; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci start_paddr = pmem_ranges[range].start_pfn << PAGE_SHIFT; 62962306a36Sopenharmony_ci size = pmem_ranges[range].pages << PAGE_SHIFT; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci map_pages((unsigned long)__va(start_paddr), start_paddr, 63262306a36Sopenharmony_ci size, PAGE_KERNEL, 0); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 63662306a36Sopenharmony_ci if (initrd_end && initrd_end > mem_limit) { 63762306a36Sopenharmony_ci printk(KERN_INFO "initrd: mapping %08lx-%08lx\n", initrd_start, initrd_end); 63862306a36Sopenharmony_ci map_pages(initrd_start, __pa(initrd_start), 63962306a36Sopenharmony_ci initrd_end - initrd_start, PAGE_KERNEL, 0); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci#endif 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci empty_zero_page = memblock_alloc(PAGE_SIZE, PAGE_SIZE); 64462306a36Sopenharmony_ci if (!empty_zero_page) 64562306a36Sopenharmony_ci panic("zero page allocation failed.\n"); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void __init gateway_init(void) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci unsigned long linux_gateway_page_addr; 65262306a36Sopenharmony_ci /* FIXME: This is 'const' in order to trick the compiler 65362306a36Sopenharmony_ci into not treating it as DP-relative data. */ 65462306a36Sopenharmony_ci extern void * const linux_gateway_page; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci linux_gateway_page_addr = LINUX_GATEWAY_ADDR & PAGE_MASK; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* 65962306a36Sopenharmony_ci * Setup Linux Gateway page. 66062306a36Sopenharmony_ci * 66162306a36Sopenharmony_ci * The Linux gateway page will reside in kernel space (on virtual 66262306a36Sopenharmony_ci * page 0), so it doesn't need to be aliased into user space. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci map_pages(linux_gateway_page_addr, __pa(&linux_gateway_page), 66662306a36Sopenharmony_ci PAGE_SIZE, PAGE_GATEWAY, 1); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic void __init fixmap_init(void) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci unsigned long addr = FIXMAP_START; 67262306a36Sopenharmony_ci unsigned long end = FIXMAP_START + FIXMAP_SIZE; 67362306a36Sopenharmony_ci pgd_t *pgd = pgd_offset_k(addr); 67462306a36Sopenharmony_ci p4d_t *p4d = p4d_offset(pgd, addr); 67562306a36Sopenharmony_ci pud_t *pud = pud_offset(p4d, addr); 67662306a36Sopenharmony_ci pmd_t *pmd; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci BUILD_BUG_ON(FIXMAP_SIZE > PMD_SIZE); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 68162306a36Sopenharmony_ci if (pud_none(*pud)) { 68262306a36Sopenharmony_ci pmd = memblock_alloc(PAGE_SIZE << PMD_TABLE_ORDER, 68362306a36Sopenharmony_ci PAGE_SIZE << PMD_TABLE_ORDER); 68462306a36Sopenharmony_ci if (!pmd) 68562306a36Sopenharmony_ci panic("fixmap: pmd allocation failed.\n"); 68662306a36Sopenharmony_ci pud_populate(NULL, pud, pmd); 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci#endif 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci pmd = pmd_offset(pud, addr); 69162306a36Sopenharmony_ci do { 69262306a36Sopenharmony_ci pte_t *pte = memblock_alloc(PAGE_SIZE, PAGE_SIZE); 69362306a36Sopenharmony_ci if (!pte) 69462306a36Sopenharmony_ci panic("fixmap: pte allocation failed.\n"); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci pmd_populate_kernel(&init_mm, pmd, pte); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci addr += PAGE_SIZE; 69962306a36Sopenharmony_ci } while (addr < end); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic void __init parisc_bootmem_free(void) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0, }; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci max_zone_pfn[0] = memblock_end_of_DRAM(); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci free_area_init(max_zone_pfn); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_civoid __init paging_init(void) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci setup_bootmem(); 71462306a36Sopenharmony_ci pagetable_init(); 71562306a36Sopenharmony_ci gateway_init(); 71662306a36Sopenharmony_ci fixmap_init(); 71762306a36Sopenharmony_ci flush_cache_all_local(); /* start with known state */ 71862306a36Sopenharmony_ci flush_tlb_all_local(NULL); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci sparse_init(); 72162306a36Sopenharmony_ci parisc_bootmem_free(); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic void alloc_btlb(unsigned long start, unsigned long end, int *slot, 72562306a36Sopenharmony_ci unsigned long entry_info) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci const int slot_max = btlb_info.fixed_range_info.num_comb; 72862306a36Sopenharmony_ci int min_num_pages = btlb_info.min_size; 72962306a36Sopenharmony_ci unsigned long size; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* map at minimum 4 pages */ 73262306a36Sopenharmony_ci if (min_num_pages < 4) 73362306a36Sopenharmony_ci min_num_pages = 4; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci size = HUGEPAGE_SIZE; 73662306a36Sopenharmony_ci while (start < end && *slot < slot_max && size >= PAGE_SIZE) { 73762306a36Sopenharmony_ci /* starting address must have same alignment as size! */ 73862306a36Sopenharmony_ci /* if correctly aligned and fits in double size, increase */ 73962306a36Sopenharmony_ci if (((start & (2 * size - 1)) == 0) && 74062306a36Sopenharmony_ci (end - start) >= (2 * size)) { 74162306a36Sopenharmony_ci size <<= 1; 74262306a36Sopenharmony_ci continue; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci /* if current size alignment is too big, try smaller size */ 74562306a36Sopenharmony_ci if ((start & (size - 1)) != 0) { 74662306a36Sopenharmony_ci size >>= 1; 74762306a36Sopenharmony_ci continue; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci if ((end - start) >= size) { 75062306a36Sopenharmony_ci if ((size >> PAGE_SHIFT) >= min_num_pages) 75162306a36Sopenharmony_ci pdc_btlb_insert(start >> PAGE_SHIFT, __pa(start) >> PAGE_SHIFT, 75262306a36Sopenharmony_ci size >> PAGE_SHIFT, entry_info, *slot); 75362306a36Sopenharmony_ci (*slot)++; 75462306a36Sopenharmony_ci start += size; 75562306a36Sopenharmony_ci continue; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci size /= 2; 75862306a36Sopenharmony_ci continue; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_civoid btlb_init_per_cpu(void) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci unsigned long s, t, e; 76562306a36Sopenharmony_ci int slot; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* BTLBs are not available on 64-bit CPUs */ 76862306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PA20)) 76962306a36Sopenharmony_ci return; 77062306a36Sopenharmony_ci else if (pdc_btlb_info(&btlb_info) < 0) { 77162306a36Sopenharmony_ci memset(&btlb_info, 0, sizeof btlb_info); 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* insert BLTLBs for code and data segments */ 77562306a36Sopenharmony_ci s = (uintptr_t) dereference_function_descriptor(&_stext); 77662306a36Sopenharmony_ci e = (uintptr_t) dereference_function_descriptor(&_etext); 77762306a36Sopenharmony_ci t = (uintptr_t) dereference_function_descriptor(&_sdata); 77862306a36Sopenharmony_ci BUG_ON(t != e); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* code segments */ 78162306a36Sopenharmony_ci slot = 0; 78262306a36Sopenharmony_ci alloc_btlb(s, e, &slot, 0x13800000); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* sanity check */ 78562306a36Sopenharmony_ci t = (uintptr_t) dereference_function_descriptor(&_edata); 78662306a36Sopenharmony_ci e = (uintptr_t) dereference_function_descriptor(&__bss_start); 78762306a36Sopenharmony_ci BUG_ON(t != e); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* data segments */ 79062306a36Sopenharmony_ci s = (uintptr_t) dereference_function_descriptor(&_sdata); 79162306a36Sopenharmony_ci e = (uintptr_t) dereference_function_descriptor(&__bss_stop); 79262306a36Sopenharmony_ci alloc_btlb(s, e, &slot, 0x11800000); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci#ifdef CONFIG_PA20 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci/* 79862306a36Sopenharmony_ci * Currently, all PA20 chips have 18 bit protection IDs, which is the 79962306a36Sopenharmony_ci * limiting factor (space ids are 32 bits). 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci#define NR_SPACE_IDS 262144 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci#else 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci/* 80762306a36Sopenharmony_ci * Currently we have a one-to-one relationship between space IDs and 80862306a36Sopenharmony_ci * protection IDs. Older parisc chips (PCXS, PCXT, PCXL, PCXL2) only 80962306a36Sopenharmony_ci * support 15 bit protection IDs, so that is the limiting factor. 81062306a36Sopenharmony_ci * PCXT' has 18 bit protection IDs, but only 16 bit spaceids, so it's 81162306a36Sopenharmony_ci * probably not worth the effort for a special case here. 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci#define NR_SPACE_IDS 32768 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci#endif /* !CONFIG_PA20 */ 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci#define RECYCLE_THRESHOLD (NR_SPACE_IDS / 2) 81962306a36Sopenharmony_ci#define SID_ARRAY_SIZE (NR_SPACE_IDS / (8 * sizeof(long))) 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic unsigned long space_id[SID_ARRAY_SIZE] = { 1 }; /* disallow space 0 */ 82262306a36Sopenharmony_cistatic unsigned long dirty_space_id[SID_ARRAY_SIZE]; 82362306a36Sopenharmony_cistatic unsigned long space_id_index; 82462306a36Sopenharmony_cistatic unsigned long free_space_ids = NR_SPACE_IDS - 1; 82562306a36Sopenharmony_cistatic unsigned long dirty_space_ids; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(sid_lock); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ciunsigned long alloc_sid(void) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci unsigned long index; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci spin_lock(&sid_lock); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (free_space_ids == 0) { 83662306a36Sopenharmony_ci if (dirty_space_ids != 0) { 83762306a36Sopenharmony_ci spin_unlock(&sid_lock); 83862306a36Sopenharmony_ci flush_tlb_all(); /* flush_tlb_all() calls recycle_sids() */ 83962306a36Sopenharmony_ci spin_lock(&sid_lock); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci BUG_ON(free_space_ids == 0); 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci free_space_ids--; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci index = find_next_zero_bit(space_id, NR_SPACE_IDS, space_id_index); 84762306a36Sopenharmony_ci space_id[BIT_WORD(index)] |= BIT_MASK(index); 84862306a36Sopenharmony_ci space_id_index = index; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci spin_unlock(&sid_lock); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci return index << SPACEID_SHIFT; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_civoid free_sid(unsigned long spaceid) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci unsigned long index = spaceid >> SPACEID_SHIFT; 85862306a36Sopenharmony_ci unsigned long *dirty_space_offset, mask; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci dirty_space_offset = &dirty_space_id[BIT_WORD(index)]; 86162306a36Sopenharmony_ci mask = BIT_MASK(index); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci spin_lock(&sid_lock); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci BUG_ON(*dirty_space_offset & mask); /* attempt to free space id twice */ 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci *dirty_space_offset |= mask; 86862306a36Sopenharmony_ci dirty_space_ids++; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci spin_unlock(&sid_lock); 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci#ifdef CONFIG_SMP 87562306a36Sopenharmony_cistatic void get_dirty_sids(unsigned long *ndirtyptr,unsigned long *dirty_array) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci int i; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* NOTE: sid_lock must be held upon entry */ 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci *ndirtyptr = dirty_space_ids; 88262306a36Sopenharmony_ci if (dirty_space_ids != 0) { 88362306a36Sopenharmony_ci for (i = 0; i < SID_ARRAY_SIZE; i++) { 88462306a36Sopenharmony_ci dirty_array[i] = dirty_space_id[i]; 88562306a36Sopenharmony_ci dirty_space_id[i] = 0; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci dirty_space_ids = 0; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic void recycle_sids(unsigned long ndirty,unsigned long *dirty_array) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci int i; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* NOTE: sid_lock must be held upon entry */ 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (ndirty != 0) { 90062306a36Sopenharmony_ci for (i = 0; i < SID_ARRAY_SIZE; i++) { 90162306a36Sopenharmony_ci space_id[i] ^= dirty_array[i]; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci free_space_ids += ndirty; 90562306a36Sopenharmony_ci space_id_index = 0; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci#else /* CONFIG_SMP */ 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic void recycle_sids(void) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci int i; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* NOTE: sid_lock must be held upon entry */ 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (dirty_space_ids != 0) { 91862306a36Sopenharmony_ci for (i = 0; i < SID_ARRAY_SIZE; i++) { 91962306a36Sopenharmony_ci space_id[i] ^= dirty_space_id[i]; 92062306a36Sopenharmony_ci dirty_space_id[i] = 0; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci free_space_ids += dirty_space_ids; 92462306a36Sopenharmony_ci dirty_space_ids = 0; 92562306a36Sopenharmony_ci space_id_index = 0; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci#endif 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci/* 93162306a36Sopenharmony_ci * flush_tlb_all() calls recycle_sids(), since whenever the entire tlb is 93262306a36Sopenharmony_ci * purged, we can safely reuse the space ids that were released but 93362306a36Sopenharmony_ci * not flushed from the tlb. 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci#ifdef CONFIG_SMP 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic unsigned long recycle_ndirty; 93962306a36Sopenharmony_cistatic unsigned long recycle_dirty_array[SID_ARRAY_SIZE]; 94062306a36Sopenharmony_cistatic unsigned int recycle_inuse; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_civoid flush_tlb_all(void) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci int do_recycle; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci do_recycle = 0; 94762306a36Sopenharmony_ci spin_lock(&sid_lock); 94862306a36Sopenharmony_ci __inc_irq_stat(irq_tlb_count); 94962306a36Sopenharmony_ci if (dirty_space_ids > RECYCLE_THRESHOLD) { 95062306a36Sopenharmony_ci BUG_ON(recycle_inuse); /* FIXME: Use a semaphore/wait queue here */ 95162306a36Sopenharmony_ci get_dirty_sids(&recycle_ndirty,recycle_dirty_array); 95262306a36Sopenharmony_ci recycle_inuse++; 95362306a36Sopenharmony_ci do_recycle++; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci spin_unlock(&sid_lock); 95662306a36Sopenharmony_ci on_each_cpu(flush_tlb_all_local, NULL, 1); 95762306a36Sopenharmony_ci if (do_recycle) { 95862306a36Sopenharmony_ci spin_lock(&sid_lock); 95962306a36Sopenharmony_ci recycle_sids(recycle_ndirty,recycle_dirty_array); 96062306a36Sopenharmony_ci recycle_inuse = 0; 96162306a36Sopenharmony_ci spin_unlock(&sid_lock); 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci#else 96562306a36Sopenharmony_civoid flush_tlb_all(void) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci spin_lock(&sid_lock); 96862306a36Sopenharmony_ci __inc_irq_stat(irq_tlb_count); 96962306a36Sopenharmony_ci flush_tlb_all_local(NULL); 97062306a36Sopenharmony_ci recycle_sids(); 97162306a36Sopenharmony_ci spin_unlock(&sid_lock); 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci#endif 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic const pgprot_t protection_map[16] = { 97662306a36Sopenharmony_ci [VM_NONE] = PAGE_NONE, 97762306a36Sopenharmony_ci [VM_READ] = PAGE_READONLY, 97862306a36Sopenharmony_ci [VM_WRITE] = PAGE_NONE, 97962306a36Sopenharmony_ci [VM_WRITE | VM_READ] = PAGE_READONLY, 98062306a36Sopenharmony_ci [VM_EXEC] = PAGE_EXECREAD, 98162306a36Sopenharmony_ci [VM_EXEC | VM_READ] = PAGE_EXECREAD, 98262306a36Sopenharmony_ci [VM_EXEC | VM_WRITE] = PAGE_EXECREAD, 98362306a36Sopenharmony_ci [VM_EXEC | VM_WRITE | VM_READ] = PAGE_EXECREAD, 98462306a36Sopenharmony_ci [VM_SHARED] = PAGE_NONE, 98562306a36Sopenharmony_ci [VM_SHARED | VM_READ] = PAGE_READONLY, 98662306a36Sopenharmony_ci [VM_SHARED | VM_WRITE] = PAGE_WRITEONLY, 98762306a36Sopenharmony_ci [VM_SHARED | VM_WRITE | VM_READ] = PAGE_SHARED, 98862306a36Sopenharmony_ci [VM_SHARED | VM_EXEC] = PAGE_EXECREAD, 98962306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_READ] = PAGE_EXECREAD, 99062306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_RWX, 99162306a36Sopenharmony_ci [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_RWX 99262306a36Sopenharmony_ci}; 99362306a36Sopenharmony_ciDECLARE_VM_GET_PAGE_PROT 994