From 24f2f3ba4e922716a4c5b95b0b58cd1a9596c876 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 28 Jan 2022 20:42:23 -0700 Subject: [PATCH] Kernel: Set up an initial boot framebuffer console Instead of seeing a black screen until GraphicsManagement was fully initialized, this allows us to see the console output much earlier. So, if the bootloader provided us with a framebuffer, set up a console as early as possible. --- Kernel/CMakeLists.txt | 1 + .../Console/BootFramebufferConsole.cpp | 64 +++++++++++++++++++ .../Graphics/Console/BootFramebufferConsole.h | 38 +++++++++++ Kernel/init.cpp | 11 +++- Kernel/kprintf.cpp | 9 +++ 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 Kernel/Graphics/Console/BootFramebufferConsole.cpp create mode 100644 Kernel/Graphics/Console/BootFramebufferConsole.h diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index fe7e851b0d..d55b4d0e7d 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -73,6 +73,7 @@ set(KERNEL_SOURCES Devices/HID/VMWareMouseDevice.cpp GlobalProcessExposed.cpp Graphics/Bochs/GraphicsAdapter.cpp + Graphics/Console/BootFramebufferConsole.cpp Graphics/Console/GenericFramebufferConsole.cpp Graphics/Console/ContiguousFramebufferConsole.cpp Graphics/Console/TextModeConsole.cpp diff --git a/Kernel/Graphics/Console/BootFramebufferConsole.cpp b/Kernel/Graphics/Console/BootFramebufferConsole.cpp new file mode 100644 index 0000000000..ac1aeb0d87 --- /dev/null +++ b/Kernel/Graphics/Console/BootFramebufferConsole.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel::Graphics { + +BootFramebufferConsole::BootFramebufferConsole(PhysicalAddress framebuffer_addr, size_t width, size_t height, size_t pitch) + : GenericFramebufferConsoleImpl(width, height, pitch) +{ + // NOTE: We're very early in the boot process, memory allocations shouldn't really fail + auto framebuffer_end = Memory::page_round_up(framebuffer_addr.offset(height * pitch * sizeof(u32)).get()).release_value(); + m_framebuffer = MM.allocate_kernel_region(framebuffer_addr.page_base(), framebuffer_end - framebuffer_addr.page_base().get(), "Boot Framebuffer"sv, Memory::Region::Access::ReadWrite).release_value(); + [[maybe_unused]] auto result = m_framebuffer->set_write_combine(true); + m_framebuffer_data = m_framebuffer->vaddr().offset(framebuffer_addr.offset_in_page()).as_ptr(); + memset(m_framebuffer_data, 0, height * pitch * sizeof(u32)); +} + +void BootFramebufferConsole::clear(size_t x, size_t y, size_t length) +{ + SpinlockLocker lock(m_lock); + if (m_framebuffer_data) + GenericFramebufferConsoleImpl::clear(x, y, length); +} + +void BootFramebufferConsole::clear_glyph(size_t x, size_t y) +{ + VERIFY(m_lock.is_locked()); + GenericFramebufferConsoleImpl::clear_glyph(x, y); +} + +void BootFramebufferConsole::enable() +{ + // Once disabled, ignore requests to re-enable +} + +void BootFramebufferConsole::disable() +{ + SpinlockLocker lock(m_lock); + GenericFramebufferConsoleImpl::disable(); + m_framebuffer = nullptr; + m_framebuffer_data = nullptr; +} + +void BootFramebufferConsole::write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical) +{ + SpinlockLocker lock(m_lock); + if (m_framebuffer_data) + GenericFramebufferConsoleImpl::write(x, y, ch, background, foreground, critical); +} + +u8* BootFramebufferConsole::framebuffer_data() +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_framebuffer_data); + return m_framebuffer_data; +} + +} diff --git a/Kernel/Graphics/Console/BootFramebufferConsole.h b/Kernel/Graphics/Console/BootFramebufferConsole.h new file mode 100644 index 0000000000..434556b28a --- /dev/null +++ b/Kernel/Graphics/Console/BootFramebufferConsole.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Kernel::Graphics { + +class BootFramebufferConsole : public GenericFramebufferConsoleImpl { +public: + virtual void clear(size_t x, size_t y, size_t length) override; + virtual void write(size_t x, size_t y, char ch, Color background, Color foreground, bool critical = false) override; + using GenericFramebufferConsoleImpl::write; + + virtual void enable() override; + virtual void disable() override; + + virtual void flush(size_t, size_t, size_t, size_t) override { } + virtual void set_resolution(size_t, size_t, size_t) override { } + + BootFramebufferConsole(PhysicalAddress framebuffer_addr, size_t width, size_t height, size_t pitch); + +protected: + virtual void clear_glyph(size_t x, size_t y) override; + + virtual u8* framebuffer_data() override; + + OwnPtr m_framebuffer; + u8* m_framebuffer_data {}; + mutable Spinlock m_lock; +}; + +} diff --git a/Kernel/init.cpp b/Kernel/init.cpp index ae34440581..f669a15176 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -135,6 +136,8 @@ READONLY_AFTER_INIT u8 multiboot_framebuffer_bpp; READONLY_AFTER_INIT u8 multiboot_framebuffer_type; } +Atomic boot_framebuffer_console; + extern "C" [[noreturn]] UNMAP_AFTER_INIT void init(BootInfo const& boot_info) { g_in_early_boot = true; @@ -187,6 +190,12 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init(BootInfo const& boot_info) CommandLine::initialize(); Memory::MemoryManager::initialize(0); + if (!multiboot_framebuffer_addr.is_null()) { + // NOTE: If the bootloader provided a framebuffer, then set up an initial console so we can see the output on the screen as soon as possible! + boot_framebuffer_console = &try_make_ref_counted(multiboot_framebuffer_addr, multiboot_framebuffer_width, multiboot_framebuffer_height, multiboot_framebuffer_pitch).value().leak_ref(); + } + dmesgln("Starting SerenityOS..."); + DeviceManagement::initialize(); SysFSComponentRegistry::initialize(); DeviceManagement::the().attach_null_device(*NullDevice::must_initialize()); @@ -224,8 +233,6 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init(BootInfo const& boot_info) APIC::the().setup_ap_boot_environment(); } - dmesgln("Starting SerenityOS..."); - { RefPtr init_stage2_thread; (void)Process::create_kernel_process(init_stage2_thread, KString::must_create("init_stage2"), init_stage2, nullptr, THREAD_AFFINITY_DEFAULT, Process::RegisterProcess::No); diff --git a/Kernel/kprintf.cpp b/Kernel/kprintf.cpp index 4c61b0d183..669a95245b 100644 --- a/Kernel/kprintf.cpp +++ b/Kernel/kprintf.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,10 @@ #include +namespace Kernel { +extern Atomic boot_framebuffer_console; +} + static bool serial_debug; // A recursive spinlock allows us to keep writing in the case where a // page fault happens in the middle of a dbgln(), etc @@ -75,6 +80,8 @@ static void critical_console_out(char ch) // especially when we want to avoid any memory allocations... if (GraphicsManagement::is_initialized() && GraphicsManagement::the().console()) { GraphicsManagement::the().console()->write(ch, true); + } else if (auto* boot_console = boot_framebuffer_console.load()) { + boot_console->write(ch, true); } } @@ -92,6 +99,8 @@ static void console_out(char ch) } if (ConsoleManagement::is_initialized()) { ConsoleManagement::the().debug_tty()->emit_char(ch); + } else if (auto* boot_console = boot_framebuffer_console.load()) { + boot_console->write(ch, true); } }