/*
 * \brief  TrustZone monitor mode exception vector
 * \author Stefan Kalkowski
 * \date   2015-02-16
 */

/*
 * Copyright (C) 2015-2017 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU Affero General Public License version 3.
 */


/*********************
 ** Constant values **
 *********************/

.set RST_TYPE, 1
.set UND_TYPE, 2
.set SVC_TYPE, 3
.set PAB_TYPE, 4
.set DAB_TYPE, 5
.set IRQ_TYPE, 6
.set FIQ_TYPE, 7
.set LR_OFFSET, 14 * 4


/**
 * Switch from normal into secure world
 *
 * \param exception_type  immediate exception type ID
 * \param pc_adjust       immediate value that gets subtracted from the
 *                        vm's PC before it gets saved
 */
.macro _nonsecure_to_secure exception_type, pc_adjust
	stmia sp, {r0-lr}^             /* save user regs r0-r12,sp,lr     */
	add   r0, sp, #15*4
	.if \pc_adjust != 0            /* adjust pc if necessary          */
		sub lr, lr, #\pc_adjust
	.endif
	stmia r0!, {lr}                /* save pc                         */
	mrs   r1, spsr                 /* spsr to r0                      */
	mov   r2, #\exception_type     /* exception reason to r1          */
	b     _nonsecure_kernel_entry
.endm /* _non_to_secure */


.section .text
.p2align 12
.global monitor_mode_exception_vector
monitor_mode_exception_vector:
	b _mon_rst_entry                  /* reset                  */
	b _mon_und_entry                  /* undefined instruction  */
	b _mon_svc_entry                  /* supervisor call        */
	b _mon_pab_entry                  /* prefetch abort         */
	b _mon_dab_entry                  /* data abort             */
	nop                               /* reserved               */
	b _mon_irq_entry                  /* interrupt request      */
	_nonsecure_to_secure FIQ_TYPE, 4  /* fast interrupt request */

	_mon_rst_entry: _nonsecure_to_secure RST_TYPE, 0
	_mon_und_entry: _nonsecure_to_secure UND_TYPE, 4
	_mon_svc_entry: _nonsecure_to_secure SVC_TYPE, 0
	_mon_pab_entry: _nonsecure_to_secure PAB_TYPE, 4
	_mon_dab_entry: _nonsecure_to_secure DAB_TYPE, 8
	_mon_irq_entry: _nonsecure_to_secure IRQ_TYPE, 4

	_nonsecure_kernel_entry:
	ldr   lr, [sp, #17*4]         /* load kernel sp from vm context  */
	stmia r0!, {r1-r2}            /* save spsr, and exception reason */
	mov   r1, #1                  /* clear exception state of the VFP */
	lsl   r1, #30
	vmsr  fpexc, r1
	mrc   p15, 0, r3, c6, c0, 0   /* move DFAR  to r3                */
	mrc   p15, 0, r4, c2, c0, 0   /* move TTBR0 to r4                */
	mrc   p15, 0, r5, c2, c0, 1   /* move TTBR1 to r5                */
	mrc   p15, 0, r6, c2, c0, 2   /* move TTBRC to r6                */
	vmrs  r7, fpscr               /* move FPU ctrl to r7             */
	mov   r1, #0
	mcr   p15, 0, r1, c1, c1, 0   /* disable non-secure bit          */
	.irp mode,27,19,23,18,17      /* save mode specific registers    */
		cps   #\mode              /* switch to given mode            */
		mrs   r1, spsr            /* store mode-specific spsr        */
		stmia r0!, {r1,sp,lr}     /* store mode-specific sp and lr   */
	.endr
	stmia r0!, {r8-r12}           /* save fiq r8-r12                 */
	stmia r0!, {r3-r7}            /* save MMU registers              */
	vstm  r0!, {d0-d15}           /* save FPU registers              */
	vstm  r0!, {d16-d31}
	cps   #22                     /* switch back to monitor mode     */
	mov   r0, #0b111010011        /* spsr to SVC mode, irqs masked   */
	msr   spsr_cxsf, r0
	mov   r1, lr
	cps   #19
	mov   sp, r1
	cps   #22
	adr   lr, _kernel_entry
	ldr   lr, [lr]
	subs  pc, lr, #0              /* jump back into kernel           */

	_kernel_entry: .long kernel


/* jump to this point to switch to TrustZone's normal world */
.global monitor_mode_enter_normal_world
monitor_mode_enter_normal_world:
	cps   #22                     /* switch to monitor mode          */
	mov   sp, r0                  /* store vm context pointer        */
	mov   lr, r1                  /* store kernel sp temporarily     */
	add   r0, r0, #18*4           /* add offset of banked modes      */
	.irp mode,27,19,23,18,17      /* save mode specific registers    */
		cps   #\mode              /* switch to given mode            */
		ldmia r0!, {r2,sp,lr}     /* load mode's sp, lr, and spsr    */
		msr   spsr_cxfs, r2       /* load mode's spsr                */
	.endr
	ldmia r0!, {r8 - r12}         /* load fiq r8-r12                 */
	add   r0, r0, #4*4
	ldr   r1, [r0]
	vmsr  fpscr, r1
	add   r1, r0, #4
	vldm  r1!, {d0-d15}
	vldm  r1!, {d16-d31}
	cps   #22                     /* switch to monitor mode          */
	ldmia sp, {r0-lr}^            /* load user r0-r12,sp,lr          */
	str   lr, [sp, #17*4]         /* store kernel sp in vm context   */
	ldr   lr, [sp, #16*4]         /* load vm's cpsr to lr            */
	msr   spsr_cxfs, lr           /* save cpsr to be load            */
	mov   lr, #13
	mcr   p15, 0, lr, c1, c1, 0   /* enable EA, FIQ, NS bit in SCTRL */
	ldr   lr, [sp, #15*4]         /* load vm's ip                    */
	subs  pc, lr, #0
