Serenity Operating System
at master 155 lines 4.4 kB view raw
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}