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