xref: /kvm-unit-tests/lib/ppc64/mmu.c (revision b9289d76b946dc45c5221e142c5473ebfff1ca86)
1d4c8e725SNicholas Piggin // SPDX-License-Identifier: GPL-2.0-only
2d4c8e725SNicholas Piggin /*
3d4c8e725SNicholas Piggin  * Radix MMU support
4d4c8e725SNicholas Piggin  *
5d4c8e725SNicholas Piggin  * Copyright (C) 2024, IBM Inc, Nicholas Piggin <npiggin@gmail.com>
6d4c8e725SNicholas Piggin  *
7d4c8e725SNicholas Piggin  * Derived from Linux kernel MMU code.
8d4c8e725SNicholas Piggin  */
9d4c8e725SNicholas Piggin #include <asm/mmu.h>
10d4c8e725SNicholas Piggin #include <asm/setup.h>
11d4c8e725SNicholas Piggin #include <asm/smp.h>
12d4c8e725SNicholas Piggin #include <asm/page.h>
13d4c8e725SNicholas Piggin #include <asm/io.h>
14d4c8e725SNicholas Piggin #include <asm/processor.h>
15d4c8e725SNicholas Piggin #include <asm/hcall.h>
16d4c8e725SNicholas Piggin 
17d4c8e725SNicholas Piggin #include "alloc_page.h"
18d4c8e725SNicholas Piggin #include "vmalloc.h"
19d4c8e725SNicholas Piggin #include <asm/pgtable-hwdef.h>
20d4c8e725SNicholas Piggin #include <asm/pgtable.h>
21d4c8e725SNicholas Piggin 
22d4c8e725SNicholas Piggin #include <linux/compiler.h>
23d4c8e725SNicholas Piggin 
24d4c8e725SNicholas Piggin static pgd_t *identity_pgd;
25d4c8e725SNicholas Piggin 
26*b9289d76SNicholas Piggin bool vm_available(void) /* weak override */
27d4c8e725SNicholas Piggin {
28d4c8e725SNicholas Piggin 	return cpu_has_radix;
29d4c8e725SNicholas Piggin }
30d4c8e725SNicholas Piggin 
31d4c8e725SNicholas Piggin bool mmu_enabled(void)
32d4c8e725SNicholas Piggin {
33d4c8e725SNicholas Piggin 	return current_cpu()->pgtable != NULL;
34d4c8e725SNicholas Piggin }
35d4c8e725SNicholas Piggin 
36d4c8e725SNicholas Piggin void mmu_enable(pgd_t *pgtable)
37d4c8e725SNicholas Piggin {
38d4c8e725SNicholas Piggin 	struct cpu *cpu = current_cpu();
39d4c8e725SNicholas Piggin 
40d4c8e725SNicholas Piggin 	if (!pgtable)
41d4c8e725SNicholas Piggin 		pgtable = identity_pgd;
42d4c8e725SNicholas Piggin 
43d4c8e725SNicholas Piggin 	cpu->pgtable = pgtable;
44d4c8e725SNicholas Piggin 
45d4c8e725SNicholas Piggin 	mtmsr(mfmsr() | (MSR_IR|MSR_DR));
46d4c8e725SNicholas Piggin }
47d4c8e725SNicholas Piggin 
48d4c8e725SNicholas Piggin void mmu_disable(void)
49d4c8e725SNicholas Piggin {
50d4c8e725SNicholas Piggin 	struct cpu *cpu = current_cpu();
51d4c8e725SNicholas Piggin 
52d4c8e725SNicholas Piggin 	cpu->pgtable = NULL;
53d4c8e725SNicholas Piggin 
54d4c8e725SNicholas Piggin 	mtmsr(mfmsr() & ~(MSR_IR|MSR_DR));
55d4c8e725SNicholas Piggin }
56d4c8e725SNicholas Piggin 
57d4c8e725SNicholas Piggin static pteval_t *get_pte(pgd_t *pgtable, uintptr_t vaddr)
58d4c8e725SNicholas Piggin {
59d4c8e725SNicholas Piggin 	pgd_t *pgd = pgd_offset(pgtable, vaddr);
60d4c8e725SNicholas Piggin 	pud_t *pud = pud_alloc(pgd, vaddr);
61d4c8e725SNicholas Piggin 	pmd_t *pmd = pmd_alloc(pud, vaddr);
62d4c8e725SNicholas Piggin 	pte_t *pte = pte_alloc(pmd, vaddr);
63d4c8e725SNicholas Piggin 
64d4c8e725SNicholas Piggin 	return &pte_val(*pte);
65d4c8e725SNicholas Piggin }
66d4c8e725SNicholas Piggin 
67d4c8e725SNicholas Piggin static pteval_t *install_pte(pgd_t *pgtable, uintptr_t vaddr, pteval_t pte)
68d4c8e725SNicholas Piggin {
69d4c8e725SNicholas Piggin 	pteval_t *p_pte = get_pte(pgtable, vaddr);
70d4c8e725SNicholas Piggin 
71d4c8e725SNicholas Piggin 	if (READ_ONCE(*p_pte) & cpu_to_be64(_PAGE_VALID)) {
72d4c8e725SNicholas Piggin 		WRITE_ONCE(*p_pte, 0);
73d4c8e725SNicholas Piggin 		flush_tlb_page(vaddr);
74d4c8e725SNicholas Piggin 	}
75d4c8e725SNicholas Piggin 
76d4c8e725SNicholas Piggin 	WRITE_ONCE(*p_pte, cpu_to_be64(pte));
77d4c8e725SNicholas Piggin 
78d4c8e725SNicholas Piggin 	return p_pte;
79d4c8e725SNicholas Piggin }
80d4c8e725SNicholas Piggin 
81d4c8e725SNicholas Piggin static pteval_t *install_page_prot(pgd_t *pgtable, phys_addr_t phys,
82d4c8e725SNicholas Piggin 				   uintptr_t vaddr, pgprot_t prot)
83d4c8e725SNicholas Piggin {
84d4c8e725SNicholas Piggin 	pteval_t pte = phys;
85d4c8e725SNicholas Piggin 	pte |= _PAGE_VALID | _PAGE_PTE;
86d4c8e725SNicholas Piggin 	pte |= pgprot_val(prot);
87d4c8e725SNicholas Piggin 	return install_pte(pgtable, vaddr, pte);
88d4c8e725SNicholas Piggin }
89d4c8e725SNicholas Piggin 
90d4c8e725SNicholas Piggin pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *virt)
91d4c8e725SNicholas Piggin {
92d4c8e725SNicholas Piggin 	if (!pgtable)
93d4c8e725SNicholas Piggin 		pgtable = identity_pgd;
94d4c8e725SNicholas Piggin 
95d4c8e725SNicholas Piggin 	return install_page_prot(pgtable, phys, (uintptr_t)virt,
96d4c8e725SNicholas Piggin 				 __pgprot(_PAGE_VALID | _PAGE_PTE |
97d4c8e725SNicholas Piggin 					  _PAGE_READ | _PAGE_WRITE |
98d4c8e725SNicholas Piggin 					  _PAGE_EXEC | _PAGE_ACCESSED |
99d4c8e725SNicholas Piggin 					  _PAGE_DIRTY));
100d4c8e725SNicholas Piggin }
101d4c8e725SNicholas Piggin 
102d4c8e725SNicholas Piggin static pteval_t *follow_pte(pgd_t *pgtable, uintptr_t vaddr)
103d4c8e725SNicholas Piggin {
104d4c8e725SNicholas Piggin 	pgd_t *pgd;
105d4c8e725SNicholas Piggin 	pud_t *pud;
106d4c8e725SNicholas Piggin 	pmd_t *pmd;
107d4c8e725SNicholas Piggin 	pte_t *pte;
108d4c8e725SNicholas Piggin 
109d4c8e725SNicholas Piggin 	pgd = pgd_offset(pgtable, vaddr);
110d4c8e725SNicholas Piggin 	if (!pgd_valid(*pgd))
111d4c8e725SNicholas Piggin 		return NULL;
112d4c8e725SNicholas Piggin 
113d4c8e725SNicholas Piggin 	pud = pud_offset(pgd, vaddr);
114d4c8e725SNicholas Piggin 	if (!pud_valid(*pud))
115d4c8e725SNicholas Piggin 		return NULL;
116d4c8e725SNicholas Piggin 
117d4c8e725SNicholas Piggin 	pmd = pmd_offset(pud, vaddr);
118d4c8e725SNicholas Piggin 	if (!pmd_valid(*pmd))
119d4c8e725SNicholas Piggin 		return NULL;
120d4c8e725SNicholas Piggin 	if (pmd_huge(*pmd))
121d4c8e725SNicholas Piggin 		return &pmd_val(*pmd);
122d4c8e725SNicholas Piggin 
123d4c8e725SNicholas Piggin 	pte = pte_offset(pmd, vaddr);
124d4c8e725SNicholas Piggin 	if (!pte_valid(*pte))
125d4c8e725SNicholas Piggin 		return NULL;
126d4c8e725SNicholas Piggin 
127d4c8e725SNicholas Piggin 	return &pte_val(*pte);
128d4c8e725SNicholas Piggin }
129d4c8e725SNicholas Piggin 
130d4c8e725SNicholas Piggin phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *virt)
131d4c8e725SNicholas Piggin {
132d4c8e725SNicholas Piggin 	phys_addr_t mask;
133d4c8e725SNicholas Piggin 	pteval_t *pteval;
134d4c8e725SNicholas Piggin 
135d4c8e725SNicholas Piggin 	if (!pgtable)
136d4c8e725SNicholas Piggin 		pgtable = identity_pgd;
137d4c8e725SNicholas Piggin 
138d4c8e725SNicholas Piggin 	pteval = follow_pte(pgtable, (uintptr_t)virt);
139d4c8e725SNicholas Piggin 	if (!pteval) {
140d4c8e725SNicholas Piggin 		install_page(pgtable, (phys_addr_t)(unsigned long)virt, virt);
141d4c8e725SNicholas Piggin 		return (phys_addr_t)(unsigned long)virt;
142d4c8e725SNicholas Piggin 	}
143d4c8e725SNicholas Piggin 
144d4c8e725SNicholas Piggin 	if (pmd_huge(__pmd(*pteval)))
145d4c8e725SNicholas Piggin 		mask = PMD_MASK;
146d4c8e725SNicholas Piggin 	else
147d4c8e725SNicholas Piggin 		mask = PAGE_MASK;
148d4c8e725SNicholas Piggin 
149d4c8e725SNicholas Piggin 	return (be64_to_cpu(*pteval) & PHYS_MASK & mask) |
150d4c8e725SNicholas Piggin 		((phys_addr_t)(unsigned long)virt & ~mask);
151d4c8e725SNicholas Piggin }
152d4c8e725SNicholas Piggin 
153d4c8e725SNicholas Piggin struct partition_table_entry {
154d4c8e725SNicholas Piggin 	uint64_t dw0;
155d4c8e725SNicholas Piggin 	uint64_t dw1;
156d4c8e725SNicholas Piggin };
157d4c8e725SNicholas Piggin 
158d4c8e725SNicholas Piggin static struct partition_table_entry *partition_table;
159d4c8e725SNicholas Piggin 
160d4c8e725SNicholas Piggin struct process_table_entry {
161d4c8e725SNicholas Piggin 	uint64_t dw0;
162d4c8e725SNicholas Piggin 	uint64_t dw1;
163d4c8e725SNicholas Piggin };
164d4c8e725SNicholas Piggin 
165d4c8e725SNicholas Piggin static struct process_table_entry *process_table;
166d4c8e725SNicholas Piggin 
167d4c8e725SNicholas Piggin void *setup_mmu(phys_addr_t phys_end, void *unused)
168d4c8e725SNicholas Piggin {
169d4c8e725SNicholas Piggin 	phys_addr_t addr;
170d4c8e725SNicholas Piggin 	uint64_t dw0, dw1;
171d4c8e725SNicholas Piggin 
172d4c8e725SNicholas Piggin 	if (identity_pgd)
173d4c8e725SNicholas Piggin 		goto enable;
174d4c8e725SNicholas Piggin 
175d4c8e725SNicholas Piggin 	assert_msg(cpu_has_radix, "MMU support requires radix MMU.");
176d4c8e725SNicholas Piggin 
177d4c8e725SNicholas Piggin 	/* 32G address is reserved for vmalloc, cap phys_end at 31G */
178d4c8e725SNicholas Piggin 	if (phys_end > (31ul << 30)) {
179d4c8e725SNicholas Piggin 		/* print warning */
180d4c8e725SNicholas Piggin 		phys_end = 31ul << 30;
181d4c8e725SNicholas Piggin 	}
182d4c8e725SNicholas Piggin 
183d4c8e725SNicholas Piggin 	init_alloc_vpage((void *)(32ul << 30));
184d4c8e725SNicholas Piggin 
185d4c8e725SNicholas Piggin 	process_table = memalign_pages(SZ_4K, SZ_4K);
186d4c8e725SNicholas Piggin 	memset(process_table, 0, SZ_4K);
187d4c8e725SNicholas Piggin 
188d4c8e725SNicholas Piggin 	identity_pgd = pgd_alloc_one();
189d4c8e725SNicholas Piggin 
190d4c8e725SNicholas Piggin 	dw0 = (unsigned long)identity_pgd;
191d4c8e725SNicholas Piggin 	dw0 |= 16UL - 3; /* 64K pgd size */
192d4c8e725SNicholas Piggin 	dw0 |= (0x2UL << 61) | (0x5UL << 5); /* 52-bit virt */
193d4c8e725SNicholas Piggin 	process_table[1].dw0 = cpu_to_be64(dw0);
194d4c8e725SNicholas Piggin 
195d4c8e725SNicholas Piggin 	if (machine_is_pseries()) {
196d4c8e725SNicholas Piggin 		int ret;
197d4c8e725SNicholas Piggin 
198d4c8e725SNicholas Piggin 		ret = hcall(H_REGISTER_PROCESS_TABLE, PTBL_NEW | PTBL_RADIX | PTBL_GTSE, process_table, 0, 0 /* 4K size */);
199d4c8e725SNicholas Piggin 		assert_msg(!ret, "H_REGISTER_PROCESS_TABLE failed! err=%d\n", ret);
200d4c8e725SNicholas Piggin 	} else if (machine_is_powernv()) {
201d4c8e725SNicholas Piggin 		partition_table = memalign_pages(SZ_4K, SZ_4K);
202d4c8e725SNicholas Piggin 		memset(partition_table, 0, SZ_4K);
203d4c8e725SNicholas Piggin 
204d4c8e725SNicholas Piggin 		/* Reuse dw0 for partition table */
205d4c8e725SNicholas Piggin 		dw0 |= 1ULL << 63; /* Host radix */
206d4c8e725SNicholas Piggin 		dw1 = (unsigned long)process_table; /* 4K size */
207d4c8e725SNicholas Piggin 		partition_table[0].dw0 = cpu_to_be64(dw0);
208d4c8e725SNicholas Piggin 		partition_table[0].dw1 = cpu_to_be64(dw1);
209d4c8e725SNicholas Piggin 
210d4c8e725SNicholas Piggin 	} else {
211d4c8e725SNicholas Piggin 		/* Only pseries and powernv support radix so far */
212d4c8e725SNicholas Piggin 		assert(0);
213d4c8e725SNicholas Piggin 	}
214d4c8e725SNicholas Piggin 
215d4c8e725SNicholas Piggin 	/*
216d4c8e725SNicholas Piggin 	 * Avoid mapping page 0 so NULL dereferences fault. Test images
217d4c8e725SNicholas Piggin 	 * run relocated well above 0, so nothing useful here except
218d4c8e725SNicholas Piggin 	 * real-mode interrupt entry code.
219d4c8e725SNicholas Piggin 	 */
220d4c8e725SNicholas Piggin 	for (addr = PAGE_SIZE; addr < phys_end; addr += PAGE_SIZE)
221d4c8e725SNicholas Piggin 		install_page(identity_pgd, addr, __va(addr));
222d4c8e725SNicholas Piggin 
223d4c8e725SNicholas Piggin enable:
224d4c8e725SNicholas Piggin 	if (machine_is_powernv()) {
225d4c8e725SNicholas Piggin 		mtspr(SPR_PTCR, (unsigned long)partition_table); /* 4KB size */
226d4c8e725SNicholas Piggin 
227d4c8e725SNicholas Piggin 		mtspr(SPR_LPIDR, 0);
228d4c8e725SNicholas Piggin 		/* Set LPCR[UPRT] and LPCR[HR] for radix */
229d4c8e725SNicholas Piggin 		mtspr(SPR_LPCR, mfspr(SPR_LPCR) | (1ULL << 22) | (1ULL << 20));
230d4c8e725SNicholas Piggin 	}
231d4c8e725SNicholas Piggin 
232d4c8e725SNicholas Piggin 	/* PID=1 is used because PID=0 is also mapped in quadrant 3 */
233d4c8e725SNicholas Piggin 	mtspr(SPR_PIDR, 1);
234d4c8e725SNicholas Piggin 
235d4c8e725SNicholas Piggin 	mmu_enable(identity_pgd);
236d4c8e725SNicholas Piggin 
237d4c8e725SNicholas Piggin 	return identity_pgd;
238d4c8e725SNicholas Piggin }
239d4c8e725SNicholas Piggin 
240d4c8e725SNicholas Piggin phys_addr_t __virt_to_phys(unsigned long addr)
241d4c8e725SNicholas Piggin {
242d4c8e725SNicholas Piggin 	if (mmu_enabled()) {
243d4c8e725SNicholas Piggin 		pgd_t *pgtable = current_cpu()->pgtable;
244d4c8e725SNicholas Piggin 		return virt_to_pte_phys(pgtable, (void *)addr);
245d4c8e725SNicholas Piggin 	}
246d4c8e725SNicholas Piggin 	return addr;
247d4c8e725SNicholas Piggin }
248d4c8e725SNicholas Piggin 
249d4c8e725SNicholas Piggin unsigned long __phys_to_virt(phys_addr_t addr)
250d4c8e725SNicholas Piggin {
251d4c8e725SNicholas Piggin 	/*
252d4c8e725SNicholas Piggin 	 * We don't guarantee that phys_to_virt(virt_to_phys(vaddr)) == vaddr, but
253d4c8e725SNicholas Piggin 	 * the default page tables do identity map all physical addresses, which
254d4c8e725SNicholas Piggin 	 * means phys_to_virt(virt_to_phys((void *)paddr)) == paddr.
255d4c8e725SNicholas Piggin 	 */
256d4c8e725SNicholas Piggin 	assert(!mmu_enabled() || __virt_to_phys(addr) == addr);
257d4c8e725SNicholas Piggin 	return addr;
258d4c8e725SNicholas Piggin }
259