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