| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2008-2015 Travis Geiselbrecht |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <assert.h> |
| #include <ctype.h> |
| #include <debug.h> |
| #include <err.h> |
| #include <lib/debuglog.h> |
| #include <lib/io.h> |
| #include <platform.h> |
| #include <string.h> |
| #include <zircon/listnode.h> |
| |
| #include <arch/ops.h> |
| #include <kernel/auto_lock.h> |
| #include <kernel/thread.h> |
| #include <platform/debug.h> |
| #include <vm/vm.h> |
| |
| /* routines for dealing with main console io */ |
| |
| static SpinLock dputc_spin_lock; |
| |
| void __kernel_serial_write(const char* str, size_t len) { |
| AutoSpinLock guard(&dputc_spin_lock); |
| |
| /* write out the serial port */ |
| platform_dputs_irq(str, len); |
| } |
| |
| static SpinLock print_spin_lock; |
| static struct list_node print_callbacks = LIST_INITIAL_VALUE(print_callbacks); |
| |
| void __kernel_console_write(const char* str, size_t len) { |
| print_callback_t* cb; |
| |
| /* print to any registered loggers */ |
| if (!list_is_empty(&print_callbacks)) { |
| AutoSpinLock guard(&print_spin_lock); |
| |
| list_for_every_entry (&print_callbacks, cb, print_callback_t, entry) { |
| if (cb->print) |
| cb->print(cb, str, len); |
| } |
| } |
| } |
| |
| static void __kernel_stdout_write(const char* str, size_t len) { |
| if (dlog_bypass() == false) { |
| if (dlog_write(0, str, len) == ZX_OK) |
| return; |
| } |
| __kernel_console_write(str, len); |
| __kernel_serial_write(str, len); |
| } |
| |
| #if WITH_DEBUG_LINEBUFFER |
| static void __kernel_stdout_write_buffered(const char* str, size_t len) { |
| Thread* t = Thread::Current::Get(); |
| |
| if (unlikely(t == NULL)) { |
| __kernel_stdout_write(str, len); |
| return; |
| } |
| |
| char* buf = t->linebuffer_; |
| size_t pos = t->linebuffer_pos_; |
| |
| // look for corruption and don't continue |
| if (unlikely(!is_kernel_address((uintptr_t)buf) || pos >= THREAD_LINEBUFFER_LENGTH)) { |
| const char* str = "<linebuffer corruption>\n"; |
| __kernel_stdout_write(str, strlen(str)); |
| return; |
| } |
| |
| while (len-- > 0) { |
| char c = *str++; |
| buf[pos++] = c; |
| if (c == '\n') { |
| __kernel_stdout_write(buf, pos); |
| pos = 0; |
| continue; |
| } |
| if (pos == (THREAD_LINEBUFFER_LENGTH - 1)) { |
| buf[pos++] = '\n'; |
| __kernel_stdout_write(buf, pos); |
| pos = 0; |
| continue; |
| } |
| } |
| t->linebuffer_pos_ = pos; |
| } |
| |
| static constexpr auto kStdoutWrite = __kernel_stdout_write_buffered; |
| |
| #else // !WITH_DEBUG_LINEBUFFER |
| |
| static constexpr auto kStdoutWrite = __kernel_stdout_write; |
| |
| #endif // WITH_DEBUG_LINEBUFFER |
| |
| void register_print_callback(print_callback_t* cb) { |
| AutoSpinLock guard(&print_spin_lock); |
| |
| list_add_head(&print_callbacks, &cb->entry); |
| } |
| |
| void unregister_print_callback(print_callback_t* cb) { |
| AutoSpinLock guard(&print_spin_lock); |
| |
| list_delete(&cb->entry); |
| } |
| |
| // This is what printf calls. Really this could and should be const. |
| // But all the stdio function signatures require non-const `FILE*`. |
| FILE FILE::stdout_{[](void*, ktl::string_view str) { |
| kStdoutWrite(str.data(), str.size()); |
| return static_cast<int>(str.size()); |
| }, |
| nullptr}; |