OpenSolaris_b135/uts/sun4v/ml/mach_xc.s

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#if defined(lint)
#include <sys/types.h>
#include <sys/cpuvar.h>
#else	/*lint */
#include "assym.h"
#endif	/* lint */

#include <sys/asm_linkage.h>
#include <sys/privregs.h>
#include <sys/x_call.h>
#include <sys/xc_impl.h>
#include <sys/machthread.h>
#include <sys/hypervisor_api.h>

#ifdef TRAPTRACE
#include <sys/traptrace.h>
#endif /* TRAPTRACE */


#if defined(lint)

/* ARGSUSED */
void
self_xcall(struct cpu *cpu, uint64_t arg1, uint64_t arg2, xcfunc_t *func)
{}

#else

/*
 * Entered by the software trap (TT=ST_SELFXCALL, TL>0) thru send_self_xcall().
 * Emulate the mondo handler - vec_interrupt().
 *
 * Global registers are the Alternate Globals.
 * Arguments:
 * 	%o0 - CPU
 * 	ILP32 kernel:
 * 		%o5 - function to call
 * 		%o1, %o2, %o3, %o4  - arguments
 * 	LP64 kernel:
 * 		%o3 - function to call
 * 		%o1, %o2 - arguments
 */
	ENTRY_NP(self_xcall)
	!
	! TL>0 handlers are expected to do "retry"
	! prepare their return PC and nPC now
	!
	rdpr	%tnpc, %g1
	wrpr	%g1, %tpc			!  PC <- TNPC[TL]
 	add	%g1, 4, %g1
	wrpr	%g1, %tnpc			! nPC <- TNPC[TL] + 4

#ifdef TRAPTRACE
	TRACE_PTR(%g4, %g6)
	GET_TRACE_TICK(%g6, %g3)
	stxa	%g6, [%g4 + TRAP_ENT_TICK]%asi
	rdpr	%tl, %g6
	stha	%g6, [%g4 + TRAP_ENT_TL]%asi
	rdpr	%tt, %g6
	stha	%g6, [%g4 + TRAP_ENT_TT]%asi
	stna	%o3, [%g4 + TRAP_ENT_TR]%asi ! pc of the TL>0 handler
	rdpr	%tpc, %g6
	stna	%g6, [%g4 + TRAP_ENT_TPC]%asi
	rdpr	%tstate, %g6
	stxa	%g6, [%g4 + TRAP_ENT_TSTATE]%asi
	stna	%sp, [%g4 + TRAP_ENT_SP]%asi
	stna	%o1, [%g4 + TRAP_ENT_F1]%asi ! arg 1
	stna	%o2, [%g4 + TRAP_ENT_F2]%asi ! arg 2
	stna	%g0, [%g4 + TRAP_ENT_F3]%asi
	stna	%g0, [%g4 + TRAP_ENT_F4]%asi
	TRACE_NEXT(%g4, %g6, %g3)
#endif /* TRAPTRACE */
	!
	! Load the arguments for the fast trap handler.
	!
	mov	%o1, %g1
	jmp	%o3				! call the fast trap handler
	mov	%o2, %g2
	/* Not Reached */
	SET_SIZE(self_xcall)

#endif	/* lint */

#ifdef  TRAPTRACE
#if defined(lint)

/* ARGSUSED */
void
xc_trace(u_int traptype, cpuset_t *cpu_set, xcfunc_t *func,
	uint64_t arg1, uint64_t arg2)
{}

#else	/* lint */
	ENTRY(xc_trace)
	rdpr	%pstate, %g1
	andn	%g1, PSTATE_IE | PSTATE_AM, %g2
	wrpr	%g0, %g2, %pstate			/* disable interrupts */
	TRACE_PTR(%g3, %g4)
	GET_TRACE_TICK(%g6, %g4)
	stxa	%g6, [%g3 + TRAP_ENT_TICK]%asi
	stha	%g0, [%g3 + TRAP_ENT_TL]%asi
	set	TT_XCALL, %g2
	or	%o0, %g2, %g4
	stha	%g4, [%g3 + TRAP_ENT_TT]%asi
	stna	%o7, [%g3 + TRAP_ENT_TPC]%asi
	ldn	[%o1], %g2
	stna	%g2, [%g3 + TRAP_ENT_SP]%asi		/* sp = cpuset */
	stna	%o2, [%g3 + TRAP_ENT_TR]%asi		/* tr = func */
	stna	%o3, [%g3 + TRAP_ENT_F1]%asi		/* f1 = arg1 */
	stna	%o4, [%g3 + TRAP_ENT_F2]%asi		/* f2 = arg2 */
	stna	%g0, [%g3 + TRAP_ENT_F3]%asi		/* f3 = 0 */
	stna	%i7, [%g3 + TRAP_ENT_F4]%asi		/* f4 = xcall caller */
	stxa	%g1, [%g3 + TRAP_ENT_TSTATE]%asi	/* tstate = pstate */
	TRACE_NEXT(%g2, %g3, %g4)
/*
 * In the case of a cpuset of greater size than a long we
 * grab extra trace buffers just to store the cpuset.
 * Seems like a waste but popular opinion opted for this 
 * rather than increase the size of the buffer.
 */
#if CPUSET_SIZE > CLONGSIZE
	add	%o1, CPUSET_SIZE, %g5			/* end of cpuset */
	clr	%o2
1:
	TRACE_PTR(%g3, %g4)
	stha	%g0, [%g3 + TRAP_ENT_TL]%asi
	set	TT_XCALL_CONT, %g2
	or	%g2, %o2, %g2				/* continuation # */
	stha	%g2, [%g3 + TRAP_ENT_TT]%asi
	stxa	%g6, [%g3 + TRAP_ENT_TICK]%asi		/* same tick */
	stna	%g0, [%g3 + TRAP_ENT_TPC]%asi		/* clr unused fields */
	stna	%g0, [%g3 + TRAP_ENT_SP]%asi
	stna	%g0, [%g3 + TRAP_ENT_TR]%asi
	stxa	%g0, [%g3 + TRAP_ENT_TSTATE]%asi
	stna	%g0, [%g3 + TRAP_ENT_F2]%asi
	stna	%g0, [%g3 + TRAP_ENT_F3]%asi
	stna	%g0, [%g3 + TRAP_ENT_F4]%asi
	ldn	[%o1], %g2
	stna	%g2, [%g3 + TRAP_ENT_F1]%asi
	add	%o1, CLONGSIZE, %o1
	cmp	%o1, %g5
	bge	2f
	ldn	[%o1], %g2
	stna	%g2, [%g3 + TRAP_ENT_F2]%asi
	add	%o1, CLONGSIZE, %o1
	cmp	%o1, %g5
	bge	2f
	ldn	[%o1], %g2
	stna	%g2, [%g3 + TRAP_ENT_F3]%asi
	add	%o1, CLONGSIZE, %o1
	cmp	%o1, %g5
	bge	2f
	ldn	[%o1], %g2
	stna	%g2, [%g3 + TRAP_ENT_F4]%asi
	add	%o1, CLONGSIZE, %o1
2:	
	TRACE_NEXT(%g2, %g3, %g4)
	cmp	%o1, %g5
	bl	1b
	inc	%o2
#endif	/* CPUSET_SIZE */
	retl
	wrpr	%g0, %g1, %pstate			/* enable interrupts */
	SET_SIZE(xc_trace)

#endif	/* lint */
#endif	/* TRAPTRACE */

#if defined(lint)

/*ARGSUSED*/
void
init_mondo(xcfunc_t *func, uint64_t arg1, uint64_t arg2)
{}

/*ARGSUSED*/
int
shipit(int n, uint64_t cpuid)
{ return(0); }

#else	/* lint */
/*
 * Setup interrupt dispatch data registers
 * Entry:
 *	%o0 - function or inumber to call
 *	%o1, %o2 - arguments (2 uint64_t's)
 */
	ENTRY(init_mondo)
	ALTENTRY(init_mondo_nocheck)
	CPU_ADDR(%g1, %g4)			! load CPU struct addr
	add	%g1, CPU_MCPU, %g1
	ldx	[%g1 + MCPU_MONDO_DATA], %g1
	stx	%o0, [%g1]
	stx	%o1, [%g1+8]
	stx	%o2, [%g1+0x10]
	stx	%g0, [%g1+0x18]
	stx	%g0, [%g1+0x20]
	stx	%g0, [%g1+0x28]
	stx	%g0, [%g1+0x30]
	stx	%g0, [%g1+0x38]
	retl
	membar	#Sync			! allowed to be in the delay slot
	SET_SIZE(init_mondo)

/*
 * Ship mondo to cpuid
 */
	ENTRY_NP(shipit)
	/* For now use dummy interface:  cpu# func arg1 arg2 */
	CPU_ADDR(%g1, %g4)
	add	%g1, CPU_MCPU, %g1
	ldx	[%g1 + MCPU_MONDO_DATA_RA],	%o2
	mov	HV_INTR_SEND, %o5
	ta	FAST_TRAP
	retl
	membar	#Sync
	SET_SIZE(shipit)

#endif	/* lint */

#if defined(lint)

/*ARGSUSED*/
uint64_t
get_cpuaddr(uint64_t reg, uint64_t scr)
{ return (0);}

#else	/* lint */
/*
 * Get cpu structure
 * Entry:
 *      %o0 - register for CPU_ADDR macro
 *      %o1 - scratch for CPU_ADDR macro
 */
	ENTRY(get_cpuaddr)
	CPU_ADDR(%o0, %o1)	! %o0 == CPU struct addr
	retl
	nop
	SET_SIZE(get_cpuaddr)

#endif	/* lint */

#if defined(lint)
/* ARGSUSED */
void
xt_sync_tl1(uint64_t *cpu_sync_addr)
{}

#else /* lint */
/*
 * This is to ensure that previously called xtrap handlers have executed on
 * sun4v. We zero out the byte corresponding to its cpuid in the
 * array passed to us from xt_sync(), so the sender knows the previous
 * mondo has been executed.
 * Register:
 *		%g1 - Addr of the cpu_sync array.
 */
	ENTRY_NP(xt_sync_tl1)
	CPU_INDEX(%g3, %g4)		/* %g3 = cpu id */
	stb	%g0, [%g1 + %g3] 
	retry
	SET_SIZE(xt_sync_tl1)

#endif  /* lint */