multi_console.S 9.46 KB
Newer Older
1
/*
2
 * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
3
4
5
6
7
8
9
10
11
12
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <asm_macros.S>
#include <assert_macros.S>
#include <console.h>

	.globl	console_register
	.globl	console_unregister
13
14
	.globl	console_is_registered
	.globl	console_set_scope
15
16
17
18
	.globl	console_switch_state
	.globl	console_putc
	.globl	console_getc
	.globl	console_flush
19
	.globl	console_list
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

	/*
	 *  The console list pointer is in the data section and not in
	 *  .bss even though it is zero-init. In particular, this allows
	 *  the console functions to start using this variable before
	 *  the runtime memory is initialized for images which do not
	 *  need to copy the .data section from ROM to RAM.
	 */
.section .data.console_list ; .align 3
	console_list: .quad 0x0
.section .data.console_state ; .align 0
	console_state: .byte CONSOLE_FLAG_BOOT

	/* -----------------------------------------------
	 * int console_register(console_t *console)
	 * Function to insert a new console structure into
	 * the console list. Should usually be called by
	 * console_<driver>_register implementations. The
	 * data structure passed will be taken over by the
	 * console framework and *MUST* be allocated in
	 * persistent memory (e.g. the data section).
	 * In : x0 - address of console_t structure
	 * Out: x0 - Always 1 (for easier tail calling)
43
	 * Clobber list: x0, x1
44
45
46
	 * -----------------------------------------------
	 */
func console_register
47
	stp	x21, x30, [sp, #-16]!
48
#if ENABLE_ASSERTIONS
49
	/* Assert that x0 isn't a NULL pointer */
50
51
	cmp	x0, #0
	ASM_ASSERT(ne)
52
	/* Assert that the struct isn't in the stack */
53
54
55
56
57
58
59
60
61
	adrp	x1, __STACKS_START__
	add	x1, x1, :lo12:__STACKS_START__
	cmp	x0, x1
	b.lo	not_on_stack
	adrp	x1, __STACKS_END__
	add	x1, x1, :lo12:__STACKS_END__
	cmp	x0, x1
	ASM_ASSERT(hs)
not_on_stack:
62
63
64
65
66
67
	/* Assert that this struct isn't in the list */
	mov	x1, x0 /* Preserve x0 and x30 */
	bl	console_is_registered
	cmp	x0, #0
	ASM_ASSERT(eq)
	mov	x0, x1
68
#endif /* ENABLE_ASSERTIONS */
69
70
71
	adrp	x21, console_list
	ldr	x1, [x21, :lo12:console_list]	/* X1 = first struct in list */
	str	x0, [x21, :lo12:console_list]	/* list head = new console */
72
73
	str	x1, [x0, #CONSOLE_T_NEXT]	/* new console next ptr = X1 */
	mov	x0, #1
74
	ldp	x21, x30, [sp], #16
75
76
77
78
79
80
81
82
83
	ret
endfunc console_register

	/* -----------------------------------------------
	 * int console_unregister(console_t *console)
	 * Function to find a specific console in the list
	 * of currently active consoles and remove it.
	 * In: x0 - address of console_t struct to remove
	 * Out: x0 - removed address, or NULL if not found
84
	 * Clobber list: x0, x1
85
86
87
	 * -----------------------------------------------
	 */
func console_unregister
88
89
90
91
92
#if ENABLE_ASSERTIONS
	/* Assert that x0 isn't a NULL pointer */
	cmp	x0, #0
	ASM_ASSERT(ne)
#endif /* ENABLE_ASSERTIONS */
93
94
95
96
	stp	x21, xzr, [sp, #-16]!
	adrp	x21, console_list
	add	x21, x21, :lo12:console_list	/* X21 = ptr to first struct */
	ldr	x1, [x21]			/* X1 = first struct */
97
98
99
100
101

unregister_loop:
	cbz	x1, unregister_not_found
	cmp	x0, x1
	b.eq	unregister_found
102
103
	ldr	x21, [x21]			/* X21 = next ptr of struct */
	ldr	x1, [x21]			/* X1 = next struct */
104
105
106
107
	b	unregister_loop

unregister_found:
	ldr	x1, [x1]			/* X1 = next struct */
108
109
	str	x1, [x21]			/* prev->next = cur->next */
	ldp	x21, xzr, [sp], #16
110
111
112
113
	ret

unregister_not_found:
	mov	x0, #0				/* return NULL if not found */
114
	ldp	x21, xzr, [sp], #16
115
116
117
	ret
endfunc console_unregister

118
119
120
121
122
123
	/* -----------------------------------------------
	 * int console_is_registered(console_t *console)
	 * Function to detect if a specific console is
	 * registered or not.
	 * In: x0 - address of console_t struct to remove
	 * Out: x0 - 1 if it is registered, 0 if not.
124
	 * Clobber list: x0
125
126
127
128
129
130
131
132
	 * -----------------------------------------------
	 */
func console_is_registered
#if ENABLE_ASSERTIONS
	/* Assert that x0 isn't a NULL pointer */
	cmp	x0, #0
	ASM_ASSERT(ne)
#endif /* ENABLE_ASSERTIONS */
133
134
135
	stp	x21, xzr, [sp, #-16]!
	adrp	x21, console_list
	ldr	x21, [x21, :lo12:console_list]	/* X21 = first console struct */
136
check_registered_loop:
137
138
	cbz	x21, console_not_registered /* Check if end of list */
	cmp	x0, x21		/* Check if the pointers are different */
139
	b.eq	console_registered
140
	ldr	x21, [x21, #CONSOLE_T_NEXT]	/* Get pointer to next struct */
141
142
143
	b	check_registered_loop
console_not_registered:
	mov	x0, #0
144
	ldp	x21, xzr, [sp], #16
145
146
147
	ret
console_registered:
	mov	x0, #1
148
	ldp	x21, xzr, [sp], #16
149
150
151
	ret
endfunc console_is_registered

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
	/* -----------------------------------------------
	 * void console_switch_state(unsigned int new_state)
	 * Function to switch the current console state.
	 * The console state determines which of the
	 * registered consoles are actually used at a time.
	 * In : w0 - global console state to move to
	 * Clobber list: x0, x1
	 * -----------------------------------------------
	 */
func console_switch_state
	adrp	x1, console_state
	strb	w0, [x1, :lo12:console_state]
	ret
endfunc console_switch_state

	/* -----------------------------------------------
	 * void console_set_scope(console_t *console,
	 *                       unsigned int scope)
	 * Function to update the states that a given console
	 * may be active in.
	 * In : x0 - pointer to console_t struct
	 *    : w1 - new active state mask
	 * Clobber list: x0, x1, x2
	 * -----------------------------------------------
	 */
func console_set_scope
#if ENABLE_ASSERTIONS
	tst	w1, #~CONSOLE_FLAG_SCOPE_MASK
	ASM_ASSERT(eq)
#endif /* ENABLE_ASSERTIONS */
	ldr	w2, [x0, #CONSOLE_T_FLAGS]
	and	w2, w2, #~CONSOLE_FLAG_SCOPE_MASK
	orr	w2, w2, w1
	str	w2, [x0, #CONSOLE_T_FLAGS]
	ret
endfunc console_set_scope

	/* ---------------------------------------------
	 * int console_putc(int c)
	 * Function to output a character. Calls all
	 * active console's putc() handlers in succession.
	 * In : x0 - character to be printed
	 * Out: x0 - printed character on success, or < 0
	             if at least one console had an error
196
	 * Clobber list : x0, x1, x2
197
198
199
	 * ---------------------------------------------
	 */
func console_putc
200
201
202
203
204
205
	stp	x21, x30, [sp, #-16]!
	stp	x19, x20, [sp, #-16]!
	mov	w20, #ERROR_NO_VALID_CONSOLE	/* W20 = current return value */
	mov	w19, w0				/* W19 = character to print */
	adrp	x21, console_list
	ldr	x21, [x21, :lo12:console_list]	/* X21 = first console struct */
206
207

putc_loop:
208
	cbz	x21, putc_done
209
210
	adrp	x1, console_state
	ldrb	w1, [x1, :lo12:console_state]
211
	ldr	w2, [x21, #CONSOLE_T_FLAGS]
212
213
	tst	w1, w2
	b.eq	putc_continue
214
	ldr	x2, [x21, #CONSOLE_T_PUTC]
215
	cbz	x2, putc_continue
216
217
	mov	w0, w19
	mov	x1, x21
218
	blr	x2
219
	cmp	w20, #ERROR_NO_VALID_CONSOLE	/* update W20 if it's NOVALID */
220
	ccmp	w0, #0, #0x8, ne		/* else update it if W0 < 0 */
221
	csel	w20, w0, w20, lt
222
putc_continue:
223
	ldr	x21, [x21]			/* X21 = next struct */
224
225
226
	b	putc_loop

putc_done:
227
228
229
230
	mov	w0, w20
	ldp	x19, x20, [sp], #16
	ldp	x21, x30, [sp], #16
	ret
231
232
233
234
235
236
237
238
239
240
241
242
endfunc console_putc

	/* ---------------------------------------------
	 * int console_getc(void)
	 * Function to get a character from any console.
	 * Keeps looping through all consoles' getc()
	 * handlers until one of them returns a
	 * character, then stops iterating and returns
	 * that character to the caller. Will stop looping
	 * if all active consoles report real errors
	 * (other than just not having a char available).
	 * Out : x0 - read character, or < 0 on error
243
	 * Clobber list : x0, x1
244
245
246
	 * ---------------------------------------------
	 */
func console_getc
247
248
	stp	x30, xzr, [sp, #-16]!
	stp	x20, x21, [sp, #-16]!
249
getc_try_again:
250
251
252
253
254
255
256
257
	mov	w20, #ERROR_NO_VALID_CONSOLE	/* W20 = current return value */
	adrp	x21, console_list
	ldr	x21, [x21, :lo12:console_list]	/* X21 = first console struct */
	cbnz	x21, getc_loop
	mov	w0, w20				/* If no consoles registered */
	ldp	x20, x21, [sp], #16
	ldp	x30, xzr, [sp], #16
	ret					/* return immediately. */
258
259
260
261

getc_loop:
	adrp	x0, console_state
	ldrb	w0, [x0, :lo12:console_state]
262
	ldr	w1, [x21, #CONSOLE_T_FLAGS]
263
264
	tst	w0, w1
	b.eq	getc_continue
265
	ldr	x1, [x21, #CONSOLE_T_GETC]
266
	cbz	x1, getc_continue
267
	mov	x0, x21
268
269
270
	blr	x1
	cmp	w0, #0				/* if X0 >= 0: return */
	b.ge	getc_found
271
272
	cmp	w20, #ERROR_NO_PENDING_CHAR	/* may update W20 (NOCHAR has */
	csel	w20, w20, w0, eq		/* precedence vs real errors) */
273
getc_continue:
274
275
276
	ldr	x21, [x21]			/* X21 = next struct */
	cbnz	x21, getc_loop
	cmp	w20, #ERROR_NO_PENDING_CHAR	/* Keep scanning if at least */
277
	b.eq	getc_try_again			/* one console returns NOCHAR */
278
	mov	w0, w20
279
280

getc_found:
281
282
283
	ldp	x20, x21, [sp], #16
	ldp	x30, xzr, [sp], #16
	ret
284
285
286
287
288
289
290
291
endfunc console_getc

	/* ---------------------------------------------
	 * int console_flush(void)
	 * Function to force a write of all buffered
	 * data that hasn't been output. Calls all
	 * console's flush() handlers in succession.
	 * Out: x0 - 0 on success, < 0 if at least one error
292
	 * Clobber list : x0, x1, x2
293
294
295
	 * ---------------------------------------------
	 */
func console_flush
296
297
298
299
300
	stp	x30, xzr, [sp, #-16]!
	stp	x20, x21, [sp, #-16]!
	mov	w20, #ERROR_NO_VALID_CONSOLE	/* W20 = current return value */
	adrp	x21, console_list
	ldr	x21, [x21, :lo12:console_list]	/* X21 = first console struct */
301
302

flush_loop:
303
	cbz	x21, flush_done
304
305
	adrp	x1, console_state
	ldrb	w1, [x1, :lo12:console_state]
306
	ldr	w2, [x21, #CONSOLE_T_FLAGS]
307
308
	tst	w1, w2
	b.eq	flush_continue
309
	ldr	x1, [x21, #CONSOLE_T_FLUSH]
310
	cbz	x1, flush_continue
311
	mov	x0, x21
312
	blr	x1
313
	cmp	w20, #ERROR_NO_VALID_CONSOLE	/* update W20 if it's NOVALID */
314
	ccmp	w0, #0, #0x8, ne		/* else update it if W0 < 0 */
315
	csel	w20, w0, w20, lt
316
flush_continue:
317
	ldr	x21, [x21]			/* X21 = next struct */
318
319
320
	b	flush_loop

flush_done:
321
322
323
324
	mov	w0, w20
	ldp	x20, x21, [sp], #16
	ldp	x30, xzr, [sp], #16
	ret
325
endfunc console_flush