1*6c9f99dfSJanosch Frank /* SPDX-License-Identifier: GPL-2.0-only */ 2c08c320bSDavid Hildenbrand /* 3c08c320bSDavid Hildenbrand * s390x MMU 4c08c320bSDavid Hildenbrand * 5c08c320bSDavid Hildenbrand * Copyright (c) 2017 Red Hat Inc 6c08c320bSDavid Hildenbrand * 7c08c320bSDavid Hildenbrand * Authors: 8c08c320bSDavid Hildenbrand * David Hildenbrand <david@redhat.com> 9c08c320bSDavid Hildenbrand */ 10c08c320bSDavid Hildenbrand 11c08c320bSDavid Hildenbrand #include <libcflat.h> 12c08c320bSDavid Hildenbrand #include <asm/pgtable.h> 13c08c320bSDavid Hildenbrand #include <asm/arch_def.h> 14c08c320bSDavid Hildenbrand #include <asm/barrier.h> 15c08c320bSDavid Hildenbrand #include <vmalloc.h> 16dfe993b0SThomas Huth #include "mmu.h" 17c08c320bSDavid Hildenbrand 1849a732c7SJanosch Frank static pgd_t *table_root; 1949a732c7SJanosch Frank 20c08c320bSDavid Hildenbrand void configure_dat(int enable) 21c08c320bSDavid Hildenbrand { 22c08c320bSDavid Hildenbrand uint64_t mask; 23c08c320bSDavid Hildenbrand 24c08c320bSDavid Hildenbrand if (enable) 25c08c320bSDavid Hildenbrand mask = extract_psw_mask() | PSW_MASK_DAT; 26c08c320bSDavid Hildenbrand else 27c08c320bSDavid Hildenbrand mask = extract_psw_mask() & ~PSW_MASK_DAT; 28c08c320bSDavid Hildenbrand 29c08c320bSDavid Hildenbrand load_psw_mask(mask); 30c08c320bSDavid Hildenbrand } 31c08c320bSDavid Hildenbrand 32c08c320bSDavid Hildenbrand static void mmu_enable(pgd_t *pgtable) 33c08c320bSDavid Hildenbrand { 3483e3ed62SDavid Hildenbrand struct lowcore *lc = NULL; 35c08c320bSDavid Hildenbrand const uint64_t asce = __pa(pgtable) | ASCE_DT_REGION1 | 36c08c320bSDavid Hildenbrand REGION_TABLE_LENGTH; 37c08c320bSDavid Hildenbrand 38c08c320bSDavid Hildenbrand /* set primary asce */ 39c08c320bSDavid Hildenbrand lctlg(1, asce); 40c08c320bSDavid Hildenbrand assert(stctg(1) == asce); 41c08c320bSDavid Hildenbrand 42c08c320bSDavid Hildenbrand /* enable dat (primary == 0 set as default) */ 43c08c320bSDavid Hildenbrand configure_dat(1); 4483e3ed62SDavid Hildenbrand 4583e3ed62SDavid Hildenbrand /* we can now also use DAT unconditionally in our PGM handler */ 4683e3ed62SDavid Hildenbrand lc->pgm_new_psw.mask |= PSW_MASK_DAT; 47c08c320bSDavid Hildenbrand } 48c08c320bSDavid Hildenbrand 49c08c320bSDavid Hildenbrand static pteval_t *get_pte(pgd_t *pgtable, uintptr_t vaddr) 50c08c320bSDavid Hildenbrand { 51c08c320bSDavid Hildenbrand pgd_t *pgd = pgd_offset(pgtable, vaddr); 52c08c320bSDavid Hildenbrand p4d_t *p4d = p4d_alloc(pgd, vaddr); 53c08c320bSDavid Hildenbrand pud_t *pud = pud_alloc(p4d, vaddr); 54c08c320bSDavid Hildenbrand pmd_t *pmd = pmd_alloc(pud, vaddr); 55c08c320bSDavid Hildenbrand pte_t *pte = pte_alloc(pmd, vaddr); 56c08c320bSDavid Hildenbrand 57c08c320bSDavid Hildenbrand return &pte_val(*pte); 58c08c320bSDavid Hildenbrand } 59c08c320bSDavid Hildenbrand 60c08c320bSDavid Hildenbrand phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *vaddr) 61c08c320bSDavid Hildenbrand { 62c08c320bSDavid Hildenbrand return (*get_pte(pgtable, (uintptr_t)vaddr) & PAGE_MASK) + 63c08c320bSDavid Hildenbrand ((unsigned long)vaddr & ~PAGE_MASK); 64c08c320bSDavid Hildenbrand } 65c08c320bSDavid Hildenbrand 6649a732c7SJanosch Frank static pteval_t *set_pte(pgd_t *pgtable, pteval_t val, void *vaddr) 67c08c320bSDavid Hildenbrand { 68c08c320bSDavid Hildenbrand pteval_t *p_pte = get_pte(pgtable, (uintptr_t)vaddr); 69c08c320bSDavid Hildenbrand 70c08c320bSDavid Hildenbrand /* first flush the old entry (if we're replacing anything) */ 71c08c320bSDavid Hildenbrand if (!(*p_pte & PAGE_ENTRY_I)) 72c08c320bSDavid Hildenbrand ipte((uintptr_t)vaddr, p_pte); 73c08c320bSDavid Hildenbrand 7449a732c7SJanosch Frank *p_pte = val; 75c08c320bSDavid Hildenbrand return p_pte; 76c08c320bSDavid Hildenbrand } 77c08c320bSDavid Hildenbrand 7849a732c7SJanosch Frank pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr) 7949a732c7SJanosch Frank { 8049a732c7SJanosch Frank return set_pte(pgtable, __pa(phys), vaddr); 8149a732c7SJanosch Frank } 8249a732c7SJanosch Frank 8349a732c7SJanosch Frank void protect_page(void *vaddr, unsigned long prot) 8449a732c7SJanosch Frank { 8549a732c7SJanosch Frank pteval_t *p_pte = get_pte(table_root, (uintptr_t)vaddr); 8649a732c7SJanosch Frank pteval_t n_pte = *p_pte | prot; 8749a732c7SJanosch Frank 8849a732c7SJanosch Frank set_pte(table_root, n_pte, vaddr); 8949a732c7SJanosch Frank } 9049a732c7SJanosch Frank 9149a732c7SJanosch Frank void unprotect_page(void *vaddr, unsigned long prot) 9249a732c7SJanosch Frank { 9349a732c7SJanosch Frank pteval_t *p_pte = get_pte(table_root, (uintptr_t)vaddr); 9449a732c7SJanosch Frank pteval_t n_pte = *p_pte & ~prot; 9549a732c7SJanosch Frank 9649a732c7SJanosch Frank set_pte(table_root, n_pte, vaddr); 9749a732c7SJanosch Frank } 9849a732c7SJanosch Frank 9949a732c7SJanosch Frank void protect_range(void *start, unsigned long len, unsigned long prot) 10049a732c7SJanosch Frank { 10149a732c7SJanosch Frank uintptr_t curr = (uintptr_t)start & PAGE_MASK; 10249a732c7SJanosch Frank 10349a732c7SJanosch Frank len &= PAGE_MASK; 10449a732c7SJanosch Frank for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE) 10549a732c7SJanosch Frank protect_page((void *)curr, prot); 10649a732c7SJanosch Frank } 10749a732c7SJanosch Frank 10849a732c7SJanosch Frank void unprotect_range(void *start, unsigned long len, unsigned long prot) 10949a732c7SJanosch Frank { 11049a732c7SJanosch Frank uintptr_t curr = (uintptr_t)start & PAGE_MASK; 11149a732c7SJanosch Frank 11249a732c7SJanosch Frank len &= PAGE_MASK; 11349a732c7SJanosch Frank for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE) 11449a732c7SJanosch Frank unprotect_page((void *)curr, prot); 11549a732c7SJanosch Frank } 11649a732c7SJanosch Frank 117c08c320bSDavid Hildenbrand static void setup_identity(pgd_t *pgtable, phys_addr_t start_addr, 118c08c320bSDavid Hildenbrand phys_addr_t end_addr) 119c08c320bSDavid Hildenbrand { 120c08c320bSDavid Hildenbrand phys_addr_t cur; 121c08c320bSDavid Hildenbrand 122c08c320bSDavid Hildenbrand start_addr &= PAGE_MASK; 123c08c320bSDavid Hildenbrand for (cur = start_addr; true; cur += PAGE_SIZE) { 124c08c320bSDavid Hildenbrand if (start_addr < end_addr && cur >= end_addr) 125c08c320bSDavid Hildenbrand break; 126c08c320bSDavid Hildenbrand if (start_addr > end_addr && cur <= end_addr) 127c08c320bSDavid Hildenbrand break; 128c08c320bSDavid Hildenbrand install_page(pgtable, cur, __va(cur)); 129c08c320bSDavid Hildenbrand } 130c08c320bSDavid Hildenbrand } 131c08c320bSDavid Hildenbrand 132c08c320bSDavid Hildenbrand void *setup_mmu(phys_addr_t phys_end){ 133c08c320bSDavid Hildenbrand pgd_t *page_root; 134c08c320bSDavid Hildenbrand 135c08c320bSDavid Hildenbrand /* allocate a region-1 table */ 136c08c320bSDavid Hildenbrand page_root = pgd_alloc_one(); 137c08c320bSDavid Hildenbrand 138c08c320bSDavid Hildenbrand /* map all physical memory 1:1 */ 139c08c320bSDavid Hildenbrand setup_identity(page_root, 0, phys_end); 140c08c320bSDavid Hildenbrand 141c08c320bSDavid Hildenbrand /* generate 128MB of invalid adresses at the end (for testing PGM) */ 142c08c320bSDavid Hildenbrand init_alloc_vpage((void *) -(1UL << 27)); 143c08c320bSDavid Hildenbrand setup_identity(page_root, -(1UL << 27), 0); 144c08c320bSDavid Hildenbrand 145c08c320bSDavid Hildenbrand /* finally enable DAT with the new table */ 146c08c320bSDavid Hildenbrand mmu_enable(page_root); 14749a732c7SJanosch Frank table_root = page_root; 148c08c320bSDavid Hildenbrand return page_root; 149c08c320bSDavid Hildenbrand } 150