Revamp how user MSR/SRR1 are dealt with.

Add a PSL_USEROK_P(psl) macro which valids the bits (replaces the use of
PSL_USERSTATIC).
Add a PSL_USERSRR1 mask which is used to mask out status bits in the upper
half of SRR1.
Make sure PSL_VEC is set appropriately in userret().  PSL_VEC is in the same
region as SSR1 status bits so it's not preserved on exceptions.  Thus we
need to make to set it.
When returning a MSR/SRR1 to userland, always clear the status bits.
Add emulation of the mfpvr, mtmsr, and mfmsr instructions.
This commit is contained in:
matt 2004-04-15 21:07:06 +00:00
parent e30c25106f
commit ee00feaab9
9 changed files with 180 additions and 53 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: psl.h,v 1.9 2004/04/04 16:49:12 matt Exp $ */
/* $NetBSD: psl.h,v 1.10 2004/04/15 21:07:06 matt Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
@ -82,7 +82,12 @@
/*
* A user is not allowed to change any MSR bits except the following:
* We restrict the test to the low 16 bits of the MSR since those are the
* only ones preserved in the trap. Note that this means PSL_VEC needs to
* restored the SRR1 in userret.
*/
#define PSL_USERSTATIC (~(PSL_VEC|PSL_FP|PSL_FE0|PSL_FE1|PSL_LE|PSL_SE|PSL_BE))
#define PSL_USERSRR1 ((PSL_USERSET|PSL_USERMOD) & 0xFFFF)
#define PSL_USERMOD (PSL_VEC|PSL_FP|PSL_FE0|PSL_FE1|PSL_LE|PSL_SE|PSL_BE)
#define PSL_USEROK_P(psl) (((psl) & ~PSL_USERMOD) == PSL_USERSET)
#endif /* _POWERPC_PSL_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: userret.h,v 1.8 2004/04/06 02:25:22 matt Exp $ */
/* $NetBSD: userret.h,v 1.9 2004/04/15 21:07:07 matt Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
@ -44,13 +44,13 @@
static __inline void
userret(struct lwp *l, struct trapframe *frame)
{
struct cpu_info *ci = curcpu();
struct pcb *pcb;
struct cpu_info * const ci = curcpu();
struct pcb * const pcb = &l->l_addr->u_pcb;
/* Invoke MI userret code */
mi_userret(l);
pcb = &l->l_addr->u_pcb;
frame->srr1 &= PSL_USERSRR1; /* clear SRR1 status bits */
/*
* If someone stole the fp or vector unit while we were away,
@ -64,9 +64,16 @@ userret(struct lwp *l, struct trapframe *frame)
}
#endif
#ifdef ALTIVEC
if ((frame->srr1 & PSL_VEC) &&
(l != ci->ci_veclwp || pcb->pcb_veccpu != ci)) {
frame->srr1 &= ~PSL_VEC;
/*
* We need to manually restore PSL_VEC each time we return
* to user mode since PSL_VEC is not preserved in SRR1.
*/
if (frame->srr1 & PSL_VEC) {
if (l != ci->ci_veclwp)
frame->srr1 &= ~PSL_VEC;
} else {
if (l == ci->ci_veclwp)
frame->srr1 |= PSL_VEC;
}
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: altivec.c,v 1.5 2003/07/15 02:54:45 lukem Exp $ */
/* $NetBSD: altivec.c,v 1.6 2004/04/15 21:07:07 matt Exp $ */
/*
* Copyright (C) 1996 Wolfgang Solfrank.
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: altivec.c,v 1.5 2003/07/15 02:54:45 lukem Exp $");
__KERNEL_RCSID(0, "$NetBSD: altivec.c,v 1.6 2004/04/15 21:07:07 matt Exp $");
#include "opt_multiprocessor.h"
@ -109,7 +109,6 @@ enable_vec()
* Enable AltiVec when we return to user-mode.
* Record the new ownership of the AltiVec unit.
*/
tf->srr1 |= PSL_VEC;
curcpu()->ci_veclwp = l;
pcb->pcb_veccpu = curcpu();
__asm __volatile ("sync");
@ -176,7 +175,6 @@ save_vec_cpu(void)
* Note that we aren't using any CPU resources and stop any
* data streams.
*/
tf->srr1 &= ~PSL_VEC;
pcb->pcb_veccpu = NULL;
ci->ci_veclwp = NULL;
__asm __volatile ("dssall; sync");

View File

@ -1,4 +1,4 @@
/* $NetBSD: compat_13_machdep.c,v 1.8 2003/09/27 04:44:42 matt Exp $ */
/* $NetBSD: compat_13_machdep.c,v 1.9 2004/04/15 21:07:07 matt Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: compat_13_machdep.c,v 1.8 2003/09/27 04:44:42 matt Exp $");
__KERNEL_RCSID(0, "$NetBSD: compat_13_machdep.c,v 1.9 2004/04/15 21:07:07 matt Exp $");
#include "opt_ppcarch.h"
@ -68,7 +68,7 @@ compat_13_sys_sigreturn(struct lwp *l, void *v, register_t *retval)
/* Restore the register context. */
tf = trapframe(l);
if ((sc.sc_frame.srr1 & PSL_USERSTATIC) != (tf->srr1 & PSL_USERSTATIC))
if (!PSL_USEROK_P(sc.sc_frame.srr1))
return (EINVAL);
/* Restore register context. */

View File

@ -1,4 +1,4 @@
/* $NetBSD: compat_16_machdep.c,v 1.3 2004/04/04 17:10:32 matt Exp $ */
/* $NetBSD: compat_16_machdep.c,v 1.4 2004/04/15 21:07:07 matt Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
@ -32,9 +32,10 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: compat_16_machdep.c,v 1.3 2004/04/04 17:10:32 matt Exp $");
__KERNEL_RCSID(0, "$NetBSD: compat_16_machdep.c,v 1.4 2004/04/15 21:07:07 matt Exp $");
#include "opt_compat_netbsd.h"
#include "opt_altivec.h"
#include "opt_ppcarch.h"
#include <sys/param.h>
@ -86,10 +87,13 @@ sendsig_sigcontext(int sig, const sigset_t *mask, u_long code)
utf->xer = tf->xer;
utf->ctr = tf->ctr;
utf->srr0 = tf->srr0;
utf->srr1 = tf->srr1;
utf->srr1 = tf->srr1 & PSL_USERSRR1;
#ifdef PPC_HAVE_FPU
utf->srr1 |= l->l_addr->u_pcb.pcb_flags & (PCB_FE0|PCB_FE1);
#endif
#ifdef ALTIVEC
utf->srr1 |= l->l_addr->u_pcb.pcb_flags & PCB_ALTIVEC ? PSL_VEC : 0;
#endif
#ifdef PPC_OEA
utf->vrsave = tf->tf_xtra[TF_VRSAVE];
utf->mq = tf->tf_xtra[TF_MQ];
@ -180,7 +184,11 @@ compat_16_sys___sigreturn14(struct lwp *l, void *v, register_t *retval)
/* Restore the register context. */
tf = trapframe(l);
if ((sc.sc_frame.srr1 & PSL_USERSTATIC) != (tf->srr1 & PSL_USERSTATIC))
/*
* Make sure SRR1 hasn't been maliciously tampered with.
*/
if (!PSL_USEROK_P(sc.sc_frame.srr1))
return (EINVAL);
/* Restore register context. */

View File

@ -1,4 +1,4 @@
/* $NetBSD: darwin_machdep.c,v 1.12 2003/12/16 13:38:26 manu Exp $ */
/* $NetBSD: darwin_machdep.c,v 1.13 2004/04/15 21:07:07 matt Exp $ */
/*-
* Copyright (c) 2002 The NetBSD Foundation, Inc.
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: darwin_machdep.c,v 1.12 2003/12/16 13:38:26 manu Exp $");
__KERNEL_RCSID(0, "$NetBSD: darwin_machdep.c,v 1.13 2004/04/15 21:07:07 matt Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -109,7 +109,7 @@ darwin_sendsig(ksi, mask)
sf.dmc.es.exception = tf->exc;
sf.dmc.ss.srr0 = tf->srr0;
sf.dmc.ss.srr1 = tf->srr1;
sf.dmc.ss.srr1 = tf->srr1 & PSL_USERSRR1;
memcpy(&sf.dmc.ss.gpreg[0], &tf->fixreg[0], sizeof(sf.dmc.ss.gpreg));
sf.dmc.ss.cr = tf->cr;
sf.dmc.ss.xer = tf->xer;
@ -215,9 +215,7 @@ darwin_sys_sigreturn(struct lwp *l, void *v, register_t *retval)
/* Check for security abuse */
tf = trapframe(l);
mctx.ss.srr1 &= ~(PSL_POW | PSL_ILE | PSL_IP | PSL_LE | PSL_RI);
mctx.ss.srr1 |= (PSL_PR | PSL_ME | PSL_IR | PSL_DR | PSL_EE);
if ((mctx.ss.srr1 & PSL_USERSTATIC) != (tf->srr1 & PSL_USERSTATIC)) {
if (!PSL_USEROK_P(mctx.ss.srr1))
DPRINTF(("uctx.ss.srr1 = 0x%08x, rf->srr1 = 0x%08lx\n",
mctx.ss.srr1, tf->srr1));
return (EINVAL);

View File

@ -1,4 +1,4 @@
/* $NetBSD: mach_machdep.c,v 1.18 2003/11/29 23:56:09 manu Exp $ */
/* $NetBSD: mach_machdep.c,v 1.19 2004/04/15 21:07:07 matt Exp $ */
/*-
* Copyright (c) 2002 The NetBSD Foundation, Inc.
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mach_machdep.c,v 1.18 2003/11/29 23:56:09 manu Exp $");
__KERNEL_RCSID(0, "$NetBSD: mach_machdep.c,v 1.19 2004/04/15 21:07:07 matt Exp $");
#include "opt_ppcarch.h"
#include <sys/param.h>
@ -109,7 +109,7 @@ mach_create_thread_child(void *arg)
regs = (struct exec_macho_powerpc_thread_state *)mctc->mctc_state;
/* Security warning */
if ((regs->srr1 & PSL_USERSTATIC) != (tf->srr1 & PSL_USERSTATIC))
if (!PSL_USEROK_P(regs->srr1))
uprintf("mach_create_thread_child: PSL_USERSTATIC change\n");
/*
* Call upcallret before setting the register context as it
@ -119,8 +119,7 @@ mach_create_thread_child(void *arg)
/* Set requested register context */
tf->srr0 = regs->srr0;
tf->srr1 = ((regs->srr1 & ~PSL_USERSTATIC) |
(tf->srr1 & PSL_USERSTATIC));
tf->srr1 = regs->srr1;
memcpy(tf->fixreg, &regs->r0, 32 * sizeof(register_t));
tf->cr = regs->cr;
tf->xer = regs->xer;
@ -158,7 +157,7 @@ mach_thread_get_state_machdep(l, flavor, state, size)
mpts = (struct mach_ppc_thread_state *)state;
mpts->srr0 = tf->srr0;
mpts->srr1 = tf->srr1;
mpts->srr1 = tf->srr1 & PSL_USERSRR1;
memcpy(mpts->gpreg, tf->fixreg, 32 * sizeof(register_t));
mpts->cr = tf->cr;
mpts->xer = tf->xer;

View File

@ -1,4 +1,4 @@
/* $NetBSD: sig_machdep.c,v 1.21 2004/04/04 17:26:10 matt Exp $ */
/* $NetBSD: sig_machdep.c,v 1.22 2004/04/15 21:07:07 matt Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
@ -32,10 +32,11 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sig_machdep.c,v 1.21 2004/04/04 17:26:10 matt Exp $");
__KERNEL_RCSID(0, "$NetBSD: sig_machdep.c,v 1.22 2004/04/15 21:07:07 matt Exp $");
#include "opt_compat_netbsd.h"
#include "opt_ppcarch.h"
#include "opt_altivec.h"
#include <sys/param.h>
#include <sys/mount.h>
@ -47,7 +48,8 @@ __KERNEL_RCSID(0, "$NetBSD: sig_machdep.c,v 1.21 2004/04/04 17:26:10 matt Exp $"
#include <sys/ucontext.h>
#include <sys/user.h>
#include <machine/fpu.h>
#include <powerpc/fpu.h>
#include <powerpc/altivec.h>
/*
* Send a signal to process.
@ -150,7 +152,7 @@ cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flagp)
{
const struct trapframe *tf = trapframe(l);
__greg_t *gr = mcp->__gregs;
#ifdef PPC_HAVE_FPU
#if defined(PPC_HAVE_FPU) || defined(ALTIVEC)
struct pcb *pcb = &l->l_addr->u_pcb;
#endif
@ -159,9 +161,12 @@ cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flagp)
gr[_REG_CR] = tf->cr;
gr[_REG_LR] = tf->lr;
gr[_REG_PC] = tf->srr0;
gr[_REG_MSR] = tf->srr1;
gr[_REG_MSR] = tf->srr1 & PSL_USERSRR1;
#ifdef PPC_HAVE_FPU
gr[_REG_MSR] |= pcb->pcb_flags & (PCB_FE0|PCB_FE1);
#endif
#ifdef ALTIVEC
gr[_REG_MSR] |= pcb->pcb_flags & PCB_ALTIVEC ? PSL_VEC : 0;
#endif
gr[_REG_CTR] = tf->ctr;
gr[_REG_XER] = tf->xer;
@ -188,8 +193,23 @@ cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flagp)
#endif
memset(&mcp->__fpregs, 0, sizeof(mcp->__fpregs));
/* No AltiVec support, for now. */
memset(&mcp->__vrf, 0, sizeof (mcp->__vrf));
#ifdef ALTIVEC
/* Save AltiVec context, if any. */
if ((pcb->pcb_flags & PCB_ALTIVEC) != 0) {
/*
* If we're the AltiVec owner, dump its context
* to the PCB first.
*/
if (pcb->pcb_veccpu)
save_vec_lwp(l);
(void)memcpy(mcp->__vrf.__vrs, pcb->pcb_vr.vreg,
sizeof (mcp->__vrf.__vrs));
mcp->__vrf.__vscr = pcb->pcb_vr.vscr;
mcp->__vrf.__vrsave = pcb->pcb_vr.vrsave;
*flagp |= _UC_POWERPC_VEC;
} else
#endif
memset(&mcp->__vrf, 0, sizeof (mcp->__vrf));
}
int
@ -203,8 +223,7 @@ cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags)
/* Restore GPR context, if any. */
if (flags & _UC_CPU) {
if ((gr[_REG_MSR] & PSL_USERSTATIC) !=
(tf->srr1 & PSL_USERSTATIC))
if (!PSL_USEROK_P(gr[_REG_MSR]))
return (EINVAL);
#ifdef PPC_HAVE_FPU
@ -227,8 +246,7 @@ cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags)
#endif
}
#ifdef PPC_HAVE_FPU
/* Restore FPR context, if any. */
#ifdef PPC_HAVE_FPU /* Restore FPR context, if any. */
if ((flags & _UC_FPU) && mcp->__fpregs.__fpu_valid != 0) {
/* XXX we don't need to save the state, just to drop it */
save_fpu_lwp(l);
@ -239,5 +257,17 @@ cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags)
}
#endif
#ifdef ALTIVEC
/* Restore AltiVec context, if any. */
if (flags & _UC_POWERPC_VEC) {
/* XXX we don't need to save the state, just to drop it */
save_vec_lwp(l);
(void)memcpy(pcb->pcb_vr.vreg, &mcp->__vrf.__vrs,
sizeof (pcb->pcb_vr.vreg));
pcb->pcb_vr.vscr = mcp->__vrf.__vscr;
pcb->pcb_vr.vrsave = mcp->__vrf.__vrsave;
}
#endif
return (0);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: trap.c,v 1.100 2004/04/04 19:21:36 matt Exp $ */
/* $NetBSD: trap.c,v 1.101 2004/04/15 21:07:07 matt Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.100 2004/04/04 19:21:36 matt Exp $");
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.101 2004/04/15 21:07:07 matt Exp $");
#include "opt_altivec.h"
#include "opt_ddb.h"
@ -64,7 +64,8 @@ __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.100 2004/04/04 19:21:36 matt Exp $");
#include <powerpc/spr.h>
#include <powerpc/userret.h>
static int fix_unaligned(struct lwp *l, struct trapframe *frame);
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);
@ -418,7 +419,6 @@ trap(struct trapframe *frame)
#endif
case EXC_MCHK|EXC_USER:
ci->ci_ev_umchk.ev_count++;
KERNEL_PROC_LOCK(l);
if (cpu_printfataltraps) {
printf("trap: pid %d (%s): user MCHK trap @ %#lx "
"(SRR1=%#lx)\n",
@ -429,13 +429,14 @@ trap(struct trapframe *frame)
ksi.ksi_trap = EXC_MCHK;
ksi.ksi_addr = (void *)frame->srr0;
ksi.ksi_code = BUS_OBJERR;
KERNEL_PROC_LOCK(l);
(*p->p_emul->e_trapsignal)(l, &ksi);
KERNEL_PROC_UNLOCK(l);
case EXC_PGM|EXC_USER:
ci->ci_ev_pgm.ev_count++;
KERNEL_PROC_LOCK(l);
if (frame->srr1 & 0x00020000) { /* Bit 14 is set if trap */
KERNEL_PROC_LOCK(l);
if (LIST_EMPTY(&p->p_raslist) ||
ras_lookup(p, (caddr_t)frame->srr0) == (caddr_t) -1) {
KSI_INIT_TRAP(&ksi);
@ -448,11 +449,8 @@ trap(struct trapframe *frame)
/* skip the trap instruction */
frame->srr0 += 4;
}
KERNEL_PROC_UNLOCK(l);
} else {
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);
KSI_INIT_TRAP(&ksi);
ksi.ksi_signo = SIGILL;
ksi.ksi_trap = EXC_PGM;
@ -461,12 +459,21 @@ trap(struct trapframe *frame)
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);
KERNEL_PROC_LOCK(l);
(*p->p_emul->e_trapsignal)(l, &ksi);
KERNEL_PROC_UNLOCK(l);
}
KERNEL_PROC_UNLOCK(l);
break;
case EXC_MCHK: {
@ -749,6 +756,81 @@ fix_unaligned(struct lwp *l, struct trapframe *frame)
return -1;
}
int
emulated_opcode(struct lwp *l, struct trapframe *tf)
{
uint32_t opcode;
if (copyin((caddr_t)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)
{