1*c08c320bSDavid Hildenbrand /* 2*c08c320bSDavid Hildenbrand * s390x MMU 3*c08c320bSDavid Hildenbrand * 4*c08c320bSDavid Hildenbrand * Copyright (c) 2017 Red Hat Inc 5*c08c320bSDavid Hildenbrand * 6*c08c320bSDavid Hildenbrand * Authors: 7*c08c320bSDavid Hildenbrand * David Hildenbrand <david@redhat.com> 8*c08c320bSDavid Hildenbrand * 9*c08c320bSDavid Hildenbrand * This code is free software; you can redistribute it and/or modify it 10*c08c320bSDavid Hildenbrand * under the terms of the GNU Library General Public License version 2. 11*c08c320bSDavid Hildenbrand */ 12*c08c320bSDavid Hildenbrand 13*c08c320bSDavid Hildenbrand #include <libcflat.h> 14*c08c320bSDavid Hildenbrand #include <asm/pgtable.h> 15*c08c320bSDavid Hildenbrand #include <asm/arch_def.h> 16*c08c320bSDavid Hildenbrand #include <asm/barrier.h> 17*c08c320bSDavid Hildenbrand #include <vmalloc.h> 18*c08c320bSDavid Hildenbrand 19*c08c320bSDavid Hildenbrand void configure_dat(int enable) 20*c08c320bSDavid Hildenbrand { 21*c08c320bSDavid Hildenbrand uint64_t mask; 22*c08c320bSDavid Hildenbrand 23*c08c320bSDavid Hildenbrand if (enable) 24*c08c320bSDavid Hildenbrand mask = extract_psw_mask() | PSW_MASK_DAT; 25*c08c320bSDavid Hildenbrand else 26*c08c320bSDavid Hildenbrand mask = extract_psw_mask() & ~PSW_MASK_DAT; 27*c08c320bSDavid Hildenbrand 28*c08c320bSDavid Hildenbrand load_psw_mask(mask); 29*c08c320bSDavid Hildenbrand } 30*c08c320bSDavid Hildenbrand 31*c08c320bSDavid Hildenbrand static void mmu_enable(pgd_t *pgtable) 32*c08c320bSDavid Hildenbrand { 33*c08c320bSDavid Hildenbrand const uint64_t asce = __pa(pgtable) | ASCE_DT_REGION1 | 34*c08c320bSDavid Hildenbrand REGION_TABLE_LENGTH; 35*c08c320bSDavid Hildenbrand 36*c08c320bSDavid Hildenbrand /* set primary asce */ 37*c08c320bSDavid Hildenbrand lctlg(1, asce); 38*c08c320bSDavid Hildenbrand assert(stctg(1) == asce); 39*c08c320bSDavid Hildenbrand 40*c08c320bSDavid Hildenbrand /* enable dat (primary == 0 set as default) */ 41*c08c320bSDavid Hildenbrand configure_dat(1); 42*c08c320bSDavid Hildenbrand } 43*c08c320bSDavid Hildenbrand 44*c08c320bSDavid Hildenbrand static pteval_t *get_pte(pgd_t *pgtable, uintptr_t vaddr) 45*c08c320bSDavid Hildenbrand { 46*c08c320bSDavid Hildenbrand pgd_t *pgd = pgd_offset(pgtable, vaddr); 47*c08c320bSDavid Hildenbrand p4d_t *p4d = p4d_alloc(pgd, vaddr); 48*c08c320bSDavid Hildenbrand pud_t *pud = pud_alloc(p4d, vaddr); 49*c08c320bSDavid Hildenbrand pmd_t *pmd = pmd_alloc(pud, vaddr); 50*c08c320bSDavid Hildenbrand pte_t *pte = pte_alloc(pmd, vaddr); 51*c08c320bSDavid Hildenbrand 52*c08c320bSDavid Hildenbrand return &pte_val(*pte); 53*c08c320bSDavid Hildenbrand } 54*c08c320bSDavid Hildenbrand 55*c08c320bSDavid Hildenbrand phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *vaddr) 56*c08c320bSDavid Hildenbrand { 57*c08c320bSDavid Hildenbrand return (*get_pte(pgtable, (uintptr_t)vaddr) & PAGE_MASK) + 58*c08c320bSDavid Hildenbrand ((unsigned long)vaddr & ~PAGE_MASK); 59*c08c320bSDavid Hildenbrand } 60*c08c320bSDavid Hildenbrand 61*c08c320bSDavid Hildenbrand pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr) 62*c08c320bSDavid Hildenbrand { 63*c08c320bSDavid Hildenbrand pteval_t *p_pte = get_pte(pgtable, (uintptr_t)vaddr); 64*c08c320bSDavid Hildenbrand 65*c08c320bSDavid Hildenbrand /* first flush the old entry (if we're replacing anything) */ 66*c08c320bSDavid Hildenbrand if (!(*p_pte & PAGE_ENTRY_I)) 67*c08c320bSDavid Hildenbrand ipte((uintptr_t)vaddr, p_pte); 68*c08c320bSDavid Hildenbrand 69*c08c320bSDavid Hildenbrand *p_pte = __pa(phys); 70*c08c320bSDavid Hildenbrand return p_pte; 71*c08c320bSDavid Hildenbrand } 72*c08c320bSDavid Hildenbrand 73*c08c320bSDavid Hildenbrand static void setup_identity(pgd_t *pgtable, phys_addr_t start_addr, 74*c08c320bSDavid Hildenbrand phys_addr_t end_addr) 75*c08c320bSDavid Hildenbrand { 76*c08c320bSDavid Hildenbrand phys_addr_t cur; 77*c08c320bSDavid Hildenbrand 78*c08c320bSDavid Hildenbrand start_addr &= PAGE_MASK; 79*c08c320bSDavid Hildenbrand for (cur = start_addr; true; cur += PAGE_SIZE) { 80*c08c320bSDavid Hildenbrand if (start_addr < end_addr && cur >= end_addr) 81*c08c320bSDavid Hildenbrand break; 82*c08c320bSDavid Hildenbrand if (start_addr > end_addr && cur <= end_addr) 83*c08c320bSDavid Hildenbrand break; 84*c08c320bSDavid Hildenbrand install_page(pgtable, cur, __va(cur)); 85*c08c320bSDavid Hildenbrand } 86*c08c320bSDavid Hildenbrand } 87*c08c320bSDavid Hildenbrand 88*c08c320bSDavid Hildenbrand void *setup_mmu(phys_addr_t phys_end){ 89*c08c320bSDavid Hildenbrand pgd_t *page_root; 90*c08c320bSDavid Hildenbrand 91*c08c320bSDavid Hildenbrand /* allocate a region-1 table */ 92*c08c320bSDavid Hildenbrand page_root = pgd_alloc_one(); 93*c08c320bSDavid Hildenbrand 94*c08c320bSDavid Hildenbrand /* map all physical memory 1:1 */ 95*c08c320bSDavid Hildenbrand setup_identity(page_root, 0, phys_end); 96*c08c320bSDavid Hildenbrand 97*c08c320bSDavid Hildenbrand /* generate 128MB of invalid adresses at the end (for testing PGM) */ 98*c08c320bSDavid Hildenbrand init_alloc_vpage((void *) -(1UL << 27)); 99*c08c320bSDavid Hildenbrand setup_identity(page_root, -(1UL << 27), 0); 100*c08c320bSDavid Hildenbrand 101*c08c320bSDavid Hildenbrand /* finally enable DAT with the new table */ 102*c08c320bSDavid Hildenbrand mmu_enable(page_root); 103*c08c320bSDavid Hildenbrand return page_root; 104*c08c320bSDavid Hildenbrand } 105