/* $NetBSD: trap.c,v 1.128.8.1 2009/08/14 21:34:32 snj Exp $ */ /* * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.128.8.1 2009/08/14 21:34:32 snj Exp $"); #include "opt_altivec.h" #include "opt_ddb.h" #include "opt_multiprocessor.h" #include <sys/param.h> #include <sys/pool.h> #include <sys/proc.h> #include <sys/ras.h> #include <sys/reboot.h> #include <sys/sa.h> #include <sys/savar.h> #include <sys/systm.h> #include <sys/user.h> #include <sys/kauth.h> #include <uvm/uvm_extern.h> #include <dev/cons.h> #include <machine/cpu.h> #include <machine/db_machdep.h> #include <machine/fpu.h> #include <machine/frame.h> #include <machine/pcb.h> #include <machine/pmap.h> #include <machine/psl.h> #include <machine/trap.h> #include <powerpc/altivec.h> #include <powerpc/spr.h> #include <powerpc/userret.h> static int emulated_opcode(struct lwp *, struct trapframe *); static int fix_unaligned(struct lwp *, struct trapframe *); static inline vaddr_t setusr(vaddr_t, size_t *); static inline void unsetusr(void); void trap(struct trapframe *); /* Called from locore / trap_subr */ /* Why are these not defined in a header? */ int badaddr(void *, size_t); int badaddr_read(void *, size_t, int *); void trap(struct trapframe *frame) { struct cpu_info * const ci = curcpu(); struct lwp *l = curlwp; struct proc *p = l ? l->l_proc : NULL; struct pcb *pcb = curpcb; struct vm_map *map; struct faultbuf *onfault; ksiginfo_t ksi; int type = frame->exc; int ftype, rv; ci->ci_ev_traps.ev_count++; if (frame->srr1 & PSL_PR) { type |= EXC_USER; #ifdef DIAGNOSTIC if (l == NULL || p == NULL) panic("trap: user trap %d with lwp = %p, proc = %p", type, l, p); #endif LWP_CACHE_CREDS(l, p); } uvmexp.traps++; switch (type) { case EXC_RUNMODETRC|EXC_USER: /* FALLTHROUGH */ case EXC_TRC|EXC_USER: frame->srr1 &= ~PSL_SE; if (p->p_raslist == NULL || ras_lookup(p, (void *)frame->srr0) == (void *) -1) { KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGTRAP; ksi.ksi_trap = EXC_TRC; ksi.ksi_addr = (void *)frame->srr0; ksi.ksi_code = TRAP_TRACE; (*p->p_emul->e_trapsignal)(l, &ksi); } break; case EXC_DSI: { struct faultbuf *fb; vaddr_t va = frame->dar; ci->ci_ev_kdsi.ev_count++; /* * Only query UVM if no interrupts are active. */ if (ci->ci_intrdepth < 0) { if ((va >> ADDR_SR_SHFT) == pcb->pcb_kmapsr) { va &= ADDR_PIDX | ADDR_POFF; va |= pcb->pcb_umapsr << ADDR_SR_SHFT; map = &p->p_vmspace->vm_map; #ifdef PPC_OEA64 if ((frame->dsisr & DSISR_NOTFOUND) && vm_map_pmap(map)->pm_ste_evictions > 0 && pmap_ste_spill(vm_map_pmap(map), trunc_page(va), false)) { return; } #endif if ((frame->dsisr & DSISR_NOTFOUND) && vm_map_pmap(map)->pm_evictions > 0 && pmap_pte_spill(vm_map_pmap(map), trunc_page(va), false)) { return; } if ((l->l_flag & LW_SA) && (~l->l_pflag & LP_SA_NOBLOCK)) { l->l_savp->savp_faultaddr = va; l->l_pflag |= LP_SA_PAGEFAULT; } #if defined(DIAGNOSTIC) && !defined(PPC_OEA64) && !defined (PPC_IBM4XX) } else if ((va >> ADDR_SR_SHFT) == USER_SR) { printf("trap: kernel %s DSI trap @ %#lx by %#lx" " (DSISR %#x): USER_SR unset\n", (frame->dsisr & DSISR_STORE) ? "write" : "read", va, frame->srr0, frame->dsisr); goto brain_damage2; #endif } else { map = kernel_map; } if (frame->dsisr & DSISR_STORE) ftype = VM_PROT_WRITE; else ftype = VM_PROT_READ; onfault = pcb->pcb_onfault; pcb->pcb_onfault = NULL; rv = uvm_fault(map, trunc_page(va), ftype); pcb->pcb_onfault = onfault; if (map != kernel_map) { /* * Record any stack growth... */ if (rv == 0) uvm_grow(p, trunc_page(va)); l->l_pflag &= ~LP_SA_PAGEFAULT; } if (rv == 0) return; if (rv == EACCES) rv = EFAULT; } else { /* * Note that this implies that access to the USER * segment is not allowed in interrupt context. */ rv = EFAULT; } if ((fb = pcb->pcb_onfault) != NULL) { frame->srr0 = fb->fb_pc; frame->fixreg[1] = fb->fb_sp; frame->fixreg[2] = fb->fb_r2; frame->fixreg[3] = rv; frame->cr = fb->fb_cr; memcpy(&frame->fixreg[13], fb->fb_fixreg, sizeof(fb->fb_fixreg)); return; } printf("trap: kernel %s DSI trap @ %#lx by %#lx (DSISR %#x, err" "=%d), lr %#lx\n", (frame->dsisr & DSISR_STORE) ? "write" : "read", va, frame->srr0, frame->dsisr, rv, frame->lr); goto brain_damage2; } case EXC_DSI|EXC_USER: ci->ci_ev_udsi.ev_count++; if (frame->dsisr & DSISR_STORE) ftype = VM_PROT_WRITE; else ftype = VM_PROT_READ; /* * Try to spill an evicted pte into the page table * if this wasn't a protection fault and the pmap * has some evicted pte's. */ map = &p->p_vmspace->vm_map; #ifdef PPC_OEA64 if ((frame->dsisr & DSISR_NOTFOUND) && vm_map_pmap(map)->pm_ste_evictions > 0 && pmap_ste_spill(vm_map_pmap(map), trunc_page(frame->dar), false)) { break; } #endif if ((frame->dsisr & DSISR_NOTFOUND) && vm_map_pmap(map)->pm_evictions > 0 && pmap_pte_spill(vm_map_pmap(map), trunc_page(frame->dar), false)) { break; } if (l->l_flag & LW_SA) { l->l_savp->savp_faultaddr = (vaddr_t)frame->dar;; l->l_pflag |= LP_SA_PAGEFAULT; } rv = uvm_fault(map, trunc_page(frame->dar), ftype); if (rv == 0) { /* * Record any stack growth... */ uvm_grow(p, trunc_page(frame->dar)); l->l_pflag &= ~LP_SA_PAGEFAULT; break; } ci->ci_ev_udsi_fatal.ev_count++; if (cpu_printfataltraps) { printf("trap: pid %d.%d (%s): user %s DSI trap @ %#lx " "by %#lx (DSISR %#x, err=%d)\n", p->p_pid, l->l_lid, p->p_comm, (frame->dsisr & DSISR_STORE) ? "write" : "read", frame->dar, frame->srr0, frame->dsisr, rv); } KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGSEGV; ksi.ksi_trap = EXC_DSI; ksi.ksi_addr = (void *)frame->dar; ksi.ksi_code = (frame->dsisr & DSISR_PROTECT ? SEGV_ACCERR : SEGV_MAPERR); if (rv == ENOMEM) { printf("UVM: pid %d.%d (%s), uid %d killed: " "out of swap\n", p->p_pid, l->l_lid, p->p_comm, l->l_cred ? kauth_cred_geteuid(l->l_cred) : -1); ksi.ksi_signo = SIGKILL; } (*p->p_emul->e_trapsignal)(l, &ksi); l->l_pflag &= ~LP_SA_PAGEFAULT; break; case EXC_ISI: ci->ci_ev_kisi.ev_count++; printf("trap: kernel ISI by %#lx (SRR1 %#lx), lr: %#lx\n", frame->srr0, frame->srr1, frame->lr); goto brain_damage2; case EXC_ISI|EXC_USER: ci->ci_ev_isi.ev_count++; /* * Try to spill an evicted pte into the page table * if this wasn't a protection fault and the pmap * has some evicted pte's. */ map = &p->p_vmspace->vm_map; #ifdef PPC_OEA64 if (vm_map_pmap(map)->pm_ste_evictions > 0 && pmap_ste_spill(vm_map_pmap(map), trunc_page(frame->srr0), true)) { break; } #endif if (vm_map_pmap(map)->pm_evictions > 0 && pmap_pte_spill(vm_map_pmap(map), trunc_page(frame->srr0), true)) { break; } if (l->l_flag & LW_SA) { l->l_savp->savp_faultaddr = (vaddr_t)frame->srr0; l->l_pflag |= LP_SA_PAGEFAULT; } ftype = VM_PROT_EXECUTE; rv = uvm_fault(map, trunc_page(frame->srr0), ftype); if (rv == 0) { l->l_pflag &= ~LP_SA_PAGEFAULT; break; } ci->ci_ev_isi_fatal.ev_count++; if (cpu_printfataltraps) { printf("trap: pid %d.%d (%s): user ISI trap @ %#lx " "(SRR1=%#lx)\n", p->p_pid, l->l_lid, p->p_comm, frame->srr0, frame->srr1); } KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGSEGV; ksi.ksi_trap = EXC_ISI; ksi.ksi_addr = (void *)frame->srr0; ksi.ksi_code = (rv == EACCES ? SEGV_ACCERR : SEGV_MAPERR); (*p->p_emul->e_trapsignal)(l, &ksi); l->l_pflag &= ~LP_SA_PAGEFAULT; break; case EXC_FPU|EXC_USER: ci->ci_ev_fpu.ev_count++; if (pcb->pcb_fpcpu) { save_fpu_lwp(l, FPU_SAVE); } enable_fpu(); break; case EXC_AST|EXC_USER: ci->ci_astpending = 0; /* we are about to do it */ uvmexp.softs++; if (l->l_pflag & LP_OWEUPC) { l->l_pflag &= ~LP_OWEUPC; ADDUPROF(p); } /* Check whether we are being preempted. */ if (ci->ci_want_resched) preempt(); break; case EXC_ALI|EXC_USER: ci->ci_ev_ali.ev_count++; if (fix_unaligned(l, frame) != 0) { ci->ci_ev_ali_fatal.ev_count++; if (cpu_printfataltraps) { printf("trap: pid %d.%d (%s): user ALI trap @ " "%#lx by %#lx (DSISR %#x)\n", p->p_pid, l->l_lid, p->p_comm, frame->dar, frame->srr0, frame->dsisr); } KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGBUS; ksi.ksi_trap = EXC_ALI; ksi.ksi_addr = (void *)frame->dar; ksi.ksi_code = BUS_ADRALN; (*p->p_emul->e_trapsignal)(l, &ksi); } else frame->srr0 += 4; break; case EXC_PERF|EXC_USER: /* Not really, but needed due to how trap_subr.S works */ case EXC_VEC|EXC_USER: ci->ci_ev_vec.ev_count++; #ifdef ALTIVEC if (pcb->pcb_veccpu) save_vec_lwp(l, ALTIVEC_SAVE); enable_vec(); break; #else if (cpu_printfataltraps) { printf("trap: pid %d.%d (%s): user VEC trap @ %#lx " "(SRR1=%#lx)\n", p->p_pid, l->l_lid, p->p_comm, frame->srr0, frame->srr1); } KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_trap = EXC_PGM; ksi.ksi_addr = (void *)frame->srr0; ksi.ksi_code = ILL_ILLOPC; (*p->p_emul->e_trapsignal)(l, &ksi); break; #endif case EXC_MCHK|EXC_USER: ci->ci_ev_umchk.ev_count++; if (cpu_printfataltraps) { printf("trap: pid %d (%s): user MCHK trap @ %#lx " "(SRR1=%#lx)\n", p->p_pid, p->p_comm, frame->srr0, frame->srr1); } KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGBUS; ksi.ksi_trap = EXC_MCHK; ksi.ksi_addr = (void *)frame->srr0; ksi.ksi_code = BUS_OBJERR; (*p->p_emul->e_trapsignal)(l, &ksi); break; case EXC_PGM|EXC_USER: ci->ci_ev_pgm.ev_count++; if (frame->srr1 & 0x00020000) { /* Bit 14 is set if trap */ if (p->p_raslist == NULL || ras_lookup(p, (void *)frame->srr0) == (void *) -1) { KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGTRAP; ksi.ksi_trap = EXC_PGM; ksi.ksi_addr = (void *)frame->srr0; ksi.ksi_code = TRAP_BRKPT; (*p->p_emul->e_trapsignal)(l, &ksi); } else { /* skip the trap instruction */ frame->srr0 += 4; } } else { KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_trap = EXC_PGM; ksi.ksi_addr = (void *)frame->srr0; if (frame->srr1 & 0x100000) { ksi.ksi_signo = SIGFPE; ksi.ksi_code = get_fpu_fault_code(); } else if (frame->srr1 & 0x40000) { if (emulated_opcode(l, frame)) { frame->srr0 += 4; break; } ksi.ksi_code = ILL_PRVOPC; } else ksi.ksi_code = ILL_ILLOPC; if (cpu_printfataltraps) printf("trap: pid %d.%d (%s): user PGM trap @" " %#lx (SRR1=%#lx)\n", p->p_pid, l->l_lid, p->p_comm, frame->srr0, frame->srr1); (*p->p_emul->e_trapsignal)(l, &ksi); } break; case EXC_MCHK: { struct faultbuf *fb; if ((fb = pcb->pcb_onfault) != NULL) { frame->srr0 = fb->fb_pc; frame->fixreg[1] = fb->fb_sp; frame->fixreg[2] = fb->fb_r2; frame->fixreg[3] = EFAULT; frame->cr = fb->fb_cr; memcpy(&frame->fixreg[13], fb->fb_fixreg, sizeof(fb->fb_fixreg)); return; } printf("trap: pid %d.%d (%s): kernel MCHK trap @" " %#lx (SRR1=%#lx)\n", p->p_pid, l->l_lid, p->p_comm, frame->srr0, frame->srr1); goto brain_damage2; } case EXC_ALI: printf("trap: pid %d.%d (%s): kernel ALI trap @ %#lx by %#lx " "(DSISR %#x)\n", p->p_pid, l->l_lid, p->p_comm, frame->dar, frame->srr0, frame->dsisr); goto brain_damage2; case EXC_PGM: printf("trap: pid %d.%d (%s): kernel PGM trap @" " %#lx (SRR1=%#lx)\n", p->p_pid, l->l_lid, p->p_comm, frame->srr0, frame->srr1); goto brain_damage2; default: printf("trap type %x at %lx\n", type, frame->srr0); brain_damage2: #ifdef DDBX if (kdb_trap(type, frame)) return; #endif #ifdef TRAP_PANICWAIT printf("Press a key to panic.\n"); cnpollc(1); cngetc(); cnpollc(0); #endif panic("trap"); } userret(l, frame); } #ifdef _LP64 static inline vaddr_t setusr(vaddr_t uva, size_t *len_p) { *len_p = SEGMENT_LENGTH - (uva & ~SEGMENT_MASK); return pmap_setusr(uva) + (uva & ~SEGMENT_MASK); } static void unsetusr(void) { pmap_unsetusr(); } #else static inline vaddr_t setusr(vaddr_t uva, size_t *len_p) { struct pcb *pcb = curpcb; vaddr_t p; KASSERT(pcb != NULL); KASSERT(pcb->pcb_kmapsr == 0); pcb->pcb_kmapsr = USER_SR; pcb->pcb_umapsr = uva >> ADDR_SR_SHFT; *len_p = SEGMENT_LENGTH - (uva & ~SEGMENT_MASK); p = (USER_SR << ADDR_SR_SHFT) + (uva & ~SEGMENT_MASK); __asm volatile ("isync; mtsr %0,%1; isync" :: "n"(USER_SR), "r"(pcb->pcb_pm->pm_sr[pcb->pcb_umapsr])); return p; } static void unsetusr(void) { curpcb->pcb_kmapsr = 0; __asm volatile ("isync; mtsr %0,%1; isync" :: "n"(USER_SR), "r"(EMPTY_SEGMENT)); } #endif int copyin(const void *udaddr, void *kaddr, size_t len) { vaddr_t uva = (vaddr_t) udaddr; char *kp = kaddr; struct faultbuf env; int rv; if ((rv = setfault(&env)) != 0) { unsetusr(); goto out; } while (len > 0) { size_t seglen; vaddr_t p = setusr(uva, &seglen); if (seglen > len) seglen = len; memcpy(kp, (const char *) p, seglen); uva += seglen; kp += seglen; len -= seglen; unsetusr(); } out: curpcb->pcb_onfault = 0; return rv; } int copyout(const void *kaddr, void *udaddr, size_t len) { const char *kp = kaddr; vaddr_t uva = (vaddr_t) udaddr; struct faultbuf env; int rv; if ((rv = setfault(&env)) != 0) { unsetusr(); goto out; } while (len > 0) { size_t seglen; vaddr_t p = setusr(uva, &seglen); if (seglen > len) seglen = len; memcpy((char *)p, kp, seglen); uva += seglen; kp += seglen; len -= seglen; unsetusr(); } out: curpcb->pcb_onfault = 0; return rv; } /* * kcopy(const void *src, void *dst, size_t len); * * Copy len bytes from src to dst, aborting if we encounter a fatal * page fault. * * kcopy() _must_ save and restore the old fault handler since it is * called by uiomove(), which may be in the path of servicing a non-fatal * page fault. */ int kcopy(const void *src, void *dst, size_t len) { struct faultbuf env, *oldfault; int rv; oldfault = curpcb->pcb_onfault; if ((rv = setfault(&env)) == 0) memcpy(dst, src, len); curpcb->pcb_onfault = oldfault; return rv; } int badaddr(void *addr, size_t size) { return badaddr_read(addr, size, NULL); } int badaddr_read(void *addr, size_t size, int *rptr) { struct faultbuf env; int x; /* Get rid of any stale machine checks that have been waiting. */ __asm volatile ("sync; isync"); if (setfault(&env)) { curpcb->pcb_onfault = 0; __asm volatile ("sync"); return 1; } __asm volatile ("sync"); switch (size) { case 1: x = *(volatile int8_t *)addr; break; case 2: x = *(volatile int16_t *)addr; break; case 4: x = *(volatile int32_t *)addr; break; default: panic("badaddr: invalid size (%lu)", (u_long) size); } /* Make sure we took the machine check, if we caused one. */ __asm volatile ("sync; isync"); curpcb->pcb_onfault = 0; __asm volatile ("sync"); /* To be sure. */ /* Use the value to avoid reorder. */ if (rptr) *rptr = x; return 0; } /* * For now, this only deals with the particular unaligned access case * that gcc tends to generate. Eventually it should handle all of the * possibilities that can happen on a 32-bit PowerPC in big-endian mode. */ static int fix_unaligned(struct lwp *l, struct trapframe *frame) { int indicator = EXC_ALI_OPCODE_INDICATOR(frame->dsisr); switch (indicator) { case EXC_ALI_DCBZ: { /* * The DCBZ (Data Cache Block Zero) instruction * gives an alignment fault if used on non-cacheable * memory. We handle the fault mainly for the * case when we are running with the cache disabled * for debugging. */ static char zeroes[MAXCACHELINESIZE]; int error; error = copyout(zeroes, (void *)(frame->dar & -curcpu()->ci_ci.dcache_line_size), curcpu()->ci_ci.dcache_line_size); if (error) return -1; return 0; } case EXC_ALI_LFD: case EXC_ALI_STFD: { struct pcb * const pcb = &l->l_addr->u_pcb; const int reg = EXC_ALI_RST(frame->dsisr); double * const fpreg = &pcb->pcb_fpu.fpreg[reg]; /* * Juggle the FPU to ensure that we've initialized * the FPRs, and that their current state is in * the PCB. */ if (pcb->pcb_fpcpu) save_fpu_lwp(l, FPU_SAVE); if ((pcb->pcb_flags & PCB_FPU) == 0) { memset(&pcb->pcb_fpu, 0, sizeof(pcb->pcb_fpu)); pcb->pcb_flags |= PCB_FPU; } if (indicator == EXC_ALI_LFD) { if (copyin((void *)frame->dar, fpreg, sizeof(double)) != 0) return -1; } else { if (copyout(fpreg, (void *)frame->dar, sizeof(double)) != 0) return -1; } enable_fpu(); return 0; } break; } return -1; } int emulated_opcode(struct lwp *l, struct trapframe *tf) { uint32_t opcode; if (copyin((void *)tf->srr0, &opcode, sizeof(opcode)) != 0) return 0; #define OPC_MFSPR_CODE 0x7c0002a6 #define OPC_MFSPR_MASK (0xfc0007ff|0x001ff800) #define OPC_MFSPR(spr) (OPC_MFSPR_CODE |\ (((spr) & 0x1f) << 16) |\ (((spr) & 0x3e0) << 6)) #define OPC_MFSPR_REG(o) (((o) >> 21) & 0x1f) #define OPC_MFSPR_P(o, spr) (((o) & OPC_MFSPR_MASK) == OPC_MFSPR(spr)) if (OPC_MFSPR_P(opcode, SPR_PVR)) { __asm ("mfpvr %0" : "=r"(tf->fixreg[OPC_MFSPR_REG(opcode)])); return 1; } #define OPC_MFMSR_CODE 0x7c0000a8 #define OPC_MFMSR_MASK 0xfc1fffff #define OPC_MFMSR OPC_MFMSR_CODE #define OPC_MFMSR_REG(o) (((o) >> 21) & 0x1f) #define OPC_MFMSR_P(o) (((o) & OPC_MFMSR_MASK) == OPC_MFMSR_CODE) if (OPC_MFMSR_P(opcode)) { struct pcb * const pcb = &l->l_addr->u_pcb; register_t msr = tf->srr1 & PSL_USERSRR1; if (pcb->pcb_flags & PCB_FPU) msr |= PSL_FP; msr |= (pcb->pcb_flags & (PCB_FE0|PCB_FE1)); #ifdef ALTIVEC if (pcb->pcb_flags & PCB_ALTIVEC) msr |= PSL_VEC; #endif tf->fixreg[OPC_MFMSR_REG(opcode)] = msr; return 1; } #define OPC_MTMSR_CODE 0x7c0000a8 #define OPC_MTMSR_MASK 0xfc1fffff #define OPC_MTMSR OPC_MTMSR_CODE #define OPC_MTMSR_REG(o) (((o) >> 21) & 0x1f) #define OPC_MTMSR_P(o) (((o) & OPC_MTMSR_MASK) == OPC_MTMSR_CODE) if (OPC_MTMSR_P(opcode)) { struct pcb * const pcb = &l->l_addr->u_pcb; register_t msr = tf->fixreg[OPC_MTMSR_REG(opcode)]; /* * Don't let the user muck with bits he's not allowed to. */ if (!PSL_USEROK_P(msr)) return 0; /* * For now, only update the FP exception mode. */ pcb->pcb_flags &= ~(PSL_FE0|PSL_FE1); pcb->pcb_flags |= msr & (PSL_FE0|PSL_FE1); /* * If we think we have the FPU, update SRR1 too. If we're * wrong userret() will take care of it. */ if (tf->srr1 & PSL_FP) { tf->srr1 &= ~(PSL_FE0|PSL_FE1); tf->srr1 |= msr & (PSL_FE0|PSL_FE1); } return 1; } return 0; } int copyinstr(const void *udaddr, void *kaddr, size_t len, size_t *done) { vaddr_t uva = (vaddr_t) udaddr; char *kp = kaddr; struct faultbuf env; int rv; if ((rv = setfault(&env)) != 0) { unsetusr(); goto out2; } while (len > 0) { size_t seglen; vaddr_t p = setusr(uva, &seglen); if (seglen > len) seglen = len; len -= seglen; uva += seglen; for (; seglen-- > 0; p++) { if ((*kp++ = *(char *)p) == 0) { unsetusr(); goto out; } } unsetusr(); } rv = ENAMETOOLONG; out: if (done != NULL) *done = kp - (char *) kaddr; out2: curpcb->pcb_onfault = 0; return rv; } int copyoutstr(const void *kaddr, void *udaddr, size_t len, size_t *done) { const char *kp = kaddr; vaddr_t uva = (vaddr_t) udaddr; struct faultbuf env; int rv; if ((rv = setfault(&env)) != 0) { unsetusr(); goto out2; } while (len > 0) { size_t seglen; vaddr_t p = setusr(uva, &seglen); if (seglen > len) seglen = len; len -= seglen; uva += seglen; for (; seglen-- > 0; p++) { if ((*(char *)p = *kp++) == 0) { unsetusr(); goto out; } } unsetusr(); } rv = ENAMETOOLONG; out: if (done != NULL) *done = kp - (const char*)kaddr; out2: curpcb->pcb_onfault = 0; return rv; } /* * Start a new LWP */ void startlwp(void *arg) { int err; ucontext_t *uc = arg; struct lwp *l = curlwp; struct trapframe *frame = trapframe(l); err = cpu_setmcontext(l, &uc->uc_mcontext, uc->uc_flags); #if DIAGNOSTIC if (err) { printf("Error %d from cpu_setmcontext.", err); } #endif pool_put(&lwp_uc_pool, uc); userret(l, frame); } void upcallret(struct lwp *l) { struct trapframe *frame = trapframe(l); KERNEL_UNLOCK_LAST(l); userret(l, frame); }