1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/arch/alpha/mm/init.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1995 Linus Torvalds
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds /* 2.3.x zone allocator, 1999 Andrea Arcangeli <andrea@suse.de> */
91da177e4SLinus Torvalds
10df2e71fbSakpm@osdl.org #include <linux/pagemap.h>
111da177e4SLinus Torvalds #include <linux/signal.h>
121da177e4SLinus Torvalds #include <linux/sched.h>
131da177e4SLinus Torvalds #include <linux/kernel.h>
141da177e4SLinus Torvalds #include <linux/errno.h>
151da177e4SLinus Torvalds #include <linux/string.h>
161da177e4SLinus Torvalds #include <linux/types.h>
171da177e4SLinus Torvalds #include <linux/ptrace.h>
181da177e4SLinus Torvalds #include <linux/mman.h>
191da177e4SLinus Torvalds #include <linux/mm.h>
201da177e4SLinus Torvalds #include <linux/swap.h>
211da177e4SLinus Torvalds #include <linux/init.h>
2257c8a661SMike Rapoport #include <linux/memblock.h> /* max_low_pfn */
231da177e4SLinus Torvalds #include <linux/vmalloc.h>
245a0e3ad6STejun Heo #include <linux/gfp.h>
251da177e4SLinus Torvalds
267c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
271da177e4SLinus Torvalds #include <asm/pgalloc.h>
281da177e4SLinus Torvalds #include <asm/hwrpb.h>
291da177e4SLinus Torvalds #include <asm/dma.h>
301da177e4SLinus Torvalds #include <asm/mmu_context.h>
311da177e4SLinus Torvalds #include <asm/console.h>
321da177e4SLinus Torvalds #include <asm/tlb.h>
33ec221208SDavid Howells #include <asm/setup.h>
34f3beeb4aSJiang Liu #include <asm/sections.h>
351da177e4SLinus Torvalds
36*6e8d0237SAl Viro #include "../kernel/proto.h"
371da177e4SLinus Torvalds
381da177e4SLinus Torvalds static struct pcb_struct original_pcb;
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds pgd_t *
pgd_alloc(struct mm_struct * mm)411da177e4SLinus Torvalds pgd_alloc(struct mm_struct *mm)
421da177e4SLinus Torvalds {
431da177e4SLinus Torvalds pgd_t *ret, *init;
441da177e4SLinus Torvalds
451da177e4SLinus Torvalds ret = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
461da177e4SLinus Torvalds init = pgd_offset(&init_mm, 0UL);
471da177e4SLinus Torvalds if (ret) {
481da177e4SLinus Torvalds #ifdef CONFIG_ALPHA_LARGE_VMALLOC
491da177e4SLinus Torvalds memcpy (ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
501da177e4SLinus Torvalds (PTRS_PER_PGD - USER_PTRS_PER_PGD - 1)*sizeof(pgd_t));
511da177e4SLinus Torvalds #else
521da177e4SLinus Torvalds pgd_val(ret[PTRS_PER_PGD-2]) = pgd_val(init[PTRS_PER_PGD-2]);
531da177e4SLinus Torvalds #endif
541da177e4SLinus Torvalds
551da177e4SLinus Torvalds /* The last PGD entry is the VPTB self-map. */
561da177e4SLinus Torvalds pgd_val(ret[PTRS_PER_PGD-1])
571da177e4SLinus Torvalds = pte_val(mk_pte(virt_to_page(ret), PAGE_KERNEL));
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds return ret;
601da177e4SLinus Torvalds }
611da177e4SLinus Torvalds
621da177e4SLinus Torvalds
631da177e4SLinus Torvalds /*
641da177e4SLinus Torvalds * BAD_PAGE is the page that is used for page faults when linux
651da177e4SLinus Torvalds * is out-of-memory. Older versions of linux just did a
661da177e4SLinus Torvalds * do_exit(), but using this instead means there is less risk
671da177e4SLinus Torvalds * for a process dying in kernel mode, possibly leaving an inode
681da177e4SLinus Torvalds * unused etc..
691da177e4SLinus Torvalds *
701da177e4SLinus Torvalds * BAD_PAGETABLE is the accompanying page-table: it is initialized
711da177e4SLinus Torvalds * to point to BAD_PAGE entries.
721da177e4SLinus Torvalds *
731da177e4SLinus Torvalds * ZERO_PAGE is a special page that is used for zero-initialized
741da177e4SLinus Torvalds * data and COW.
751da177e4SLinus Torvalds */
761da177e4SLinus Torvalds pmd_t *
__bad_pagetable(void)771da177e4SLinus Torvalds __bad_pagetable(void)
781da177e4SLinus Torvalds {
79545c2722SKees Cook memset(absolute_pointer(EMPTY_PGT), 0, PAGE_SIZE);
801da177e4SLinus Torvalds return (pmd_t *) EMPTY_PGT;
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds
831da177e4SLinus Torvalds pte_t
__bad_page(void)841da177e4SLinus Torvalds __bad_page(void)
851da177e4SLinus Torvalds {
86545c2722SKees Cook memset(absolute_pointer(EMPTY_PGE), 0, PAGE_SIZE);
871da177e4SLinus Torvalds return pte_mkdirty(mk_pte(virt_to_page(EMPTY_PGE), PAGE_SHARED));
881da177e4SLinus Torvalds }
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds static inline unsigned long
load_PCB(struct pcb_struct * pcb)911da177e4SLinus Torvalds load_PCB(struct pcb_struct *pcb)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds register unsigned long sp __asm__("$30");
941da177e4SLinus Torvalds pcb->ksp = sp;
951da177e4SLinus Torvalds return __reload_thread(pcb);
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds
981da177e4SLinus Torvalds /* Set up initial PCB, VPTB, and other such nicities. */
991da177e4SLinus Torvalds
1001da177e4SLinus Torvalds static inline void
switch_to_system_map(void)1011da177e4SLinus Torvalds switch_to_system_map(void)
1021da177e4SLinus Torvalds {
1031da177e4SLinus Torvalds unsigned long newptbr;
1041da177e4SLinus Torvalds unsigned long original_pcb_ptr;
1051da177e4SLinus Torvalds
1061da177e4SLinus Torvalds /* Initialize the kernel's page tables. Linux puts the vptb in
1071da177e4SLinus Torvalds the last slot of the L1 page table. */
1081da177e4SLinus Torvalds memset(swapper_pg_dir, 0, PAGE_SIZE);
1091da177e4SLinus Torvalds newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT;
1101da177e4SLinus Torvalds pgd_val(swapper_pg_dir[1023]) =
1111da177e4SLinus Torvalds (newptbr << 32) | pgprot_val(PAGE_KERNEL);
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds /* Set the vptb. This is often done by the bootloader, but
1141da177e4SLinus Torvalds shouldn't be required. */
1151da177e4SLinus Torvalds if (hwrpb->vptb != 0xfffffffe00000000UL) {
1161da177e4SLinus Torvalds wrvptptr(0xfffffffe00000000UL);
1171da177e4SLinus Torvalds hwrpb->vptb = 0xfffffffe00000000UL;
1181da177e4SLinus Torvalds hwrpb_update_checksum(hwrpb);
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds
1211da177e4SLinus Torvalds /* Also set up the real kernel PCB while we're at it. */
1221da177e4SLinus Torvalds init_thread_info.pcb.ptbr = newptbr;
1231da177e4SLinus Torvalds init_thread_info.pcb.flags = 1; /* set FEN, clear everything else */
1241da177e4SLinus Torvalds original_pcb_ptr = load_PCB(&init_thread_info.pcb);
1251da177e4SLinus Torvalds tbia();
1261da177e4SLinus Torvalds
1271da177e4SLinus Torvalds /* Save off the contents of the original PCB so that we can
1281da177e4SLinus Torvalds restore the original console's page tables for a clean reboot.
1291da177e4SLinus Torvalds
1301da177e4SLinus Torvalds Note that the PCB is supposed to be a physical address, but
1311da177e4SLinus Torvalds since KSEG values also happen to work, folks get confused.
1321da177e4SLinus Torvalds Check this here. */
1331da177e4SLinus Torvalds
1341da177e4SLinus Torvalds if (original_pcb_ptr < PAGE_OFFSET) {
1351da177e4SLinus Torvalds original_pcb_ptr = (unsigned long)
1361da177e4SLinus Torvalds phys_to_virt(original_pcb_ptr);
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds original_pcb = *(struct pcb_struct *) original_pcb_ptr;
1391da177e4SLinus Torvalds }
1401da177e4SLinus Torvalds
1411da177e4SLinus Torvalds int callback_init_done;
1421da177e4SLinus Torvalds
1431da177e4SLinus Torvalds void * __init
callback_init(void * kernel_end)1441da177e4SLinus Torvalds callback_init(void * kernel_end)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds struct crb_struct * crb;
1471da177e4SLinus Torvalds pgd_t *pgd;
148a73c9489SMike Rapoport p4d_t *p4d;
149a73c9489SMike Rapoport pud_t *pud;
1501da177e4SLinus Torvalds pmd_t *pmd;
1511da177e4SLinus Torvalds void *two_pages;
1521da177e4SLinus Torvalds
1531da177e4SLinus Torvalds /* Starting at the HWRPB, locate the CRB. */
1541da177e4SLinus Torvalds crb = (struct crb_struct *)((char *)hwrpb + hwrpb->crb_offset);
1551da177e4SLinus Torvalds
1561da177e4SLinus Torvalds if (alpha_using_srm) {
1571da177e4SLinus Torvalds /* Tell the console whither it is to be remapped. */
1581da177e4SLinus Torvalds if (srm_fixup(VMALLOC_START, (unsigned long)hwrpb))
1591da177e4SLinus Torvalds __halt(); /* "We're boned." --Bender */
1601da177e4SLinus Torvalds
1611da177e4SLinus Torvalds /* Edit the procedure descriptors for DISPATCH and FIXUP. */
1621da177e4SLinus Torvalds crb->dispatch_va = (struct procdesc_struct *)
1631da177e4SLinus Torvalds (VMALLOC_START + (unsigned long)crb->dispatch_va
1641da177e4SLinus Torvalds - crb->map[0].va);
1651da177e4SLinus Torvalds crb->fixup_va = (struct procdesc_struct *)
1661da177e4SLinus Torvalds (VMALLOC_START + (unsigned long)crb->fixup_va
1671da177e4SLinus Torvalds - crb->map[0].va);
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds
1701da177e4SLinus Torvalds switch_to_system_map();
1711da177e4SLinus Torvalds
1721da177e4SLinus Torvalds /* Allocate one PGD and one PMD. In the case of SRM, we'll need
1731da177e4SLinus Torvalds these to actually remap the console. There is an assumption
1741da177e4SLinus Torvalds here that only one of each is needed, and this allows for 8MB.
1751da177e4SLinus Torvalds On systems with larger consoles, additional pages will be
1761da177e4SLinus Torvalds allocated as needed during the mapping process.
1771da177e4SLinus Torvalds
1781da177e4SLinus Torvalds In the case of not SRM, but not CONFIG_ALPHA_LARGE_VMALLOC,
1791da177e4SLinus Torvalds we need to allocate the PGD we use for vmalloc before we start
1801da177e4SLinus Torvalds forking other tasks. */
1811da177e4SLinus Torvalds
1821da177e4SLinus Torvalds two_pages = (void *)
1831da177e4SLinus Torvalds (((unsigned long)kernel_end + ~PAGE_MASK) & PAGE_MASK);
1841da177e4SLinus Torvalds kernel_end = two_pages + 2*PAGE_SIZE;
1851da177e4SLinus Torvalds memset(two_pages, 0, 2*PAGE_SIZE);
1861da177e4SLinus Torvalds
1871da177e4SLinus Torvalds pgd = pgd_offset_k(VMALLOC_START);
188a73c9489SMike Rapoport p4d = p4d_offset(pgd, VMALLOC_START);
189a73c9489SMike Rapoport pud = pud_offset(p4d, VMALLOC_START);
190a73c9489SMike Rapoport pud_set(pud, (pmd_t *)two_pages);
191a73c9489SMike Rapoport pmd = pmd_offset(pud, VMALLOC_START);
1921da177e4SLinus Torvalds pmd_set(pmd, (pte_t *)(two_pages + PAGE_SIZE));
1931da177e4SLinus Torvalds
1941da177e4SLinus Torvalds if (alpha_using_srm) {
1951da177e4SLinus Torvalds static struct vm_struct console_remap_vm;
196f0aa6617STejun Heo unsigned long nr_pages = 0;
197f0aa6617STejun Heo unsigned long vaddr;
1981da177e4SLinus Torvalds unsigned long i, j;
1991da177e4SLinus Torvalds
200f0aa6617STejun Heo /* calculate needed size */
201f0aa6617STejun Heo for (i = 0; i < crb->map_entries; ++i)
202f0aa6617STejun Heo nr_pages += crb->map[i].count;
203f0aa6617STejun Heo
204f0aa6617STejun Heo /* register the vm area */
205f0aa6617STejun Heo console_remap_vm.flags = VM_ALLOC;
206f0aa6617STejun Heo console_remap_vm.size = nr_pages << PAGE_SHIFT;
207c0c0a293STejun Heo vm_area_register_early(&console_remap_vm, PAGE_SIZE);
208f0aa6617STejun Heo
209af6326d7STejun Heo vaddr = (unsigned long)console_remap_vm.addr;
210f0aa6617STejun Heo
2111da177e4SLinus Torvalds /* Set up the third level PTEs and update the virtual
2121da177e4SLinus Torvalds addresses of the CRB entries. */
2131da177e4SLinus Torvalds for (i = 0; i < crb->map_entries; ++i) {
2141da177e4SLinus Torvalds unsigned long pfn = crb->map[i].pa >> PAGE_SHIFT;
2151da177e4SLinus Torvalds crb->map[i].va = vaddr;
2161da177e4SLinus Torvalds for (j = 0; j < crb->map[i].count; ++j) {
217c3a2ddeeSSimon Arlott /* Newer consoles (especially on larger
2181da177e4SLinus Torvalds systems) may require more pages of
2191da177e4SLinus Torvalds PTEs. Grab additional pages as needed. */
220a73c9489SMike Rapoport if (pmd != pmd_offset(pud, vaddr)) {
2211da177e4SLinus Torvalds memset(kernel_end, 0, PAGE_SIZE);
222a73c9489SMike Rapoport pmd = pmd_offset(pud, vaddr);
2231da177e4SLinus Torvalds pmd_set(pmd, (pte_t *)kernel_end);
2241da177e4SLinus Torvalds kernel_end += PAGE_SIZE;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds set_pte(pte_offset_kernel(pmd, vaddr),
2271da177e4SLinus Torvalds pfn_pte(pfn, PAGE_KERNEL));
2281da177e4SLinus Torvalds pfn++;
2291da177e4SLinus Torvalds vaddr += PAGE_SIZE;
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds }
2331da177e4SLinus Torvalds
2341da177e4SLinus Torvalds callback_init_done = 1;
2351da177e4SLinus Torvalds return kernel_end;
2361da177e4SLinus Torvalds }
2371da177e4SLinus Torvalds
2381da177e4SLinus Torvalds /*
2391da177e4SLinus Torvalds * paging_init() sets up the memory map.
2401da177e4SLinus Torvalds */
paging_init(void)24164d158bcSSam Ravnborg void __init paging_init(void)
2421da177e4SLinus Torvalds {
243fa3354e4SMike Rapoport unsigned long max_zone_pfn[MAX_NR_ZONES] = {0, };
244fa3354e4SMike Rapoport unsigned long dma_pfn;
2451da177e4SLinus Torvalds
2461da177e4SLinus Torvalds dma_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
247fa3354e4SMike Rapoport max_pfn = max_low_pfn;
2481da177e4SLinus Torvalds
249fa3354e4SMike Rapoport max_zone_pfn[ZONE_DMA] = dma_pfn;
250fa3354e4SMike Rapoport max_zone_pfn[ZONE_NORMAL] = max_pfn;
2511da177e4SLinus Torvalds
2521da177e4SLinus Torvalds /* Initialize mem_map[]. */
253fa3354e4SMike Rapoport free_area_init(max_zone_pfn);
2541da177e4SLinus Torvalds
2551da177e4SLinus Torvalds /* Initialize the kernel's ZERO_PGE. */
256545c2722SKees Cook memset(absolute_pointer(ZERO_PGE), 0, PAGE_SIZE);
2571da177e4SLinus Torvalds }
2581da177e4SLinus Torvalds
2591da177e4SLinus Torvalds #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM)
2601da177e4SLinus Torvalds void
srm_paging_stop(void)2611da177e4SLinus Torvalds srm_paging_stop (void)
2621da177e4SLinus Torvalds {
2631da177e4SLinus Torvalds /* Move the vptb back to where the SRM console expects it. */
2641da177e4SLinus Torvalds swapper_pg_dir[1] = swapper_pg_dir[1023];
2651da177e4SLinus Torvalds tbia();
2661da177e4SLinus Torvalds wrvptptr(0x200000000UL);
2671da177e4SLinus Torvalds hwrpb->vptb = 0x200000000UL;
2681da177e4SLinus Torvalds hwrpb_update_checksum(hwrpb);
2691da177e4SLinus Torvalds
2701da177e4SLinus Torvalds /* Reload the page tables that the console had in use. */
2711da177e4SLinus Torvalds load_PCB(&original_pcb);
2721da177e4SLinus Torvalds tbia();
2731da177e4SLinus Torvalds }
2741da177e4SLinus Torvalds #endif
2751da177e4SLinus Torvalds
2761da177e4SLinus Torvalds void __init
mem_init(void)2771da177e4SLinus Torvalds mem_init(void)
2781da177e4SLinus Torvalds {
2792fb1cd5aSJiang Liu set_max_mapnr(max_low_pfn);
2801da177e4SLinus Torvalds high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
281c6ffc5caSMike Rapoport memblock_free_all();
2821da177e4SLinus Torvalds }
2834975f604SAnshuman Khandual
2844975f604SAnshuman Khandual static const pgprot_t protection_map[16] = {
2854975f604SAnshuman Khandual [VM_NONE] = _PAGE_P(_PAGE_FOE | _PAGE_FOW |
2864975f604SAnshuman Khandual _PAGE_FOR),
2874975f604SAnshuman Khandual [VM_READ] = _PAGE_P(_PAGE_FOE | _PAGE_FOW),
2884975f604SAnshuman Khandual [VM_WRITE] = _PAGE_P(_PAGE_FOE),
2894975f604SAnshuman Khandual [VM_WRITE | VM_READ] = _PAGE_P(_PAGE_FOE),
2904975f604SAnshuman Khandual [VM_EXEC] = _PAGE_P(_PAGE_FOW | _PAGE_FOR),
2914975f604SAnshuman Khandual [VM_EXEC | VM_READ] = _PAGE_P(_PAGE_FOW),
2924975f604SAnshuman Khandual [VM_EXEC | VM_WRITE] = _PAGE_P(0),
2934975f604SAnshuman Khandual [VM_EXEC | VM_WRITE | VM_READ] = _PAGE_P(0),
2944975f604SAnshuman Khandual [VM_SHARED] = _PAGE_S(_PAGE_FOE | _PAGE_FOW |
2954975f604SAnshuman Khandual _PAGE_FOR),
2964975f604SAnshuman Khandual [VM_SHARED | VM_READ] = _PAGE_S(_PAGE_FOE | _PAGE_FOW),
2974975f604SAnshuman Khandual [VM_SHARED | VM_WRITE] = _PAGE_S(_PAGE_FOE),
2984975f604SAnshuman Khandual [VM_SHARED | VM_WRITE | VM_READ] = _PAGE_S(_PAGE_FOE),
2994975f604SAnshuman Khandual [VM_SHARED | VM_EXEC] = _PAGE_S(_PAGE_FOW | _PAGE_FOR),
3004975f604SAnshuman Khandual [VM_SHARED | VM_EXEC | VM_READ] = _PAGE_S(_PAGE_FOW),
3014975f604SAnshuman Khandual [VM_SHARED | VM_EXEC | VM_WRITE] = _PAGE_S(0),
3024975f604SAnshuman Khandual [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = _PAGE_S(0)
3034975f604SAnshuman Khandual };
3044975f604SAnshuman Khandual DECLARE_VM_GET_PAGE_PROT
305