1 /*
2 * Copyright (c) 2008-2015 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23 #include <lib/io.h>
24
25 #include <err.h>
26 #include <ctype.h>
27 #include <debug.h>
28 #include <assert.h>
29 #include <list.h>
30 #include <string.h>
31 #include <lib/cbuf.h>
32 #include <arch/ops.h>
33 #include <platform.h>
34 #include <platform/debug.h>
35 #include <kernel/mutex.h>
36 #include <kernel/thread.h>
37 #include <lk/init.h>
38
39 /* routines for dealing with main console io */
40
41 #if WITH_LIB_SM
42 #define PRINT_LOCK_FLAGS SPIN_LOCK_FLAG_IRQ_FIQ
43 #else
44 #define PRINT_LOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS
45 #endif
46
47
48 /*
49 * The console is protected by two locks - print_mutex and print_spin_lock.
50 * When printing from a context where interrupts are enabled, print_mutex is
51 * acquired to guard the entire output, and then print_spin_lock is periodically
52 * acquired and released.
53 * When printing from a context where interrupts and disabled, print_spin_lock
54 * is aquired to guard the entire output.
55 * This setup means that, in general, writes to the console will complete
56 * atomically. It is possible, however, for a context with disabled interrupts
57 * to preempt a context with enabled interrupts. This reduces the chance a print
58 * operation inside an interrupt handler will be blocked, at the cost of some
59 * log corruption.
60 */
61 static mutex_t print_mutex = MUTEX_INITIAL_VALUE(print_mutex);
62 static spin_lock_t print_spin_lock = 0;
63 static spin_lock_saved_state_t print_saved_state = 0;
64 static unsigned int lock_held_by = SMP_MAX_CPUS;
65 static struct list_node print_callbacks = LIST_INITIAL_VALUE(print_callbacks);
66
67 #if CONSOLE_HAS_INPUT_BUFFER
68 #ifndef CONSOLE_BUF_LEN
69 #define CONSOLE_BUF_LEN 256
70 #endif
71
72 /* global input circular buffer */
73 cbuf_t console_input_cbuf;
74 static uint8_t console_cbuf_buf[CONSOLE_BUF_LEN];
75 #endif // CONSOLE_HAS_INPUT_BUFFER
76
77 #if EARLY_LOG_BUFFER_SIZE
78 /*
79 * Very early boot logs are captured in a buffer, and then dumped to the
80 * first print callback that is registered
81 */
82
83 static char early_log_buffer[EARLY_LOG_BUFFER_SIZE];
84 static char* early_log_writeptr = early_log_buffer;
85 static char* early_log_end = early_log_buffer + sizeof(early_log_buffer);
86
early_log_print(const char * str,size_t len)87 static void early_log_print(const char *str, size_t len)
88 {
89 size_t remaining = early_log_end - early_log_writeptr;
90 if (remaining == 0) {
91 return;
92 }
93 if (len > remaining) {
94 /*
95 * Don't bother with partial lines, just mark the buffer as full
96 * and drop all further logs
97 */
98 early_log_end = early_log_writeptr;
99 return;
100 }
101 memcpy(early_log_writeptr, str, len);
102 early_log_writeptr += len;
103 }
104 #endif
105
106 /* print lock must be held when invoking out, outs, outc */
out_count(const char * str,size_t len)107 static void out_count(const char *str, size_t len)
108 {
109 print_callback_t *cb;
110 size_t i;
111 int need_lock = !arch_ints_disabled();
112 spin_lock_saved_state_t state = 0;
113
114 DEBUG_ASSERT(need_lock || lock_held_by == arch_curr_cpu_num());
115
116 /* copy to the early log buffer if configured */
117 #if EARLY_LOG_BUFFER_SIZE
118 if (need_lock) {
119 spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
120 }
121 early_log_print(str, len);
122 if (need_lock) {
123 spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS);
124 }
125 #endif
126
127 /* print to any registered loggers */
128 if (!list_is_empty(&print_callbacks)) {
129 if (need_lock) {
130 spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
131 }
132 list_for_every_entry(&print_callbacks, cb, print_callback_t, entry) {
133 if (cb->print) {
134 cb->print(cb, str, len);
135 }
136 }
137 if (need_lock) {
138 spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS);
139 }
140 #if CONSOLE_CALLBACK_DISABLES_SERIAL
141 return;
142 #endif
143 }
144
145 /* write out the serial port */
146 for (i = 0; i < len; i++) {
147 if (need_lock) {
148 spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
149 }
150 platform_dputc(str[i]);
151 if (need_lock) {
152 spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS);
153 }
154 }
155 }
156
157 /* Signal that the write operation is complete. */
out_commit(void)158 static void out_commit(void)
159 {
160 print_callback_t *cb;
161
162 int need_lock = !arch_ints_disabled();
163 spin_lock_saved_state_t state = 0;
164
165 DEBUG_ASSERT(need_lock || lock_held_by == arch_curr_cpu_num());
166
167 /* commit to any registered loggers */
168 if (!list_is_empty(&print_callbacks)) {
169 if (need_lock) {
170 spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
171 }
172 list_for_every_entry(&print_callbacks, cb, print_callback_t, entry) {
173 if (cb->commit) {
174 cb->commit(cb);
175 }
176 }
177 if (need_lock) {
178 spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS);
179 }
180 }
181 }
182
out_lock(void)183 static void out_lock(void)
184 {
185 if (arch_ints_disabled()) {
186 /*
187 * Even though interupts are disabled, FIQs may not be. So save and
188 * restore the interrupt state like a normal spin lock. Due to how
189 * lock/unlock gets called, we need to stash the state in a global.
190 */
191 spin_lock_saved_state_t state;
192 spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
193 print_saved_state = state;
194 lock_held_by = arch_curr_cpu_num();
195 } else {
196 mutex_acquire(&print_mutex);
197 }
198 }
199
out_unlock(void)200 static void out_unlock(void)
201 {
202 if (arch_ints_disabled()) {
203 DEBUG_ASSERT(lock_held_by == arch_curr_cpu_num());
204 lock_held_by = SMP_MAX_CPUS;
205 spin_unlock_restore(&print_spin_lock, print_saved_state,
206 PRINT_LOCK_FLAGS);
207 } else {
208 mutex_release(&print_mutex);
209 }
210 }
211
register_print_callback(print_callback_t * cb)212 void register_print_callback(print_callback_t *cb)
213 {
214 spin_lock_saved_state_t state;
215 spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
216
217 #if EARLY_LOG_BUFFER_SIZE
218 size_t early_log_len = early_log_writeptr - early_log_buffer;
219 if (early_log_len) {
220 if (cb->print) {
221 cb->print(cb, early_log_buffer, early_log_len);
222 }
223 if (cb->commit) {
224 cb->commit(cb);
225 }
226 }
227 #endif
228 list_add_head(&print_callbacks, &cb->entry);
229
230 spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS);
231 }
232
unregister_print_callback(print_callback_t * cb)233 void unregister_print_callback(print_callback_t *cb)
234 {
235 spin_lock_saved_state_t state;
236 spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
237
238 list_delete(&cb->entry);
239
240 spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS);
241 }
242
__debug_stdio_write(io_handle_t * io,const char * s,size_t len)243 static ssize_t __debug_stdio_write(io_handle_t *io, const char *s, size_t len)
244 {
245 out_count(s, len);
246 return len;
247 }
248
__debug_stdio_write_commit(io_handle_t * io)249 static void __debug_stdio_write_commit(io_handle_t *io)
250 {
251 out_commit();
252 }
253
__debug_stdio_lock(io_handle_t * io)254 static void __debug_stdio_lock(io_handle_t *io)
255 {
256 out_lock();
257 }
258
__debug_stdio_unlock(io_handle_t * io)259 static void __debug_stdio_unlock(io_handle_t *io)
260 {
261 out_unlock();
262 }
263
__debug_stdio_read(io_handle_t * io,char * s,size_t len)264 static ssize_t __debug_stdio_read(io_handle_t *io, char *s, size_t len)
265 {
266 if (len == 0)
267 return 0;
268
269 #if CONSOLE_HAS_INPUT_BUFFER
270 ssize_t err = cbuf_read(&console_input_cbuf, s, len, true);
271 return err;
272 #else
273 int err = platform_dgetc(s, true);
274 if (err < 0)
275 return err;
276
277 return 1;
278 #endif
279 }
280
281 #if CONSOLE_HAS_INPUT_BUFFER
console_init_hook(uint level)282 void console_init_hook(uint level)
283 {
284 cbuf_initialize_etc(&console_input_cbuf, sizeof(console_cbuf_buf), console_cbuf_buf);
285 }
286
287 LK_INIT_HOOK(console, console_init_hook, LK_INIT_LEVEL_PLATFORM_EARLY - 1);
288 #endif
289
290 /* global console io handle */
291 static const io_handle_hooks_t console_io_hooks = {
292 .write = __debug_stdio_write,
293 .write_commit = __debug_stdio_write_commit,
294 .read = __debug_stdio_read,
295 .lock = __debug_stdio_lock,
296 .unlock = __debug_stdio_unlock,
297 };
298
299 io_handle_t console_io = IO_HANDLE_INITIAL_VALUE(&console_io_hooks);
300