Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/PrintfImplementation.h>
8#include <AK/StringView.h>
9#include <AK/Types.h>
10#include <Kernel/Arch/DebugOutput.h>
11#if ARCH(X86_64)
12# include <Kernel/Arch/x86_64/BochsDebugOutput.h>
13#endif
14#include <Kernel/Devices/ConsoleDevice.h>
15#include <Kernel/Devices/DeviceManagement.h>
16#include <Kernel/Devices/PCISerialDevice.h>
17#include <Kernel/Graphics/Console/BootFramebufferConsole.h>
18#include <Kernel/Graphics/GraphicsManagement.h>
19#include <Kernel/Locking/Spinlock.h>
20#include <Kernel/TTY/ConsoleManagement.h>
21#include <Kernel/kstdio.h>
22
23namespace Kernel {
24extern Atomic<Graphics::Console*> g_boot_console;
25}
26
27static bool s_serial_debug_enabled;
28// A recursive spinlock allows us to keep writing in the case where a
29// page fault happens in the middle of a dbgln(), etc
30static RecursiveSpinlock<LockRank::None> s_log_lock {};
31
32void set_serial_debug_enabled(bool desired_state)
33{
34 s_serial_debug_enabled = desired_state;
35}
36
37bool is_serial_debug_enabled()
38{
39 return s_serial_debug_enabled;
40}
41
42static void serial_putch(char ch)
43{
44 if (PCISerialDevice::is_available())
45 return PCISerialDevice::the().put_char(ch);
46
47 debug_output(ch);
48}
49
50static void critical_console_out(char ch)
51{
52 if (s_serial_debug_enabled)
53 serial_putch(ch);
54
55#if ARCH(X86_64)
56 // No need to output things to the real ConsoleDevice as no one is likely
57 // to read it (because we are in a fatal situation, so only print things and halt)
58 bochs_debug_output(ch);
59#endif
60
61 // We emit chars directly to the string. this is necessary in few cases,
62 // especially when we want to avoid any memory allocations...
63 if (GraphicsManagement::is_initialized() && GraphicsManagement::the().console()) {
64 GraphicsManagement::the().console()->write(ch, true);
65 } else if (auto* boot_console = g_boot_console.load()) {
66 boot_console->write(ch, true);
67 }
68}
69
70static void console_out(char ch)
71{
72 if (s_serial_debug_enabled)
73 serial_putch(ch);
74
75 // It would be bad to reach the assert in ConsoleDevice()::the() and do a stack overflow
76
77 if (DeviceManagement::the().is_console_device_attached()) {
78 DeviceManagement::the().console_device().put_char(ch);
79 } else {
80#if ARCH(X86_64)
81 bochs_debug_output(ch);
82#endif
83 }
84 if (ConsoleManagement::is_initialized()) {
85 ConsoleManagement::the().debug_tty()->emit_char(ch);
86 } else if (auto* boot_console = g_boot_console.load()) {
87 boot_console->write(ch, true);
88 }
89}
90
91// Declare it, so that the symbol is exported, because libstdc++ uses it.
92// However, *only* libstdc++ uses it, and none of the rest of the Kernel.
93extern "C" int sprintf(char* buffer, char const* fmt, ...);
94
95int sprintf(char*, char const*, ...)
96{
97 VERIFY_NOT_REACHED();
98}
99
100static inline void internal_dbgputch(char ch)
101{
102 if (s_serial_debug_enabled)
103 serial_putch(ch);
104#if ARCH(X86_64)
105 bochs_debug_output(ch);
106#endif
107}
108
109extern "C" void dbgputchar(char ch)
110{
111 internal_dbgputch(ch);
112}
113
114extern "C" void dbgputstr(char const* characters, size_t length)
115{
116 if (!characters)
117 return;
118 SpinlockLocker lock(s_log_lock);
119 for (size_t i = 0; i < length; ++i)
120 internal_dbgputch(characters[i]);
121}
122
123void dbgputstr(StringView view)
124{
125 ::dbgputstr(view.characters_without_null_termination(), view.length());
126}
127
128extern "C" void kernelputstr(char const* characters, size_t length)
129{
130 if (!characters)
131 return;
132 SpinlockLocker lock(s_log_lock);
133 for (size_t i = 0; i < length; ++i)
134 console_out(characters[i]);
135}
136
137extern "C" void kernelcriticalputstr(char const* characters, size_t length)
138{
139 if (!characters)
140 return;
141 SpinlockLocker lock(s_log_lock);
142 for (size_t i = 0; i < length; ++i)
143 critical_console_out(characters[i]);
144}
145
146extern "C" void kernelearlyputstr(char const* characters, size_t length)
147{
148 if (!characters)
149 return;
150 // NOTE: We do not lock the log lock here, as this function is called before this or any other processor was initialized, meaning:
151 // A) The $gs base was not setup yet, so we cannot enter into critical sections, and as a result we cannot use SpinLocks
152 // B) No other processors may try to print at the same time anyway
153 for (size_t i = 0; i < length; ++i)
154 internal_dbgputch(characters[i]);
155}