multi_console.S 9.44 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
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
	.globl	console_switch_state
	.globl	console_putc
	.globl	console_getc
	.globl	console_flush

	/*
	 *  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)
42
	 * Clobber list: x0, x1
43
44
45
	 * -----------------------------------------------
	 */
func console_register
46
	stp	x21, x30, [sp, #-16]!
47
#if ENABLE_ASSERTIONS
48
	/* Assert that x0 isn't a NULL pointer */
49
50
	cmp	x0, #0
	ASM_ASSERT(ne)
51
	/* Assert that the struct isn't in the stack */
52
53
54
55
56
57
58
59
60
	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:
61
62
63
64
65
66
	/* 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
67
#endif /* ENABLE_ASSERTIONS */
68
69
70
	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 */
71
72
	str	x1, [x0, #CONSOLE_T_NEXT]	/* new console next ptr = X1 */
	mov	x0, #1
73
	ldp	x21, x30, [sp], #16
74
75
76
77
78
79
80
81
82
	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
83
	 * Clobber list: x0, x1
84
85
86
	 * -----------------------------------------------
	 */
func console_unregister
87
88
89
90
91
#if ENABLE_ASSERTIONS
	/* Assert that x0 isn't a NULL pointer */
	cmp	x0, #0
	ASM_ASSERT(ne)
#endif /* ENABLE_ASSERTIONS */
92
93
94
95
	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 */
96
97
98
99
100

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

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

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

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

151
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
	/* -----------------------------------------------
	 * 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
195
	 * Clobber list : x0, x1, x2
196
197
198
	 * ---------------------------------------------
	 */
func console_putc
199
200
201
202
203
204
	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 */
205
206

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

putc_done:
226
227
228
229
	mov	w0, w20
	ldp	x19, x20, [sp], #16
	ldp	x21, x30, [sp], #16
	ret
230
231
232
233
234
235
236
237
238
239
240
241
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
242
	 * Clobber list : x0, x1
243
244
245
	 * ---------------------------------------------
	 */
func console_getc
246
247
	stp	x30, xzr, [sp, #-16]!
	stp	x20, x21, [sp, #-16]!
248
getc_try_again:
249
250
251
252
253
254
255
256
	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. */
257
258
259
260

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

getc_found:
280
281
282
	ldp	x20, x21, [sp], #16
	ldp	x30, xzr, [sp], #16
	ret
283
284
285
286
287
288
289
290
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
291
	 * Clobber list : x0, x1, x2
292
293
294
	 * ---------------------------------------------
	 */
func console_flush
295
296
297
298
299
	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 */
300
301

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

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