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