/*
 * (c) 1999, Eric Valette valette@crf.canon.fr
 *
 * Modified and partially rewritten by Till Straumann, 2007
 *
 * Modified by Sebastian Huber <sebastian.huber@embedded-brains.de>, 2008.
 *
 * Low-level assembly code for PPC exceptions.
 *
 * This file was written with the goal to eliminate
 * ALL #ifdef <cpu_flavor> conditionals -- please do not
 * reintroduce such statements.
 */

/* Load macro definitions */
#include <rtems/asm.h>
#include <rtems/score/percpu.h>

/*
 * This code uses the small-data area which is not available in the 64-bit
 * PowerPC ELFv2 ABI.
 */
#ifndef __powerpc64__

#include "ppc_exc_asm_macros.h"

/******************************************************/
/*  PROLOGUES                                         */
/******************************************************/

	/*
	 * Expand prologue snippets for classic, ppc405-critical, bookE-critical
	 * and E500 machine-check, synchronous and asynchronous exceptions
	 */
	PPC_EXC_MIN_PROLOG_SYNC _NAME=tmpl_std        _VEC=0 _PRI=std  _FLVR=std
	PPC_EXC_MIN_PROLOG_SYNC _NAME=tmpl_p405_crit  _VEC=0 _PRI=crit _FLVR=p405_crit
	PPC_EXC_MIN_PROLOG_SYNC _NAME=tmpl_bookE_crit _VEC=0 _PRI=crit _FLVR=bookE_crit
	PPC_EXC_MIN_PROLOG_SYNC _NAME=tmpl_e500_mchk  _VEC=0 _PRI=mchk _FLVR=e500_mchk

	PPC_EXC_MIN_PROLOG_ASYNC _NAME=tmpl_std        _VEC=0 _PRI=std  _FLVR=std
	PPC_EXC_MIN_PROLOG_ASYNC _NAME=tmpl_p405_crit  _VEC=0 _PRI=crit _FLVR=p405_crit
	PPC_EXC_MIN_PROLOG_ASYNC _NAME=tmpl_bookE_crit _VEC=0 _PRI=crit _FLVR=bookE_crit
	PPC_EXC_MIN_PROLOG_ASYNC _NAME=tmpl_e500_mchk  _VEC=0 _PRI=mchk _FLVR=e500_mchk

	.global ppc_exc_min_prolog_size
ppc_exc_min_prolog_size      = 4 * 4

/* Special prologue for 603e-style CPUs.
 *
 * 603e shadows GPR0..GPR3 for certain exceptions. We must switch
 * that off before we can use the stack pointer. Note that this is
 * ONLY safe if the shadowing is actually active -- otherwise, r1
 * is destroyed. We deliberately use r1 so problems become obvious
 * if this is misused!
 */
	.global ppc_exc_tgpr_clr_prolog
ppc_exc_tgpr_clr_prolog:
	mfmsr   r1
	rlwinm  r1,r1,0,15,13
	mtmsr   r1
	isync
	/* FALL THRU TO 'auto' PROLOG */

/* Determine vector dynamically/automatically
 *
 * BUT: - only standard exceptions (no critical ones)
 *      - vector offset must be on 256 Byte boundary.
 */
	.global ppc_exc_min_prolog_auto
ppc_exc_min_prolog_auto:
	stwu	r1, -EXCEPTION_FRAME_END(r1)
	stw	VECTOR_REGISTER, VECTOR_OFFSET(r1)
	mflr	VECTOR_REGISTER

	/*
	 * We store the absolute branch target address here.  It will be used
	 * to generate the branch operation in ppc_exc_make_prologue().
	 *
	 * We add one to request the link in the generated branch instruction.
	 */
	.int	ppc_exc_wrap_auto + 1

	.global ppc_exc_tgpr_clr_prolog_size
ppc_exc_tgpr_clr_prolog_size = . - ppc_exc_tgpr_clr_prolog

/*
 * Automatic vector, asynchronous exception; however,
 * automatic vector calculation is less efficient than
 * using an explicit vector in a minimal prolog snippet.
 * The latter method is preferable since there usually
 * are few asynchronous exceptions.
 *
 * For generic exceptions (which are the bulk) using
 * the 'auto' prologue is OK since performance is not
 * really an issue.
 */
	.global ppc_exc_min_prolog_auto_async
ppc_exc_min_prolog_auto_async:
	stw	r1, ppc_exc_lock_std@sdarel(r13)
	stw	VECTOR_REGISTER, ppc_exc_vector_register_std@sdarel(r13)
	mflr	VECTOR_REGISTER

	/*
	 * We store the absolute branch target address here.  It will be used
	 * to generate the branch operation in ppc_exc_make_prologue().
	 *
	 * We add one to request the link in the generated branch instruction.
	 */
	.int	ppc_exc_wrap_auto_async + 1

/******************************************************/
/*  WRAPPERS                                          */
/******************************************************/

	/* Tag start and end of the wrappers.
	 * If exceptions are installed farther removed
	 * from the text area than 32M then the wrappers
	 * must be moved to an area that is reachable
	 * from where the prologues reside. Branches into
	 * C-code are far.
	 */

	.global	__ppc_exc_wrappers_start
__ppc_exc_wrappers_start = .

	/* Expand wrappers for different exception flavors */

	/* Standard/classic powerpc */
	WRAP	_FLVR=std _PRI=std _SRR0=srr0 _SRR1=srr1 _RFI=rfi

	/* ppc405 has a critical exception using srr2/srr3 */
	WRAP	_FLVR=p405_crit _PRI=crit _SRR0=srr2 _SRR1=srr3 _RFI=rfci

	/* bookE has critical exception using csrr0 cssr1 */
	WRAP	_FLVR=bookE_crit _PRI=crit _SRR0=csrr0 _SRR1=csrr1 _RFI=rfci

	/* e500 has machine-check exception using mcsrr0 mcssr1 */
	WRAP	_FLVR=e500_mchk _PRI=mchk _SRR0=mcsrr0 _SRR1=mcsrr1 _RFI=rfmci

	/* LR holds vector, VECTOR_REGISTER holds orig. LR */
        .global ppc_exc_wrap_auto
ppc_exc_wrap_auto:
	stw	FRAME_REGISTER, FRAME_OFFSET(r1)

	/* Find address where we jumped from */
	mflr	FRAME_REGISTER

	/* Restore LR */
	mtlr	VECTOR_REGISTER

	/* Compute vector into R3 */
	rlwinm	VECTOR_REGISTER, FRAME_REGISTER, 24, 26, 31

	/*
	 * We're now in almost the same state as if called by
	 * min_prolog_std but we must skip saving FRAME_REGISTER
	 * since that's done already
	 */
	b	wrap_no_save_frame_register_std

        .global ppc_exc_wrap_auto_async
ppc_exc_wrap_auto_async:
	stwu	r1, -EXCEPTION_FRAME_END(r1)
	stw	FRAME_REGISTER, FRAME_OFFSET(r1)
	/* find address where we jumped from */
	mflr	FRAME_REGISTER
	/* restore LR     */
	mtlr	VECTOR_REGISTER
	/* set upper bits to indicate that non-volatile
	 * registers should not be saved/restored.
	 */
	li	VECTOR_REGISTER, 0xffff8000
	/* compute vector into R3 */
	rlwimi	VECTOR_REGISTER, FRAME_REGISTER, 24, 26, 31
	/* we're now in almost the same state as if called by
	 * min_prolog_std but we must skip saving FRAME_REGISTER
	 * since that's done already
	 */
	b	wrap_no_save_frame_register_std

	.global	__ppc_exc_wrappers_end
__ppc_exc_wrappers_end = .

#endif /* !__powerpc64__ */
