NetBSD-5.0.2/sys/arch/hppa/hppa/hppa_machdep.c

Compare this file to the similar file:
Show the results in this format:

/*	$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();
	}
}