1/*
2 * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arch.h>
8#include <asm_macros.S>
9#include <common/debug.h>
10
11	.globl	asm_print_str
12	.globl	asm_print_hex
13	.globl	asm_print_hex_bits
14	.globl	asm_assert
15	.globl	do_panic
16	.globl	report_exception
17
18/* Since the max decimal input number is 65536 */
19#define MAX_DEC_DIVISOR		10000
20/* The offset to add to get ascii for numerals '0 - 9' */
21#define ASCII_OFFSET_NUM	'0'
22
23#if ENABLE_ASSERTIONS
24.section .rodata.assert_str, "aS"
25assert_msg1:
26	.asciz "ASSERT: File "
27assert_msg2:
28#if ARM_ARCH_MAJOR == 7 && !defined(ARMV7_SUPPORTS_VIRTUALIZATION)
29	/******************************************************************
30	 * Virtualization comes with the UDIV/SDIV instructions. If missing
31	 * write file line number in hexadecimal format.
32	 ******************************************************************/
33	.asciz " Line 0x"
34#else
35	.asciz " Line "
36
37	/*
38	 * This macro is intended to be used to print the
39	 * line number in decimal. Used by asm_assert macro.
40	 * The max number expected is 65536.
41	 * In: r4 = the decimal to print.
42	 * Clobber: lr, r0, r1, r2, r5, r6
43	 */
44	.macro asm_print_line_dec
45	mov	r6, #10		/* Divide by 10 after every loop iteration */
46	ldr	r5, =MAX_DEC_DIVISOR
47dec_print_loop:
48	udiv	r0, r4, r5			/* Get the quotient */
49	mls	r4, r0, r5, r4			/* Find the remainder */
50	add	r0, r0, #ASCII_OFFSET_NUM	/* Convert to ascii */
51	bl	plat_crash_console_putc
52	udiv	r5, r5, r6			/* Reduce divisor */
53	cmp	r5, #0
54	bne	dec_print_loop
55	.endm
56#endif
57
58/* ---------------------------------------------------------------------------
59 * Assertion support in assembly.
60 * The below function helps to support assertions in assembly where we do not
61 * have a C runtime stack. Arguments to the function are :
62 * r0 - File name
63 * r1 - Line no
64 * Clobber list : lr, r0 - r6
65 * ---------------------------------------------------------------------------
66 */
67func asm_assert
68#if LOG_LEVEL >= LOG_LEVEL_INFO
69	/*
70	 * Only print the output if LOG_LEVEL is higher or equal to
71	 * LOG_LEVEL_INFO, which is the default value for builds with DEBUG=1.
72	 */
73	/* Stash the parameters already in r0 and r1 */
74	mov	r5, r0
75	mov	r6, r1
76
77	/* Ensure the console is initialized */
78	bl	plat_crash_console_init
79
80	/* Check if the console is initialized */
81	cmp	r0, #0
82	beq	_assert_loop
83
84	/* The console is initialized */
85	ldr	r4, =assert_msg1
86	bl	asm_print_str
87	mov	r4, r5
88	bl	asm_print_str
89	ldr	r4, =assert_msg2
90	bl	asm_print_str
91
92	/* Check if line number higher than max permitted */
93	ldr	r4, =~0xffff
94	tst	r6, r4
95	bne	_assert_loop
96	mov	r4, r6
97
98#if ARM_ARCH_MAJOR == 7 && !defined(ARMV7_SUPPORTS_VIRTUALIZATION)
99	/******************************************************************
100	 * Virtualization comes with the UDIV/SDIV instructions. If missing
101	 * write file line number in hexadecimal format.
102	 ******************************************************************/
103	bl	asm_print_hex
104#else
105	asm_print_line_dec
106#endif
107	bl	plat_crash_console_flush
108_assert_loop:
109#endif /* LOG_LEVEL >= LOG_LEVEL_INFO */
110	no_ret	plat_panic_handler
111endfunc asm_assert
112#endif /* ENABLE_ASSERTIONS */
113
114/*
115 * This function prints a string from address in r4
116 * Clobber: lr, r0 - r4
117 */
118func asm_print_str
119	mov	r3, lr
1201:
121	ldrb	r0, [r4], #0x1
122	cmp	r0, #0
123	beq	2f
124	bl	plat_crash_console_putc
125	b	1b
1262:
127	bx	r3
128endfunc asm_print_str
129
130/*
131 * This function prints a hexadecimal number in r4.
132 * In: r4 = the hexadecimal to print.
133 * Clobber: lr, r0 - r3, r5
134 */
135func asm_print_hex
136	mov	r5, #32  /* No of bits to convert to ascii */
137
138	/* Convert to ascii number of bits in r5 */
139asm_print_hex_bits:
140	mov	r3, lr
1411:
142	sub	r5, r5, #4
143	lsr	r0, r4, r5
144	and	r0, r0, #0xf
145	cmp	r0, #0xa
146	blo	2f
147	/* Add by 0x27 in addition to ASCII_OFFSET_NUM
148	 * to get ascii for characters 'a - f'.
149	 */
150	add	r0, r0, #0x27
1512:
152	add	r0, r0, #ASCII_OFFSET_NUM
153	bl	plat_crash_console_putc
154	cmp	r5, #0
155	bne	1b
156	bx	r3
157endfunc asm_print_hex
158
159	/***********************************************************
160	 * The common implementation of do_panic for all BL stages
161	 ***********************************************************/
162
163.section .rodata.panic_str, "aS"
164	panic_msg: .asciz "PANIC at PC : 0x"
165	panic_end: .asciz "\r\n"
166
167func do_panic
168	/* Have LR copy point to PC at the time of panic */
169	sub	r6, lr, #4
170
171	/* Initialize crash console and verify success */
172	bl	plat_crash_console_init
173
174	/* Check if the console is initialized */
175	cmp	r0, #0
176	beq	_panic_handler
177
178	/* The console is initialized */
179	ldr	r4, =panic_msg
180	bl	asm_print_str
181
182	/* Print LR in hex */
183	mov	r4, r6
184	bl	asm_print_hex
185
186	/* Print new line */
187	ldr	r4, =panic_end
188	bl	asm_print_str
189
190	bl	plat_crash_console_flush
191
192_panic_handler:
193	mov	lr, r6
194	b	plat_panic_handler
195endfunc do_panic
196
197	/***********************************************************
198	 * This function is called from the vector table for
199	 * unhandled exceptions. It reads the current mode and
200	 * passes it to platform.
201	 ***********************************************************/
202func report_exception
203	mrs	r0, cpsr
204	and	r0, #MODE32_MASK
205	bl	plat_report_exception
206	no_ret	plat_panic_handler
207endfunc report_exception
208