xref: /netbsd/sys/arch/powerpc/powerpc/vm_machdep.c (revision 52325c80)
1 /*	$NetBSD: vm_machdep.c,v 1.105 2022/12/05 16:01:03 martin Exp $	*/
2 
3 /*
4  * Copyright (C) 1995, 1996 Wolfgang Solfrank.
5  * Copyright (C) 1995, 1996 TooLs GmbH.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by TooLs GmbH.
19  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.105 2022/12/05 16:01:03 martin Exp $");
36 
37 #ifdef _KERNEL_OPT
38 #include "opt_altivec.h"
39 #include "opt_ppcarch.h"
40 #include "opt_ppccache.h"
41 #endif
42 
43 #include <sys/param.h>
44 #include <sys/core.h>
45 #include <sys/exec.h>
46 #include <sys/proc.h>
47 #include <sys/systm.h>
48 #include <sys/vnode.h>
49 #include <sys/buf.h>
50 
51 #include <uvm/uvm.h>
52 
53 #if defined(ALTIVEC) || defined(PPC_HAVE_SPE)
54 #include <powerpc/altivec.h>
55 #endif
56 #include <machine/fpu.h>
57 #include <machine/pcb.h>
58 #include <machine/psl.h>
59 
60 #ifdef PPC_IBM4XX
61 vaddr_t vmaprange(struct proc *, vaddr_t, vsize_t, int);
62 void vunmaprange(vaddr_t, vsize_t);
63 #endif
64 
65 void cpu_lwp_bootstrap(void);
66 
67 /*
68  * Finish a fork operation, with execution context l2 nearly set up.
69  * Copy and update the pcb and trap frame, making the child ready to run.
70  *
71  * Rig the child's kernel stack so that it will have a switch frame which
72  * returns to cpu_lwp_bootstrap() which will call child_return() with l2
73  * as its argument.  This causes the newly-created child process to go
74  * directly to user level with an apparent return value of 0 from
75  * fork(), while the parent process returns normally.
76  *
77  * l1 is the execution context being forked; if l1 == &lwp0, we are creating
78  * a kernel thread, and the return path and argument are specified with
79  * `func' and `arg'.
80  *
81  * If an alternate user-level stack is requested (with non-zero values
82  * in both the stack and stacksize args), set up the user stack pointer
83  * accordingly.
84  */
85 void
cpu_lwp_fork(struct lwp * l1,struct lwp * l2,void * stack,size_t stacksize,void (* func)(void *),void * arg)86 cpu_lwp_fork(struct lwp *l1, struct lwp *l2, void *stack, size_t stacksize,
87 	void (*func)(void *), void *arg)
88 {
89 
90 	/*
91 	 * If l1 != curlwp && l1 == &lwp0, we're creating a kernel thread.
92 	 */
93 	KASSERT(l1 == curlwp || l1 == &lwp0);
94 
95 	struct pcb * const pcb1 = lwp_getpcb(l1);
96 	struct pcb * const pcb2 = lwp_getpcb(l2);
97 
98 	/* Set up user trapframe pointer. */
99 	l2->l_md.md_utf = trapframe(l2);
100 
101 	/* Copy PCB. */
102 	*pcb2 = *pcb1;
103 
104 	pcb2->pcb_pm = l2->l_proc->p_vmspace->vm_map.pmap;
105 
106 	/*
107 	 * Setup the trap frame for the new process
108 	 */
109 	*l2->l_md.md_utf = *l1->l_md.md_utf;
110 
111 	/*
112 	 * If specified, give the child a different stack.  Make sure to
113 	 * reserve enough at the top to store the previous LR.
114 	 */
115 	if (stack != NULL) {
116 		l2->l_md.md_utf->tf_fixreg[1] =
117 		    ((register_t)stack + stacksize - STACK_ALIGNBYTES)
118 			& ~STACK_ALIGNBYTES;
119 	}
120 
121 	/*
122 	 * Now deal setting up the initial function and its argument.
123 	 */
124 	struct ktrapframe * const ktf = ktrapframe(l2);
125 	struct callframe * const cf = ((struct callframe *)ktf) - 1;
126 	struct switchframe * const sf = ((struct switchframe *)cf) - 1;
127 
128 	/*
129 	 * Align stack pointer
130 	 * struct ktrapframe has a partial callframe (sp & lr)
131 	 * followed by a real trapframe.  The partial callframe
132 	 * is for the callee to store LR.  The SP isn't really used
133 	 * since trap/syscall will use the SP in the trapframe.
134 	 * There happens to be a partial callframe in front of the
135 	 * trapframe, too.
136 	 */
137 	ktf->ktf_lr = (register_t) cpu_lwp_bootstrap;
138 	ktf->ktf_sp = (register_t) (ktf + 1);		/* just in case */
139 
140 	cf->cf_sp = (register_t) ktf;
141 	cf->cf_r31 = (register_t) func;
142 	cf->cf_r30 = (register_t) arg;
143 
144 	memset((void *)sf, 0, sizeof *sf);		/* just in case */
145 	sf->sf_sp = (register_t) cf;
146 #if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
147 	sf->sf_user_sr = pmap_kernel()->pm_sr[USER_SR]; /* again, just in case */
148 #endif
149 	pcb2->pcb_sp = (register_t)sf;
150 	pcb2->pcb_kmapsr = 0;
151 	pcb2->pcb_umapsr = 0;
152 #ifdef PPC_HAVE_FPU
153 	pcb2->pcb_flags = PSL_FE_DFLT;
154 #endif
155 #ifdef CACHE_PROTO_MEI
156 	{
157 		paddr_t pa;
158 		int dcache_line_size, i;
159 
160 		/* Flush on cache values for other cpu. */
161 
162 		dcache_line_size = curcpu()->ci_ci.dcache_line_size;
163 		pa = vtophys((vaddr_t)sf);
164 		for (i = 0; i < SFRAMELEN + CALLFRAMELEN + FRAMELEN;
165 		    i += dcache_line_size) {
166 			__asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
167 			pa += dcache_line_size;
168 		}
169 		__asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
170 		pa = vtophys((vaddr_t)pcb2->pcb_pm);
171 		for (i = 0; i < sizeof(*pcb2->pcb_pm); i += dcache_line_size) {
172 			__asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
173 			pa += dcache_line_size;
174 		}
175 		__asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
176 		pa = vtophys((vaddr_t)pcb2);
177 		for (i = 0; i < sizeof(*pcb2); i += dcache_line_size) {
178 			__asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
179 			pa += dcache_line_size;
180 		}
181 		__asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
182 
183 		/* Need more flush? */
184 	}
185 #endif
186 }
187 
188 void
cpu_lwp_free(struct lwp * l,int proc)189 cpu_lwp_free(struct lwp *l, int proc)
190 {
191 
192 	(void)l;
193 }
194 
195 void
cpu_lwp_free2(struct lwp * l)196 cpu_lwp_free2(struct lwp *l)
197 {
198 
199 	(void)l;
200 }
201 
202 #ifdef PPC_IBM4XX
203 /*
204  * Map a range of user addresses into the kernel.
205  */
206 vaddr_t
vmaprange(struct proc * p,vaddr_t uaddr,vsize_t len,int prot)207 vmaprange(struct proc *p, vaddr_t uaddr, vsize_t len, int prot)
208 {
209 	vaddr_t faddr, taddr, kaddr;
210 	vsize_t off;
211 	paddr_t pa;
212 
213 	faddr = trunc_page(uaddr);
214 	off = uaddr - faddr;
215 	len = round_page(off + len);
216 	taddr = uvm_km_alloc(phys_map, len, 0, UVM_KMF_VAONLY | UVM_KMF_WAITVA);
217 	kaddr = taddr + off;
218 	for (; len > 0; len -= PAGE_SIZE) {
219 		(void) pmap_extract(vm_map_pmap(&p->p_vmspace->vm_map),
220 		    faddr, &pa);
221 		pmap_kenter_pa(taddr, pa, prot, 0);
222 		faddr += PAGE_SIZE;
223 		taddr += PAGE_SIZE;
224 	}
225 	return (kaddr);
226 }
227 
228 /*
229  * Undo vmaprange.
230  */
231 void
vunmaprange(vaddr_t kaddr,vsize_t len)232 vunmaprange(vaddr_t kaddr, vsize_t len)
233 {
234 	vaddr_t addr;
235 	vsize_t off;
236 
237 	addr = trunc_page(kaddr);
238 	off = kaddr - addr;
239 	len = round_page(off + len);
240 	pmap_kremove(addr, len);
241 	uvm_km_free(phys_map, addr, len, UVM_KMF_VAONLY);
242 }
243 #endif /* PPC_IBM4XX */
244 
245 /*
246  * Map a user I/O request into kernel virtual address space.
247  * Note: these pages have already been locked by uvm_vslock.
248  */
249 int
vmapbuf(struct buf * bp,vsize_t len)250 vmapbuf(struct buf *bp, vsize_t len)
251 {
252 	vaddr_t faddr, taddr;
253 	vsize_t off;
254 	paddr_t pa;
255 	int prot = VM_PROT_READ | ((bp->b_flags & B_READ) ? VM_PROT_WRITE : 0);
256 
257 #ifdef	DIAGNOSTIC
258 	if (!(bp->b_flags & B_PHYS))
259 		panic("vmapbuf");
260 #endif
261 	/*
262 	 * XXX Reimplement this with vmaprange (on at least PPC_IBM4XX CPUs).
263 	 */
264 	bp->b_saveaddr = bp->b_data;
265 	faddr = trunc_page((vaddr_t)bp->b_saveaddr);
266 	off = (vaddr_t)bp->b_data - faddr;
267 	len = round_page(off + len);
268 	taddr = uvm_km_alloc(phys_map, len, 0, UVM_KMF_VAONLY | UVM_KMF_WAITVA);
269 	bp->b_data = (void *)(taddr + off);
270 	for (; len > 0; len -= PAGE_SIZE) {
271 		(void) pmap_extract(vm_map_pmap(&bp->b_proc->p_vmspace->vm_map),
272 		    faddr, &pa);
273 		/*
274 		 * Use pmap_enter so the referenced and modified bits are
275 		 * appropriately set.
276 		 */
277 		pmap_kenter_pa(taddr, pa, prot, 0);
278 		faddr += PAGE_SIZE;
279 		taddr += PAGE_SIZE;
280 	}
281 	pmap_update(pmap_kernel());
282 
283 	return 0;
284 }
285 
286 /*
287  * Unmap a previously-mapped user I/O request.
288  */
289 void
vunmapbuf(struct buf * bp,vsize_t len)290 vunmapbuf(struct buf *bp, vsize_t len)
291 {
292 	vaddr_t addr;
293 	vsize_t off;
294 
295 #ifdef	DIAGNOSTIC
296 	if (!(bp->b_flags & B_PHYS))
297 		panic("vunmapbuf");
298 #endif
299 	addr = trunc_page((vaddr_t)bp->b_data);
300 	off = (vaddr_t)bp->b_data - addr;
301 	len = round_page(off + len);
302 	/*
303 	 * Since the pages were entered by pmap_enter, use pmap_remove
304 	 * to remove them.
305 	 */
306 	pmap_kremove(addr, len);
307 	pmap_update(pmap_kernel());
308 	uvm_km_free(phys_map, addr, len, UVM_KMF_VAONLY);
309 	bp->b_data = bp->b_saveaddr;
310 	bp->b_saveaddr = 0;
311 }
312 
313 #ifdef __HAVE_CPU_UAREA_ROUTINES
314 void *
cpu_uarea_alloc(bool system)315 cpu_uarea_alloc(bool system)
316 {
317 #ifdef PMAP_MAP_POOLPAGE
318 	struct pglist pglist;
319 	int error;
320 
321 	/*
322 	 * Allocate a new physically contiguous uarea which can be
323 	 * direct-mapped.
324 	 */
325 	error = uvm_pglistalloc(USPACE, 0, ~0UL, 0, 0, &pglist, 1, 1);
326 	if (error) {
327 		return NULL;
328 	}
329 
330 	/*
331 	 * Get the physical address from the first page.
332 	 */
333 	const struct vm_page * const pg = TAILQ_FIRST(&pglist);
334 	KASSERT(pg != NULL);
335 	const paddr_t pa = VM_PAGE_TO_PHYS(pg);
336 
337 	/*
338 	 * We need to return a direct-mapped VA for the pa.
339 	 */
340 
341 	return (void *)(uintptr_t)PMAP_MAP_POOLPAGE(pa);
342 #else
343 	return NULL;
344 #endif
345 }
346 
347 /*
348  * Return true if we freed it, false if we didn't.
349  */
350 bool
cpu_uarea_free(void * vva)351 cpu_uarea_free(void *vva)
352 {
353 #ifdef PMAP_UNMAP_POOLPAGE
354 	vaddr_t va = (vaddr_t) vva;
355 	if (va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS)
356 		return false;
357 
358 	/*
359 	 * Since the pages are physically contiguous, the vm_page structure
360 	 * will be as well.
361 	 */
362 	struct vm_page *pg = PHYS_TO_VM_PAGE(PMAP_UNMAP_POOLPAGE(va));
363 	KASSERT(pg != NULL);
364 	for (size_t i = 0; i < UPAGES; i++, pg++) {
365 		uvm_pagefree(pg);
366 	}
367 	return true;
368 #else
369 	return false;
370 #endif
371 }
372 #endif /* __HAVE_CPU_UAREA_ROUTINES */
373