xref: /kvm-unit-tests/lib/s390x/mmu.c (revision 49a732c7249d2493ba9a2f0e25d63048eb6a7bae)
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