/*
 * Copyright (C) 2011-2012, 2014 Apple Computer, Inc. All rights reserved.
 *
 * This document is the property of Apple Computer, Inc.
 * It is considered confidential and proprietary.
 *
 * This document may not be reproduced or transmitted in any form,
 * in whole or in part, without the express written permission of
 * Apple Computer, Inc.
 */

#if	!WITH_EL3
#error	"This file should only be built for platforms WITH_EL3"
#endif

////////////////////////////////////////////////////////////
// This is what exception frame looks like:
//
//	struct arm_exception_frame64 {
//		uint64_t	regs[29];	// x0-x28
//		uint64_t	fp;
//		uint64_t	lr;
//		uint64_t	sp;		
//		uint32_t	spsr;
//		uint32_t	reserved0;		
//		uint64_t	pc;
//		uint64_t	far;
//		uint32_t	esr;
//		uint32_t	reserved1;
//		uint64_t	reserved2;	// stp requires multiple of 16 in imm field
//		uint64_t	reserved3;	// stp requires multiple of 16 in imm field
//		union {
//			uint128_t	q;
//			uint64_t	d;
//			uint32_t	s;
//		} vregs[32];			// v0-v31
//		uint32_t	fpsr;
//		uint32_t	reserved4;
//		uint32_t	fpcr;
//		uint32_t	reserved5;
//	};
////////////////////////////////////////////////////////////

.macro EL3_SP0_VECTOR
	msr	SPSel, #0		// Switch to SP0
	sub	sp, sp, #832		// Create exception frame
	stp	x0, x1, [sp, #0]	// Save x0, x1 to exception frame
	add	x0, sp, #832		// Calculate the original stack pointer
	str	x0, [sp, #248]		// Save stack pointer to exception frame
	stp	fp, lr, [sp, #232]	// Save fp and lr to exception frame
	mrs     lr, ELR_EL3		// Get exception link reg so _dispatch64 can make a frame
	mov	x0, sp			// Copy saved state pointer to x0
.endmacro

.macro FASTSIM_DEBUG_HINT
	hint	0x45	// make fastsim drop to debugger (nop on hardware)
.endmacro

	.text
	.align 12
	.globl _exception_vector_base
_exception_vector_base:
L__el3_sp0_synchronous_vector:
	EL3_SP0_VECTOR
	mrs	x1, ESR_EL3					// Load exception syndrome
	str	x1, [x0, #280]
	mrs	x1, FAR_EL3					// Load fault address
	str	x1, [x0, #272]
	adrp	x1, _arm_synchronous_exception@page
	add	x1, x1, _arm_synchronous_exception@pageoff
	b	__dispatch64

	.text
	.align 7
L__el3_sp0_irq_vector:
	EL3_SP0_VECTOR
	adrp	x1, _interrupt_stack_top@page
	add	x1, x1, _interrupt_stack_top@pageoff
	ldr	x1, [x1]
	mov	sp, x1
	adrp	x1, _arm_irq@page
	add	x1, x1, _arm_irq@pageoff
	b	__dispatch64

	.text
	.align 7
L__el3_sp0_fiq_vector:
	EL3_SP0_VECTOR
	adrp	x1, _interrupt_stack_top@page
	add	x1, x1, _interrupt_stack_top@pageoff
	ldr	x1, [x1]
	mov	sp, x1
	adrp	x1, _arm_fiq@page
	add	x1, x1, _arm_fiq@pageoff
	b	__dispatch64

	.text
	.align 7
L__el3_sp0_serror_vector:
	EL3_SP0_VECTOR
	mrs	x1, ESR_EL3					// Load exception syndrome
	str	x1, [x0, #280]
	mrs	x1, FAR_EL3					// Load fault address
	str	x1, [x0, #272]
	adrp	x1, _arm_serror_exception@page
	add	x1, x1, _arm_serror_exception@pageoff
	b	__dispatch64

	.text
	.align 7
L__el3_sp3_synchronous_vector:
	FASTSIM_DEBUG_HINT
    	b       .

	.text
	.align 7
L__el3_sp3_irq_vector:
	FASTSIM_DEBUG_HINT
	b       .

	.text
	.align 7
L__el3_sp3_fiq_vector:
	FASTSIM_DEBUG_HINT
	b       .

	.text
	.align 7
L__el3_sp3_serror_vector:
	FASTSIM_DEBUG_HINT
	b	.

	.text
	.align 7
L__elx_64_synchronous_vector:
	// Monitor (EL3) call.
	// Check "smc #0x5ec3" was executed and no other reason code.
	movz	x1, #0x5ec3
	movk	x1, #0x5e00, lsl #16
	mrs	x2, ESR_EL3
	cmp	x1, x2
	b.eq	1f
	FASTSIM_DEBUG_HINT
	b	.
1:	// Got here from an "smc". Branch to x0.
	br	x0

	.text
	.align 7
L__elx_64_irq_vector:
	FASTSIM_DEBUG_HINT
	b       .

	.text
	.align 7
L__elx_64_fiq_vector:
	FASTSIM_DEBUG_HINT
	b       .

	.text
	.align 7
L__elx_64_serror_vector:
	FASTSIM_DEBUG_HINT
	b	.

	.text
	.align 7
L__elx_32_synchronous_vector:
	FASTSIM_DEBUG_HINT
    	b       .

	.text
	.align 7
L__elx_32_irq_vector:
	FASTSIM_DEBUG_HINT
	b       .

	.text
	.align 7
L__elx_32_fiq_vector:
	FASTSIM_DEBUG_HINT
	b       .

	.text
	.align 7
L__elx_32_serror_vector:
	FASTSIM_DEBUG_HINT
	b	.

/* Placeholder for macho_post_process.py. Placing the UUID here
   allows debuggers to locate the UUID based on the value of VBAR */
	.text
	.align 7
_UUID:
	.space 32

/* 64-bit first level exception handler dispatcher.
* Completes register context saving and branches to FLEH.
* Expects:
*  {x0, x1, fp, lr, sp} - saved
*  x0 - arm_context_t
*  x1 - address of FLEH
*  fp - previous stack frame if EL1
*  lr - unused
*  sp - kernel stack
*/
	.text
	.align 2
__dispatch64:
	stp	fp, lr, [sp, #-16]!		// Create a frame to help debuggers unwind
	mov	fp, sp				// past the exception handler
	stp	x2, x3, [x0, #16]               // Save remaining GPRs
	stp	x4, x5, [x0, #32]
	stp	x6, x7, [x0, #48]
	stp	x8, x9, [x0, #64]
	stp	x10, x11, [x0, #80]
	stp	x12, x13, [x0, #96]
	stp	x14, x15, [x0, #112]
	stp	x16, x17, [x0, #128]
	stp	x18, x19, [x0, #144]
	stp	x20, x21, [x0, #160]
	stp	x22, x23, [x0, #176]
	stp	x24, x25, [x0, #192]
	stp	x26, x27, [x0, #208]
	str	x28, [x0, #224]
	mrs	lr, ELR_EL3                      // Get exception link register
	str	lr, [x0, #264]                   // Save ELR to PCB
	mrs	x2, SPSR_EL3                     // Get CPSR
	str	w2, [x0, #256]                   // Save CPSR to PCB

#if WITH_VFP
	stp	q0, q1, [x0, #304]
	stp	q2, q3, [x0, #336]
	stp	q4, q5, [x0, #368]
	stp	q6, q7, [x0, #400]
	stp	q8, q9, [x0, #432]
	stp	q10, q11, [x0, #464]
	stp	q12, q13, [x0, #496]
	stp	q14, q15, [x0, #528]
	stp	q16, q17, [x0, #560]
	stp	q18, q19, [x0, #592]
	stp	q20, q21, [x0, #624]
	stp	q22, q23, [x0, #656]
	stp	q24, q25, [x0, #688]
	stp	q26, q27, [x0, #720]
	stp	q28, q29, [x0, #752]
	stp	q30, q31, [x0, #784]
	mrs	x2, FPSR
	str	w2, [x0, #816]
	mrs	x2, FPCR
	str	w2, [x0, #820]
#endif

	mov	x21, x0                          // Copy arm_context_t pointer to x21
	blr	x1
	b	__exception_return
	
	.text
	.align 2
__exception_return:
	mov     sp, x21                          // Reload the arm_context_t pointer

#if WITH_VFP
	ldp	q0, q1, [sp, #304]
	ldp	q2, q3, [sp, #336]
	ldp	q4, q5, [sp, #368]
	ldp	q6, q7, [sp, #400]
	ldp	q8, q9, [sp, #432]
	ldp	q10, q11, [sp, #464]
	ldp	q12, q13, [sp, #496]
	ldp	q14, q15, [sp, #528]
	ldp	q16, q17, [sp, #560]
	ldp	q18, q19, [sp, #592]
	ldp	q20, q21, [sp, #624]
	ldp	q22, q23, [sp, #656]
	ldp	q24, q25, [sp, #688]
	ldp	q26, q27, [sp, #720]
	ldp	q28, q29, [sp, #752]
	ldp	q30, q31, [sp, #784]
	ldr	w3, [sp, #816]
	msr	FPSR, x3
	ldr	w4, [sp, #820]
	msr	FPCR, x4
#endif

	ldr     x0, [sp, #264]                   // Get the return address
	msr     ELR_EL3, x0                      // Load the return address into ELR
	ldr     w1, [sp, #256]                   // Get the return CPSR
	msr     SPSR_EL3, x1                     // Load the return CPSR into SPSR
	ldp     x0, x1, [sp, #0]                 // Restore the GPRs
	ldp     x2, x3, [sp, #16]
	ldp     x4, x5, [sp, #32]
	ldp     x6, x7, [sp, #48]
	ldp     x8, x9, [sp, #64]
	ldp     x10, x11, [sp, #80]
	ldp     x12, x13, [sp, #96]
	ldp     x14, x15, [sp, #112]
	ldp     x16, x17, [sp, #128]
	ldp     x18, x19, [sp, #144]
	ldp     x20, x21, [sp, #160]
	ldp     x22, x23, [sp, #176]
	ldp     x24, x25, [sp, #192]
	ldp     x26, x27, [sp, #208]
	ldr     x28, [sp, #224]
	ldp     fp, lr, [sp, #232]

	/* Use exception stack to restore SP0 */

	msr     SPSel, #1			// Switch to SPx
	stp     x0, x1, [sp, #-16]!		// Save x0 and x1
	mrs     x0, SP_EL0			// Reload the pcb pointer from SP0
	ldr     x1, [x0, #248]			// Get the saved SP from the pcb
	msr     SP_EL0, x1			// Restore SP0
	ldp     x0, x1, [sp], #16		// Restore x0 and x1
	eret
