18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/alpha/mm/init.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1995 Linus Torvalds 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 2.3.x zone allocator, 1999 Andrea Arcangeli <andrea@suse.de> */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 118c2ecf20Sopenharmony_ci#include <linux/signal.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/string.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 188c2ecf20Sopenharmony_ci#include <linux/mman.h> 198c2ecf20Sopenharmony_ci#include <linux/mm.h> 208c2ecf20Sopenharmony_ci#include <linux/swap.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/memblock.h> /* max_low_pfn */ 238c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 248c2ecf20Sopenharmony_ci#include <linux/gfp.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 278c2ecf20Sopenharmony_ci#include <asm/pgalloc.h> 288c2ecf20Sopenharmony_ci#include <asm/hwrpb.h> 298c2ecf20Sopenharmony_ci#include <asm/dma.h> 308c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 318c2ecf20Sopenharmony_ci#include <asm/console.h> 328c2ecf20Sopenharmony_ci#include <asm/tlb.h> 338c2ecf20Sopenharmony_ci#include <asm/setup.h> 348c2ecf20Sopenharmony_ci#include <asm/sections.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ciextern void die_if_kernel(char *,struct pt_regs *,long); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic struct pcb_struct original_pcb; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cipgd_t * 418c2ecf20Sopenharmony_cipgd_alloc(struct mm_struct *mm) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci pgd_t *ret, *init; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci ret = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 468c2ecf20Sopenharmony_ci init = pgd_offset(&init_mm, 0UL); 478c2ecf20Sopenharmony_ci if (ret) { 488c2ecf20Sopenharmony_ci#ifdef CONFIG_ALPHA_LARGE_VMALLOC 498c2ecf20Sopenharmony_ci memcpy (ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, 508c2ecf20Sopenharmony_ci (PTRS_PER_PGD - USER_PTRS_PER_PGD - 1)*sizeof(pgd_t)); 518c2ecf20Sopenharmony_ci#else 528c2ecf20Sopenharmony_ci pgd_val(ret[PTRS_PER_PGD-2]) = pgd_val(init[PTRS_PER_PGD-2]); 538c2ecf20Sopenharmony_ci#endif 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* The last PGD entry is the VPTB self-map. */ 568c2ecf20Sopenharmony_ci pgd_val(ret[PTRS_PER_PGD-1]) 578c2ecf20Sopenharmony_ci = pte_val(mk_pte(virt_to_page(ret), PAGE_KERNEL)); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci return ret; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * BAD_PAGE is the page that is used for page faults when linux 658c2ecf20Sopenharmony_ci * is out-of-memory. Older versions of linux just did a 668c2ecf20Sopenharmony_ci * do_exit(), but using this instead means there is less risk 678c2ecf20Sopenharmony_ci * for a process dying in kernel mode, possibly leaving an inode 688c2ecf20Sopenharmony_ci * unused etc.. 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * BAD_PAGETABLE is the accompanying page-table: it is initialized 718c2ecf20Sopenharmony_ci * to point to BAD_PAGE entries. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * ZERO_PAGE is a special page that is used for zero-initialized 748c2ecf20Sopenharmony_ci * data and COW. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_cipmd_t * 778c2ecf20Sopenharmony_ci__bad_pagetable(void) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci memset((void *) EMPTY_PGT, 0, PAGE_SIZE); 808c2ecf20Sopenharmony_ci return (pmd_t *) EMPTY_PGT; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cipte_t 848c2ecf20Sopenharmony_ci__bad_page(void) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci memset((void *) EMPTY_PGE, 0, PAGE_SIZE); 878c2ecf20Sopenharmony_ci return pte_mkdirty(mk_pte(virt_to_page(EMPTY_PGE), PAGE_SHARED)); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic inline unsigned long 918c2ecf20Sopenharmony_ciload_PCB(struct pcb_struct *pcb) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci register unsigned long sp __asm__("$30"); 948c2ecf20Sopenharmony_ci pcb->ksp = sp; 958c2ecf20Sopenharmony_ci return __reload_thread(pcb); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Set up initial PCB, VPTB, and other such nicities. */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline void 1018c2ecf20Sopenharmony_ciswitch_to_system_map(void) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci unsigned long newptbr; 1048c2ecf20Sopenharmony_ci unsigned long original_pcb_ptr; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* Initialize the kernel's page tables. Linux puts the vptb in 1078c2ecf20Sopenharmony_ci the last slot of the L1 page table. */ 1088c2ecf20Sopenharmony_ci memset(swapper_pg_dir, 0, PAGE_SIZE); 1098c2ecf20Sopenharmony_ci newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT; 1108c2ecf20Sopenharmony_ci pgd_val(swapper_pg_dir[1023]) = 1118c2ecf20Sopenharmony_ci (newptbr << 32) | pgprot_val(PAGE_KERNEL); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Set the vptb. This is often done by the bootloader, but 1148c2ecf20Sopenharmony_ci shouldn't be required. */ 1158c2ecf20Sopenharmony_ci if (hwrpb->vptb != 0xfffffffe00000000UL) { 1168c2ecf20Sopenharmony_ci wrvptptr(0xfffffffe00000000UL); 1178c2ecf20Sopenharmony_ci hwrpb->vptb = 0xfffffffe00000000UL; 1188c2ecf20Sopenharmony_ci hwrpb_update_checksum(hwrpb); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Also set up the real kernel PCB while we're at it. */ 1228c2ecf20Sopenharmony_ci init_thread_info.pcb.ptbr = newptbr; 1238c2ecf20Sopenharmony_ci init_thread_info.pcb.flags = 1; /* set FEN, clear everything else */ 1248c2ecf20Sopenharmony_ci original_pcb_ptr = load_PCB(&init_thread_info.pcb); 1258c2ecf20Sopenharmony_ci tbia(); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Save off the contents of the original PCB so that we can 1288c2ecf20Sopenharmony_ci restore the original console's page tables for a clean reboot. 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci Note that the PCB is supposed to be a physical address, but 1318c2ecf20Sopenharmony_ci since KSEG values also happen to work, folks get confused. 1328c2ecf20Sopenharmony_ci Check this here. */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (original_pcb_ptr < PAGE_OFFSET) { 1358c2ecf20Sopenharmony_ci original_pcb_ptr = (unsigned long) 1368c2ecf20Sopenharmony_ci phys_to_virt(original_pcb_ptr); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci original_pcb = *(struct pcb_struct *) original_pcb_ptr; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ciint callback_init_done; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_civoid * __init 1448c2ecf20Sopenharmony_cicallback_init(void * kernel_end) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct crb_struct * crb; 1478c2ecf20Sopenharmony_ci pgd_t *pgd; 1488c2ecf20Sopenharmony_ci p4d_t *p4d; 1498c2ecf20Sopenharmony_ci pud_t *pud; 1508c2ecf20Sopenharmony_ci pmd_t *pmd; 1518c2ecf20Sopenharmony_ci void *two_pages; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Starting at the HWRPB, locate the CRB. */ 1548c2ecf20Sopenharmony_ci crb = (struct crb_struct *)((char *)hwrpb + hwrpb->crb_offset); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (alpha_using_srm) { 1578c2ecf20Sopenharmony_ci /* Tell the console whither it is to be remapped. */ 1588c2ecf20Sopenharmony_ci if (srm_fixup(VMALLOC_START, (unsigned long)hwrpb)) 1598c2ecf20Sopenharmony_ci __halt(); /* "We're boned." --Bender */ 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* Edit the procedure descriptors for DISPATCH and FIXUP. */ 1628c2ecf20Sopenharmony_ci crb->dispatch_va = (struct procdesc_struct *) 1638c2ecf20Sopenharmony_ci (VMALLOC_START + (unsigned long)crb->dispatch_va 1648c2ecf20Sopenharmony_ci - crb->map[0].va); 1658c2ecf20Sopenharmony_ci crb->fixup_va = (struct procdesc_struct *) 1668c2ecf20Sopenharmony_ci (VMALLOC_START + (unsigned long)crb->fixup_va 1678c2ecf20Sopenharmony_ci - crb->map[0].va); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci switch_to_system_map(); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Allocate one PGD and one PMD. In the case of SRM, we'll need 1738c2ecf20Sopenharmony_ci these to actually remap the console. There is an assumption 1748c2ecf20Sopenharmony_ci here that only one of each is needed, and this allows for 8MB. 1758c2ecf20Sopenharmony_ci On systems with larger consoles, additional pages will be 1768c2ecf20Sopenharmony_ci allocated as needed during the mapping process. 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci In the case of not SRM, but not CONFIG_ALPHA_LARGE_VMALLOC, 1798c2ecf20Sopenharmony_ci we need to allocate the PGD we use for vmalloc before we start 1808c2ecf20Sopenharmony_ci forking other tasks. */ 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci two_pages = (void *) 1838c2ecf20Sopenharmony_ci (((unsigned long)kernel_end + ~PAGE_MASK) & PAGE_MASK); 1848c2ecf20Sopenharmony_ci kernel_end = two_pages + 2*PAGE_SIZE; 1858c2ecf20Sopenharmony_ci memset(two_pages, 0, 2*PAGE_SIZE); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci pgd = pgd_offset_k(VMALLOC_START); 1888c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, VMALLOC_START); 1898c2ecf20Sopenharmony_ci pud = pud_offset(p4d, VMALLOC_START); 1908c2ecf20Sopenharmony_ci pud_set(pud, (pmd_t *)two_pages); 1918c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, VMALLOC_START); 1928c2ecf20Sopenharmony_ci pmd_set(pmd, (pte_t *)(two_pages + PAGE_SIZE)); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (alpha_using_srm) { 1958c2ecf20Sopenharmony_ci static struct vm_struct console_remap_vm; 1968c2ecf20Sopenharmony_ci unsigned long nr_pages = 0; 1978c2ecf20Sopenharmony_ci unsigned long vaddr; 1988c2ecf20Sopenharmony_ci unsigned long i, j; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* calculate needed size */ 2018c2ecf20Sopenharmony_ci for (i = 0; i < crb->map_entries; ++i) 2028c2ecf20Sopenharmony_ci nr_pages += crb->map[i].count; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* register the vm area */ 2058c2ecf20Sopenharmony_ci console_remap_vm.flags = VM_ALLOC; 2068c2ecf20Sopenharmony_ci console_remap_vm.size = nr_pages << PAGE_SHIFT; 2078c2ecf20Sopenharmony_ci vm_area_register_early(&console_remap_vm, PAGE_SIZE); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci vaddr = (unsigned long)console_remap_vm.addr; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Set up the third level PTEs and update the virtual 2128c2ecf20Sopenharmony_ci addresses of the CRB entries. */ 2138c2ecf20Sopenharmony_ci for (i = 0; i < crb->map_entries; ++i) { 2148c2ecf20Sopenharmony_ci unsigned long pfn = crb->map[i].pa >> PAGE_SHIFT; 2158c2ecf20Sopenharmony_ci crb->map[i].va = vaddr; 2168c2ecf20Sopenharmony_ci for (j = 0; j < crb->map[i].count; ++j) { 2178c2ecf20Sopenharmony_ci /* Newer consoles (especially on larger 2188c2ecf20Sopenharmony_ci systems) may require more pages of 2198c2ecf20Sopenharmony_ci PTEs. Grab additional pages as needed. */ 2208c2ecf20Sopenharmony_ci if (pmd != pmd_offset(pud, vaddr)) { 2218c2ecf20Sopenharmony_ci memset(kernel_end, 0, PAGE_SIZE); 2228c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, vaddr); 2238c2ecf20Sopenharmony_ci pmd_set(pmd, (pte_t *)kernel_end); 2248c2ecf20Sopenharmony_ci kernel_end += PAGE_SIZE; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci set_pte(pte_offset_kernel(pmd, vaddr), 2278c2ecf20Sopenharmony_ci pfn_pte(pfn, PAGE_KERNEL)); 2288c2ecf20Sopenharmony_ci pfn++; 2298c2ecf20Sopenharmony_ci vaddr += PAGE_SIZE; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci callback_init_done = 1; 2358c2ecf20Sopenharmony_ci return kernel_end; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci#ifndef CONFIG_DISCONTIGMEM 2408c2ecf20Sopenharmony_ci/* 2418c2ecf20Sopenharmony_ci * paging_init() sets up the memory map. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_civoid __init paging_init(void) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci unsigned long max_zone_pfn[MAX_NR_ZONES] = {0, }; 2468c2ecf20Sopenharmony_ci unsigned long dma_pfn; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci dma_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; 2498c2ecf20Sopenharmony_ci max_pfn = max_low_pfn; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci max_zone_pfn[ZONE_DMA] = dma_pfn; 2528c2ecf20Sopenharmony_ci max_zone_pfn[ZONE_NORMAL] = max_pfn; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Initialize mem_map[]. */ 2558c2ecf20Sopenharmony_ci free_area_init(max_zone_pfn); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Initialize the kernel's ZERO_PGE. */ 2588c2ecf20Sopenharmony_ci memset((void *)ZERO_PGE, 0, PAGE_SIZE); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci#endif /* CONFIG_DISCONTIGMEM */ 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM) 2638c2ecf20Sopenharmony_civoid 2648c2ecf20Sopenharmony_cisrm_paging_stop (void) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci /* Move the vptb back to where the SRM console expects it. */ 2678c2ecf20Sopenharmony_ci swapper_pg_dir[1] = swapper_pg_dir[1023]; 2688c2ecf20Sopenharmony_ci tbia(); 2698c2ecf20Sopenharmony_ci wrvptptr(0x200000000UL); 2708c2ecf20Sopenharmony_ci hwrpb->vptb = 0x200000000UL; 2718c2ecf20Sopenharmony_ci hwrpb_update_checksum(hwrpb); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Reload the page tables that the console had in use. */ 2748c2ecf20Sopenharmony_ci load_PCB(&original_pcb); 2758c2ecf20Sopenharmony_ci tbia(); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci#endif 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_civoid __init 2808c2ecf20Sopenharmony_cimem_init(void) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci set_max_mapnr(max_low_pfn); 2838c2ecf20Sopenharmony_ci high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); 2848c2ecf20Sopenharmony_ci memblock_free_all(); 2858c2ecf20Sopenharmony_ci mem_init_print_info(NULL); 2868c2ecf20Sopenharmony_ci} 287