Commit d33a40e5 authored by Paul Mackerras's avatar Paul Mackerras

Merge samba.org:/home/paulus/kernel/linux-2.5

into samba.org:/home/paulus/kernel/for-linus-ppc
parents 79b8787e 2550d7f1
...@@ -21,11 +21,11 @@ struct aligninfo { ...@@ -21,11 +21,11 @@ struct aligninfo {
unsigned char flags; unsigned char flags;
}; };
#if defined(CONFIG_4xx) #if defined(CONFIG_4xx) || defined(CONFIG_POWER4)
#define OPCD(inst) (((inst) & 0xFC000000) >> 26) #define OPCD(inst) (((inst) & 0xFC000000) >> 26)
#define RS(inst) (((inst) & 0x03E00000) >> 21) #define RS(inst) (((inst) & 0x03E00000) >> 21)
#define RA(inst) (((inst) & 0x001F0000) >> 16) #define RA(inst) (((inst) & 0x001F0000) >> 16)
#define IS_DFORM(code) ((code) >= 32 && (code) <= 55) #define IS_XFORM(code) ((code) == 31)
#endif #endif
#define INVALID { 0, 0 } #define INVALID { 0, 0 }
...@@ -61,9 +61,9 @@ static struct aligninfo aligninfo[128] = { ...@@ -61,9 +61,9 @@ static struct aligninfo aligninfo[128] = {
{ 4, ST+F+S }, /* 00 0 1010: stfs */ { 4, ST+F+S }, /* 00 0 1010: stfs */
{ 8, ST+F }, /* 00 0 1011: stfd */ { 8, ST+F }, /* 00 0 1011: stfd */
INVALID, /* 00 0 1100 */ INVALID, /* 00 0 1100 */
INVALID, /* 00 0 1101 */ INVALID, /* 00 0 1101: ld/ldu/lwa */
INVALID, /* 00 0 1110 */ INVALID, /* 00 0 1110 */
INVALID, /* 00 0 1111 */ INVALID, /* 00 0 1111: std/stdu */
{ 4, LD+U }, /* 00 1 0000: lwzu */ { 4, LD+U }, /* 00 1 0000: lwzu */
INVALID, /* 00 1 0001 */ INVALID, /* 00 1 0001 */
{ 4, ST+U }, /* 00 1 0010: stwu */ { 4, ST+U }, /* 00 1 0010: stwu */
...@@ -80,12 +80,12 @@ static struct aligninfo aligninfo[128] = { ...@@ -80,12 +80,12 @@ static struct aligninfo aligninfo[128] = {
INVALID, /* 00 1 1101 */ INVALID, /* 00 1 1101 */
INVALID, /* 00 1 1110 */ INVALID, /* 00 1 1110 */
INVALID, /* 00 1 1111 */ INVALID, /* 00 1 1111 */
INVALID, /* 01 0 0000 */ INVALID, /* 01 0 0000: ldx */
INVALID, /* 01 0 0001 */ INVALID, /* 01 0 0001 */
INVALID, /* 01 0 0010 */ INVALID, /* 01 0 0010: stdx */
INVALID, /* 01 0 0011 */ INVALID, /* 01 0 0011 */
INVALID, /* 01 0 0100 */ INVALID, /* 01 0 0100 */
INVALID, /* 01 0 0101: lwax?? */ INVALID, /* 01 0 0101: lwax */
INVALID, /* 01 0 0110 */ INVALID, /* 01 0 0110 */
INVALID, /* 01 0 0111 */ INVALID, /* 01 0 0111 */
{ 0, LD+HARD }, /* 01 0 1000: lswx */ { 0, LD+HARD }, /* 01 0 1000: lswx */
...@@ -96,12 +96,12 @@ static struct aligninfo aligninfo[128] = { ...@@ -96,12 +96,12 @@ static struct aligninfo aligninfo[128] = {
INVALID, /* 01 0 1101 */ INVALID, /* 01 0 1101 */
INVALID, /* 01 0 1110 */ INVALID, /* 01 0 1110 */
INVALID, /* 01 0 1111 */ INVALID, /* 01 0 1111 */
INVALID, /* 01 1 0000 */ INVALID, /* 01 1 0000: ldux */
INVALID, /* 01 1 0001 */ INVALID, /* 01 1 0001 */
INVALID, /* 01 1 0010 */ INVALID, /* 01 1 0010: stdux */
INVALID, /* 01 1 0011 */ INVALID, /* 01 1 0011 */
INVALID, /* 01 1 0100 */ INVALID, /* 01 1 0100 */
INVALID, /* 01 1 0101: lwaux?? */ INVALID, /* 01 1 0101: lwaux */
INVALID, /* 01 1 0110 */ INVALID, /* 01 1 0110 */
INVALID, /* 01 1 0111 */ INVALID, /* 01 1 0111 */
INVALID, /* 01 1 1000 */ INVALID, /* 01 1 1000 */
...@@ -157,9 +157,9 @@ static struct aligninfo aligninfo[128] = { ...@@ -157,9 +157,9 @@ static struct aligninfo aligninfo[128] = {
{ 4, ST+F+S }, /* 11 0 1010: stfsx */ { 4, ST+F+S }, /* 11 0 1010: stfsx */
{ 8, ST+F }, /* 11 0 1011: stfdx */ { 8, ST+F }, /* 11 0 1011: stfdx */
INVALID, /* 11 0 1100 */ INVALID, /* 11 0 1100 */
INVALID, /* 11 0 1101 */ INVALID, /* 11 0 1101: lmd */
INVALID, /* 11 0 1110 */ INVALID, /* 11 0 1110 */
INVALID, /* 11 0 1111 */ INVALID, /* 11 0 1111: stmd */
{ 4, LD+U }, /* 11 1 0000: lwzux */ { 4, LD+U }, /* 11 1 0000: lwzux */
INVALID, /* 11 1 0001 */ INVALID, /* 11 1 0001 */
{ 4, ST+U }, /* 11 1 0010: stwux */ { 4, ST+U }, /* 11 1 0010: stwux */
...@@ -184,7 +184,7 @@ int ...@@ -184,7 +184,7 @@ int
fix_alignment(struct pt_regs *regs) fix_alignment(struct pt_regs *regs)
{ {
int instr, nb, flags; int instr, nb, flags;
#if defined(CONFIG_4xx) #if defined(CONFIG_4xx) || defined(CONFIG_POWER4)
int opcode, f1, f2, f3; int opcode, f1, f2, f3;
#endif #endif
int i, t; int i, t;
...@@ -199,9 +199,11 @@ fix_alignment(struct pt_regs *regs) ...@@ -199,9 +199,11 @@ fix_alignment(struct pt_regs *regs)
CHECK_FULL_REGS(regs); CHECK_FULL_REGS(regs);
#if defined(CONFIG_4xx) #if defined(CONFIG_4xx) || defined(CONFIG_POWER4)
/* The 4xx-family processors have no DSISR register, /* The 4xx-family processors have no DSISR register,
* so we emulate it. * so we emulate it.
* The POWER4 has a DSISR register but doesn't set it on
* an alignment fault. -- paulus
*/ */
instr = *((unsigned int *)regs->nip); instr = *((unsigned int *)regs->nip);
...@@ -209,7 +211,7 @@ fix_alignment(struct pt_regs *regs) ...@@ -209,7 +211,7 @@ fix_alignment(struct pt_regs *regs)
reg = RS(instr); reg = RS(instr);
areg = RA(instr); areg = RA(instr);
if (IS_DFORM(opcode)) { if (!IS_XFORM(opcode)) {
f1 = 0; f1 = 0;
f2 = (instr & 0x04000000) >> 26; f2 = (instr & 0x04000000) >> 26;
f3 = (instr & 0x78000000) >> 27; f3 = (instr & 0x78000000) >> 27;
......
...@@ -52,6 +52,7 @@ main(void) ...@@ -52,6 +52,7 @@ main(void)
DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0])); DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0]));
DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave)); DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave));
DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr)); DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr));
DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr));
#endif /* CONFIG_ALTIVEC */ #endif /* CONFIG_ALTIVEC */
/* Interrupt register frame */ /* Interrupt register frame */
DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD);
...@@ -101,7 +102,7 @@ main(void) ...@@ -101,7 +102,7 @@ main(void)
DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer)); DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer));
DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar));
DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr));
/* The PowerPC 400-class processors have neither the DAR nor the DSISR /* The PowerPC 400-class & Book-E processors have neither the DAR nor the DSISR
* SPRs. Hence, we overload them to hold the similar DEAR and ESR SPRs * SPRs. Hence, we overload them to hold the similar DEAR and ESR SPRs
* for such processors. For critical interrupts we use them to * for such processors. For critical interrupts we use them to
* hold SRR0 and SRR1. * hold SRR0 and SRR1.
......
...@@ -915,7 +915,9 @@ load_up_altivec: ...@@ -915,7 +915,9 @@ load_up_altivec:
/* enable use of AltiVec after return */ /* enable use of AltiVec after return */
oris r9,r9,MSR_VEC@h oris r9,r9,MSR_VEC@h
mfspr r5,SPRG3 /* current task's THREAD (phys) */ mfspr r5,SPRG3 /* current task's THREAD (phys) */
li r4,1
li r10,THREAD_VSCR li r10,THREAD_VSCR
stw r4,THREAD_USED_VR(r5)
lvx vr0,r10,r5 lvx vr0,r10,r5
mtvscr vr0 mtvscr vr0
REST_32VR(0,r10,r5) REST_32VR(0,r10,r5)
......
...@@ -1379,7 +1379,7 @@ _GLOBAL(sys_call_table) ...@@ -1379,7 +1379,7 @@ _GLOBAL(sys_call_table)
.long sys_clock_gettime .long sys_clock_gettime
.long sys_clock_getres .long sys_clock_getres
.long sys_clock_nanosleep .long sys_clock_nanosleep
.long sys_ni_syscall /* reserved for swapcontext */ .long sys_swapcontext
.long sys_tgkill /* 250 */ .long sys_tgkill /* 250 */
.long sys_utimes .long sys_utimes
.long sys_statfs64 .long sys_statfs64
......
...@@ -415,6 +415,7 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) ...@@ -415,6 +415,7 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp)
memset(current->thread.vr, 0, sizeof(current->thread.vr)); memset(current->thread.vr, 0, sizeof(current->thread.vr));
memset(&current->thread.vscr, 0, sizeof(current->thread.vscr)); memset(&current->thread.vscr, 0, sizeof(current->thread.vscr));
current->thread.vrsave = 0; current->thread.vrsave = 0;
current->thread.used_vr = 0;
#endif /* CONFIG_ALTIVEC */ #endif /* CONFIG_ALTIVEC */
} }
......
...@@ -41,18 +41,6 @@ extern void sigreturn_exit(struct pt_regs *); ...@@ -41,18 +41,6 @@ extern void sigreturn_exit(struct pt_regs *);
#define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs)) #define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
/*
* These are the flags in the MSR that the user is allowed to change
* by modifying the saved value of the MSR on the stack. SE and BE
* should not be in this list since gdb may want to change these. I.e,
* you should be able to step out of a signal handler to see what
* instruction executes next after the signal handler completes.
* Alternately, if you stepped into a signal handler, you should be
* able to continue 'til the next breakpoint from within the signal
* handler, even if the handler returns.
*/
#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1)
int do_signal(sigset_t *oldset, struct pt_regs *regs); int do_signal(sigset_t *oldset, struct pt_regs *regs);
/* /*
...@@ -72,8 +60,8 @@ sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, ...@@ -72,8 +60,8 @@ sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7,
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->sighand->siglock);
regs->result = -EINTR; regs->result = -EINTR;
regs->ccr |= 0x10000000;
regs->gpr[3] = EINTR; regs->gpr[3] = EINTR;
regs->ccr |= 0x10000000;
while (1) { while (1) {
current->state = TASK_INTERRUPTIBLE; current->state = TASK_INTERRUPTIBLE;
schedule(); schedule();
...@@ -103,8 +91,8 @@ sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int p4, ...@@ -103,8 +91,8 @@ sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int p4,
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->sighand->siglock);
regs->result = -EINTR; regs->result = -EINTR;
regs->ccr |= 0x10000000;
regs->gpr[3] = EINTR; regs->gpr[3] = EINTR;
regs->ccr |= 0x10000000;
while (1) { while (1) {
current->state = TASK_INTERRUPTIBLE; current->state = TASK_INTERRUPTIBLE;
schedule(); schedule();
...@@ -164,305 +152,389 @@ sys_sigaction(int sig, const struct old_sigaction __user *act, ...@@ -164,305 +152,389 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
* *
*/ */
struct sigregs { struct sigregs {
elf_gregset_t gp_regs; struct mcontext mctx; /* all the register values */
double fp_regs[ELF_NFPREG];
unsigned long tramp[2];
/* Programs using the rs6000/xcoff abi can save up to 19 gp regs /* Programs using the rs6000/xcoff abi can save up to 19 gp regs
and 18 fp regs below sp before decrementing it. */ and 18 fp regs below sp before decrementing it. */
int abigap[56]; int abigap[56];
}; };
struct rt_sigframe /* We use the mc_pad field for the signal return trampoline. */
{ #define tramp mc_pad
unsigned long _unused[2];
struct siginfo *pinfo;
void *puc;
struct siginfo info;
struct ucontext uc;
};
/* /*
* When we have rt signals to deliver, we set up on the * When we have rt signals to deliver, we set up on the
* user stack, going down from the original stack pointer: * user stack, going down from the original stack pointer:
* a sigregs struct * one rt_sigframe struct (siginfo + ucontext + ABI gap)
* one rt_sigframe struct (siginfo + ucontext) * a gap of __SIGNAL_FRAMESIZE+16 bytes
* a gap of __SIGNAL_FRAMESIZE bytes * (the +16 is to get the siginfo and ucontext in the same
* positions as in older kernels).
* *
* Each of these things must be a multiple of 16 bytes in size. * Each of these things must be a multiple of 16 bytes in size.
* *
*/ */
int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, struct rt_sigframe
struct pt_regs *regs)
{ {
struct rt_sigframe __user *rt_sf; struct siginfo info;
struct sigcontext sigctx; struct ucontext uc;
struct sigregs __user *sr; /* Programs using the rs6000/xcoff abi can save up to 19 gp regs
elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ and 18 fp regs below sp before decrementing it. */
sigset_t set; int abigap[56];
stack_t st; };
rt_sf = (struct rt_sigframe __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx))
|| copy_from_user(&set, &rt_sf->uc.uc_sigmask, sizeof(set))
|| copy_from_user(&st, &rt_sf->uc.uc_stack, sizeof(st)))
goto badframe;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (regs->msr & MSR_FP)
giveup_fpu(current);
/* restore registers - /*
* sigctx is initialized to point to the * Save the current user registers on the user stack.
* preamble frame (where registers are stored) * We only save the altivec registers if the process has used
* see handle_signal() * altivec instructions at some point.
*/ */
sr = (struct sigregs __user *) sigctx.regs; static int
if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) save_user_regs(struct pt_regs *regs, struct mcontext *frame, int sigret)
goto badframe;
saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE)
| (saved_regs[PT_MSR] & MSR_USERCHANGE);
memcpy(regs, saved_regs, GP_REGS_SIZE);
if (copy_from_user(current->thread.fpr, &sr->fp_regs,
sizeof(sr->fp_regs)))
goto badframe;
sigreturn_exit(regs); /* doesn't return here */
return 0;
badframe:
do_exit(SIGSEGV);
}
static void
setup_rt_frame(struct pt_regs *regs, struct sigregs __user *frame,
signed long newsp)
{ {
struct rt_sigframe __user *rt_sf = (struct rt_sigframe __user *) newsp; /* save general and floating-point registers */
/* Set up preamble frame */
if (verify_area(VERIFY_WRITE, frame, sizeof(*frame)))
goto badframe;
CHECK_FULL_REGS(regs); CHECK_FULL_REGS(regs);
if (regs->msr & MSR_FP) if (regs->msr & MSR_FP)
giveup_fpu(current); giveup_fpu(current);
if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) if (__copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE)
|| __copy_to_user(&frame->fp_regs, current->thread.fpr, || __copy_to_user(&frame->mc_fregs, current->thread.fpr,
ELF_NFPREG * sizeof(double)) ELF_NFPREG * sizeof(double)))
/* Set up to return from user space. return 1;
It calls the sc exception at offset 0x9999
for sys_rt_sigreturn().
*/
|| __put_user(0x38000000UL + __NR_rt_sigreturn, &frame->tramp[0])
|| __put_user(0x44000002UL, &frame->tramp[1])) /* sc */
goto badframe;
flush_icache_range((unsigned long) &frame->tramp[0],
(unsigned long) &frame->tramp[2]);
current->thread.fpscr = 0; /* turn off all fp exceptions */ current->thread.fpscr = 0; /* turn off all fp exceptions */
/* Retrieve rt_sigframe from stack and #ifdef CONFIG_ALTIVEC
set up registers for signal handler /* save altivec registers */
*/ if (current->thread.used_vr) {
newsp -= __SIGNAL_FRAMESIZE; if (regs->msr & MSR_VEC)
if (put_user(regs->gpr[1], (unsigned long __user *)newsp) giveup_altivec(current);
|| get_user(regs->nip, &rt_sf->uc.uc_mcontext.handler) if (__copy_to_user(&frame->mc_vregs, current->thread.vr,
|| get_user(regs->gpr[3], &rt_sf->uc.uc_mcontext.signal) ELF_NVRREG * sizeof(vector128)))
|| get_user(regs->gpr[4], (unsigned long __user *)&rt_sf->pinfo) return 1;
|| get_user(regs->gpr[5], (unsigned long __user *)&rt_sf->puc)) /* set MSR_VEC in the saved MSR value to indicate that
goto badframe; frame->mc_vregs contains valid data */
if (__put_user(regs->msr | MSR_VEC, &frame->mc_gregs[PT_MSR]))
return 1;
}
/* else assert((regs->msr & MSR_VEC) == 0) */
regs->gpr[1] = newsp; /* We always copy to/from vrsave, it's 0 if we don't have or don't
regs->gpr[6] = (unsigned long) rt_sf; * use altivec. Since VSCR only contains 32 bits saved in the least
regs->link = (unsigned long) frame->tramp; * significant bits of a vector, we "cheat" and stuff VRSAVE in the
* most significant bits of that same vector. --BenH
*/
if (__put_user(current->thread.vrsave, (u32 *)&frame->mc_vregs[32]))
return 1;
#endif /* CONFIG_ALTIVEC */
return; if (sigret) {
/* Set up the sigreturn trampoline: li r0,sigret; sc */
if (__put_user(0x38000000UL + sigret, &frame->tramp[0])
|| __put_user(0x44000002UL, &frame->tramp[1]))
return 1;
flush_icache_range((unsigned long) &frame->tramp[0],
(unsigned long) &frame->tramp[2]);
}
badframe: return 0;
#if DEBUG_SIG
printk("badframe in setup_rt_frame, regs=%p frame=%p newsp=%lx\n",
regs, frame, newsp);
#endif
do_exit(SIGSEGV);
} }
/* /*
* Do a signal return; undo the signal stack. * Restore the current user register values from the user stack,
* (except for MSR).
*/ */
int sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, static int
struct pt_regs *regs) restore_user_regs(struct pt_regs *regs, struct mcontext __user *sr)
{ {
struct sigcontext __user *sc; #ifdef CONFIG_ALTIVEC
struct sigcontext sigctx; unsigned long msr;
struct sigregs __user *sr; #endif
elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */
sigset_t set;
sc = (struct sigcontext __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); /* copy up to but not including MSR */
if (copy_from_user(&sigctx, sc, sizeof(sigctx))) if (__copy_from_user(regs, &sr->mc_gregs, PT_MSR * sizeof(elf_greg_t)))
goto badframe; return 1;
/* copy from orig_r3 (the word after the MSR) up to the end */
if (__copy_from_user(&regs->orig_gpr3, &sr->mc_gregs[PT_ORIG_R3],
GP_REGS_SIZE - PT_ORIG_R3 * sizeof(elf_greg_t)))
return 1;
set.sig[0] = sigctx.oldmask; /* force the process to reload the FP registers from
#if _NSIG_WORDS > 1 current->thread when it next does FP instructions */
set.sig[1] = sigctx._unused[3]; regs->msr &= ~MSR_FP;
#endif if (__copy_from_user(current->thread.fpr, &sr->mc_fregs,
sigdelsetmask(&set, ~_BLOCKABLE); sizeof(sr->mc_fregs)))
spin_lock_irq(&current->sighand->siglock); return 1;
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (regs->msr & MSR_FP )
giveup_fpu(current);
/* restore registers */ #ifdef CONFIG_ALTIVEC
sr = (struct sigregs __user *) sigctx.regs; /* force the process to reload the altivec registers from
if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) current->thread when it next does altivec instructions */
goto badframe; regs->msr &= ~MSR_VEC;
saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_VEC) != 0) {
| (saved_regs[PT_MSR] & MSR_USERCHANGE); /* restore altivec registers from the stack */
memcpy(regs, saved_regs, GP_REGS_SIZE); if (__copy_from_user(current->thread.vr, &sr->mc_vregs,
sizeof(sr->mc_vregs)))
return 1;
} else if (current->thread.used_vr)
memset(&current->thread.vr, 0, sizeof(current->thread.vr));
if (copy_from_user(current->thread.fpr, &sr->fp_regs, /* Always get VRSAVE back */
sizeof(sr->fp_regs))) if (__get_user(current->thread.vrsave, (u32 *)&sr->mc_vregs[32]))
goto badframe; return 1;
#endif /* CONFIG_ALTIVEC */
sigreturn_exit(regs); /* doesn't return here */
return 0; return 0;
}
badframe: /*
do_exit(SIGSEGV); * Restore the user process's signal mask
*/
static void
restore_sigmask(sigset_t *set)
{
sigdelsetmask(set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = *set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
} }
/* /*
* Set up a signal frame. * Set up a signal frame for a "real-time" signal handler
* (one which gets siginfo).
*/ */
static void static void
setup_frame(struct pt_regs *regs, struct sigregs __user *frame, handle_rt_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, sigset_t *oldset, struct pt_regs * regs,
unsigned long newsp) unsigned long newsp)
{ {
struct sigcontext __user *sc = (struct sigcontext __user *) newsp; struct rt_sigframe __user *rt_sf;
struct mcontext __user *frame;
unsigned long origsp = newsp;
/* Set up Signal Frame */
/* Put a Real Time Context onto stack */
newsp -= sizeof(*rt_sf);
rt_sf = (struct rt_sigframe __user *) newsp;
/* create a stack frame for the caller of the handler */
newsp -= __SIGNAL_FRAMESIZE + 16;
if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) if (verify_area(VERIFY_WRITE, (void __user *) newsp, origsp - newsp))
goto badframe; goto badframe;
CHECK_FULL_REGS(regs);
if (regs->msr & MSR_FP) /* Put the siginfo & fill in most of the ucontext */
giveup_fpu(current); if (copy_siginfo_to_user(&rt_sf->info, info)
if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) || __put_user(0, &rt_sf->uc.uc_flags)
|| __copy_to_user(&frame->fp_regs, current->thread.fpr, || __put_user(0, &rt_sf->uc.uc_link)
ELF_NFPREG * sizeof(double)) || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp)
|| __put_user(0x38000000UL + __NR_sigreturn, &frame->tramp[0]) || __put_user(sas_ss_flags(regs->gpr[1]),
|| __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ &rt_sf->uc.uc_stack.ss_flags)
|| __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size)
|| __put_user(&rt_sf->uc.uc_mcontext, &rt_sf->uc.uc_regs)
|| __copy_to_user(&rt_sf->uc.uc_oldsigmask, oldset, sizeof(*oldset))
|| __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset)))
goto badframe; goto badframe;
flush_icache_range((unsigned long) &frame->tramp[0],
(unsigned long) &frame->tramp[2]);
current->thread.fpscr = 0; /* turn off all fp exceptions */
newsp -= __SIGNAL_FRAMESIZE; /* Save user registers on the stack */
if (put_user(regs->gpr[1], (unsigned long __user *)newsp) frame = &rt_sf->uc.uc_mcontext;
|| get_user(regs->nip, &sc->handler) if (save_user_regs(regs, frame, __NR_rt_sigreturn))
|| get_user(regs->gpr[3], &sc->signal)) goto badframe;
if (put_user(regs->gpr[1], (unsigned long __user *)newsp))
goto badframe; goto badframe;
regs->gpr[1] = newsp; regs->gpr[1] = newsp;
regs->gpr[4] = (unsigned long) sc; regs->gpr[3] = sig;
regs->gpr[4] = (unsigned long) &rt_sf->info;
regs->gpr[5] = (unsigned long) &rt_sf->uc;
regs->gpr[6] = (unsigned long) rt_sf;
regs->nip = (unsigned long) ka->sa.sa_handler;
regs->link = (unsigned long) frame->tramp; regs->link = (unsigned long) frame->tramp;
regs->trap = 0;
return; return;
badframe: badframe:
#if DEBUG_SIG #if DEBUG_SIG
printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", printk("badframe in handle_rt_signal, regs=%p frame=%p newsp=%lx\n",
regs, frame, newsp); regs, frame, newsp);
#endif #endif
if (sig == SIGSEGV)
ka->sa.sa_handler = SIG_DFL;
force_sig(SIGSEGV, current);
}
static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs)
{
sigset_t set;
if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(set)))
return -EFAULT;
restore_sigmask(&set);
if (restore_user_regs(regs, &ucp->uc_mcontext))
return -EFAULT;
return 0;
}
int sys_swapcontext(struct ucontext __user *old_ctx,
struct ucontext __user *new_ctx,
int r5, int r6, int r7, int r8, struct pt_regs *regs)
{
unsigned char tmp;
if (old_ctx != NULL) {
if (verify_area(VERIFY_WRITE, old_ctx, sizeof(*old_ctx))
|| save_user_regs(regs, &old_ctx->uc_mcontext, 0)
|| __copy_to_user(&old_ctx->uc_sigmask,
&current->blocked, sizeof(sigset_t))
/* the next 2 things aren't strictly necessary */
|| __copy_to_user(&old_ctx->uc_oldsigmask,
&current->blocked, sizeof(sigset_t))
|| __put_user(&old_ctx->uc_mcontext, &old_ctx->uc_regs))
return -EFAULT;
}
if (new_ctx == NULL)
return 0;
if (verify_area(VERIFY_READ, new_ctx, sizeof(*new_ctx))
|| __get_user(tmp, (u8 *) new_ctx)
|| __get_user(tmp, (u8 *) (new_ctx + 1) - 1))
return -EFAULT;
/*
* If we get a fault copying the context into the kernel's
* image of the user's registers, we can't just return -EFAULT
* because the user's registers will be corrupted. For instance
* the NIP value may have been updated but not some of the
* other registers. Given that we have done the verify_area
* and successfully read the first and last bytes of the region
* above, this should only happen in an out-of-memory situation
* or if another thread unmaps the region containing the context.
* We kill the task with a SIGSEGV in this situation.
*/
if (do_setcontext(new_ctx, regs))
do_exit(SIGSEGV); do_exit(SIGSEGV);
sigreturn_exit(regs);
/* doesn't actually return back to here */
return 0;
}
int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
struct pt_regs *regs)
{
struct rt_sigframe __user *rt_sf;
rt_sf = (struct rt_sigframe __user *)
(regs->gpr[1] + __SIGNAL_FRAMESIZE + 16);
if (verify_area(VERIFY_READ, rt_sf, sizeof(struct rt_sigframe)))
goto bad;
if (do_setcontext(&rt_sf->uc, regs))
goto bad;
/*
* It's not clear whether or why it is desirable to save the
* sigaltstack setting on signal delivery and restore it on
* signal return. But other architectures do this and we have
* always done it up until now so it is probably better not to
* change it. -- paulus
*/
do_sigaltstack(&rt_sf->uc.uc_stack, NULL, regs->gpr[1]);
sigreturn_exit(regs); /* doesn't return here */
return 0;
bad:
force_sig(SIGSEGV, current);
return 0;
} }
/* /*
* OK, we're invoking a handler * OK, we're invoking a handler
*/ */
static void static void
handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, handle_signal(unsigned long sig, struct k_sigaction *ka,
struct pt_regs * regs, unsigned long *newspp, unsigned long frame) siginfo_t *info, sigset_t *oldset, struct pt_regs * regs,
unsigned long newsp)
{ {
struct sigcontext __user *sc; struct sigcontext __user *sc;
struct rt_sigframe __user *rt_sf; struct sigregs __user *frame;
struct k_sigaction *ka = &current->sighand->action[sig-1]; unsigned long origsp = newsp;
if (TRAP(regs) == 0x0C00 /* System Call! */
&& ((int)regs->result == -ERESTARTNOHAND ||
(int)regs->result == -ERESTART_RESTARTBLOCK ||
((int)regs->result == -ERESTARTSYS &&
!(ka->sa.sa_flags & SA_RESTART)))) {
if ((int)regs->result == -ERESTART_RESTARTBLOCK)
current_thread_info()->restart_block.fn
= do_no_restart_syscall;
regs->result = -EINTR;
regs->gpr[3] = EINTR;
regs->ccr |= 0x10000000;
}
/* Set up Signal Frame */ /* Set up Signal Frame */
if (ka->sa.sa_flags & SA_SIGINFO) { newsp -= sizeof(struct sigregs);
/* Put a Real Time Context onto stack */ frame = (struct sigregs __user *) newsp;
*newspp -= sizeof(*rt_sf);
rt_sf = (struct rt_sigframe __user *) *newspp;
if (verify_area(VERIFY_WRITE, rt_sf, sizeof(*rt_sf)))
goto badframe;
if (__put_user((unsigned long) ka->sa.sa_handler, &rt_sf->uc.uc_mcontext.handler)
|| __put_user(&rt_sf->info, &rt_sf->pinfo)
|| __put_user(&rt_sf->uc, &rt_sf->puc)
/* Put the siginfo */
|| copy_siginfo_to_user(&rt_sf->info, info)
/* Create the ucontext */
|| __put_user(0, &rt_sf->uc.uc_flags)
|| __put_user(0, &rt_sf->uc.uc_link)
|| __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp)
|| __put_user(sas_ss_flags(regs->gpr[1]),
&rt_sf->uc.uc_stack.ss_flags)
|| __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size)
|| __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset))
/* mcontext.regs points to preamble register frame */
|| __put_user((struct pt_regs *)frame, &rt_sf->uc.uc_mcontext.regs)
|| __put_user(sig, &rt_sf->uc.uc_mcontext.signal))
goto badframe;
} else {
/* Put a sigcontext on the stack */ /* Put a sigcontext on the stack */
*newspp -= sizeof(*sc); newsp -= sizeof(*sc);
sc = (struct sigcontext __user *) *newspp; sc = (struct sigcontext __user *) newsp;
if (verify_area(VERIFY_WRITE, sc, sizeof(*sc)))
/* create a stack frame for the caller of the handler */
newsp -= __SIGNAL_FRAMESIZE;
if (verify_area(VERIFY_WRITE, (void *) newsp, origsp - newsp))
goto badframe; goto badframe;
#if _NSIG != 64
#error "Please adjust handle_signal()"
#endif
if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler)
|| __put_user(oldset->sig[0], &sc->oldmask) || __put_user(oldset->sig[0], &sc->oldmask)
#if _NSIG_WORDS > 1
|| __put_user(oldset->sig[1], &sc->_unused[3]) || __put_user(oldset->sig[1], &sc->_unused[3])
#endif
|| __put_user((struct pt_regs *)frame, &sc->regs) || __put_user((struct pt_regs *)frame, &sc->regs)
|| __put_user(sig, &sc->signal)) || __put_user(sig, &sc->signal))
goto badframe; goto badframe;
}
if (ka->sa.sa_flags & SA_ONESHOT) if (save_user_regs(regs, &frame->mctx, __NR_sigreturn))
ka->sa.sa_handler = SIG_DFL; goto badframe;
if (put_user(regs->gpr[1], (unsigned long *)newsp))
goto badframe;
regs->gpr[1] = newsp;
regs->gpr[3] = sig;
regs->gpr[4] = (unsigned long) sc;
regs->nip = (unsigned long) ka->sa.sa_handler;
regs->link = (unsigned long) frame->mctx.tramp;
regs->trap = 0;
if (!(ka->sa.sa_flags & SA_NODEFER)) {
spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
sigaddset(&current->blocked,sig);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
return; return;
badframe: badframe:
#if DEBUG_SIG #if DEBUG_SIG
printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n", printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n",
regs, frame, *newspp); regs, frame, *newspp);
printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset);
#endif #endif
do_exit(SIGSEGV); if (sig == SIGSEGV)
ka->sa.sa_handler = SIG_DFL;
force_sig(SIGSEGV, current);
}
/*
* Do a signal return; undo the signal stack.
*/
int sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
struct pt_regs *regs)
{
struct sigcontext __user *sc;
struct sigcontext sigctx;
struct mcontext __user *sr;
sigset_t set;
sc = (struct sigcontext __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
if (copy_from_user(&sigctx, sc, sizeof(sigctx)))
goto badframe;
set.sig[0] = sigctx.oldmask;
set.sig[1] = sigctx._unused[3];
restore_sigmask(&set);
sr = (struct mcontext *) sigctx.regs;
if (verify_area(VERIFY_READ, sr, sizeof(*sr))
|| restore_user_regs(regs, sr))
goto badframe;
sigreturn_exit(regs); /* doesn't return */
return 0;
badframe:
force_sig(SIGSEGV, current);
return 0;
} }
/* /*
...@@ -475,7 +547,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) ...@@ -475,7 +547,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
siginfo_t info; siginfo_t info;
struct k_sigaction *ka; struct k_sigaction *ka;
unsigned long frame, newsp; unsigned long frame, newsp;
int signr; int signr, ret;
if (!oldset) if (!oldset)
oldset = &current->blocked; oldset = &current->blocked;
...@@ -483,40 +555,65 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) ...@@ -483,40 +555,65 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
newsp = frame = 0; newsp = frame = 0;
signr = get_signal_to_deliver(&info, regs, NULL); signr = get_signal_to_deliver(&info, regs, NULL);
if (signr > 0) {
ka = &current->sighand->action[signr-1];
if ( (ka->sa.sa_flags & SA_ONSTACK)
&& (! on_sig_stack(regs->gpr[1])))
newsp = (current->sas_ss_sp + current->sas_ss_size);
else
newsp = regs->gpr[1];
newsp = frame = newsp - sizeof(struct sigregs);
/* Whee! Actually deliver the signal. */ ka = (signr == 0)? NULL: &current->sighand->action[signr-1];
handle_signal(signr, &info, oldset, regs, &newsp, frame);
}
if (TRAP(regs) == 0x0C00) { /* System Call! */ if (TRAP(regs) == 0x0C00 /* System Call! */
if ((int)regs->result == -ERESTARTNOHAND || && regs->ccr & 0x10000000 /* error signalled */
(int)regs->result == -ERESTARTSYS || && ((ret = regs->gpr[3]) == ERESTARTSYS
(int)regs->result == -ERESTARTNOINTR) { || ret == ERESTARTNOHAND || ret == ERESTARTNOINTR
regs->gpr[3] = regs->orig_gpr3; || ret == ERESTART_RESTARTBLOCK)) {
if (signr > 0
&& (ret == ERESTARTNOHAND || ret == ERESTART_RESTARTBLOCK
|| (ret == ERESTARTSYS
&& !(ka->sa.sa_flags & SA_RESTART)))) {
/* make the system call return an EINTR error */
regs->result = -EINTR;
regs->gpr[3] = EINTR;
/* note that the cr0.SO bit is already set */
/* clear any restart function that was set */
if (ret == ERESTART_RESTARTBLOCK)
current_thread_info()->restart_block.fn
= do_no_restart_syscall;
} else {
regs->nip -= 4; /* Back up & retry system call */ regs->nip -= 4; /* Back up & retry system call */
regs->result = 0; regs->result = 0;
} else if ((int)regs->result == -ERESTART_RESTARTBLOCK) { regs->trap = 0;
if (ret == ERESTART_RESTARTBLOCK)
regs->gpr[0] = __NR_restart_syscall; regs->gpr[0] = __NR_restart_syscall;
regs->nip -= 4; else
regs->result = 0; regs->gpr[3] = regs->orig_gpr3;
} }
} }
if (newsp == frame) if (signr == 0)
return 0; /* no signals delivered */ return 0; /* no signals delivered */
if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size
&& !on_sig_stack(regs->gpr[1]))
newsp = current->sas_ss_sp + current->sas_ss_size;
else
newsp = regs->gpr[1];
newsp &= ~0xfUL;
/* Whee! Actually deliver the signal. */
if (ka->sa.sa_flags & SA_SIGINFO) if (ka->sa.sa_flags & SA_SIGINFO)
setup_rt_frame(regs, (struct sigregs __user *) frame, newsp); handle_rt_signal(signr, ka, &info, oldset, regs, newsp);
else else
setup_frame(regs, (struct sigregs __user *) frame, newsp); handle_signal(signr, ka, &info, oldset, regs, newsp);
if (ka->sa.sa_flags & SA_ONESHOT)
ka->sa.sa_handler = SIG_DFL;
if (!(ka->sa.sa_flags & SA_NODEFER)) {
spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
sigaddset(&current->blocked, signr);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
return 1; return 1;
} }
...@@ -95,14 +95,19 @@ void die(const char * str, struct pt_regs * fp, long err) ...@@ -95,14 +95,19 @@ void die(const char * str, struct pt_regs * fp, long err)
} }
void void
_exception(int signr, struct pt_regs *regs) _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
{ {
if (!user_mode(regs)) siginfo_t info;
{
if (!user_mode(regs)) {
debugger(regs); debugger(regs);
die("Exception in kernel mode", regs, signr); die("Exception in kernel mode", regs, signr);
} }
force_sig(signr, current); info.si_signo = signr;
info.si_errno = 0;
info.si_code = code;
info.si_addr = (void *) addr;
force_sig_info(signr, &info, current);
} }
/* /*
...@@ -154,12 +159,40 @@ static inline int check_io_access(struct pt_regs *regs) ...@@ -154,12 +159,40 @@ static inline int check_io_access(struct pt_regs *regs)
return 0; return 0;
} }
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
/* On 4xx, the reason for the machine check or program exception
is in the ESR. */
#define get_reason(regs) ((regs)->dsisr)
#define REASON_FP 0
#define REASON_ILLEGAL ESR_PIL
#define REASON_PRIVILEGED ESR_PPR
#define REASON_TRAP ESR_PTR
/* single-step stuff */
#define single_stepping(regs) (current->thread.dbcr0 & DBCR0_IC)
#define clear_single_step(regs) (current->thread.dbcr0 &= ~DBCR0_IC)
#else
/* On non-4xx, the reason for the machine check or program
exception is in the MSR. */
#define get_reason(regs) ((regs)->msr)
#define REASON_FP 0x100000
#define REASON_ILLEGAL 0x80000
#define REASON_PRIVILEGED 0x40000
#define REASON_TRAP 0x20000
#define single_stepping(regs) ((regs)->msr & MSR_SE)
#define clear_single_step(regs) ((regs)->msr &= ~MSR_SE)
#endif
void void
MachineCheckException(struct pt_regs *regs) MachineCheckException(struct pt_regs *regs)
{ {
unsigned long reason = get_reason(regs);
if (user_mode(regs)) { if (user_mode(regs)) {
regs->msr |= MSR_RI; regs->msr |= MSR_RI;
_exception(SIGSEGV, regs); _exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
return; return;
} }
...@@ -178,10 +211,18 @@ MachineCheckException(struct pt_regs *regs) ...@@ -178,10 +211,18 @@ MachineCheckException(struct pt_regs *regs)
if (check_io_access(regs)) if (check_io_access(regs))
return; return;
#ifndef CONFIG_4xx #ifdef CONFIG_4xx
printk(KERN_CRIT "Machine check in kernel mode.\n"); if (reason & ESR_IMCP) {
printk(KERN_CRIT "Caused by (from SRR1=%lx): ", regs->msr); printk("Instruction");
switch (regs->msr & 0x601F0000) { mtspr(SPRN_ESR, reason & ~ESR_IMCP);
} else
printk("Data");
printk(" machine check in kernel mode.\n");
#else /* !CONFIG_4xx */
printk("Machine check in kernel mode.\n");
printk("Caused by (from SRR1=%lx): ", reason);
switch (reason & 0x601F0000) {
case 0x80000: case 0x80000:
printk("Machine check signal\n"); printk("Machine check signal\n");
break; break;
...@@ -208,15 +249,6 @@ MachineCheckException(struct pt_regs *regs) ...@@ -208,15 +249,6 @@ MachineCheckException(struct pt_regs *regs)
default: default:
printk("Unknown values in msr\n"); printk("Unknown values in msr\n");
} }
#else /* CONFIG_4xx */
/* Note that the ESR gets stored in regs->dsisr on 4xx. */
if (regs->dsisr & ESR_MCI) {
printk(KERN_CRIT "Instruction");
mtspr(SPRN_ESR, regs->dsisr & ~ESR_MCI);
} else
printk(KERN_CRIT "Data");
printk(" machine check in kernel mode.\n");
#endif /* CONFIG_4xx */ #endif /* CONFIG_4xx */
debugger(regs); debugger(regs);
...@@ -238,7 +270,7 @@ UnknownException(struct pt_regs *regs) ...@@ -238,7 +270,7 @@ UnknownException(struct pt_regs *regs)
{ {
printk("Bad trap at PC: %lx, MSR: %lx, vector=%lx %s\n", printk("Bad trap at PC: %lx, MSR: %lx, vector=%lx %s\n",
regs->nip, regs->msr, regs->trap, print_tainted()); regs->nip, regs->msr, regs->trap, print_tainted());
_exception(SIGTRAP, regs); _exception(SIGTRAP, regs, 0, 0);
} }
void void
...@@ -246,13 +278,13 @@ InstructionBreakpoint(struct pt_regs *regs) ...@@ -246,13 +278,13 @@ InstructionBreakpoint(struct pt_regs *regs)
{ {
if (debugger_iabr_match(regs)) if (debugger_iabr_match(regs))
return; return;
_exception(SIGTRAP, regs); _exception(SIGTRAP, regs, TRAP_BRKPT, 0);
} }
void void
RunModeException(struct pt_regs *regs) RunModeException(struct pt_regs *regs)
{ {
_exception(SIGTRAP, regs); _exception(SIGTRAP, regs, 0, 0);
} }
/* Illegal instruction emulation support. Originally written to /* Illegal instruction emulation support. Originally written to
...@@ -271,17 +303,17 @@ RunModeException(struct pt_regs *regs) ...@@ -271,17 +303,17 @@ RunModeException(struct pt_regs *regs)
static int static int
emulate_instruction(struct pt_regs *regs) emulate_instruction(struct pt_regs *regs)
{ {
uint instword; u32 instword;
uint rd; u32 rd;
uint retval; int retval;
retval = EINVAL; retval = -EINVAL;
if (!user_mode(regs)) if (!user_mode(regs))
return retval; return retval;
CHECK_FULL_REGS(regs); CHECK_FULL_REGS(regs);
if (get_user(instword, (uint __user *)(regs->nip))) if (get_user(instword, (u32 __user *)(regs->nip)))
return -EFAULT; return -EFAULT;
/* Emulate the mfspr rD, PVR. /* Emulate the mfspr rD, PVR.
...@@ -290,10 +322,23 @@ emulate_instruction(struct pt_regs *regs) ...@@ -290,10 +322,23 @@ emulate_instruction(struct pt_regs *regs)
rd = (instword >> 21) & 0x1f; rd = (instword >> 21) & 0x1f;
regs->gpr[rd] = mfspr(PVR); regs->gpr[rd] = mfspr(PVR);
retval = 0; retval = 0;
}
if (retval == 0)
regs->nip += 4; regs->nip += 4;
return(retval); }
return retval;
}
/*
* After we have successfully emulated an instruction, we have to
* check if the instruction was being single-stepped, and if so,
* pretend we got a single-step exception. This was pointed out
* by Kumar Gala. -- paulus
*/
static void emulate_single_step(struct pt_regs *regs)
{
if (single_stepping(regs)) {
clear_single_step(regs);
_exception(SIGTRAP, regs, TRAP_TRACE, 0);
}
} }
/* /*
...@@ -349,29 +394,47 @@ check_bug_trap(struct pt_regs *regs) ...@@ -349,29 +394,47 @@ check_bug_trap(struct pt_regs *regs)
void void
ProgramCheckException(struct pt_regs *regs) ProgramCheckException(struct pt_regs *regs)
{ {
int errcode; unsigned int reason = get_reason(regs);
#if defined(CONFIG_4xx)
unsigned int esr = regs->dsisr;
int isbpt = esr & ESR_PTR;
extern int do_mathemu(struct pt_regs *regs); extern int do_mathemu(struct pt_regs *regs);
#ifdef CONFIG_MATH_EMULATION #ifdef CONFIG_MATH_EMULATION
if (!isbpt && do_mathemu(regs) == 0) /* (reason & REASON_ILLEGAL) would be the obvious thing here,
* but there seems to be a hardware bug on the 405GP (RevD)
* that means ESR is sometimes set incorrectly - either to
* ESR_DST (!?) or 0. In the process of chasing this with the
* hardware people - not sure if it can happen on any illegal
* instruction or only on FP instructions, whether there is a
* pattern to occurences etc. -dgibson 31/Mar/2003 */
if (!(reason & REASON_TRAP) && do_mathemu(regs) == 0) {
emulate_single_step(regs);
return; return;
}
#endif /* CONFIG_MATH_EMULATION */ #endif /* CONFIG_MATH_EMULATION */
#else /* ! CONFIG_4xx */ if (reason & REASON_FP) {
int isbpt = regs->msr & 0x20000;
if (regs->msr & 0x100000) {
/* IEEE FP exception */ /* IEEE FP exception */
_exception(SIGFPE, regs); int code = 0;
u32 fpscr;
if (regs->msr & MSR_FP)
giveup_fpu(current);
fpscr = current->thread.fpscr;
fpscr &= fpscr << 22; /* mask summary bits with enables */
if (fpscr & FPSCR_VX)
code = FPE_FLTINV;
else if (fpscr & FPSCR_OX)
code = FPE_FLTOVF;
else if (fpscr & FPSCR_UX)
code = FPE_FLTUND;
else if (fpscr & FPSCR_ZX)
code = FPE_FLTDIV;
else if (fpscr & FPSCR_XX)
code = FPE_FLTRES;
_exception(SIGFPE, regs, code, regs->nip);
return; return;
} }
#endif /* ! CONFIG_4xx */
if (isbpt) { if (reason & REASON_TRAP) {
/* trap exception */ /* trap exception */
if (debugger_bpt(regs)) if (debugger_bpt(regs))
return; return;
...@@ -379,17 +442,21 @@ ProgramCheckException(struct pt_regs *regs) ...@@ -379,17 +442,21 @@ ProgramCheckException(struct pt_regs *regs)
regs->nip += 4; regs->nip += 4;
return; return;
} }
_exception(SIGTRAP, regs); _exception(SIGTRAP, regs, TRAP_BRKPT, 0);
return; return;
} }
if (reason & REASON_PRIVILEGED) {
/* Try to emulate it if we should. */ /* Try to emulate it if we should. */
if ((errcode = emulate_instruction(regs))) { if (emulate_instruction(regs) == 0) {
if (errcode == -EFAULT) emulate_single_step(regs);
_exception(SIGBUS, regs); return;
else }
_exception(SIGILL, regs); _exception(SIGILL, regs, ILL_PRVOPC, regs->nip);
return;
} }
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
} }
void void
...@@ -398,7 +465,7 @@ SingleStepException(struct pt_regs *regs) ...@@ -398,7 +465,7 @@ SingleStepException(struct pt_regs *regs)
regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */
if (debugger_sstep(regs)) if (debugger_sstep(regs))
return; return;
_exception(SIGTRAP, regs); _exception(SIGTRAP, regs, TRAP_TRACE, 0);
} }
void void
...@@ -414,12 +481,12 @@ AlignmentException(struct pt_regs *regs) ...@@ -414,12 +481,12 @@ AlignmentException(struct pt_regs *regs)
if (fixed == -EFAULT) { if (fixed == -EFAULT) {
/* fixed == -EFAULT means the operand address was bad */ /* fixed == -EFAULT means the operand address was bad */
if (user_mode(regs)) if (user_mode(regs))
force_sig(SIGSEGV, current); _exception(SIGSEGV, regs, SEGV_ACCERR, regs->dar);
else else
bad_page_fault(regs, regs->dar, SIGSEGV); bad_page_fault(regs, regs->dar, SIGSEGV);
return; return;
} }
_exception(SIGBUS, regs); _exception(SIGBUS, regs, BUS_ADRALN, regs->dar);
} }
void void
...@@ -470,16 +537,17 @@ SoftwareEmulation(struct pt_regs *regs) ...@@ -470,16 +537,17 @@ SoftwareEmulation(struct pt_regs *regs)
#endif #endif
if (errcode) { if (errcode) {
if (errcode > 0) if (errcode > 0)
_exception(SIGFPE, regs); _exception(SIGFPE, regs, 0, 0);
else if (errcode == -EFAULT) else if (errcode == -EFAULT)
_exception(SIGSEGV, regs); _exception(SIGSEGV, regs, 0, 0);
else else
_exception(SIGILL, regs); _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
} } else
emulate_single_step(regs);
} }
#endif /* CONFIG_8xx */ #endif /* CONFIG_8xx */
#if defined(CONFIG_4xx) #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
void DebugException(struct pt_regs *regs, unsigned long debug_status) void DebugException(struct pt_regs *regs, unsigned long debug_status)
{ {
...@@ -487,7 +555,7 @@ void DebugException(struct pt_regs *regs, unsigned long debug_status) ...@@ -487,7 +555,7 @@ void DebugException(struct pt_regs *regs, unsigned long debug_status)
if (debug_status & DBSR_TIE) { /* trap instruction*/ if (debug_status & DBSR_TIE) { /* trap instruction*/
if (!user_mode(regs) && debugger_bpt(regs)) if (!user_mode(regs) && debugger_bpt(regs))
return; return;
_exception(SIGTRAP, regs); _exception(SIGTRAP, regs, 0, 0);
} }
#endif #endif
...@@ -495,10 +563,10 @@ void DebugException(struct pt_regs *regs, unsigned long debug_status) ...@@ -495,10 +563,10 @@ void DebugException(struct pt_regs *regs, unsigned long debug_status)
if (!user_mode(regs) && debugger_sstep(regs)) if (!user_mode(regs) && debugger_sstep(regs))
return; return;
current->thread.dbcr0 &= ~DBCR0_IC; current->thread.dbcr0 &= ~DBCR0_IC;
_exception(SIGTRAP, regs); _exception(SIGTRAP, regs, TRAP_TRACE, 0);
} }
} }
#endif /* CONFIG_4xx */ #endif /* CONFIG_4xx || CONFIG_BOOKE */
#if !defined(CONFIG_TAU_INT) #if !defined(CONFIG_TAU_INT)
void void
......
...@@ -276,7 +276,7 @@ static __inline__ int fls(unsigned int x) ...@@ -276,7 +276,7 @@ static __inline__ int fls(unsigned int x)
* Find the first bit set in a 140-bit bitmap. * Find the first bit set in a 140-bit bitmap.
* The first 100 bits are unlikely to be set. * The first 100 bits are unlikely to be set.
*/ */
static inline int sched_find_first_bit(unsigned long *b) static inline int sched_find_first_bit(const unsigned long *b)
{ {
if (unlikely(b[0])) if (unlikely(b[0]))
return __ffs(b[0]); return __ffs(b[0]);
...@@ -295,7 +295,7 @@ static inline int sched_find_first_bit(unsigned long *b) ...@@ -295,7 +295,7 @@ static inline int sched_find_first_bit(unsigned long *b)
* @offset: The bitnumber to start searching at * @offset: The bitnumber to start searching at
* @size: The maximum size to search * @size: The maximum size to search
*/ */
static __inline__ unsigned long find_next_bit(unsigned long *addr, static __inline__ unsigned long find_next_bit(const unsigned long *addr,
unsigned long size, unsigned long offset) unsigned long size, unsigned long offset)
{ {
unsigned int *p = ((unsigned int *) addr) + (offset >> 5); unsigned int *p = ((unsigned int *) addr) + (offset >> 5);
...@@ -352,7 +352,7 @@ static __inline__ unsigned long find_next_bit(unsigned long *addr, ...@@ -352,7 +352,7 @@ static __inline__ unsigned long find_next_bit(unsigned long *addr,
#define find_first_zero_bit(addr, size) \ #define find_first_zero_bit(addr, size) \
find_next_zero_bit((addr), (size), 0) find_next_zero_bit((addr), (size), 0)
static __inline__ unsigned long find_next_zero_bit(unsigned long * addr, static __inline__ unsigned long find_next_zero_bit(const unsigned long *addr,
unsigned long size, unsigned long offset) unsigned long size, unsigned long offset)
{ {
unsigned int * p = ((unsigned int *) addr) + (offset >> 5); unsigned int * p = ((unsigned int *) addr) + (offset >> 5);
...@@ -411,7 +411,7 @@ static __inline__ int ext2_test_bit(int nr, __const__ void * addr) ...@@ -411,7 +411,7 @@ static __inline__ int ext2_test_bit(int nr, __const__ void * addr)
#define ext2_find_first_zero_bit(addr, size) \ #define ext2_find_first_zero_bit(addr, size) \
ext2_find_next_zero_bit((addr), (size), 0) ext2_find_next_zero_bit((addr), (size), 0)
static __inline__ unsigned long ext2_find_next_zero_bit(void *addr, static __inline__ unsigned long ext2_find_next_zero_bit(const void *addr,
unsigned long size, unsigned long offset) unsigned long size, unsigned long offset)
{ {
unsigned int *p = ((unsigned int *) addr) + (offset >> 5); unsigned int *p = ((unsigned int *) addr) + (offset >> 5);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <asm/kmap_types.h> #include <asm/kmap_types.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/page.h>
/* undef for production */ /* undef for production */
#define HIGHMEM_DEBUG 1 #define HIGHMEM_DEBUG 1
...@@ -42,7 +43,7 @@ extern void kmap_init(void) __init; ...@@ -42,7 +43,7 @@ extern void kmap_init(void) __init;
* chunk of RAM. * chunk of RAM.
*/ */
#define PKMAP_BASE CONFIG_HIGHMEM_START #define PKMAP_BASE CONFIG_HIGHMEM_START
#define LAST_PKMAP 1024 #define LAST_PKMAP (1 << PTE_SHIFT)
#define LAST_PKMAP_MASK (LAST_PKMAP-1) #define LAST_PKMAP_MASK (LAST_PKMAP-1)
#define PKMAP_NR(virt) ((virt-PKMAP_BASE) >> PAGE_SHIFT) #define PKMAP_NR(virt) ((virt-PKMAP_BASE) >> PAGE_SHIFT)
#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT)) #define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT))
......
...@@ -848,6 +848,7 @@ struct thread_struct { ...@@ -848,6 +848,7 @@ struct thread_struct {
/* AltiVec status */ /* AltiVec status */
vector128 vscr __attribute((aligned(16))); vector128 vscr __attribute((aligned(16)));
unsigned long vrsave; unsigned long vrsave;
int used_vr; /* set if process has used altivec */
#endif /* CONFIG_ALTIVEC */ #endif /* CONFIG_ALTIVEC */
}; };
......
#ifndef _ASMPPC_UCONTEXT_H #ifndef _ASMPPC_UCONTEXT_H
#define _ASMPPC_UCONTEXT_H #define _ASMPPC_UCONTEXT_H
/* Copied from i386. */ #include <asm/elf.h>
#include <asm/signal.h>
struct mcontext {
elf_gregset_t mc_gregs;
elf_fpregset_t mc_fregs;
unsigned long mc_pad[2];
elf_vrregset_t mc_vregs __attribute__((__aligned__(16)));
};
struct ucontext { struct ucontext {
unsigned long uc_flags; unsigned long uc_flags;
struct ucontext *uc_link; struct ucontext *uc_link;
stack_t uc_stack; stack_t uc_stack;
struct sigcontext uc_mcontext; int uc_pad[7];
sigset_t uc_sigmask; /* mask last for extensibility */ struct mcontext *uc_regs; /* backward compat */
sigset_t uc_oldsigmask; /* backward compat */
int uc_pad2;
sigset_t uc_sigmask;
/* glibc has 1024-bit signal masks, ours are 64-bit */
int uc_maskext[30];
struct mcontext uc_mcontext;
}; };
#endif /* !_ASMPPC_UCONTEXT_H */ #endif /* !_ASMPPC_UCONTEXT_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment