blob: 44f39e82481f8aef498f5917ca9b664ae5f91d42 [file] [log] [blame]
// 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};