xref: /freebsd/sys/powerpc/ps3/mmu_ps3.c (revision 734e82fe33aa764367791a7d603b383996c6b40b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 2010 Nathan Whitehorn
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/ktr.h>
32 #include <sys/lock.h>
33 #include <sys/msgbuf.h>
34 #include <sys/mutex.h>
35 #include <sys/proc.h>
36 #include <sys/sysctl.h>
37 #include <sys/systm.h>
38 #include <sys/vmmeter.h>
39 
40 #include <vm/vm.h>
41 #include <vm/vm_param.h>
42 #include <vm/vm_kern.h>
43 #include <vm/vm_page.h>
44 #include <vm/vm_map.h>
45 #include <vm/vm_object.h>
46 #include <vm/vm_extern.h>
47 #include <vm/vm_pageout.h>
48 #include <vm/uma.h>
49 
50 #include <powerpc/aim/mmu_oea64.h>
51 
52 #include "ps3-hvcall.h"
53 
54 #define VSID_HASH_MASK		0x0000007fffffffffUL
55 #define PTESYNC()		__asm __volatile("ptesync")
56 
57 extern int ps3fb_remap(void);
58 
59 static uint64_t mps3_vas_id;
60 
61 /*
62  * Kernel MMU interface
63  */
64 
65 static void	mps3_install(void);
66 static void	mps3_bootstrap(vm_offset_t kernelstart,
67 		    vm_offset_t kernelend);
68 static void	mps3_cpu_bootstrap(int ap);
69 static int64_t	mps3_pte_synch(struct pvo_entry *);
70 static int64_t	mps3_pte_clear(struct pvo_entry *, uint64_t ptebit);
71 static int64_t	mps3_pte_unset(struct pvo_entry *);
72 static int64_t	mps3_pte_insert(struct pvo_entry *);
73 
74 static struct pmap_funcs mps3_methods = {
75 	.install = mps3_install,
76         .bootstrap = mps3_bootstrap,
77         .cpu_bootstrap = mps3_cpu_bootstrap,
78 };
79 
80 static struct moea64_funcs mps3_funcs = {
81 	.pte_synch = mps3_pte_synch,
82 	.pte_clear = mps3_pte_clear,
83 	.pte_unset = mps3_pte_unset,
84 	.pte_insert = mps3_pte_insert,
85 };
86 
87 MMU_DEF_INHERIT(ps3_mmu, "mmu_ps3", mps3_methods, oea64_mmu);
88 
89 static struct mtx mps3_table_lock;
90 
91 static void
92 mps3_install(void)
93 {
94 	moea64_ops = &mps3_funcs;
95 	moea64_install();
96 }
97 
98 static void
99 mps3_bootstrap(vm_offset_t kernelstart, vm_offset_t kernelend)
100 {
101 	uint64_t final_pteg_count;
102 
103 	mtx_init(&mps3_table_lock, "page table", NULL, MTX_DEF);
104 
105 	moea64_early_bootstrap(kernelstart, kernelend);
106 
107 	/* In case we had a page table already */
108 	lv1_destruct_virtual_address_space(0);
109 
110 	/* Allocate new hardware page table */
111 	lv1_construct_virtual_address_space(
112 	    20 /* log_2(moea64_pteg_count) */, 2 /* n page sizes */,
113 	    (24UL << 56) | (16UL << 48) /* page sizes 16 MB + 64 KB */,
114 	    &mps3_vas_id, &final_pteg_count
115 	);
116 
117 	lv1_select_virtual_address_space(mps3_vas_id);
118 
119 	moea64_pteg_count = final_pteg_count / sizeof(struct lpteg);
120 
121 	moea64_mid_bootstrap(kernelstart, kernelend);
122 	moea64_late_bootstrap(kernelstart, kernelend);
123 }
124 
125 static void
126 mps3_cpu_bootstrap(int ap)
127 {
128 	struct slb *slb = PCPU_GET(aim.slb);
129 	register_t seg0;
130 	int i;
131 
132 	mtmsr(mfmsr() & ~PSL_DR & ~PSL_IR);
133 
134 	/*
135 	 * Select the page table we configured above and set up the FB mapping
136 	 * so we can have a console.
137 	 */
138 	lv1_select_virtual_address_space(mps3_vas_id);
139 
140 	if (!ap)
141 		ps3fb_remap();
142 
143 	/*
144 	 * Install kernel SLB entries
145 	 */
146 
147         __asm __volatile ("slbia");
148         __asm __volatile ("slbmfee %0,%1; slbie %0;" : "=r"(seg0) : "r"(0));
149 	for (i = 0; i < 64; i++) {
150 		if (!(slb[i].slbe & SLBE_VALID))
151 			continue;
152 
153 		__asm __volatile ("slbmte %0, %1" ::
154 		    "r"(slb[i].slbv), "r"(slb[i].slbe));
155 	}
156 }
157 
158 static int64_t
159 mps3_pte_synch_locked(struct pvo_entry *pvo)
160 {
161 	uint64_t halfbucket[4], rcbits;
162 
163 	PTESYNC();
164 	lv1_read_htab_entries(mps3_vas_id, pvo->pvo_pte.slot & ~0x3UL,
165 	    &halfbucket[0], &halfbucket[1], &halfbucket[2], &halfbucket[3],
166 	    &rcbits);
167 
168 	/* Check if present in page table */
169 	if ((halfbucket[pvo->pvo_pte.slot & 0x3] & LPTE_AVPN_MASK) !=
170 	    ((pvo->pvo_vpn >> (ADDR_API_SHFT64 - ADDR_PIDX_SHFT)) &
171 	    LPTE_AVPN_MASK))
172 		return (-1);
173 	if (!(halfbucket[pvo->pvo_pte.slot & 0x3] & LPTE_VALID))
174 		return (-1);
175 
176 	/*
177 	 * rcbits contains the low 12 bits of each PTE's 2nd part,
178 	 * spaced at 16-bit intervals
179 	 */
180 
181 	return ((rcbits >> ((3 - (pvo->pvo_pte.slot & 0x3))*16)) &
182 	    (LPTE_CHG | LPTE_REF));
183 }
184 
185 static int64_t
186 mps3_pte_synch(struct pvo_entry *pvo)
187 {
188 	int64_t retval;
189 
190 	mtx_lock(&mps3_table_lock);
191 	retval = mps3_pte_synch_locked(pvo);
192 	mtx_unlock(&mps3_table_lock);
193 
194 	return (retval);
195 }
196 
197 static int64_t
198 mps3_pte_clear(struct pvo_entry *pvo, uint64_t ptebit)
199 {
200 	int64_t refchg;
201 	struct lpte pte;
202 
203 	mtx_lock(&mps3_table_lock);
204 
205 	refchg = mps3_pte_synch_locked(pvo);
206 	if (refchg < 0) {
207 		mtx_unlock(&mps3_table_lock);
208 		return (refchg);
209 	}
210 
211 	moea64_pte_from_pvo(pvo, &pte);
212 
213 	pte.pte_lo |= refchg;
214 	pte.pte_lo &= ~ptebit;
215 	/* XXX: race on RC bits between write and sync. Anything to do? */
216 	lv1_write_htab_entry(mps3_vas_id, pvo->pvo_pte.slot, pte.pte_hi,
217 	    pte.pte_lo);
218 	mtx_unlock(&mps3_table_lock);
219 
220 	return (refchg);
221 }
222 
223 static int64_t
224 mps3_pte_unset(struct pvo_entry *pvo)
225 {
226 	int64_t refchg;
227 
228 	mtx_lock(&mps3_table_lock);
229 	refchg = mps3_pte_synch_locked(pvo);
230 	if (refchg < 0) {
231 		STAT_MOEA64(moea64_pte_overflow--);
232 		mtx_unlock(&mps3_table_lock);
233 		return (-1);
234 	}
235 	/* XXX: race on RC bits between unset and sync. Anything to do? */
236 	lv1_write_htab_entry(mps3_vas_id, pvo->pvo_pte.slot, 0, 0);
237 	mtx_unlock(&mps3_table_lock);
238 	STAT_MOEA64(moea64_pte_valid--);
239 
240 	return (refchg & (LPTE_REF | LPTE_CHG));
241 }
242 
243 static int64_t
244 mps3_pte_insert(struct pvo_entry *pvo)
245 {
246 	int result;
247 	struct lpte pte, evicted;
248 	uint64_t index;
249 
250 	if (pvo->pvo_vaddr & PVO_HID) {
251 		/* Hypercall needs primary PTEG */
252 		pvo->pvo_vaddr &= ~PVO_HID;
253 		pvo->pvo_pte.slot ^= (moea64_pteg_mask << 3);
254 	}
255 
256 	pvo->pvo_pte.slot &= ~7UL;
257 	moea64_pte_from_pvo(pvo, &pte);
258 	evicted.pte_hi = 0;
259 	PTESYNC();
260 	mtx_lock(&mps3_table_lock);
261 	result = lv1_insert_htab_entry(mps3_vas_id, pvo->pvo_pte.slot,
262 	    pte.pte_hi, pte.pte_lo, LPTE_LOCKED | LPTE_WIRED, 0,
263 	    &index, &evicted.pte_hi, &evicted.pte_lo);
264 	mtx_unlock(&mps3_table_lock);
265 
266 	if (result != 0) {
267 		/* No freeable slots in either PTEG? We're hosed. */
268 		panic("mps3_pte_insert: overflow (%d)", result);
269 		return (-1);
270 	}
271 
272 	/*
273 	 * See where we ended up.
274 	 */
275 	if ((index & ~7UL) != pvo->pvo_pte.slot)
276 		pvo->pvo_vaddr |= PVO_HID;
277 	pvo->pvo_pte.slot = index;
278 
279 	STAT_MOEA64(moea64_pte_valid++);
280 
281 	if (evicted.pte_hi) {
282 		KASSERT((evicted.pte_hi & (LPTE_WIRED | LPTE_LOCKED)) == 0,
283 		    ("Evicted a wired PTE"));
284 		STAT_MOEA64(moea64_pte_valid--);
285 		STAT_MOEA64(moea64_pte_overflow++);
286 	}
287 
288 	return (0);
289 }
290