xref: /netbsd/sys/arch/powerpc/powerpc/vm_machdep.c (revision c4a72b64)
1 /*	$NetBSD: vm_machdep.c,v 1.43 2002/08/11 02:17:30 matt 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 "opt_altivec.h"
35 #include "opt_multiprocessor.h"
36 #include "opt_ppcarch.h"
37 
38 #include <sys/param.h>
39 #include <sys/core.h>
40 #include <sys/exec.h>
41 #include <sys/proc.h>
42 #include <sys/systm.h>
43 #include <sys/user.h>
44 #include <sys/vnode.h>
45 
46 #include <uvm/uvm_extern.h>
47 
48 #ifdef ALTIVEC
49 #include <powerpc/altivec.h>
50 #endif
51 #include <machine/fpu.h>
52 #include <machine/pcb.h>
53 
54 #ifdef PPC_IBM4XX
55 vaddr_t vmaprange(struct proc *, vaddr_t, vsize_t, int);
56 void vunmaprange(vaddr_t, vsize_t);
57 #endif
58 
59 /*
60  * Finish a fork operation, with process p2 nearly set up.
61  * Copy and update the pcb and trap frame, making the child ready to run.
62  *
63  * Rig the child's kernel stack so that it will start out in
64  * fork_trampoline() and call child_return() with p2 as an
65  * argument. This causes the newly-created child process to go
66  * directly to user level with an apparent return value of 0 from
67  * fork(), while the parent process returns normally.
68  *
69  * p1 is the process being forked; if p1 == &proc0, we are creating
70  * a kernel thread, and the return path and argument are specified with
71  * `func' and `arg'.
72  *
73  * If an alternate user-level stack is requested (with non-zero values
74  * in both the stack and stacksize args), set up the user stack pointer
75  * accordingly.
76  */
77 void
78 cpu_fork(p1, p2, stack, stacksize, func, arg)
79 	struct proc *p1, *p2;
80 	void *stack;
81 	size_t stacksize;
82 	void (*func)(void *);
83 	void *arg;
84 {
85 	struct trapframe *tf;
86 	struct callframe *cf;
87 	struct switchframe *sf;
88 	caddr_t stktop1, stktop2;
89 	void fork_trampoline(void);
90 	struct pcb *pcb = &p2->p_addr->u_pcb;
91 
92 #ifdef DIAGNOSTIC
93 	/*
94 	 * if p1 != curproc && p1 == &proc0, we're creating a kernel thread.
95 	 */
96 	if (p1 != curproc && p1 != &proc0)
97 		panic("cpu_fork: curproc");
98 #endif
99 
100 #ifdef PPC_HAVE_FPU
101 	if (p1->p_addr->u_pcb.pcb_fpcpu)
102 		save_fpu_proc(p1);
103 #endif
104 	*pcb = p1->p_addr->u_pcb;
105 #ifdef ALTIVEC
106 	if (p1->p_addr->u_pcb.pcb_vr != NULL) {
107 		save_vec_proc(p1);
108 		pcb->pcb_vr = pool_get(&vecpool, PR_WAITOK);
109 		*pcb->pcb_vr = *p1->p_addr->u_pcb.pcb_vr;
110 	}
111 #endif
112 
113 	pcb->pcb_pm = p2->p_vmspace->vm_map.pmap;
114 #ifndef OLDPMAP
115 	pcb->pcb_pmreal = pcb->pcb_pm;		/* XXX */
116 #else
117 	(void) pmap_extract(pmap_kernel(), (vaddr_t)pcb->pcb_pm,
118 	    (paddr_t *)&pcb->pcb_pmreal);
119 #endif
120 
121 	/*
122 	 * Setup the trap frame for the new process
123 	 */
124 	stktop1 = (caddr_t)trapframe(p1);
125 	stktop2 = (caddr_t)trapframe(p2);
126 	memcpy(stktop2, stktop1, sizeof(struct trapframe));
127 
128 	/*
129 	 * If specified, give the child a different stack.
130 	 */
131 	if (stack != NULL) {
132 		tf = trapframe(p2);
133 		tf->fixreg[1] = (register_t)stack + stacksize;
134 	}
135 
136 	stktop2 = (caddr_t)((u_long)stktop2 & ~15);	/* Align stack pointer */
137 
138 	/*
139 	 * There happens to be a callframe, too.
140 	 */
141 	cf = (struct callframe *)stktop2;
142 	cf->lr = (int)fork_trampoline;
143 
144 	/*
145 	 * Below the trap frame, there is another call frame:
146 	 */
147 	stktop2 -= 16;
148 	cf = (struct callframe *)stktop2;
149 	cf->r31 = (register_t)func;
150 	cf->r30 = (register_t)arg;
151 
152 	/*
153 	 * Below that, we allocate the switch frame:
154 	 */
155 	stktop2 -= roundup(sizeof *sf, 16);	/* must match SFRAMELEN in genassym */
156 	sf = (struct switchframe *)stktop2;
157 	memset((void *)sf, 0, sizeof *sf);		/* just in case */
158 	sf->sp = (int)cf;
159 #ifndef PPC_IBM4XX
160 	sf->user_sr = pmap_kernel()->pm_sr[USER_SR]; /* again, just in case */
161 #endif
162 	pcb->pcb_sp = (int)stktop2;
163 	pcb->pcb_spl = 0;
164 }
165 
166 void
167 cpu_swapin(p)
168 	struct proc *p;
169 {
170 	struct pcb *pcb = &p->p_addr->u_pcb;
171 
172 #ifndef OLDPMAP
173 	pcb->pcb_pmreal = pcb->pcb_pm;		/* XXX */
174 #else
175 	(void) pmap_extract(pmap_kernel(), (vaddr_t)pcb->pcb_pm,
176 	    (paddr_t *)&pcb->pcb_pmreal);
177 #endif
178 }
179 
180 /*
181  * Move pages from one kernel virtual address to another.
182  */
183 void
184 pagemove(from, to, size)
185 	caddr_t from, to;
186 	size_t size;
187 {
188 	paddr_t pa;
189 	vaddr_t va;
190 
191 	for (va = (vaddr_t)from; size > 0; size -= NBPG) {
192 		(void) pmap_extract(pmap_kernel(), va, &pa);
193 		pmap_kremove(va, NBPG);
194 		pmap_kenter_pa((vaddr_t)to, pa, VM_PROT_READ|VM_PROT_WRITE);
195 		va += NBPG;
196 		to += NBPG;
197 	}
198 	pmap_update(pmap_kernel());
199 }
200 
201 /*
202  * cpu_exit is called as the last action during exit.
203  *
204  * We clean up a little and then call switchexit() with the old proc
205  * as an argument.  switchexit() switches to the idle context, schedules
206  * the old vmspace and stack to be freed, then selects a new process to
207  * run.
208  */
209 void
210 cpu_exit(p)
211 	struct proc *p;
212 {
213 	void switchexit(struct proc *);		/* Defined in locore.S */
214 #if defined(PPC_HAVE_FPU) || defined(ALTIVEC)
215 	struct pcb *pcb = &p->p_addr->u_pcb;
216 #endif
217 
218 #ifdef PPC_HAVE_FPU
219 	if (pcb->pcb_fpcpu)			/* release the FPU */
220 		save_fpu_proc(p);
221 #endif
222 #ifdef ALTIVEC
223 	if (pcb->pcb_veccpu) {			/* release the AltiVEC */
224 		save_vec_proc(p);
225 		__asm __volatile("dssall;sync"); /* stop any streams */
226 /* XXX this stops streams on the current cpu, should be pcb->pcb_veccpu */
227 	}
228 	if (pcb->pcb_vr != NULL)
229 		pool_put(&vecpool, pcb->pcb_vr);
230 #endif
231 
232 	splsched();
233 	switchexit(p);
234 }
235 
236 /*
237  * Write the machine-dependent part of a core dump.
238  */
239 int
240 cpu_coredump(p, vp, cred, chdr)
241 	struct proc *p;
242 	struct vnode *vp;
243 	struct ucred *cred;
244 	struct core *chdr;
245 {
246 	struct coreseg cseg;
247 	struct md_coredump md_core;
248 	struct pcb *pcb = &p->p_addr->u_pcb;
249 	int error;
250 
251 	CORE_SETMAGIC(*chdr, COREMAGIC, MID_POWERPC, 0);
252 	chdr->c_hdrsize = ALIGN(sizeof *chdr);
253 	chdr->c_seghdrsize = ALIGN(sizeof cseg);
254 	chdr->c_cpusize = sizeof md_core;
255 
256 	md_core.frame = *trapframe(p);
257 	if (pcb->pcb_flags & PCB_FPU) {
258 #ifdef PPC_HAVE_FPU
259 		if (p->p_addr->u_pcb.pcb_fpcpu)
260 			save_fpu_proc(p);
261 #endif
262 		md_core.fpstate = pcb->pcb_fpu;
263 	} else
264 		memset(&md_core.fpstate, 0, sizeof(md_core.fpstate));
265 
266 #ifdef ALTIVEC
267 	if (pcb->pcb_flags & PCB_ALTIVEC) {
268 		if (pcb->pcb_veccpu)
269 			save_vec_proc(p);
270 		md_core.vstate = *pcb->pcb_vr;
271 	} else
272 #endif
273 		memset(&md_core.vstate, 0, sizeof(md_core.vstate));
274 
275 	CORE_SETMAGIC(cseg, CORESEGMAGIC, MID_MACHINE, CORE_CPU);
276 	cseg.c_addr = 0;
277 	cseg.c_size = chdr->c_cpusize;
278 
279 	if ((error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&cseg, chdr->c_seghdrsize,
280 			    (off_t)chdr->c_hdrsize, UIO_SYSSPACE,
281 			    IO_NODELOCKED|IO_UNIT, cred, NULL, p)) != 0)
282 		return error;
283 	if ((error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&md_core, sizeof md_core,
284 			    (off_t)(chdr->c_hdrsize + chdr->c_seghdrsize), UIO_SYSSPACE,
285 			    IO_NODELOCKED|IO_UNIT, cred, NULL, p)) != 0)
286 		return error;
287 
288 	chdr->c_nseg++;
289 	return 0;
290 }
291 
292 #ifdef PPC_IBM4XX
293 /*
294  * Map a range of user addresses into the kernel.
295  */
296 vaddr_t
297 vmaprange(p, uaddr, len, prot)
298 	struct proc *p;
299 	vaddr_t uaddr;
300 	vsize_t len;
301 	int prot;
302 {
303 	vaddr_t faddr, taddr, kaddr;
304 	vsize_t off;
305 	paddr_t pa;
306 
307 	faddr = trunc_page(uaddr);
308 	off = uaddr - faddr;
309 	len = round_page(off + len);
310 	taddr = uvm_km_valloc_wait(phys_map, len);
311 	kaddr = taddr + off;
312 	for (; len > 0; len -= NBPG) {
313 		(void) pmap_extract(vm_map_pmap(&p->p_vmspace->vm_map),
314 		    faddr, &pa);
315 		pmap_kenter_pa(taddr, pa, prot);
316 		faddr += NBPG;
317 		taddr += NBPG;
318 	}
319 	return (kaddr);
320 }
321 
322 /*
323  * Undo vmaprange.
324  */
325 void
326 vunmaprange(kaddr, len)
327 	vaddr_t kaddr;
328 	vsize_t len;
329 {
330 	vaddr_t addr;
331 	vsize_t off;
332 
333 	addr = trunc_page(kaddr);
334 	off = kaddr - addr;
335 	len = round_page(off + len);
336 	pmap_kremove(addr, len);
337 	uvm_km_free_wakeup(phys_map, addr, len);
338 }
339 #endif /* PPC_IBM4XX */
340 
341 /*
342  * Map a user I/O request into kernel virtual address space.
343  * Note: these pages have already been locked by uvm_vslock.
344  */
345 void
346 vmapbuf(bp, len)
347 	struct buf *bp;
348 	vsize_t len;
349 {
350 	vaddr_t faddr, taddr;
351 	vsize_t off;
352 	paddr_t pa;
353 	int prot = VM_PROT_READ | ((bp->b_flags & B_READ) ? VM_PROT_WRITE : 0);
354 
355 #ifdef	DIAGNOSTIC
356 	if (!(bp->b_flags & B_PHYS))
357 		panic("vmapbuf");
358 #endif
359 	/*
360 	 * XXX Reimplement this with vmaprange (on at least PPC_IBM4XX CPUs).
361 	 */
362 	faddr = trunc_page((vaddr_t)bp->b_saveaddr = bp->b_data);
363 	off = (vaddr_t)bp->b_data - faddr;
364 	len = round_page(off + len);
365 	taddr = uvm_km_valloc_wait(phys_map, len);
366 	bp->b_data = (caddr_t)(taddr + off);
367 	for (; len > 0; len -= NBPG) {
368 		(void) pmap_extract(vm_map_pmap(&bp->b_proc->p_vmspace->vm_map),
369 		    faddr, &pa);
370 		/*
371 		 * Use pmap_enter so the referenced and modified bits are
372 		 * appropriately set.
373 		 */
374 		pmap_kenter_pa(taddr, pa, prot);
375 		faddr += NBPG;
376 		taddr += NBPG;
377 	}
378 	pmap_update(pmap_kernel());
379 }
380 
381 /*
382  * Unmap a previously-mapped user I/O request.
383  */
384 void
385 vunmapbuf(bp, len)
386 	struct buf *bp;
387 	vsize_t len;
388 {
389 	vaddr_t addr;
390 	vsize_t off;
391 
392 #ifdef	DIAGNOSTIC
393 	if (!(bp->b_flags & B_PHYS))
394 		panic("vunmapbuf");
395 #endif
396 	addr = trunc_page((vaddr_t)bp->b_data);
397 	off = (vaddr_t)bp->b_data - addr;
398 	len = round_page(off + len);
399 	/*
400 	 * Since the pages were entered by pmap_enter, use pmap_remove
401 	 * to remove them.
402 	 */
403 	pmap_kremove(addr, len);
404 	pmap_update(pmap_kernel());
405 	uvm_km_free_wakeup(phys_map, addr, len);
406 	bp->b_data = bp->b_saveaddr;
407 	bp->b_saveaddr = 0;
408 }
409