blob: 4a673f1b19df9dbaa501c95406efc5041b6f4149 [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 <lib/io.h>
#include <err.h>
#include <ctype.h>
#include <debug.h>
#include <assert.h>
#include <list.h>
#include <string.h>
#include <arch/ops.h>
#include <platform.h>
#include <platform/debug.h>
#include <kernel/thread.h>
#include <vm/vm.h>
#if WITH_LIB_DEBUGLOG
#include <lib/debuglog.h>
#endif
/* enable this to cause the kernel-originated messages to splat messages out of the platform
* putc mechanism immediately instead of going through the debug log
*/
#ifndef ENABLE_KERNEL_LL_DEBUG
#define ENABLE_KERNEL_LL_DEBUG 0
#endif
/* routines for dealing with main console io */
#if WITH_LIB_SM
#define PRINT_LOCK_FLAGS SPIN_LOCK_FLAG_IRQ_FIQ
#else
#define PRINT_LOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS
#endif
static spin_lock_t dputc_spin_lock = 0;
void __kernel_serial_write(const char *str, size_t len) {
spin_lock_saved_state_t state;
spin_lock_save(&dputc_spin_lock, &state, PRINT_LOCK_FLAGS);
/* write out the serial port */
platform_dputs(str, len);
spin_unlock_restore(&dputc_spin_lock, state, PRINT_LOCK_FLAGS);
}
static spin_lock_t print_spin_lock = 0;
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)) {
spin_lock_saved_state_t state;
spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
list_for_every_entry(&print_callbacks, cb, print_callback_t, entry) {
if (cb->print)
cb->print(cb, str, len);
}
spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS);
}
}
static void __kernel_stdout_write(const char *str, size_t len)
{
#if WITH_LIB_DEBUGLOG && !ENABLE_KERNEL_LL_DEBUG
if (dlog_write(0, str, len)) {
__kernel_console_write(str, len);
__kernel_serial_write(str, len);
}
#else
__kernel_console_write(str, len);
__kernel_serial_write(str, len);
#endif
}
#if WITH_DEBUG_LINEBUFFER
static void __kernel_stdout_write_buffered(const char *str, size_t len) {
thread_t *t = get_current_thread();
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;
}
#endif
void register_print_callback(print_callback_t *cb)
{
spin_lock_saved_state_t state;
spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
list_add_head(&print_callbacks, &cb->entry);
spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS);
}
void unregister_print_callback(print_callback_t *cb)
{
spin_lock_saved_state_t state;
spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS);
list_delete(&cb->entry);
spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS);
}
int __printf_output_func(const char *s, size_t len, void *state)
{
#if WITH_DEBUG_LINEBUFFER
__kernel_stdout_write_buffered(s, len);
#else
__kernel_stdout_write(s, len);
#endif
return len;
}