void flush_thread(void) { /* Make sure old user windows don't get in the way. */ flush_user_windows(); current->tss.w_saved = 0; current->tss.uwinmask = 0; current->tss.sig_address = 0; current->tss.sig_desc = 0; current->tss.sstk_info.cur_status = 0; current->tss.sstk_info.the_stack = 0; if(last_task_used_math == current) { /* Clean the fpu. */ put_psr(get_psr() | PSR_EF); fpsave(¤t->tss.float_regs[0], ¤t->tss.fsr, ¤t->tss.fpqueue[0], ¤t->tss.fpqdepth); last_task_used_math = NULL; } memset(¤t->tss.reg_window[0], 0, (sizeof(struct dummy_reg_window) * NSWINS)); memset(¤t->tss.rwbuf_stkptrs[0], 0, (sizeof(unsigned long) * NSWINS)); /* Now, this task is no longer a kernel thread. */ current->tss.flags &= ~SPARC_FLAG_KTHREAD; }
/* Place as many of the user's current register windows * on the stack that we can. Even if the %sp is unaligned * we still copy the window there, the only case that we don't * succeed is if the %sp points to a bum mapping altogether. * setup_frame() and do_sigreturn() use this before shifting * the user stack around. Future instruction and hardware * bug workaround routines will need this functionality as * well. */ void synchronize_user_stack(void) { struct thread_struct *tp = ¤t->tss; int window; flush_user_windows(); if(!tp->w_saved) return; /* Ok, there is some dirty work to do. */ for(window = tp->w_saved - 1; window >= 0; window--) { unsigned long sp = tp->rwbuf_stkptrs[window]; /* See if %sp is reasonable at all. */ if(verify_area(VERIFY_WRITE, (char *) sp, sizeof(struct reg_window))) continue; /* Ok, let it rip. */ memcpy((char *) sp, &tp->reg_window[window], sizeof(struct reg_window)); shift_window_buffer(window, tp->w_saved - 1, tp); tp->w_saved--; } }
/* {set, get}context() needed for 64-bit SparcLinux userland. */ asmlinkage void sparc64_set_context(struct pt_regs *regs) { struct ucontext __user *ucp = (struct ucontext __user *) regs->u_regs[UREG_I0]; mc_gregset_t __user *grp; unsigned long pc, npc, tstate; unsigned long fp, i7; unsigned char fenab; int err; flush_user_windows(); if (get_thread_wsaved() || (((unsigned long)ucp) & (sizeof(unsigned long)-1)) || (!__access_ok(ucp, sizeof(*ucp)))) goto do_sigsegv; grp = &ucp->uc_mcontext.mc_gregs; err = __get_user(pc, &((*grp)[MC_PC])); err |= __get_user(npc, &((*grp)[MC_NPC])); if (err || ((pc | npc) & 3)) goto do_sigsegv; if (regs->u_regs[UREG_I1]) { sigset_t set; if (_NSIG_WORDS == 1) { if (__get_user(set.sig[0], &ucp->uc_sigmask.sig[0])) goto do_sigsegv; } else { if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(sigset_t))) goto do_sigsegv; } set_current_blocked(&set); } if (test_thread_flag(TIF_32BIT)) { pc &= 0xffffffff; npc &= 0xffffffff; } regs->tpc = pc; regs->tnpc = npc; err |= __get_user(regs->y, &((*grp)[MC_Y])); err |= __get_user(tstate, &((*grp)[MC_TSTATE])); regs->tstate &= ~(TSTATE_ASI | TSTATE_ICC | TSTATE_XCC); regs->tstate |= (tstate & (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC)); err |= __get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1])); err |= __get_user(regs->u_regs[UREG_G2], (&(*grp)[MC_G2])); err |= __get_user(regs->u_regs[UREG_G3], (&(*grp)[MC_G3])); err |= __get_user(regs->u_regs[UREG_G4], (&(*grp)[MC_G4])); err |= __get_user(regs->u_regs[UREG_G5], (&(*grp)[MC_G5])); err |= __get_user(regs->u_regs[UREG_G6], (&(*grp)[MC_G6])); /* Skip %g7 as that's the thread register in userspace. */ err |= __get_user(regs->u_regs[UREG_I0], (&(*grp)[MC_O0])); err |= __get_user(regs->u_regs[UREG_I1], (&(*grp)[MC_O1])); err |= __get_user(regs->u_regs[UREG_I2], (&(*grp)[MC_O2])); err |= __get_user(regs->u_regs[UREG_I3], (&(*grp)[MC_O3])); err |= __get_user(regs->u_regs[UREG_I4], (&(*grp)[MC_O4])); err |= __get_user(regs->u_regs[UREG_I5], (&(*grp)[MC_O5])); err |= __get_user(regs->u_regs[UREG_I6], (&(*grp)[MC_O6])); err |= __get_user(regs->u_regs[UREG_I7], (&(*grp)[MC_O7])); err |= __get_user(fp, &(ucp->uc_mcontext.mc_fp)); err |= __get_user(i7, &(ucp->uc_mcontext.mc_i7)); err |= __put_user(fp, (&(((struct reg_window __user *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6]))); err |= __put_user(i7, (&(((struct reg_window __user *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7]))); err |= __get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab)); if (fenab) { unsigned long *fpregs = current_thread_info()->fpregs; unsigned long fprs; fprs_write(0); err |= __get_user(fprs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fprs)); if (fprs & FPRS_DL) err |= copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs), (sizeof(unsigned int) * 32)); if (fprs & FPRS_DU) err |= copy_from_user(fpregs+16, ((unsigned long __user *)&(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs))+16, (sizeof(unsigned int) * 32)); err |= __get_user(current_thread_info()->xfsr[0], &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr)); err |= __get_user(current_thread_info()->gsr[0], &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr)); regs->tstate &= ~TSTATE_PEF; } if (err) goto do_sigsegv; return; do_sigsegv: force_sig(SIGSEGV, current); }
static void ctrl_break(void) { extern int stop_a_enabled; unsigned long timeout; int status, data; int mode; if (!stop_a_enabled) return; pcikbd_write(KBD_DATA_REG, KBD_CMD_DISABLE); if(pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Enter: Disable keyboard: no ACK\n"); /* Save current mode register settings */ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_READ_MODE); if ((mode = pcikbd_wait_for_input()) == -1) printk("Prom Enter: Read Mode: no ACK\n"); /* Disable PC scancode translation */ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE); pcikbd_write(KBD_DATA_REG, mode & ~(KBD_MODE_KCC)); pcikbd_write(KBD_DATA_REG, KBD_CMD_ENABLE); if (pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Enter: Enable Keyboard: no ACK\n"); /* Drop into OBP. * Note that we must flush the user windows * first before giving up control. */ flush_user_windows(); prom_cmdline(); /* Read prom's key up event (use short timeout) */ do { timeout = 10; do { mdelay(1); status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG); if (!(status & KBD_STAT_OBF)) continue; data = pcikbd_inb(pcikbd_iobase + KBD_DATA_REG); if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) continue; break; } while (--timeout > 0); } while (timeout > 0); /* Reenable PC scancode translation */ pcikbd_write(KBD_DATA_REG, KBD_CMD_DISABLE); if(pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Leave: Disable keyboard: no ACK\n"); pcikbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE); pcikbd_write(KBD_DATA_REG, mode); pcikbd_write(KBD_DATA_REG, KBD_CMD_ENABLE); if (pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Leave: Enable Keyboard: no ACK\n"); /* Reset keyboard rate */ write_kbd_rate(parse_kbd_rate(&kbdrate)); }
static int genregs32_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { const struct pt_regs *regs = target->thread.kregs; unsigned long __user *reg_window; unsigned long *k = kbuf; unsigned long __user *u = ubuf; unsigned long reg; if (target == current) flush_user_windows(); pos /= sizeof(reg); count /= sizeof(reg); if (kbuf) { for (; count > 0 && pos < 16; count--) *k++ = regs->u_regs[pos++]; reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; reg_window -= 16; for (; count > 0 && pos < 32; count--) { if (get_user(*k++, ®_window[pos++])) return -EFAULT; } } else { for (; count > 0 && pos < 16; count--) { if (put_user(regs->u_regs[pos++], u++)) return -EFAULT; } reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; reg_window -= 16; for (; count > 0 && pos < 32; count--) { if (get_user(reg, ®_window[pos++]) || put_user(reg, u++)) return -EFAULT; } } while (count > 0) { switch (pos) { case 32: /* PSR */ reg = regs->psr; break; case 33: /* PC */ reg = regs->pc; break; case 34: /* NPC */ reg = regs->npc; break; case 35: /* Y */ reg = regs->y; break; case 36: /* WIM */ case 37: /* TBR */ reg = 0; break; default: goto finish; } if (kbuf) *k++ = reg; else if (put_user(reg, u++)) return -EFAULT; pos++; count--; } finish: pos *= sizeof(reg); count *= sizeof(reg); return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 38 * sizeof(reg), -1); }
static int genregs32_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { struct pt_regs *regs = target->thread.kregs; unsigned long __user *reg_window; const unsigned long *k = kbuf; const unsigned long __user *u = ubuf; unsigned long reg; if (target == current) flush_user_windows(); pos /= sizeof(reg); count /= sizeof(reg); if (kbuf) { for (; count > 0 && pos < 16; count--) regs->u_regs[pos++] = *k++; reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; reg_window -= 16; for (; count > 0 && pos < 32; count--) { if (put_user(*k++, ®_window[pos++])) return -EFAULT; } } else { for (; count > 0 && pos < 16; count--) { if (get_user(reg, u++)) return -EFAULT; regs->u_regs[pos++] = reg; } reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; reg_window -= 16; for (; count > 0 && pos < 32; count--) { if (get_user(reg, u++) || put_user(reg, ®_window[pos++])) return -EFAULT; } } while (count > 0) { unsigned long psr; if (kbuf) reg = *k++; else if (get_user(reg, u++)) return -EFAULT; switch (pos) { case 32: /* PSR */ psr = regs->psr; psr &= ~(PSR_ICC | PSR_SYSCALL); psr |= (reg & (PSR_ICC | PSR_SYSCALL)); regs->psr = psr; break; case 33: /* PC */ regs->pc = reg; break; case 34: /* NPC */ regs->npc = reg; break; case 35: /* Y */ regs->y = reg; break; case 36: /* WIM */ case 37: /* TBR */ break; default: goto finish; } pos++; count--; } finish: pos *= sizeof(reg); count *= sizeof(reg); return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 38 * sizeof(reg), -1); }
/* {set, get}context() needed for 64-bit SparcLinux userland. */ asmlinkage void sparc64_set_context(struct pt_regs *regs) { struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0]; struct thread_struct *tp = ¤t->thread; mc_gregset_t *grp; unsigned long pc, npc, tstate; unsigned long fp, i7; unsigned char fenab; int err; flush_user_windows(); if(tp->w_saved || (((unsigned long)ucp) & (sizeof(unsigned long)-1)) || (!__access_ok((unsigned long)ucp, sizeof(*ucp)))) goto do_sigsegv; grp = &ucp->uc_mcontext.mc_gregs; err = __get_user(pc, &((*grp)[MC_PC])); err |= __get_user(npc, &((*grp)[MC_NPC])); if(err || ((pc | npc) & 3)) goto do_sigsegv; if(regs->u_regs[UREG_I1]) { sigset_t set; if (_NSIG_WORDS == 1) { if (__get_user(set.sig[0], &ucp->uc_sigmask.sig[0])) goto do_sigsegv; } else { if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(sigset_t))) goto do_sigsegv; } sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); } if ((tp->flags & SPARC_FLAG_32BIT) != 0) { pc &= 0xffffffff; npc &= 0xffffffff; } regs->tpc = pc; regs->tnpc = npc; err |= __get_user(regs->y, &((*grp)[MC_Y])); err |= __get_user(tstate, &((*grp)[MC_TSTATE])); regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC)); err |= __get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1])); err |= __get_user(regs->u_regs[UREG_G2], (&(*grp)[MC_G2])); err |= __get_user(regs->u_regs[UREG_G3], (&(*grp)[MC_G3])); err |= __get_user(regs->u_regs[UREG_G4], (&(*grp)[MC_G4])); err |= __get_user(regs->u_regs[UREG_G5], (&(*grp)[MC_G5])); err |= __get_user(regs->u_regs[UREG_G6], (&(*grp)[MC_G6])); err |= __get_user(regs->u_regs[UREG_G7], (&(*grp)[MC_G7])); err |= __get_user(regs->u_regs[UREG_I0], (&(*grp)[MC_O0])); err |= __get_user(regs->u_regs[UREG_I1], (&(*grp)[MC_O1])); err |= __get_user(regs->u_regs[UREG_I2], (&(*grp)[MC_O2])); err |= __get_user(regs->u_regs[UREG_I3], (&(*grp)[MC_O3])); err |= __get_user(regs->u_regs[UREG_I4], (&(*grp)[MC_O4])); err |= __get_user(regs->u_regs[UREG_I5], (&(*grp)[MC_O5])); err |= __get_user(regs->u_regs[UREG_I6], (&(*grp)[MC_O6])); err |= __get_user(regs->u_regs[UREG_I7], (&(*grp)[MC_O7])); err |= __get_user(fp, &(ucp->uc_mcontext.mc_fp)); err |= __get_user(i7, &(ucp->uc_mcontext.mc_i7)); err |= __put_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6]))); err |= __put_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7]))); err |= __get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab)); if(fenab) { unsigned long *fpregs = (unsigned long *)(((char *)current) + AOFF_task_fpregs); unsigned long fprs; fprs_write(0); err |= __get_user(fprs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fprs)); if (fprs & FPRS_DL) err |= copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs), (sizeof(unsigned int) * 32)); if (fprs & FPRS_DU) err |= copy_from_user(fpregs+16, ((unsigned long *)&(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs))+16, (sizeof(unsigned int) * 32)); err |= __get_user(current->thread.xfsr[0], &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr)); err |= __get_user(current->thread.gsr[0], &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr)); regs->tstate &= ~TSTATE_PEF; } if (err) goto do_sigsegv; return; do_sigsegv: do_exit(SIGSEGV); }
/* Do save's until all user register windows are out of the cpu. */ void flush_user_windows(void) { if(current->tss.uwinmask) flush_user_windows(); }