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

7
8
#if MULTI_CONSOLE_API

9
10
11
12
13
14
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#include <asm_macros.S>
#include <assert_macros.S>
#include <console.h>

	.globl	console_register
	.globl	console_unregister
	.globl	console_is_registered
	.globl	console_set_scope
	.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 2
	console_list: .word 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 : r0 - address of console_t structure
	 * Out: r0 - Always 1 (for easier tail calling)
	 * Clobber list: r0, r1
	 * -----------------------------------------------
	 */
func console_register
	push	{r6,  lr}
#if ENABLE_ASSERTIONS
	/* Assert that r0 isn't a NULL pointer */
	cmp	r0, #0
	ASM_ASSERT(ne)
	/* Assert that the struct isn't in the stack */
	ldr	r1, =__STACKS_START__
	cmp	r0, r1
	blo	not_on_stack
	ldr	r1, =__STACKS_END__
	cmp	r0, r1
	ASM_ASSERT(hs)
not_on_stack:
	/* Assert that this struct isn't in the list */
	mov	r1, r0 /* Preserve r0 and lr */
	bl	console_is_registered
	cmp	r0, #0
	ASM_ASSERT(eq)
	mov	r0, r1
#endif /* ENABLE_ASSERTIONS */
	ldr	r6, =console_list
	ldr	r1, [r6]	/* R1 = first struct in list */
	str	r0, [r6]	/* list head = new console */
	str	r1, [r0, #CONSOLE_T_NEXT]	/* new console next ptr = R1 */
	mov	r0, #1
	pop	{r6, pc}
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: r0 - address of console_t struct to remove
	 * Out: r0 - removed address, or NULL if not found
	 * Clobber list: r0, r1
	 * -----------------------------------------------
	 */
func console_unregister
#if ENABLE_ASSERTIONS
	/* Assert that r0 isn't a NULL pointer */
	cmp	r0, #0
	ASM_ASSERT(ne)
#endif /* ENABLE_ASSERTIONS */
	push	{r6}
	ldr	r6, =console_list		/* R6 = ptr to first struct */
	ldr	r1, [r6]			/* R1 = first struct */

unregister_loop:
	cmp	r1, #0
	beq	unregister_not_found
	cmp	r0, r1
	beq	unregister_found
	ldr	r6, [r6]			/* R6 = next ptr of struct */
	ldr	r1, [r6]			/* R1 = next struct */
	b	unregister_loop

unregister_found:
	ldr	r1, [r1]			/* R1 = next struct */
	str	r1, [r6]			/* prev->next = cur->next */
	pop	{r6}
	bx	lr

unregister_not_found:
	mov	r0, #0				/* return NULL if not found */
	pop	{r6}
	bx	lr
endfunc console_unregister

	/* -----------------------------------------------
	 * int console_is_registered(console_t *console)
	 * Function to detect if a specific console is
	 * registered or not.
	 * In: r0 - address of console_t struct to remove
	 * Out: r0 - 1 if it is registered, 0 if not.
	 * Clobber list: r0
	 * -----------------------------------------------
	 */
func console_is_registered
#if ENABLE_ASSERTIONS
	/* Assert that r0 isn't a NULL pointer */
	cmp	r0, #0
	ASM_ASSERT(ne)
#endif /* ENABLE_ASSERTIONS */
	push	{r6}
	ldr	r6, =console_list
	ldr	r6, [r6]	/* R6 = first console struct */
check_registered_loop:
	cmp	r6, #0			/* Check if end of list */
	beq	console_not_registered
	cmp	r0, r6		/* Check if the pointers are different */
	beq	console_registered
	ldr	r6, [r6, #CONSOLE_T_NEXT]	/* Get pointer to next struct */
	b	check_registered_loop
console_not_registered:
	mov	r0, #0
	pop	{r6}
	bx	lr
console_registered:
	mov	r0, #1
	pop	{r6}
	bx	lr
endfunc console_is_registered

	/* -----------------------------------------------
	 * 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 : r0 - global console state to move to
	 * Clobber list: r0, r1
	 * -----------------------------------------------
	 */
func console_switch_state
	ldr	r1, =console_state
	strb	r0, [r1]
	bx	lr
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 : r0 - pointer to console_t struct
	 *    : r1 - new active state mask
	 * Clobber list: r0, r1, r2
	 * -----------------------------------------------
	 */
func console_set_scope
#if ENABLE_ASSERTIONS
	ands	r2, r1, #~CONSOLE_FLAG_SCOPE_MASK
	ASM_ASSERT(eq)
#endif /* ENABLE_ASSERTIONS */
	ldr	r2, [r0, #CONSOLE_T_FLAGS]
	and	r2, r2, #~CONSOLE_FLAG_SCOPE_MASK
	orr	r2, r2, r1
	str	r2, [r0, #CONSOLE_T_FLAGS]
	bx	lr
endfunc console_set_scope

	/* ---------------------------------------------
	 * int console_putc(int c)
	 * Function to output a character. Calls all
	 * active console's putc() handlers in succession.
	 * In : r0 - character to be printed
	 * Out: r0 - printed character on success, or < 0
	             if at least one console had an error
	 * Clobber list : r0, r1, r2
	 * ---------------------------------------------
	 */
func console_putc
	push	{r4-r6, lr}
	mov	r5, #ERROR_NO_VALID_CONSOLE	/* R5 = current return value */
	mov	r4, r0				/* R4 = character to print */
	ldr	r6, =console_list
	ldr	r6, [r6]	/* R6 = first console struct */

putc_loop:
	cmp	r6, #0
	beq	putc_done
	ldr	r1, =console_state
	ldrb	r1, [r1]
	ldr	r2, [r6, #CONSOLE_T_FLAGS]
	tst	r1, r2
	beq	putc_continue
	ldr	r2, [r6, #CONSOLE_T_PUTC]
	cmp	r2, #0
	beq	putc_continue
	mov	r0, r4
	mov	r1, r6
	blx	r2
	cmp	r5, #ERROR_NO_VALID_CONSOLE	/* update R5 if it's NOVALID */
	cmpne	r0, #0				/* else update it if R0 < 0 */
	movlt	r5, r0
putc_continue:
	ldr	r6, [r6]			/* R6 = next struct */
	b	putc_loop

putc_done:
	mov	r0, r5
	pop	{r4-r6, pc}
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 : r0 - read character, or < 0 on error
	 * Clobber list : r0, r1
	 * ---------------------------------------------
	 */
func console_getc
	push	{r5-r6, lr}
getc_try_again:
	mov	r5, #ERROR_NO_VALID_CONSOLE	/* R5 = current return value */
	ldr	r6, =console_list
	ldr	r6, [r6]			/* R6 = first console struct */
	cmp	r6, #0
	bne	getc_loop
	mov	r0, r5				/* If no consoles registered */
	pop	{r5-r6, pc}			/* return immediately. */

getc_loop:
	ldr	r0, =console_state
	ldrb	r0, [r0]
	ldr	r1, [r6, #CONSOLE_T_FLAGS]
	tst	r0, r1
	beq	getc_continue
	ldr	r1, [r6, #CONSOLE_T_GETC]
	cmp	r1, #0
	beq	getc_continue
	mov	r0, r6
	blx	r1
	cmp	r0, #0				/* if R0 >= 0: return */
	bge	getc_found
	cmp	r5, #ERROR_NO_PENDING_CHAR	/* may update R5 (NOCHAR has */
	movne	r5, r0				/* precedence vs real errors) */
getc_continue:
	ldr	r6, [r6]			/* R6 = next struct */
	cmp	r6, #0
	bne	getc_loop
	cmp	r5, #ERROR_NO_PENDING_CHAR	/* Keep scanning if at least */
	beq	getc_try_again			/* one console returns NOCHAR */
	mov	r0, r5

getc_found:
	pop	{r5-r6, pc}
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: r0 - 0 on success, < 0 if at least one error
	 * Clobber list : r0, r1, r2
	 * ---------------------------------------------
	 */
func console_flush
	push	{r5-r6, lr}
	mov	r5, #ERROR_NO_VALID_CONSOLE	/* R5 = current return value */
	ldr	r6, =console_list
	ldr	r6, [r6]			/* R6 = first console struct */

flush_loop:
	cmp	r6, #0
	beq	flush_done
	ldr	r1, =console_state
	ldrb	r1, [r1]
	ldr	r2, [r6, #CONSOLE_T_FLAGS]
	tst	r1, r2
	beq	flush_continue
	ldr	r1, [r6, #CONSOLE_T_FLUSH]
	cmp	r1, #0
	beq	flush_continue
	mov	r0, r6
	blx	r1
	cmp	r5, #ERROR_NO_VALID_CONSOLE	/* update R5 if it's NOVALID */
	cmpne	r0, #0				/* else update it if R0 < 0 */
	movlt	r5, r0
flush_continue:
	ldr	r6, [r6]			/* R6 = next struct */
	b	flush_loop

flush_done:
	mov	r0, r5
	pop	{r5-r6, pc}
endfunc console_flush
321
322

#endif	/* MULTI_CONSOLE_API */