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