blob: 15c3ba43d7261e9955a4a01cec0cc1d303f41fd4 [file] [log] [blame]
Julius Werner94f89072017-07-31 18:15:11 -07001/*
2 * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <asm_macros.S>
8#include <assert_macros.S>
9#include <console.h>
10
11 .globl console_register
12 .globl console_unregister
13 .globl console_set_scope
14 .globl console_switch_state
15 .globl console_putc
16 .globl console_getc
17 .globl console_flush
18
19 /*
20 * The console list pointer is in the data section and not in
21 * .bss even though it is zero-init. In particular, this allows
22 * the console functions to start using this variable before
23 * the runtime memory is initialized for images which do not
24 * need to copy the .data section from ROM to RAM.
25 */
26.section .data.console_list ; .align 3
27 console_list: .quad 0x0
28.section .data.console_state ; .align 0
29 console_state: .byte CONSOLE_FLAG_BOOT
30
31 /* -----------------------------------------------
32 * int console_register(console_t *console)
33 * Function to insert a new console structure into
34 * the console list. Should usually be called by
35 * console_<driver>_register implementations. The
36 * data structure passed will be taken over by the
37 * console framework and *MUST* be allocated in
38 * persistent memory (e.g. the data section).
39 * In : x0 - address of console_t structure
40 * Out: x0 - Always 1 (for easier tail calling)
41 * Clobber list: x0, x1, x14
42 * -----------------------------------------------
43 */
44func console_register
45#if ENABLE_ASSERTIONS
46 cmp x0, #0
47 ASM_ASSERT(ne)
48 adrp x1, __STACKS_START__
49 add x1, x1, :lo12:__STACKS_START__
50 cmp x0, x1
51 b.lo not_on_stack
52 adrp x1, __STACKS_END__
53 add x1, x1, :lo12:__STACKS_END__
54 cmp x0, x1
55 ASM_ASSERT(hs)
56not_on_stack:
57#endif /* ENABLE_ASSERTIONS */
58 adrp x14, console_list
59 ldr x1, [x14, :lo12:console_list] /* X1 = first struct in list */
60 str x0, [x14, :lo12:console_list] /* list head = new console */
61 str x1, [x0, #CONSOLE_T_NEXT] /* new console next ptr = X1 */
62 mov x0, #1
63 ret
64endfunc console_register
65
66 /* -----------------------------------------------
67 * int console_unregister(console_t *console)
68 * Function to find a specific console in the list
69 * of currently active consoles and remove it.
70 * In: x0 - address of console_t struct to remove
71 * Out: x0 - removed address, or NULL if not found
72 * Clobber list: x0, x1, x14
73 * -----------------------------------------------
74 */
75func console_unregister
76 adrp x14, console_list
77 add x14, x14, :lo12:console_list /* X14 = ptr to first struct */
78 ldr x1, [x14] /* X1 = first struct */
79
80unregister_loop:
81 cbz x1, unregister_not_found
82 cmp x0, x1
83 b.eq unregister_found
84 ldr x14, [x14] /* X14 = next ptr of struct */
85 ldr x1, [x14] /* X1 = next struct */
86 b unregister_loop
87
88unregister_found:
89 ldr x1, [x1] /* X1 = next struct */
90 str x1, [x14] /* prev->next = cur->next */
91 ret
92
93unregister_not_found:
94 mov x0, #0 /* return NULL if not found */
95 ret
96endfunc console_unregister
97
98 /* -----------------------------------------------
99 * void console_switch_state(unsigned int new_state)
100 * Function to switch the current console state.
101 * The console state determines which of the
102 * registered consoles are actually used at a time.
103 * In : w0 - global console state to move to
104 * Clobber list: x0, x1
105 * -----------------------------------------------
106 */
107func console_switch_state
108 adrp x1, console_state
109 strb w0, [x1, :lo12:console_state]
110 ret
111endfunc console_switch_state
112
113 /* -----------------------------------------------
114 * void console_set_scope(console_t *console,
115 * unsigned int scope)
116 * Function to update the states that a given console
117 * may be active in.
118 * In : x0 - pointer to console_t struct
119 * : w1 - new active state mask
120 * Clobber list: x0, x1, x2
121 * -----------------------------------------------
122 */
123func console_set_scope
124#if ENABLE_ASSERTIONS
125 tst w1, #~CONSOLE_FLAG_SCOPE_MASK
126 ASM_ASSERT(eq)
127#endif /* ENABLE_ASSERTIONS */
128 ldr w2, [x0, #CONSOLE_T_FLAGS]
129 and w2, w2, #~CONSOLE_FLAG_SCOPE_MASK
130 orr w2, w2, w1
131 str w2, [x0, #CONSOLE_T_FLAGS]
132 ret
133endfunc console_set_scope
134
135 /* ---------------------------------------------
136 * int console_putc(int c)
137 * Function to output a character. Calls all
138 * active console's putc() handlers in succession.
139 * In : x0 - character to be printed
140 * Out: x0 - printed character on success, or < 0
141 if at least one console had an error
142 * Clobber list : x0, x1, x2, x12, x13, x14, x15
143 * ---------------------------------------------
144 */
145func console_putc
146 mov x15, x30
147 mov w13, #ERROR_NO_VALID_CONSOLE /* W13 = current return value */
148 mov w12, w0 /* W12 = character to print */
149 adrp x14, console_list
150 ldr x14, [x14, :lo12:console_list] /* X14 = first console struct */
151
152putc_loop:
153 cbz x14, putc_done
154 adrp x1, console_state
155 ldrb w1, [x1, :lo12:console_state]
156 ldr x2, [x14, #CONSOLE_T_FLAGS]
157 tst w1, w2
158 b.eq putc_continue
159 ldr x2, [x14, #CONSOLE_T_PUTC]
160 cbz x2, putc_continue
161 mov w0, w12
162 mov x1, x14
163 blr x2
164 cmp w13, #ERROR_NO_VALID_CONSOLE /* update W13 if it's NOVALID */
165 ccmp w0, #0, #0x8, ne /* else update it if W0 < 0 */
166 csel w13, w0, w13, lt
167putc_continue:
168 ldr x14, [x14] /* X14 = next struct */
169 b putc_loop
170
171putc_done:
172 mov w0, w13
173 ret x15
174endfunc console_putc
175
176 /* ---------------------------------------------
177 * int console_getc(void)
178 * Function to get a character from any console.
179 * Keeps looping through all consoles' getc()
180 * handlers until one of them returns a
181 * character, then stops iterating and returns
182 * that character to the caller. Will stop looping
183 * if all active consoles report real errors
184 * (other than just not having a char available).
185 * Out : x0 - read character, or < 0 on error
186 * Clobber list : x0, x1, x13, x14, x15
187 * ---------------------------------------------
188 */
189func console_getc
190 mov x15, x30
191getc_try_again:
192 mov w13, #ERROR_NO_VALID_CONSOLE /* W13 = current return value */
193 adrp x14, console_list
194 ldr x14, [x14, :lo12:console_list] /* X14 = first console struct */
195 cbnz x14, getc_loop
196 mov w0, w13 /* If no consoles registered */
197 ret x15 /* return immediately. */
198
199getc_loop:
200 adrp x0, console_state
201 ldrb w0, [x0, :lo12:console_state]
202 ldr x1, [x14, #CONSOLE_T_FLAGS]
203 tst w0, w1
204 b.eq getc_continue
205 ldr x1, [x14, #CONSOLE_T_GETC]
206 cbz x1, getc_continue
207 mov x0, x14
208 blr x1
209 cmp w0, #0 /* if X0 >= 0: return */
210 b.ge getc_found
211 cmp w13, #ERROR_NO_PENDING_CHAR /* may update W13 (NOCHAR has */
212 csel w13, w13, w0, eq /* precedence vs real errors) */
213getc_continue:
214 ldr x14, [x14] /* X14 = next struct */
215 cbnz x14, getc_loop
216 cmp w13, #ERROR_NO_PENDING_CHAR /* Keep scanning if at least */
217 b.eq getc_try_again /* one console returns NOCHAR */
218 mov w0, w13
219
220getc_found:
221 ret x15
222endfunc console_getc
223
224 /* ---------------------------------------------
225 * int console_flush(void)
226 * Function to force a write of all buffered
227 * data that hasn't been output. Calls all
228 * console's flush() handlers in succession.
229 * Out: x0 - 0 on success, < 0 if at least one error
230 * Clobber list : x0, x1, x2, x3, x4, x5, x13, x14, x15
231 * ---------------------------------------------
232 */
233func console_flush
234 mov x15, x30
235 mov w13, #ERROR_NO_VALID_CONSOLE /* W13 = current return value */
236 adrp x14, console_list
237 ldr x14, [x14, :lo12:console_list] /* X14 = first console struct */
238
239flush_loop:
240 cbz x14, flush_done
241 adrp x1, console_state
242 ldrb w1, [x1, :lo12:console_state]
243 ldr x2, [x14, #CONSOLE_T_FLAGS]
244 tst w1, w2
245 b.eq flush_continue
246 ldr x1, [x14, #CONSOLE_T_FLUSH]
247 cbz x1, flush_continue
248 mov x0, x14
249 blr x1
250 cmp w13, #ERROR_NO_VALID_CONSOLE /* update W13 if it's NOVALID */
251 ccmp w0, #0, #0x8, ne /* else update it if W0 < 0 */
252 csel w13, w0, w13, lt
253flush_continue:
254 ldr x14, [x14] /* X14 = next struct */
255 b flush_loop
256
257flush_done:
258 mov w0, w13
259 ret x15
260endfunc console_flush