1c08c320bSDavid Hildenbrand /* 2c08c320bSDavid Hildenbrand * s390x MMU 3c08c320bSDavid Hildenbrand * 4c08c320bSDavid Hildenbrand * Copyright (c) 2017 Red Hat Inc 5c08c320bSDavid Hildenbrand * 6c08c320bSDavid Hildenbrand * Authors: 7c08c320bSDavid Hildenbrand * David Hildenbrand <david@redhat.com> 8c08c320bSDavid Hildenbrand * 9c08c320bSDavid Hildenbrand * This code is free software; you can redistribute it and/or modify it 10c08c320bSDavid Hildenbrand * under the terms of the GNU Library General Public License version 2. 11c08c320bSDavid Hildenbrand */ 12c08c320bSDavid Hildenbrand 13c08c320bSDavid Hildenbrand #include <libcflat.h> 14c08c320bSDavid Hildenbrand #include <asm/pgtable.h> 15c08c320bSDavid Hildenbrand #include <asm/arch_def.h> 16c08c320bSDavid Hildenbrand #include <asm/barrier.h> 17c08c320bSDavid Hildenbrand #include <vmalloc.h> 18c08c320bSDavid Hildenbrand 19*49a732c7SJanosch Frank static pgd_t *table_root; 20*49a732c7SJanosch Frank 21c08c320bSDavid Hildenbrand void configure_dat(int enable) 22c08c320bSDavid Hildenbrand { 23c08c320bSDavid Hildenbrand uint64_t mask; 24c08c320bSDavid Hildenbrand 25c08c320bSDavid Hildenbrand if (enable) 26c08c320bSDavid Hildenbrand mask = extract_psw_mask() | PSW_MASK_DAT; 27c08c320bSDavid Hildenbrand else 28c08c320bSDavid Hildenbrand mask = extract_psw_mask() & ~PSW_MASK_DAT; 29c08c320bSDavid Hildenbrand 30c08c320bSDavid Hildenbrand load_psw_mask(mask); 31c08c320bSDavid Hildenbrand } 32c08c320bSDavid Hildenbrand 33c08c320bSDavid Hildenbrand static void mmu_enable(pgd_t *pgtable) 34c08c320bSDavid Hildenbrand { 3583e3ed62SDavid Hildenbrand struct lowcore *lc = NULL; 36c08c320bSDavid Hildenbrand const uint64_t asce = __pa(pgtable) | ASCE_DT_REGION1 | 37c08c320bSDavid Hildenbrand REGION_TABLE_LENGTH; 38c08c320bSDavid Hildenbrand 39c08c320bSDavid Hildenbrand /* set primary asce */ 40c08c320bSDavid Hildenbrand lctlg(1, asce); 41c08c320bSDavid Hildenbrand assert(stctg(1) == asce); 42c08c320bSDavid Hildenbrand 43c08c320bSDavid Hildenbrand /* enable dat (primary == 0 set as default) */ 44c08c320bSDavid Hildenbrand configure_dat(1); 4583e3ed62SDavid Hildenbrand 4683e3ed62SDavid Hildenbrand /* we can now also use DAT unconditionally in our PGM handler */ 4783e3ed62SDavid Hildenbrand lc->pgm_new_psw.mask |= PSW_MASK_DAT; 48c08c320bSDavid Hildenbrand } 49c08c320bSDavid Hildenbrand 50c08c320bSDavid Hildenbrand static pteval_t *get_pte(pgd_t *pgtable, uintptr_t vaddr) 51c08c320bSDavid Hildenbrand { 52c08c320bSDavid Hildenbrand pgd_t *pgd = pgd_offset(pgtable, vaddr); 53c08c320bSDavid Hildenbrand p4d_t *p4d = p4d_alloc(pgd, vaddr); 54c08c320bSDavid Hildenbrand pud_t *pud = pud_alloc(p4d, vaddr); 55c08c320bSDavid Hildenbrand pmd_t *pmd = pmd_alloc(pud, vaddr); 56c08c320bSDavid Hildenbrand pte_t *pte = pte_alloc(pmd, vaddr); 57c08c320bSDavid Hildenbrand 58c08c320bSDavid Hildenbrand return &pte_val(*pte); 59c08c320bSDavid Hildenbrand } 60c08c320bSDavid Hildenbrand 61c08c320bSDavid Hildenbrand phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *vaddr) 62c08c320bSDavid Hildenbrand { 63c08c320bSDavid Hildenbrand return (*get_pte(pgtable, (uintptr_t)vaddr) & PAGE_MASK) + 64c08c320bSDavid Hildenbrand ((unsigned long)vaddr & ~PAGE_MASK); 65c08c320bSDavid Hildenbrand } 66c08c320bSDavid Hildenbrand 67*49a732c7SJanosch Frank static pteval_t *set_pte(pgd_t *pgtable, pteval_t val, void *vaddr) 68c08c320bSDavid Hildenbrand { 69c08c320bSDavid Hildenbrand pteval_t *p_pte = get_pte(pgtable, (uintptr_t)vaddr); 70c08c320bSDavid Hildenbrand 71c08c320bSDavid Hildenbrand /* first flush the old entry (if we're replacing anything) */ 72c08c320bSDavid Hildenbrand if (!(*p_pte & PAGE_ENTRY_I)) 73c08c320bSDavid Hildenbrand ipte((uintptr_t)vaddr, p_pte); 74c08c320bSDavid Hildenbrand 75*49a732c7SJanosch Frank *p_pte = val; 76c08c320bSDavid Hildenbrand return p_pte; 77c08c320bSDavid Hildenbrand } 78c08c320bSDavid Hildenbrand 79*49a732c7SJanosch Frank pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr) 80*49a732c7SJanosch Frank { 81*49a732c7SJanosch Frank return set_pte(pgtable, __pa(phys), vaddr); 82*49a732c7SJanosch Frank } 83*49a732c7SJanosch Frank 84*49a732c7SJanosch Frank void protect_page(void *vaddr, unsigned long prot) 85*49a732c7SJanosch Frank { 86*49a732c7SJanosch Frank pteval_t *p_pte = get_pte(table_root, (uintptr_t)vaddr); 87*49a732c7SJanosch Frank pteval_t n_pte = *p_pte | prot; 88*49a732c7SJanosch Frank 89*49a732c7SJanosch Frank set_pte(table_root, n_pte, vaddr); 90*49a732c7SJanosch Frank } 91*49a732c7SJanosch Frank 92*49a732c7SJanosch Frank void unprotect_page(void *vaddr, unsigned long prot) 93*49a732c7SJanosch Frank { 94*49a732c7SJanosch Frank pteval_t *p_pte = get_pte(table_root, (uintptr_t)vaddr); 95*49a732c7SJanosch Frank pteval_t n_pte = *p_pte & ~prot; 96*49a732c7SJanosch Frank 97*49a732c7SJanosch Frank set_pte(table_root, n_pte, vaddr); 98*49a732c7SJanosch Frank } 99*49a732c7SJanosch Frank 100*49a732c7SJanosch Frank void protect_range(void *start, unsigned long len, unsigned long prot) 101*49a732c7SJanosch Frank { 102*49a732c7SJanosch Frank uintptr_t curr = (uintptr_t)start & PAGE_MASK; 103*49a732c7SJanosch Frank 104*49a732c7SJanosch Frank len &= PAGE_MASK; 105*49a732c7SJanosch Frank for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE) 106*49a732c7SJanosch Frank protect_page((void *)curr, prot); 107*49a732c7SJanosch Frank } 108*49a732c7SJanosch Frank 109*49a732c7SJanosch Frank void unprotect_range(void *start, unsigned long len, unsigned long prot) 110*49a732c7SJanosch Frank { 111*49a732c7SJanosch Frank uintptr_t curr = (uintptr_t)start & PAGE_MASK; 112*49a732c7SJanosch Frank 113*49a732c7SJanosch Frank len &= PAGE_MASK; 114*49a732c7SJanosch Frank for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE) 115*49a732c7SJanosch Frank unprotect_page((void *)curr, prot); 116*49a732c7SJanosch Frank } 117*49a732c7SJanosch Frank 118c08c320bSDavid Hildenbrand static void setup_identity(pgd_t *pgtable, phys_addr_t start_addr, 119c08c320bSDavid Hildenbrand phys_addr_t end_addr) 120c08c320bSDavid Hildenbrand { 121c08c320bSDavid Hildenbrand phys_addr_t cur; 122c08c320bSDavid Hildenbrand 123c08c320bSDavid Hildenbrand start_addr &= PAGE_MASK; 124c08c320bSDavid Hildenbrand for (cur = start_addr; true; cur += PAGE_SIZE) { 125c08c320bSDavid Hildenbrand if (start_addr < end_addr && cur >= end_addr) 126c08c320bSDavid Hildenbrand break; 127c08c320bSDavid Hildenbrand if (start_addr > end_addr && cur <= end_addr) 128c08c320bSDavid Hildenbrand break; 129c08c320bSDavid Hildenbrand install_page(pgtable, cur, __va(cur)); 130c08c320bSDavid Hildenbrand } 131c08c320bSDavid Hildenbrand } 132c08c320bSDavid Hildenbrand 133c08c320bSDavid Hildenbrand void *setup_mmu(phys_addr_t phys_end){ 134c08c320bSDavid Hildenbrand pgd_t *page_root; 135c08c320bSDavid Hildenbrand 136c08c320bSDavid Hildenbrand /* allocate a region-1 table */ 137c08c320bSDavid Hildenbrand page_root = pgd_alloc_one(); 138c08c320bSDavid Hildenbrand 139c08c320bSDavid Hildenbrand /* map all physical memory 1:1 */ 140c08c320bSDavid Hildenbrand setup_identity(page_root, 0, phys_end); 141c08c320bSDavid Hildenbrand 142c08c320bSDavid Hildenbrand /* generate 128MB of invalid adresses at the end (for testing PGM) */ 143c08c320bSDavid Hildenbrand init_alloc_vpage((void *) -(1UL << 27)); 144c08c320bSDavid Hildenbrand setup_identity(page_root, -(1UL << 27), 0); 145c08c320bSDavid Hildenbrand 146c08c320bSDavid Hildenbrand /* finally enable DAT with the new table */ 147c08c320bSDavid Hildenbrand mmu_enable(page_root); 148*49a732c7SJanosch Frank table_root = page_root; 149c08c320bSDavid Hildenbrand return page_root; 150c08c320bSDavid Hildenbrand } 151