summaryrefslogtreecommitdiff
path: root/src/soc/qualcomm/ipq806x/tz_wrapper.S
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/qualcomm/ipq806x/tz_wrapper.S')
-rw-r--r--src/soc/qualcomm/ipq806x/tz_wrapper.S106
1 files changed, 106 insertions, 0 deletions
diff --git a/src/soc/qualcomm/ipq806x/tz_wrapper.S b/src/soc/qualcomm/ipq806x/tz_wrapper.S
new file mode 100644
index 0000000000..70cc170f64
--- /dev/null
+++ b/src/soc/qualcomm/ipq806x/tz_wrapper.S
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * TZ expects the ARM core to be in 'ARM' mode. However, coreboot seems
+ * to be compiled in mixed thumb/arm mode. Hence create a glue function
+ * to invoke TZ.
+ */
+
+#include <arch/asm.h>
+
+/*
+ * Force ARM mode. Else this gets assembled with mixed ARM and
+ * Thumb instructions. We set up everything and jump to TZBSP
+ * using the 'blx' instruction. For 'blx' if the last bit of the
+ * destination address is zero, it switches to ARM mode. Since,
+ * we are already in ARM mode, nothing to switch as such.
+ *
+ * However, when TZBSP returns, the CPU is still in ARM mode.
+ * If the assembler inserts Thumb instructions between the point
+ * of return from TZBSP to the 'bx' instruction we are hosed.
+ * Hence forcing ARM mode.
+ *
+ * Rest of the code can be compiled in mixed ARM/Thumb mode.
+ * Since tz_init_wrapper is being forced as an ARM symbol,
+ * callers will use 'blx' to come here forcing a switch to
+ * ARM mode. The wrapper does its job and returns back to the
+ * Thumb caller.
+ */
+.arm
+/*
+ * int tz_init_wrapper(int, int, void *);
+ */
+ENTRY(tz_init_wrapper)
+ .global tz_init_wrapper
+
+ /*
+ * r0 = tz_arg1
+ * r1 = tz_arg2
+ * r2 = tz_load_addr
+ */
+
+ /*
+ * Per the AAPCS
+ * r0, r1, r2, r3, r12 can be clobbered
+ * r4, r5, r6, r7, r8, r9, r10, r11 have to be preserved
+ *
+ * Following code clobbers
+ * r0 - Setting return value to zero
+ * r1 - For doing a thumb return
+ * r3 - Passing 'SP' from current mode to 'svc' mode
+ * r4 - To save & restore CPSR
+ *
+ * Per AAPCS, save and restore r4, rest are 'clobberable' :)
+ * The invoked code takes care of saving and restoring the other
+ * preserved registers (i.e. r5 - r11)
+ *
+ * Stack Usage:
+ * SP -> | LR | (Lower address)
+ * | r4 |
+ * | CPSR |
+ * |-------|
+ * | . |
+ * | . |
+ * | . | (Higher address)
+ */
+
+ sub sp, sp, #12 /* Allocate stack frame */
+ str lr, [sp] /* Save return address */
+ str r4, [sp, #4] /* Use r4 to hold the new CPSR value */
+
+ mov r3, sp /* Get current stack pointer */
+
+ mrs r4, CPSR /* save CPSR */
+ str r4, [sp, #8]
+
+ bic r4, r4, 0x1f /* Clear mode bits */
+ orr r4, r4, 0x13 /* 'svc' mode */
+ msr cpsr_cxf, r4 /* Switch to Supervisor mode. */
+ mov sp, r3 /* Use the same stack as the previous mode */
+
+ blx r2 /* Jump to TZ in ARM mode */
+
+ nop /* back from TZ, in ARM mode */
+
+ ldr r4, [sp, #8] /* restore CPSR */
+ msr cpsr_cxf, r4
+
+ ldr r4, [sp, #4] /* restore r4 */
+
+ ldr lr, [sp] /* saved return address */
+ add sp, sp, #12 /* free stack frame */
+
+ bx lr /* back to thumb caller */
+
+ENDPROC(tz_init_wrapper)