NetBSD-5.0.2/sys/arch/hppa/hppa/hppa_machdep.c
/* $NetBSD: hppa_machdep.c,v 1.15 2008/10/16 17:49:23 skrll Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 THE FOUNDATION OR CONTRIBUTORS
* 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: hppa_machdep.c,v 1.15 2008/10/16 17:49:23 skrll Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sa.h>
#include <sys/lwp.h>
#include <sys/savar.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/ras.h>
#include <sys/cpu.h>
#include <sys/kernel.h>
#include <uvm/uvm_extern.h>
#include <machine/cpufunc.h>
#include <machine/mcontext.h>
#include <hppa/hppa/machdep.h>
/* the following is used externally (sysctl_hw) */
char machine_arch[] = MACHINE_ARCH; /* from <machine/param.h> */
/*
* XXX fredette - much of the TLB trap handler setup should
* probably be moved here from hp700/hp700/machdep.c, seeing
* that there's related code already in hppa/hppa/trap.S.
*/
/*
* Scheduler activations upcall frame. Pushed onto user stack before
* calling an SA upcall.
*/
struct saframe {
/* first 4 arguments passed in registers on entry to upcallcode */
void * sa_arg;
int sa_interrupted; /* arg3 */
int sa_events; /* arg2 */
struct sa_t ** sa_sas; /* arg1 */
int sa_type; /* arg0 */
};
/*
* cpu_upcall:
*
* Send an an upcall to userland.
*/
void
cpu_upcall(struct lwp *l, int type, int nevents, int ninterrupted,
void *sas, void *ap, void *sp, sa_upcall_t upcall)
{
struct saframe *sf, frame;
struct proc *p = l->l_proc;
struct trapframe *tf;
uintptr_t upva;
vaddr_t va;
tf = (struct trapframe *)l->l_md.md_regs;
frame.sa_type = type;
frame.sa_sas = sas;
frame.sa_events = nevents;
frame.sa_interrupted = ninterrupted;
frame.sa_arg = ap;
pmap_activate(l);
va = HPPA_FRAME_ROUND((uintptr_t)sp + sizeof(frame) + HPPA_FRAME_SIZE);
sf = (void *)(va - 32 - sizeof(frame));
if (copyout(&frame, sf, sizeof(frame)) != 0) {
/* Copying onto the stack didn't work. Die. */
mutex_enter(p->p_lock);
sigexit(l, SIGILL);
/* NOTREACHED */
}
/*
* Deal with the upcall function pointer being a PLABEL.
*/
upva = (uintptr_t)upcall;
if (upva & 2) {
upva &= ~3;
if (copyin((void *)(upva + 4), &tf->tf_t4, 4)) {
printf("copyin t4 failed\n");
mutex_enter(p->p_lock);
sigexit(l, SIGILL);
/* NOTREACHED */
}
if (copyin((void *)upva, &upcall, 4)) {
printf("copyin upcall failed\n");
mutex_enter(p->p_lock);
sigexit(l, SIGILL);
/* NOTREACHED */
}
}
tf->tf_iioq_head = (uintptr_t)upcall | HPPA_PC_PRIV_USER;
tf->tf_iioq_tail = tf->tf_iioq_head + 4;
tf->tf_sp = va;
tf->tf_arg0 = type;
tf->tf_arg1 = (uintptr_t)sas;
tf->tf_arg2 = nevents;
tf->tf_arg3 = ninterrupted;
tf->tf_rp = 0;
}
void
cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flags)
{
struct trapframe *tf = l->l_md.md_regs;
__greg_t *gr = mcp->__gregs;
__greg_t ras_pc;
gr[0] = tf->tf_ipsw;
gr[1] = tf->tf_r1;
gr[2] = tf->tf_rp;
gr[3] = tf->tf_r3;
gr[4] = tf->tf_r4;
gr[5] = tf->tf_r5;
gr[6] = tf->tf_r6;
gr[7] = tf->tf_r7;
gr[8] = tf->tf_r8;
gr[9] = tf->tf_r9;
gr[10] = tf->tf_r10;
gr[11] = tf->tf_r11;
gr[12] = tf->tf_r12;
gr[13] = tf->tf_r13;
gr[14] = tf->tf_r14;
gr[15] = tf->tf_r15;
gr[16] = tf->tf_r16;
gr[17] = tf->tf_r17;
gr[18] = tf->tf_r18;
gr[19] = tf->tf_t4;
gr[20] = tf->tf_t3;
gr[21] = tf->tf_t2;
gr[22] = tf->tf_t1;
gr[23] = tf->tf_arg3;
gr[24] = tf->tf_arg2;
gr[25] = tf->tf_arg1;
gr[26] = tf->tf_arg0;
gr[27] = tf->tf_dp;
gr[28] = tf->tf_ret0;
gr[29] = tf->tf_ret1;
gr[30] = tf->tf_sp;
gr[31] = tf->tf_r31;
gr[_REG_SAR] = tf->tf_sar;
gr[_REG_PCSQH] = tf->tf_iisq_head;
gr[_REG_PCSQT] = tf->tf_iisq_tail;
gr[_REG_PCOQH] = tf->tf_iioq_head;
gr[_REG_PCOQT] = tf->tf_iioq_tail;
gr[_REG_SR0] = tf->tf_sr0;
gr[_REG_SR1] = tf->tf_sr1;
gr[_REG_SR2] = tf->tf_sr2;
gr[_REG_SR3] = tf->tf_sr3;
gr[_REG_SR4] = tf->tf_sr4;
#if 0
gr[_REG_CR26] = tf->tf_cr26;
gr[_REG_CR27] = tf->tf_cr27;
#endif
ras_pc = (__greg_t)ras_lookup(l->l_proc,
(void *)(gr[_REG_PCOQH] & ~HPPA_PC_PRIV_MASK));
if (ras_pc != -1) {
ras_pc |= HPPA_PC_PRIV_USER;
gr[_REG_PCOQH] = ras_pc;
gr[_REG_PCOQT] = ras_pc + 4;
}
*flags |= _UC_CPU;
if (l->l_md.md_flags & 0) {
return;
}
hppa_fpu_flush(l);
memcpy(&mcp->__fpregs, l->l_addr->u_pcb.pcb_fpregs,
sizeof(mcp->__fpregs));
fdcache(HPPA_SID_KERNEL, (vaddr_t)l->l_addr->u_pcb.pcb_fpregs,
sizeof(l->l_addr->u_pcb.pcb_fpregs));
*flags |= _UC_FPU;
}
int
cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags)
{
struct trapframe *tf = l->l_md.md_regs;
struct proc *p = l->l_proc;
struct pmap *pmap = p->p_vmspace->vm_map.pmap;
const __greg_t *gr = mcp->__gregs;
if ((flags & _UC_CPU) != 0) {
if ((gr[_REG_PSW] & (PSW_MBS|PSW_MBZ)) != PSW_MBS) {
return EINVAL;
}
#if 0
/*
* XXX
* Force the space regs and priviledge bits to
* the right values in the trapframe for now.
*/
if (gr[_REG_PCSQH] != pmap_sid(pmap, gr[_REG_PCOQH])) {
return EINVAL;
}
if (gr[_REG_PCSQT] != pmap_sid(pmap, gr[_REG_PCOQT])) {
return EINVAL;
}
if (gr[_REG_PCOQH] < 0xc0000020 &&
(gr[_REG_PCOQH] & HPPA_PC_PRIV_MASK) != HPPA_PC_PRIV_USER) {
return EINVAL;
}
if (gr[_REG_PCOQT] < 0xc0000020 &&
(gr[_REG_PCOQT] & HPPA_PC_PRIV_MASK) != HPPA_PC_PRIV_USER) {
return EINVAL;
}
#endif
tf->tf_ipsw = gr[0];
tf->tf_r1 = gr[1];
tf->tf_rp = gr[2];
tf->tf_r3 = gr[3];
tf->tf_r4 = gr[4];
tf->tf_r5 = gr[5];
tf->tf_r6 = gr[6];
tf->tf_r7 = gr[7];
tf->tf_r8 = gr[8];
tf->tf_r9 = gr[9];
tf->tf_r10 = gr[10];
tf->tf_r11 = gr[11];
tf->tf_r12 = gr[12];
tf->tf_r13 = gr[13];
tf->tf_r14 = gr[14];
tf->tf_r15 = gr[15];
tf->tf_r16 = gr[16];
tf->tf_r17 = gr[17];
tf->tf_r18 = gr[18];
tf->tf_t4 = gr[19];
tf->tf_t3 = gr[20];
tf->tf_t2 = gr[21];
tf->tf_t1 = gr[22];
tf->tf_arg3 = gr[23];
tf->tf_arg2 = gr[24];
tf->tf_arg1 = gr[25];
tf->tf_arg0 = gr[26];
tf->tf_dp = gr[27];
tf->tf_ret0 = gr[28];
tf->tf_ret1 = gr[29];
tf->tf_sp = gr[30];
tf->tf_r31 = gr[31];
tf->tf_sar = gr[_REG_SAR];
tf->tf_iisq_head = pmap_sid(pmap, gr[_REG_PCOQH]);
tf->tf_iisq_tail = pmap_sid(pmap, gr[_REG_PCOQT]);
tf->tf_iioq_head = gr[_REG_PCOQH];
tf->tf_iioq_tail = gr[_REG_PCOQT];
if (tf->tf_iioq_head >= 0xc0000020) {
tf->tf_iioq_head &= ~HPPA_PC_PRIV_MASK;
} else {
tf->tf_iioq_head |= HPPA_PC_PRIV_USER;
}
if (tf->tf_iioq_tail >= 0xc0000020) {
tf->tf_iioq_tail &= ~HPPA_PC_PRIV_MASK;
} else {
tf->tf_iioq_tail |= HPPA_PC_PRIV_USER;
}
#if 0
tf->tf_sr0 = gr[_REG_SR0];
tf->tf_sr1 = gr[_REG_SR1];
tf->tf_sr2 = gr[_REG_SR2];
tf->tf_sr3 = gr[_REG_SR3];
tf->tf_sr4 = gr[_REG_SR4];
tf->tf_cr26 = gr[_REG_CR26];
tf->tf_cr27 = gr[_REG_CR27];
#endif
}
if ((flags & _UC_FPU) != 0) {
hppa_fpu_flush(l);
memcpy(l->l_addr->u_pcb.pcb_fpregs, &mcp->__fpregs,
sizeof(mcp->__fpregs));
fdcache(HPPA_SID_KERNEL, (vaddr_t)l->l_addr->u_pcb.pcb_fpregs,
sizeof(l->l_addr->u_pcb.pcb_fpregs));
}
mutex_enter(p->p_lock);
if (flags & _UC_SETSTACK)
l->l_sigstk.ss_flags |= SS_ONSTACK;
if (flags & _UC_CLRSTACK)
l->l_sigstk.ss_flags &= ~SS_ONSTACK;
mutex_exit(p->p_lock);
return 0;
}
/*
* Do RAS processing.
*/
void
hppa_ras(struct lwp *l)
{
struct proc *p;
struct trapframe *tf;
intptr_t rasaddr;
p = l->l_proc;
tf = l->l_md.md_regs;
rasaddr = (intptr_t)ras_lookup(p, (void *)tf->tf_iioq_head);
if (rasaddr != -1) {
rasaddr |= HPPA_PC_PRIV_USER;
tf->tf_iioq_head = rasaddr;
tf->tf_iioq_tail = rasaddr + 4;
}
}
void
cpu_need_resched(struct cpu_info *ci, int flags)
{
bool immed = (flags & RESCHED_IMMED) != 0;
if (ci->ci_want_resched && !immed)
return;
ci->ci_want_resched = 1;
if (ci->ci_curlwp != ci->ci_data.cpu_idlelwp) {
/* aston(ci->ci_curlwp); */
setsoftast();
}
}