| // 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 <lib/debuglog.h> |
| #include <lib/io.h> |
| #include <lib/zircon-internal/macros.h> |
| #include <platform.h> |
| #include <string.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <arch/ops.h> |
| #include <kernel/lockdep.h> |
| #include <kernel/thread.h> |
| #include <platform/debug.h> |
| #include <vm/vm.h> |
| |
| namespace { |
| |
| DECLARE_SINGLETON_SPINLOCK_WITH_TYPE(dputc_spin_lock, MonitoredSpinLock); |
| DECLARE_SINGLETON_SPINLOCK_WITH_TYPE(print_spin_lock, MonitoredSpinLock); |
| static fbl::DoublyLinkedList<PrintCallback*> print_callbacks TA_GUARDED(print_spin_lock::Get()); |
| |
| } // namespace |
| |
| void serial_write(ktl::string_view str) { |
| Guard<MonitoredSpinLock, IrqSave> guard{dputc_spin_lock::Get(), SOURCE_TAG}; |
| |
| // Write out the serial port. |
| platform_dputs_irq(str.data(), str.size()); |
| } |
| |
| void console_write(ktl::string_view str) { |
| // Print to any registered console loggers. |
| Guard<MonitoredSpinLock, IrqSave> guard{print_spin_lock::Get(), SOURCE_TAG}; |
| |
| for (PrintCallback& print_callback : print_callbacks) { |
| print_callback.Print(str); |
| } |
| } |
| |
| static void stdout_write(ktl::string_view str) { |
| if (dlog_bypass() == false) { |
| if (dlog_write(DEBUGLOG_INFO, 0, str) == ZX_OK) |
| return; |
| } |
| console_write(str); |
| serial_write(str); |
| } |
| |
| static void stdout_write_buffered(ktl::string_view str) { |
| Thread* t = Thread::Current::Get(); |
| |
| if (unlikely(t == nullptr)) { |
| stdout_write(str); |
| return; |
| } |
| |
| t->linebuffer().Write(str); |
| } |
| |
| void Linebuffer::Write(ktl::string_view str) { |
| // Look for corruption and don't continue. |
| if (unlikely(!is_kernel_address((uintptr_t)buffer_.data()) || pos_ >= buffer_.size())) { |
| stdout_write("<linebuffer corruption>\n"sv); |
| return; |
| } |
| |
| while (!str.empty()) { |
| size_t remaining = buffer_.size() - pos_; |
| auto substring = str.substr(0, remaining); |
| size_t newline_pos = substring.find_first_of('\n'); |
| |
| size_t size; |
| bool inject; |
| bool flush; |
| if (newline_pos != substring.npos) { |
| // A newline that fits in our remaining buffer. |
| size = newline_pos + 1; |
| inject = false; |
| flush = true; |
| } else if (substring.size() == remaining) { |
| // We fill the buffer, injecting a newline. |
| size = remaining - 1; |
| inject = true; |
| flush = true; |
| } else { |
| // We only add to the buffer. |
| size = substring.size(); |
| inject = false; |
| flush = false; |
| } |
| |
| memcpy(&buffer_[pos_], substring.data(), size); |
| str.remove_prefix(size); |
| pos_ += size; |
| |
| if (inject) { |
| buffer_[pos_] = '\n'; |
| pos_ += 1; |
| } |
| if (flush) { |
| stdout_write({buffer_.data(), pos_}); |
| pos_ = 0; |
| } |
| } |
| } |
| |
| void register_print_callback(PrintCallback* cb) { |
| Guard<MonitoredSpinLock, IrqSave> guard{print_spin_lock::Get(), SOURCE_TAG}; |
| |
| print_callbacks.push_front(cb); |
| } |
| |
| void unregister_print_callback(PrintCallback* cb) { |
| Guard<MonitoredSpinLock, IrqSave> guard{print_spin_lock::Get(), SOURCE_TAG}; |
| |
| print_callbacks.erase(*cb); |
| } |
| |
| // 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) { |
| stdout_write_buffered(str); |
| return static_cast<int>(str.size()); |
| }, |
| nullptr}; |
| |
| FILE gConsoleFile{[](void*, ktl::string_view str) { |
| console_write(str); |
| return static_cast<int>(str.size()); |
| }, |
| nullptr}; |
| |
| FILE gSerialFile{[](void*, ktl::string_view str) { |
| serial_write(str); |
| return static_cast<int>(str.size()); |
| }, |
| nullptr}; |