diff --git a/AK/Format.cpp b/AK/Format.cpp index 537a3cd06f..9ee6a04cda 100644 --- a/AK/Format.cpp +++ b/AK/Format.cpp @@ -677,6 +677,29 @@ void vdmesgln(StringView fmtstr, TypeErasedFormatParams params) const auto string = builder.string_view(); kernelputstr(string.characters_without_null_termination(), string.length()); } + +void v_critical_dmesgln(StringView fmtstr, TypeErasedFormatParams params) +{ + // FIXME: Try to avoid memory allocations further to prevent faulting + // at OOM conditions. + + StringBuilder builder; +# ifdef __serenity__ + if (Kernel::Processor::is_initialized() && Kernel::Thread::current()) { + auto& thread = *Kernel::Thread::current(); + builder.appendff("[{}({}:{})]: ", thread.process().name(), thread.pid().value(), thread.tid().value()); + } else { + builder.appendff("[Kernel]: "); + } +# endif + + vformat(builder, fmtstr, params); + builder.append('\n'); + + const auto string = builder.string_view(); + kernelcriticalputstr(string.characters_without_null_termination(), string.length()); +} + #endif template struct Formatter; diff --git a/AK/Format.h b/AK/Format.h index 5e05a55ac1..f1ef2e8fb8 100644 --- a/AK/Format.h +++ b/AK/Format.h @@ -436,6 +436,16 @@ void dmesgln(CheckedFormatString&& fmt, const Parameters&... para { vdmesgln(fmt.view(), VariadicFormatParams { parameters... }); } + +void v_critical_dmesgln(StringView fmtstr, TypeErasedFormatParams); + +// be very careful to not cause any allocations here, since we could be in +// a very unstable situation +template +void critical_dmesgln(CheckedFormatString&& fmt, const Parameters&... parameters) +{ + v_critical_dmesgln(fmt.view(), VariadicFormatParams { parameters... }); +} #endif template @@ -492,6 +502,7 @@ struct Formatter : Formatter { } // namespace AK #ifdef KERNEL +using AK::critical_dmesgln; using AK::dmesgln; #else using AK::out; diff --git a/Kernel/Arch/i386/CPU.cpp b/Kernel/Arch/i386/CPU.cpp index 661eb8653c..be267ea97b 100644 --- a/Kernel/Arch/i386/CPU.cpp +++ b/Kernel/Arch/i386/CPU.cpp @@ -2455,8 +2455,8 @@ void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, con void __assertion_failed(const char* msg, const char* file, unsigned line, const char* func) { asm volatile("cli"); - dmesgln("ASSERTION FAILED: {}", msg); - dmesgln("{}:{} in {}", file, line, func); + critical_dmesgln("ASSERTION FAILED: {}", msg); + critical_dmesgln("{}:{} in {}", file, line, func); abort(); } diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index c1ef3e98a9..b2068ade5c 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -50,6 +50,9 @@ set(KERNEL_SOURCES Devices/HID/PS2KeyboardDevice.cpp Devices/HID/PS2MouseDevice.cpp Devices/HID/VMWareMouseDevice.cpp + Graphics/Console/FramebufferConsole.cpp + Graphics/Console/TextModeConsole.cpp + Graphics/Console/VGAConsole.cpp Graphics/BochsFramebufferDevice.cpp Graphics/BochsGraphicsAdapter.cpp Graphics/FramebufferDevice.cpp @@ -205,6 +208,7 @@ set(KERNEL_SOURCES Syscalls/waitid.cpp Syscalls/inode_watcher.cpp Syscalls/write.cpp + TTY/ConsoleManagement.cpp TTY/MasterPTY.cpp TTY/PTYMultiplexer.cpp TTY/SlavePTY.cpp diff --git a/Kernel/CommandLine.cpp b/Kernel/CommandLine.cpp index 2cfb9b406e..6740fad3c0 100644 --- a/Kernel/CommandLine.cpp +++ b/Kernel/CommandLine.cpp @@ -171,8 +171,8 @@ UNMAP_AFTER_INIT AHCIResetMode CommandLine::ahci_reset_mode() const UNMAP_AFTER_INIT BootMode CommandLine::boot_mode() const { const auto boot_mode = lookup("boot_mode").value_or("graphical"); - if (boot_mode == "text") { - return BootMode::Text; + if (boot_mode == "no-fbdev") { + return BootMode::NoFramebufferDevices; } else if (boot_mode == "self-test") { return BootMode::SelfTest; } else if (boot_mode == "graphical") { @@ -181,10 +181,10 @@ UNMAP_AFTER_INIT BootMode CommandLine::boot_mode() const PANIC("Unknown BootMode: {}", boot_mode); } -UNMAP_AFTER_INIT bool CommandLine::is_text_mode() const +UNMAP_AFTER_INIT bool CommandLine::is_no_framebuffer_devices_mode() const { const auto mode = boot_mode(); - return mode == BootMode::Text || mode == BootMode::SelfTest; + return mode == BootMode::NoFramebufferDevices || mode == BootMode::SelfTest; } String CommandLine::userspace_init() const diff --git a/Kernel/CommandLine.h b/Kernel/CommandLine.h index 50edf15ce6..850ab848ab 100644 --- a/Kernel/CommandLine.h +++ b/Kernel/CommandLine.h @@ -14,7 +14,7 @@ namespace Kernel { enum class BootMode { - Text, + NoFramebufferDevices, SelfTest, Graphical }; @@ -59,7 +59,7 @@ public: [[nodiscard]] bool is_vmmouse_enabled() const; [[nodiscard]] PCIAccessLevel pci_access_level() const; [[nodiscard]] bool is_legacy_time_enabled() const; - [[nodiscard]] bool is_text_mode() const; + [[nodiscard]] bool is_no_framebuffer_devices_mode() const; [[nodiscard]] bool is_force_pio() const; [[nodiscard]] AcpiFeatureLevel acpi_feature_level() const; [[nodiscard]] BootMode boot_mode() const; diff --git a/Kernel/Debug.h.in b/Kernel/Debug.h.in index 134bcf1b4e..a51ab138d2 100644 --- a/Kernel/Debug.h.in +++ b/Kernel/Debug.h.in @@ -306,6 +306,10 @@ #cmakedefine01 VIRTIO_DEBUG #endif +#ifndef VIRTUAL_CONSOLE_DEBUG +#cmakedefine01 VIRTUAL_CONSOLE_DEBUG +#endif + #ifndef VRA_DEBUG #cmakedefine01 VRA_DEBUG #endif diff --git a/Kernel/Devices/HID/PS2KeyboardDevice.cpp b/Kernel/Devices/HID/PS2KeyboardDevice.cpp index 8ff6d5b3b0..a20c9e6044 100644 --- a/Kernel/Devices/HID/PS2KeyboardDevice.cpp +++ b/Kernel/Devices/HID/PS2KeyboardDevice.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include namespace Kernel { @@ -69,7 +69,7 @@ void PS2KeyboardDevice::irq_handle_byte_read(u8 byte) if (m_modifiers & Mod_Alt) { switch (ch) { case 0x02 ... 0x07: // 1 to 6 - VirtualConsole::switch_to(ch - 0x02); + ConsoleManagement::the().switch_to(ch - 0x02); break; default: key_state_changed(ch, pressed); diff --git a/Kernel/Graphics/BochsGraphicsAdapter.cpp b/Kernel/Graphics/BochsGraphicsAdapter.cpp index d6f42b1662..0fb47c36fe 100644 --- a/Kernel/Graphics/BochsGraphicsAdapter.cpp +++ b/Kernel/Graphics/BochsGraphicsAdapter.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -52,19 +54,24 @@ UNMAP_AFTER_INIT BochsGraphicsAdapter::BochsGraphicsAdapter(PCI::Address pci_add , m_mmio_registers(PCI::get_BAR2(pci_address) & 0xfffffff0) { set_safe_resolution(); + // We assume safe resolutio is 1024x768x32 + m_framebuffer_console = Graphics::FramebufferConsole::initialize(PhysicalAddress(PCI::get_BAR0(pci_address) & 0xfffffff0), 1024, 768, 1024 * sizeof(u32)); + // FIXME: This is a very wrong way to do this... + GraphicsManagement::the().m_console = m_framebuffer_console; } UNMAP_AFTER_INIT void BochsGraphicsAdapter::initialize_framebuffer_devices() { // FIXME: Find a better way to determine default resolution... - m_framebuffer = BochsFramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024 * 4, 1024, 768); + m_framebuffer_device = BochsFramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024 * 4, 1024, 768); + m_framebuffer_device->initialize(); } GraphicsDevice::Type BochsGraphicsAdapter::type() const { if (PCI::get_class(pci_address()) == 0x3 && PCI::get_subclass(pci_address()) == 0x0) return Type::VGACompatible; - return Type::Bochs; + return Type::Bochs; } void BochsGraphicsAdapter::set_safe_resolution() @@ -119,8 +126,33 @@ bool BochsGraphicsAdapter::validate_setup_resolution(size_t width, size_t height void BochsGraphicsAdapter::set_y_offset(size_t y_offset) { + if (m_console_enabled) + return; auto registers = map_typed_writable(m_mmio_registers); registers->bochs_regs.y_offset = y_offset; } +void BochsGraphicsAdapter::enable_consoles() +{ + ScopedSpinLock lock(m_console_mode_switch_lock); + VERIFY(m_framebuffer_console); + m_console_enabled = true; + auto registers = map_typed_writable(m_mmio_registers); + registers->bochs_regs.y_offset = 0; + if (m_framebuffer_device) + m_framebuffer_device->dectivate_writes(); + m_framebuffer_console->enable(); +} +void BochsGraphicsAdapter::disable_consoles() +{ + ScopedSpinLock lock(m_console_mode_switch_lock); + VERIFY(m_framebuffer_console); + VERIFY(m_framebuffer_device); + m_console_enabled = false; + auto registers = map_typed_writable(m_mmio_registers); + registers->bochs_regs.y_offset = 0; + m_framebuffer_console->disable(); + m_framebuffer_device->activate_writes(); +} + } diff --git a/Kernel/Graphics/BochsGraphicsAdapter.h b/Kernel/Graphics/BochsGraphicsAdapter.h index e2c2b1cdd5..af647e0a87 100644 --- a/Kernel/Graphics/BochsGraphicsAdapter.h +++ b/Kernel/Graphics/BochsGraphicsAdapter.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -25,12 +26,16 @@ class BochsGraphicsAdapter final : public GraphicsDevice public: static NonnullRefPtr initialize(PCI::Address); virtual ~BochsGraphicsAdapter() = default; + virtual bool framebuffer_devices_initialized() const override { return !m_framebuffer_device.is_null(); } private: // ^GraphicsDevice virtual void initialize_framebuffer_devices() override; virtual Type type() const override; + virtual void enable_consoles() override; + virtual void disable_consoles() override; + explicit BochsGraphicsAdapter(PCI::Address); void set_safe_resolution(); @@ -43,7 +48,10 @@ private: void set_y_offset(size_t); PhysicalAddress m_mmio_registers; - RefPtr m_framebuffer; + RefPtr m_framebuffer_device; + RefPtr m_framebuffer_console; + SpinLock m_console_mode_switch_lock; + bool m_console_enabled { false }; }; } diff --git a/Kernel/Graphics/Console/Console.h b/Kernel/Graphics/Console/Console.h new file mode 100644 index 0000000000..c391dc32fb --- /dev/null +++ b/Kernel/Graphics/Console/Console.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::Graphics { + +class Console : public RefCounted { +public: + // Stanadard VGA text mode colors + enum Color : u8 { + Black = 0, + Blue, + Green, + Cyan, + Red, + Magenta, + Brown, + LightGray, + DarkGray, + BrightBlue, + BrightGreen, + BrightCyan, + BrightRed, + BrightMagenta, + Yellow, + White, + }; + +public: + size_t width() const { return m_width; } + size_t height() const { return m_height; } + size_t pitch() const { return bytes_per_base_glyph() * width(); } + virtual size_t max_column() const { return m_width; } + virtual size_t max_row() const { return m_height; } + virtual size_t bytes_per_base_glyph() const = 0; + virtual size_t chars_per_line() const = 0; + + virtual void enable() = 0; + virtual void disable() = 0; + + virtual bool is_hardware_paged_capable() const = 0; + virtual bool has_hardware_cursor() const = 0; + + virtual void set_cursor(size_t x, size_t y) = 0; + virtual void hide_cursor() = 0; + virtual void show_cursor() = 0; + + virtual void clear(size_t x, size_t y, size_t length) const = 0; + virtual void write(size_t x, size_t y, char ch, Color background, Color foreground) const = 0; + virtual void write(size_t x, size_t y, String, Color background, Color foreground) const = 0; + virtual void write(size_t x, size_t y, char ch) const = 0; + virtual void write(size_t x, size_t y, String) const = 0; + virtual void write(char ch) const = 0; + + virtual ~Console() { } + +protected: + Console(size_t width, size_t height) + : m_width(width) + , m_height(height) + { + m_enabled.store(true); + } + + Atomic m_enabled; + Color m_default_foreground_color { Color::White }; + Color m_default_background_color { Color::Black }; + const size_t m_width; + const size_t m_height; + mutable size_t m_x { 0 }; + mutable size_t m_y { 0 }; +}; +} diff --git a/Kernel/Graphics/Console/FramebufferConsole.cpp b/Kernel/Graphics/Console/FramebufferConsole.cpp new file mode 100644 index 0000000000..8ae2e3122e --- /dev/null +++ b/Kernel/Graphics/Console/FramebufferConsole.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Kernel::Graphics { + +constexpr unsigned char const font8x8_basic[128][8] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0000 (nul) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0001 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0002 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0003 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0004 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0005 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0006 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0007 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0008 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0009 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0010 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0011 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0012 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0013 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0014 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0015 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0016 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0017 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0018 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0019 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0020 (space) + { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00 }, // U+0021 (!) + { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0022 (") + { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00 }, // U+0023 (#) + { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00 }, // U+0024 ($) + { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00 }, // U+0025 (%) + { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00 }, // U+0026 (&) + { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0027 (') + { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00 }, // U+0028 (() + { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00 }, // U+0029 ()) + { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00 }, // U+002A (*) + { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00 }, // U+002B (+) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06 }, // U+002C (,) + { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00 }, // U+002D (-) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, // U+002F (/) + { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, // U+0030 (0) + { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 }, // U+0031 (1) + { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, // U+0032 (2) + { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 }, // U+0033 (3) + { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, // U+0034 (4) + { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 }, // U+0035 (5) + { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, // U+0036 (6) + { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 }, // U+0037 (7) + { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, // U+0038 (8) + { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 }, // U+0039 (9) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, // U+003A (:) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06 }, // U+003B (;) + { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00 }, // U+003C (<) + { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00 }, // U+003D (=) + { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00 }, // U+003E (>) + { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00 }, // U+003F (?) + { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00 }, // U+0040 (@) + { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00 }, // U+0041 (A) + { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00 }, // U+0042 (B) + { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00 }, // U+0043 (C) + { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00 }, // U+0044 (D) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00 }, // U+0045 (E) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00 }, // U+0046 (F) + { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00 }, // U+0047 (G) + { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00 }, // U+0048 (H) + { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, // U+0049 (I) + { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00 }, // U+004A (J) + { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00 }, // U+004B (K) + { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00 }, // U+004C (L) + { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00 }, // U+004D (M) + { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00 }, // U+004E (N) + { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00 }, // U+004F (O) + { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00 }, // U+0050 (P) + { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00 }, // U+0051 (Q) + { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00 }, // U+0052 (R) + { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00 }, // U+0053 (S) + { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, // U+0054 (T) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00 }, // U+0055 (U) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00 }, // U+0056 (V) + { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00 }, // U+0057 (W) + { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00 }, // U+0058 (X) + { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00 }, // U+0059 (Y) + { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00 }, // U+005A (Z) + { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00 }, // U+005B ([) + { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00 }, // U+005C (\) + { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00 }, // U+005D (]) + { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00 }, // U+005E (^) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, // U+005F (_) + { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0060 (`) + { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00 }, // U+0061 (a) + { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00 }, // U+0062 (b) + { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00 }, // U+0063 (c) + { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00 }, // U+0064 (d) + { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00 }, // U+0065 (e) + { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00 }, // U+0066 (f) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F }, // U+0067 (g) + { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00 }, // U+0068 (h) + { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, // U+0069 (i) + { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E }, // U+006A (j) + { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00 }, // U+006B (k) + { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, // U+006C (l) + { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00 }, // U+006D (m) + { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00 }, // U+006E (n) + { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00 }, // U+006F (o) + { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F }, // U+0070 (p) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78 }, // U+0071 (q) + { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00 }, // U+0072 (r) + { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00 }, // U+0073 (s) + { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00 }, // U+0074 (t) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00 }, // U+0075 (u) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00 }, // U+0076 (v) + { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00 }, // U+0077 (w) + { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00 }, // U+0078 (x) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F }, // U+0079 (y) + { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00 }, // U+007A (z) + { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00 }, // U+007B ({) + { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00 }, // U+007C (|) + { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00 }, // U+007D (}) + { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // U+007F +}; + +// FIXME: This assumes 32 bit BGR (Blue-Green-Red) palette +enum BGRColor : u32 { + Black = 0, + Blue = 0x0000FF, + Green = 0x00FF00, + Cyan = 0x0000FFFF, + Red = 0xFF0000, + Magenta = 0x00FF00FF, + Brown = 0x00964B00, + LightGray = 0x00D3D3D3, + DarkGray = 0x00A9A9A9, + BrightBlue = 0x0ADD8E6, + BrightGreen = 0x0090EE90, + BrightCyan = 0x00E0FFFF, + BrightRed = 0x00D70A53, + BrightMagenta = 0x00F984E5, + Yellow = 0x00FFE135, + White = 0x00FFFFFF, +}; + +static inline BGRColor convert_standard_color_to_bgr_color(Console::Color color) +{ + switch (color) { + case Console::Color::Black: + return BGRColor::Black; + case Console::Color::Red: + return BGRColor::Red; + case Console::Color::Brown: + return BGRColor::Brown; + case Console::Color::Blue: + return BGRColor::Blue; + case Console::Color::Magenta: + return BGRColor::Magenta; + case Console::Color::Green: + return BGRColor::Green; + case Console::Color::Cyan: + return BGRColor::Cyan; + case Console::Color::LightGray: + return BGRColor::LightGray; + case Console::Color::DarkGray: + return BGRColor::DarkGray; + case Console::Color::BrightRed: + return BGRColor::BrightRed; + case Console::Color::BrightGreen: + return BGRColor::BrightGreen; + case Console::Color::Yellow: + return BGRColor::Yellow; + case Console::Color::BrightBlue: + return BGRColor::BrightBlue; + case Console::Color::BrightMagenta: + return BGRColor::BrightMagenta; + case Console::Color::BrightCyan: + return BGRColor::BrightCyan; + case Console::Color::White: + return BGRColor::White; + default: + VERIFY_NOT_REACHED(); + } +} + +NonnullRefPtr FramebufferConsole::initialize(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch) +{ + return adopt_ref(*new FramebufferConsole(framebuffer_address, width, height, pitch)); +} + +FramebufferConsole::FramebufferConsole(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch) + : Console(width, height) + , m_framebuffer_address(framebuffer_address) + , m_pitch(pitch) +{ + dbgln("Framebuffer Console: taking {} bytes", page_round_up(pitch * height)); + m_framebuffer_region = MM.allocate_kernel_region(m_framebuffer_address, page_round_up(pitch * height), "Framebuffer Console", Region::Access::Read | Region::Access::Write, Region::Cacheable::Yes); + VERIFY(m_framebuffer_region); + + // Just to start cleanly, we clean the entire framebuffer + memset(m_framebuffer_region->vaddr().as_ptr(), 0, pitch * height); +} + +size_t FramebufferConsole::bytes_per_base_glyph() const +{ + // FIXME: We assume we have 32 bit bpp framebuffer. + return 8 * 32; +} +size_t FramebufferConsole::chars_per_line() const +{ + return width() / bytes_per_base_glyph(); +} + +void FramebufferConsole::set_cursor(size_t, size_t) +{ +} +void FramebufferConsole::hide_cursor() +{ +} +void FramebufferConsole::show_cursor() +{ +} + +void FramebufferConsole::clear(size_t x, size_t y, size_t length) const +{ + ScopedSpinLock lock(m_lock); + if (x == 0 && length == max_column()) { + // if we need to clear the entire row, just clean it with quick memset :) + auto* offset_in_framebuffer = (u32*)m_framebuffer_region->vaddr().offset(x * sizeof(u32) * 8).offset(y * 8 * sizeof(u32) * width()).as_ptr(); + for (size_t current_x = 0; current_x < 8; current_x++) { + memset(offset_in_framebuffer, 0, width() * sizeof(u32)); + offset_in_framebuffer = (u32*)((u8*)offset_in_framebuffer + width() * 4); + } + return; + } + for (size_t index = 0; index < length; index++) { + if (x >= max_column()) { + x = 0; + y++; + if (y >= max_row()) + y = 0; + } + clear_glyph(x, y); + } +} + +void FramebufferConsole::clear_glyph(size_t x, size_t y) const +{ + VERIFY(m_lock.is_locked()); + auto* offset_in_framebuffer = (u32*)m_framebuffer_region->vaddr().offset(x * sizeof(u32) * 8).offset(y * 8 * sizeof(u32) * width()).as_ptr(); + for (size_t current_x = 0; current_x < 8; current_x++) { + memset(offset_in_framebuffer, 0, 8 * sizeof(u32)); + offset_in_framebuffer = (u32*)((u8*)offset_in_framebuffer + width() * sizeof(u32)); + } +} + +void FramebufferConsole::enable() +{ + ScopedSpinLock lock(m_lock); + memset(m_framebuffer_region->vaddr().as_ptr(), 0, height() * width() * sizeof(u32)); + m_enabled.store(true); +} +void FramebufferConsole::disable() +{ + ScopedSpinLock lock(m_lock); + m_enabled.store(false); +} + +void FramebufferConsole::write(size_t x, size_t y, char ch, Color background, Color foreground) const +{ + ScopedSpinLock lock(m_lock); + if (!m_enabled.load()) + return; + if (ch == '\r' || ch == '\n') { + m_x = 0; + m_y += 1; + if (m_y >= max_row()) + m_y = 0; + return; + } + if ((int)ch < 0x20 || (int)ch == 0x7f) { + // FIXME: There's no point in printing empty glyphs... + // Maybe try to add these special glyphs and print them. + return; + } + clear_glyph(x, y); + auto* offset_in_framebuffer = (u32*)m_framebuffer_region->vaddr().offset(x * sizeof(u32) * 8).offset(y * 8 * sizeof(u32) * width()).as_ptr(); + int current_bitpixels = 0; + int current_bitpixel = 0; + auto bitmap = font8x8_basic[(int)ch]; + bool set; + BGRColor foreground_color = convert_standard_color_to_bgr_color(foreground); + BGRColor background_color = convert_standard_color_to_bgr_color(background); + for (current_bitpixels = 0; current_bitpixels < 8; current_bitpixels++) { + for (current_bitpixel = 0; current_bitpixel < 8; current_bitpixel++) { + set = bitmap[current_bitpixels] & (1 << current_bitpixel); + if (set) { + offset_in_framebuffer[current_bitpixel] = foreground_color; + } else { + offset_in_framebuffer[current_bitpixel] = background_color; + } + } + offset_in_framebuffer = (u32*)((u8*)offset_in_framebuffer + width() * 4); + } + m_x = x + 1; + if (m_x >= max_column()) { + m_x = 0; + m_y = y + 1; + if (m_y >= max_row()) + m_y = 0; + } +} + +void FramebufferConsole::write(size_t, size_t, String, Color, Color) const +{ + TODO(); +} +void FramebufferConsole::write(size_t x, size_t y, char ch) const +{ + write(x, y, ch, m_default_background_color, m_default_foreground_color); +} +void FramebufferConsole::write(size_t, size_t, String) const +{ + TODO(); +} + +void FramebufferConsole::write(char ch) const +{ + write(m_x, m_y, ch, m_default_background_color, m_default_foreground_color); +} + +} diff --git a/Kernel/Graphics/Console/FramebufferConsole.h b/Kernel/Graphics/Console/FramebufferConsole.h new file mode 100644 index 0000000000..336d117367 --- /dev/null +++ b/Kernel/Graphics/Console/FramebufferConsole.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::Graphics { +class FramebufferConsole final : public Console { +public: + static NonnullRefPtr initialize(PhysicalAddress, size_t width, size_t height, size_t bpp); + + virtual size_t bytes_per_base_glyph() const override; + virtual size_t chars_per_line() const override; + + virtual size_t max_column() const { return m_width / 8; } + virtual size_t max_row() const { return m_height / 8; } + + virtual bool is_hardware_paged_capable() const override { return false; } + virtual bool has_hardware_cursor() const override { return false; } + + virtual void set_cursor(size_t x, size_t y) override; + virtual void hide_cursor() override; + virtual void show_cursor() override; + + virtual void clear(size_t x, size_t y, size_t length) const override; + virtual void write(size_t x, size_t y, char ch, Color background, Color foreground) const override; + virtual void write(size_t x, size_t y, String cstring, Color background, Color foreground) const override; + virtual void write(size_t x, size_t y, char ch) const override; + virtual void write(size_t x, size_t y, String) const override; + virtual void write(char ch) const override; + + virtual void enable() override; + virtual void disable() override; + +protected: + void clear_glyph(size_t x, size_t y) const; + FramebufferConsole(PhysicalAddress, size_t width, size_t height, size_t bpp); + OwnPtr m_framebuffer_region; + PhysicalAddress m_framebuffer_address; + size_t m_pitch; + mutable SpinLock m_lock; +}; +} diff --git a/Kernel/Graphics/Console/TextModeConsole.cpp b/Kernel/Graphics/Console/TextModeConsole.cpp new file mode 100644 index 0000000000..847f899ffe --- /dev/null +++ b/Kernel/Graphics/Console/TextModeConsole.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel::Graphics { + +UNMAP_AFTER_INIT NonnullRefPtr TextModeConsole::initialize(const VGACompatibleAdapter& adapter) +{ + return adopt_ref(*new TextModeConsole(adapter)); +} + +UNMAP_AFTER_INIT TextModeConsole::TextModeConsole(const VGACompatibleAdapter& adapter) + : VGAConsole(adapter, VGAConsole::Mode::TextMode, 80, 25) + , m_current_vga_window(m_vga_region->vaddr().offset(0x18000).as_ptr()) +{ + for (size_t index = 0; index < height(); index++) { + clear_vga_row(index); + } + dbgln("Text mode console initialized!"); +} + +enum VGAColor : u8 { + Black = 0, + Blue, + Green, + Cyan, + Red, + Magenta, + Brown, + LightGray, + DarkGray, + BrightBlue, + BrightGreen, + BrightCyan, + BrightRed, + BrightMagenta, + Yellow, + White, +}; + +static inline VGAColor convert_standard_color_to_vga_color(Console::Color color) +{ + switch (color) { + case Console::Color::Black: + return VGAColor::Black; + case Console::Color::Red: + return VGAColor::Red; + case Console::Color::Brown: + return VGAColor::Brown; + case Console::Color::Blue: + return VGAColor::Blue; + case Console::Color::Magenta: + return VGAColor::Magenta; + case Console::Color::Green: + return VGAColor::Green; + case Console::Color::Cyan: + return VGAColor::Cyan; + case Console::Color::LightGray: + return VGAColor::LightGray; + case Console::Color::DarkGray: + return VGAColor::DarkGray; + case Console::Color::BrightRed: + return VGAColor::BrightRed; + case Console::Color::BrightGreen: + return VGAColor::BrightGreen; + case Console::Color::Yellow: + return VGAColor::Yellow; + case Console::Color::BrightBlue: + return VGAColor::BrightBlue; + case Console::Color::BrightMagenta: + return VGAColor::BrightMagenta; + case Console::Color::BrightCyan: + return VGAColor::BrightCyan; + case Console::Color::White: + return VGAColor::White; + default: + VERIFY_NOT_REACHED(); + } +} + +void TextModeConsole::set_cursor(size_t x, size_t y) +{ + ScopedSpinLock main_lock(GraphicsManagement::the().main_vga_lock()); + ScopedSpinLock lock(m_vga_lock); + m_cursor_x = x; + m_cursor_y = y; + u16 value = m_current_vga_start_address + (y * width() + x); + IO::out8(0x3d4, 0x0e); + IO::out8(0x3d5, MSB(value)); + IO::out8(0x3d4, 0x0f); + IO::out8(0x3d5, LSB(value)); +} +void TextModeConsole::hide_cursor() +{ + ScopedSpinLock main_lock(GraphicsManagement::the().main_vga_lock()); + ScopedSpinLock lock(m_vga_lock); + IO::out8(0x3D4, 0xA); + IO::out8(0x3D5, 0x20); +} +void TextModeConsole::show_cursor() +{ + ScopedSpinLock main_lock(GraphicsManagement::the().main_vga_lock()); + ScopedSpinLock lock(m_vga_lock); + IO::out8(0x3D4, 0xA); + IO::out8(0x3D5, 0x20); +} + +void TextModeConsole::clear(size_t x, size_t y, size_t length) const +{ + ScopedSpinLock lock(m_vga_lock); + auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2)); + for (size_t index = 0; index < length; index++) { + buf[index] = 0x0720; + } +} +void TextModeConsole::write(size_t x, size_t y, char ch) const +{ + ScopedSpinLock lock(m_vga_lock); + auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2)); + *buf = (m_default_foreground_color << 8) | (m_default_background_color << 12) | ch; + m_x = x + 1; + if (m_x >= max_column()) { + m_x = 0; + m_y = y + 1; + if (m_y >= max_row()) + m_y = 0; + } +} +void TextModeConsole::write(size_t x, size_t y, String cstring) const +{ + ScopedSpinLock lock(m_vga_lock); + auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2)); + u16 color_mask = (m_default_foreground_color << 8) | (m_default_background_color << 12); + for (size_t index = 0; index < cstring.length(); index++) { + buf[index] = color_mask | cstring[index]; + } + m_x = x + cstring.length(); + if (m_x >= max_column()) { + m_x = 0; + m_y = y + 1; + if (m_y >= max_row()) + m_y = 0; + } +} +void TextModeConsole::write(size_t x, size_t y, char ch, Color background, Color foreground) const +{ + ScopedSpinLock lock(m_vga_lock); + auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2)); + *buf = foreground << 8 | background << 12 | ch; + m_x = x + 1; + if (m_x >= max_column()) { + m_x = 0; + m_y = y + 1; + if (m_y >= max_row()) + m_y = 0; + } +} +void TextModeConsole::write(size_t x, size_t y, String cstring, Color background, Color foreground) const +{ + ScopedSpinLock lock(m_vga_lock); + auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2)); + u16 color_mask = foreground << 8 | background << 12; + for (size_t index = 0; index < cstring.length(); index++) { + buf[index] = color_mask | cstring[index]; + } + m_x = x + cstring.length(); + if (m_x >= max_column()) { + m_x = 0; + m_y = y + 1; + if (m_y >= max_row()) + m_y = 0; + } +} + +void TextModeConsole::clear_vga_row(u16 row) +{ + clear(row * width(), width(), width()); +} + +void TextModeConsole::set_vga_start_row(u16 row) +{ + ScopedSpinLock lock(m_vga_lock); + m_vga_start_row = row; + m_current_vga_start_address = row * width(); + m_current_vga_window = m_current_vga_window + row * width() * bytes_per_base_glyph(); + IO::out8(0x3d4, 0x0c); + IO::out8(0x3d5, MSB(m_current_vga_start_address)); + IO::out8(0x3d4, 0x0d); + IO::out8(0x3d5, LSB(m_current_vga_start_address)); +} + +void TextModeConsole::write(char ch) const +{ + write(m_x, m_y, ch); +} + +} diff --git a/Kernel/Graphics/Console/TextModeConsole.h b/Kernel/Graphics/Console/TextModeConsole.h new file mode 100644 index 0000000000..c465b63f39 --- /dev/null +++ b/Kernel/Graphics/Console/TextModeConsole.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::Graphics { +class TextModeConsole final : public VGAConsole { +public: + static NonnullRefPtr initialize(const VGACompatibleAdapter& adapter); + virtual size_t chars_per_line() const override { return width(); }; + + virtual bool has_hardware_cursor() const override { return true; } + virtual bool is_hardware_paged_capable() const override { return true; } + + virtual size_t bytes_per_base_glyph() const override { return 2; } + virtual void set_cursor(size_t x, size_t y) override; + virtual void hide_cursor() override; + virtual void show_cursor() override; + virtual void clear(size_t x, size_t y, size_t length) const override; + virtual void write(size_t x, size_t y, char ch) const override; + virtual void write(size_t x, size_t y, String cstring) const override; + virtual void write(size_t x, size_t y, char ch, Color background, Color foreground) const override; + virtual void write(size_t x, size_t y, String, Color background, Color foreground) const override; + virtual void write(char ch) const override; + + virtual void enable() override { } + virtual void disable() override { VERIFY_NOT_REACHED(); } + +private: + void clear_vga_row(u16 row); + void set_vga_start_row(u16 row); + + explicit TextModeConsole(const VGACompatibleAdapter&); + + mutable SpinLock m_vga_lock; + u16 m_vga_start_row { 0 }; + u16 m_current_vga_start_address { 0 }; + u8* m_current_vga_window { nullptr }; + u16 m_cursor_x { 0 }; + u16 m_cursor_y { 0 }; +}; +} diff --git a/Kernel/Graphics/Console/VGAConsole.cpp b/Kernel/Graphics/Console/VGAConsole.cpp new file mode 100644 index 0000000000..70472b55e7 --- /dev/null +++ b/Kernel/Graphics/Console/VGAConsole.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Kernel::Graphics { + +UNMAP_AFTER_INIT VGAConsole::VGAConsole(const VGACompatibleAdapter& adapter, Mode mode, size_t width, size_t height) + : Console(width, height) + , m_vga_region(MM.allocate_kernel_region(PhysicalAddress(0xa0000), page_round_up(0xc0000 - 0xa0000), "VGA Display", Region::Access::Read | Region::Access::Write).release_nonnull()) + , m_adapter(adapter) + , m_mode(mode) +{ +} + +} diff --git a/Kernel/Graphics/Console/VGAConsole.h b/Kernel/Graphics/Console/VGAConsole.h new file mode 100644 index 0000000000..e82f1d5e59 --- /dev/null +++ b/Kernel/Graphics/Console/VGAConsole.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::Graphics { +class VGAConsole : public Console { +public: + // Note: these are the modes we will support and only these + enum class Mode { + TextMode = 1, // Text Mode + Colored256, // 320x200 256 color mode + Colored16, // 640x480 16 color mode + }; + +public: + static NonnullRefPtr initialize(const VGACompatibleAdapter&, Mode, size_t width, size_t height); + + virtual bool is_hardware_paged_capable() const override { return false; } + virtual bool has_hardware_cursor() const override { return false; } + + virtual ~VGAConsole() = default; + +protected: + VGAConsole(const VGACompatibleAdapter&, Mode, size_t width, size_t height); + + NonnullOwnPtr m_vga_region; + NonnullRefPtr m_adapter; + const Mode m_mode; +}; +} diff --git a/Kernel/Graphics/FramebufferDevice.cpp b/Kernel/Graphics/FramebufferDevice.cpp index c6d45774cf..fd1bd27277 100644 --- a/Kernel/Graphics/FramebufferDevice.cpp +++ b/Kernel/Graphics/FramebufferDevice.cpp @@ -16,6 +16,8 @@ #include #include +#include + namespace Kernel { KResultOr FramebufferDevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared) @@ -28,16 +30,51 @@ KResultOr FramebufferDevice::mmap(Process& process, FileDescription&, c if (range.size() != page_round_up(framebuffer_size_in_bytes())) return EOVERFLOW; - auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes()); + // FIXME: We rely on the fact that only the WindowServer will mmap the framebuffer + // and only once when starting to work with it. If other program wants to do so, we need to fix this. + VERIFY(!m_userspace_framebuffer_region); + VERIFY(!m_userspace_real_framebuffer_vmobject); + + auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, page_round_up(framebuffer_size_in_bytes())); if (!vmobject) return ENOMEM; - return process.space().allocate_region_with_vmobject( + m_userspace_real_framebuffer_vmobject = vmobject; + + auto result = process.space().allocate_region_with_vmobject( range, vmobject.release_nonnull(), 0, "Framebuffer", prot, shared); + if (!result.is_error()) { + m_userspace_framebuffer_region = result.value(); + } + return result; +} + +void FramebufferDevice::dectivate_writes() +{ + ScopedSpinLock lock(m_activation_lock); + if (!m_userspace_framebuffer_region) + return; + memcpy(m_swapped_framebuffer_region->vaddr().as_ptr(), m_real_framebuffer_region->vaddr().as_ptr(), page_round_up(framebuffer_size_in_bytes())); + auto vmobject = m_swapped_framebuffer_vmobject; + m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull()); + m_userspace_framebuffer_region->remap(); +} +void FramebufferDevice::activate_writes() +{ + ScopedSpinLock lock(m_activation_lock); + if (!m_userspace_framebuffer_region || !m_real_framebuffer_vmobject) + return; + // restore the image we had in the void area + // FIXME: if we happen to have multiple Framebuffers that are writing to that location + // we will experience glitches... + memcpy(m_real_framebuffer_region->vaddr().as_ptr(), m_swapped_framebuffer_region->vaddr().as_ptr(), page_round_up(framebuffer_size_in_bytes())); + auto vmobject = m_userspace_real_framebuffer_vmobject; + m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull()); + m_userspace_framebuffer_region->remap(); } String FramebufferDevice::device_name() const @@ -45,6 +82,18 @@ String FramebufferDevice::device_name() const return String::formatted("fb{}", minor()); } +UNMAP_AFTER_INIT void FramebufferDevice::initialize() +{ + m_real_framebuffer_vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, page_round_up(framebuffer_size_in_bytes())); + VERIFY(m_real_framebuffer_vmobject); + m_real_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, framebuffer_size_in_bytes(), "Framebuffer", Region::Access::Read | Region::Access::Write); + VERIFY(m_real_framebuffer_region); + m_swapped_framebuffer_vmobject = AnonymousVMObject::create_with_size(page_round_up(framebuffer_size_in_bytes()), AllocationStrategy::AllocateNow); + VERIFY(m_swapped_framebuffer_vmobject); + m_swapped_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, page_round_up(framebuffer_size_in_bytes()), "Framebuffer Swap (Blank)", Region::Access::Read | Region::Access::Write); + VERIFY(m_swapped_framebuffer_region); +} + UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size_t pitch, size_t width, size_t height) : BlockDevice(29, GraphicsManagement::the().current_minor_number()) , m_framebuffer_address(addr) @@ -52,6 +101,10 @@ UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size , m_framebuffer_width(width) , m_framebuffer_height(height) { + VERIFY(!m_framebuffer_address.is_null()); + VERIFY(m_framebuffer_pitch); + VERIFY(m_framebuffer_width); + VERIFY(m_framebuffer_height); dbgln("Framebuffer {}: address={}, pitch={}, width={}, height={}", minor(), addr, pitch, width, height); } diff --git a/Kernel/Graphics/FramebufferDevice.h b/Kernel/Graphics/FramebufferDevice.h index 104fc5739d..f52f20ff4c 100644 --- a/Kernel/Graphics/FramebufferDevice.h +++ b/Kernel/Graphics/FramebufferDevice.h @@ -6,11 +6,14 @@ #pragma once +#include #include #include #include #include #include +#include +#include namespace Kernel { @@ -24,9 +27,12 @@ public: virtual mode_t required_mode() const override { return 0660; } virtual String device_name() const override; + virtual void dectivate_writes(); + virtual void activate_writes(); virtual size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height; } virtual ~FramebufferDevice() {}; + void initialize(); protected: virtual bool set_resolution(size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch); @@ -44,6 +50,17 @@ protected: size_t m_framebuffer_pitch { 0 }; size_t m_framebuffer_width { 0 }; size_t m_framebuffer_height { 0 }; + +private: + SpinLock m_activation_lock; + + RefPtr m_real_framebuffer_vmobject; + RefPtr m_swapped_framebuffer_vmobject; + OwnPtr m_real_framebuffer_region; + OwnPtr m_swapped_framebuffer_region; + + RefPtr m_userspace_real_framebuffer_vmobject; + Region* m_userspace_framebuffer_region { nullptr }; }; } diff --git a/Kernel/Graphics/GraphicsDevice.h b/Kernel/Graphics/GraphicsDevice.h index f4d3ae7e29..2279338f4f 100644 --- a/Kernel/Graphics/GraphicsDevice.h +++ b/Kernel/Graphics/GraphicsDevice.h @@ -25,9 +25,15 @@ public: virtual ~GraphicsDevice() = default; virtual void initialize_framebuffer_devices() = 0; virtual Type type() const = 0; + virtual void enable_consoles() = 0; + virtual void disable_consoles() = 0; + bool consoles_enabled() const { return m_consoles_enabled; } + virtual bool framebuffer_devices_initialized() const = 0; protected: GraphicsDevice() = default; + + bool m_consoles_enabled { false }; }; } diff --git a/Kernel/Graphics/GraphicsManagement.cpp b/Kernel/Graphics/GraphicsManagement.cpp index e329d99700..0c5ad939bd 100644 --- a/Kernel/Graphics/GraphicsManagement.cpp +++ b/Kernel/Graphics/GraphicsManagement.cpp @@ -9,10 +9,14 @@ #include #include #include +#include +#include #include #include #include +#include #include +#include namespace Kernel { @@ -29,10 +33,24 @@ bool GraphicsManagement::is_initialized() } UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement() - : m_textmode_enabled(kernel_command_line().is_text_mode()) + : m_vga_font_region(MM.allocate_kernel_region(PAGE_SIZE, "VGA font", Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow).release_nonnull()) + , m_framebuffer_devices_allowed(!kernel_command_line().is_no_framebuffer_devices_mode()) { } +void GraphicsManagement::deactivate_graphical_mode() +{ + for (auto& graphics_device : m_graphics_devices) { + graphics_device.enable_consoles(); + } +} +void GraphicsManagement::activate_graphical_mode() +{ + for (auto& graphics_device : m_graphics_devices) { + graphics_device.disable_consoles(); + } +} + UNMAP_AFTER_INIT RefPtr GraphicsManagement::determine_graphics_device(PCI::Address address, PCI::ID id) const { if ((id.vendor_id == 0x1234 && id.device_id == 0x1111) || (id.vendor_id == 0x80ee && id.device_id == 0xbeef)) { @@ -44,31 +62,93 @@ UNMAP_AFTER_INIT RefPtr GraphicsManagement::determine_graphics_d if (!adapter.is_null()) return adapter; } - VERIFY(multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB || multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT); - return VGACompatibleAdapter::initialize_with_preset_resolution(address, - PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)), - multiboot_info_ptr->framebuffer_pitch, - multiboot_info_ptr->framebuffer_width, - multiboot_info_ptr->framebuffer_height); + if (multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) { + dmesgln("Graphics: Using a preset resolution from the bootloader"); + return VGACompatibleAdapter::initialize_with_preset_resolution(address, + PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)), + multiboot_info_ptr->framebuffer_pitch, + multiboot_info_ptr->framebuffer_width, + multiboot_info_ptr->framebuffer_height); + } + return VGACompatibleAdapter::initialize(address); } return {}; } UNMAP_AFTER_INIT bool GraphicsManagement::initialize() { - if (kernel_command_line().is_text_mode()) { - dbgln("Text mode enabled"); - return false; + + /* Explanation on the flow when not requesting to force not creating any + * framebuffer devices: + * If the user wants to use a Console instead of the graphical environment, + * they doesn't need to request text mode. + * Graphical mode might not be accessible on bare-metal hardware because + * the bootloader didn't set a framebuffer and we don't have a native driver + * to set a framebuffer for it. We don't have VBE modesetting capabilities + * in the kernel yet, so what will happen is one of the following situations: + * 1. The bootloader didn't specify settings of a pre-set framebuffer. The + * kernel has a native driver for a detected display adapter, therefore + * the kernel can still set a framebuffer. + * 2. The bootloader specified settings of a pre-set framebuffer, and the + * kernel has a native driver for a detected display adapter, therefore + * the kernel can still set a framebuffer and change the settings of it. + * In that situation, the kernel will simply ignore the Multiboot pre-set + * framebuffer. + * 2. The bootloader specified settings of a pre-set framebuffer, and the + * kernel does not have a native driver for a detected display adapter, + * therefore the kernel will use the pre-set framebuffer. Modesetting is not + * availabe in this situation. + * 3. The bootloader didn't specify settings of a pre-set framebuffer, and + * the kernel does not have a native driver for a detected display adapter, + * therefore the kernel will try to initialize a VGA text mode console. + * In that situation, the kernel will assume that VGA text mode was already + * initialized, but will still try to modeset it. No switching to graphical + * enviroment is allowed in this case. + * + * By default, the kernel assumes that no framebuffer was created until it + * was proven that there's an existing framebuffer or we can modeset the + * screen resolution to create a framebuffer. + * + * If the user requests to force no initialization of framebuffer devices + * the same flow above will happen, except that no framebuffer device will + * be created, so SystemServer will not try to initialize WindowServer. + */ + + if (kernel_command_line().is_no_framebuffer_devices_mode()) { + dbgln("Forcing no initialization of framebuffer devices"); } PCI::enumerate([&](const PCI::Address& address, PCI::ID id) { + // Note: Each graphics controller will try to set its native screen resolution + // upon creation. Later on, if we don't want to have framebuffer devices, a + // framebuffer console will take the control instead. auto adapter = determine_graphics_device(address, id); if (!adapter) return; - adapter->initialize_framebuffer_devices(); - m_graphics_devices.append(adapter.release_nonnull()); + + // If IO space is enabled, this VGA adapter is operating in VGA mode. + if (adapter->type() == GraphicsDevice::Type::VGACompatible && PCI::is_io_space_enabled(address)) { + VERIFY(m_vga_adapter.is_null()); + dbgln("Graphics adapter @ {} is operating in VGA mode", address); + m_vga_adapter = adapter; + } + auto display_adapter = adapter.release_nonnull(); + m_graphics_devices.append(display_adapter); + if (!m_framebuffer_devices_allowed) { + display_adapter->enable_consoles(); + return; + } + display_adapter->initialize_framebuffer_devices(); }); return true; } +bool GraphicsManagement::framebuffer_devices_exist() const +{ + for (auto& graphics_device : m_graphics_devices) { + if (graphics_device.framebuffer_devices_initialized()) + return true; + } + return false; +} } diff --git a/Kernel/Graphics/GraphicsManagement.h b/Kernel/Graphics/GraphicsManagement.h index bcf0cded39..da830259a2 100644 --- a/Kernel/Graphics/GraphicsManagement.h +++ b/Kernel/Graphics/GraphicsManagement.h @@ -6,16 +6,26 @@ #pragma once +#include #include #include #include +#include #include +#include #include +#include namespace Kernel { +class BochsGraphicsAdapter; +class IntelNativeGraphicsAdapter; +class VGACompatibleAdapter; class GraphicsManagement { - AK_MAKE_ETERNAL; + friend class BochsGraphicsAdapter; + friend class IntelNativeGraphicsAdapter; + friend class VGACompatibleAdapter; + AK_MAKE_ETERNAL public: static GraphicsManagement& the(); @@ -25,14 +35,28 @@ public: unsigned current_minor_number() { return m_current_minor_number++; }; GraphicsManagement(); - bool is_text_mode_enabled() const { return m_textmode_enabled; } + bool framebuffer_devices_allowed() const { return m_framebuffer_devices_allowed; } + bool framebuffer_devices_exist() const; + + SpinLock& main_vga_lock() { return m_main_vga_lock; } + RefPtr console() const { return m_console; } + + void deactivate_graphical_mode(); + void activate_graphical_mode(); private: RefPtr determine_graphics_device(PCI::Address address, PCI::ID id) const; NonnullRefPtrVector m_graphics_devices; + NonnullOwnPtr m_vga_font_region; + RefPtr m_console; + + // Note: there could be multiple VGA adapters, but only one can operate in VGA mode + RefPtr m_vga_adapter; unsigned m_current_minor_number { 0 }; - bool m_textmode_enabled; + const bool m_framebuffer_devices_allowed; + + SpinLock m_main_vga_lock; }; } diff --git a/Kernel/Graphics/IntelNativeGraphicsAdapter.cpp b/Kernel/Graphics/IntelNativeGraphicsAdapter.cpp index fa3b0b4792..2ce0266920 100644 --- a/Kernel/Graphics/IntelNativeGraphicsAdapter.cpp +++ b/Kernel/Graphics/IntelNativeGraphicsAdapter.cpp @@ -4,7 +4,9 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include +#include #include #include #include @@ -165,7 +167,7 @@ Optional IntelNativeGraphicsAdapter::cr } IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address) - : PCI::DeviceController(address) + : VGACompatibleAdapter(address) , m_registers(PCI::get_BAR0(address) & 0xfffffffc) , m_framebuffer_addr(PCI::get_BAR2(address) & 0xfffffffc) { @@ -182,10 +184,24 @@ IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address) set_gmbus_pin_pair(GMBusPinPair::DedicatedAnalog); } gmbus_read_edid(); - auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 0); - dmesgln("Intel Native Graphics Adapter @ {}, preferred resolution is {:d}x{:d}", address, modesetting.horizontal.active, modesetting.vertical.active); + auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 0); + dmesgln("Intel Native Graphics Adapter @ {}, preferred resolution is {:d}x{:d}", pci_address(), modesetting.horizontal.active, modesetting.vertical.active); set_crt_resolution(modesetting.horizontal.active, modesetting.vertical.active); + auto framebuffer_address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0); + VERIFY(!framebuffer_address.is_null()); + VERIFY(m_framebuffer_pitch != 0); + VERIFY(m_framebuffer_height != 0); + VERIFY(m_framebuffer_width != 0); + m_framebuffer_console = Graphics::FramebufferConsole::initialize(framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch); + // FIXME: This is a very wrong way to do this... + GraphicsManagement::the().m_console = m_framebuffer_console; +} + +void IntelNativeGraphicsAdapter::enable_vga_plane() +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); } static inline const char* convert_register_index_to_string(IntelGraphics::RegisterIndex index) @@ -269,6 +285,7 @@ bool IntelNativeGraphicsAdapter::pipe_a_enabled() const VERIFY(m_control_lock.is_locked()); return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30); } + bool IntelNativeGraphicsAdapter::pipe_b_enabled() const { VERIFY(m_control_lock.is_locked()); @@ -396,7 +413,7 @@ bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height) VERIFY_NOT_REACHED(); auto settings = pll_settings.value(); dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); - set_dpll_registers(pll_settings.value(), dac_multiplier); + enable_dpll_without_vga(pll_settings.value(), dac_multiplier); set_display_timings(modesetting); auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0); VERIFY(!address.is_null()); @@ -404,7 +421,7 @@ bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height) m_framebuffer_width = width; m_framebuffer_height = height; - m_framebuffer_stride = width * 4; + m_framebuffer_pitch = width * 4; return true; } @@ -538,7 +555,7 @@ void IntelNativeGraphicsAdapter::enable_primary_plane(PhysicalAddress fb_address write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, (read_from_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl) & (~(0b1111 << 26))) | (0b0110 << 26) | (1 << 31)); } -void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings, size_t dac_multiplier) +void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings) { VERIFY(m_control_lock.is_locked()); VERIFY(m_modeset_lock.is_locked()); @@ -546,6 +563,14 @@ void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings, write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA1, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16)); write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & ~0x80000000); +} + +void IntelNativeGraphicsAdapter::enable_dpll_without_vga(const PLLSettings& settings, size_t dac_multiplier) +{ + VERIFY(m_control_lock.is_locked()); + VERIFY(m_modeset_lock.is_locked()); + + set_dpll_registers(settings); IO::delay(200); @@ -597,9 +622,10 @@ void IntelNativeGraphicsAdapter::initialize_framebuffer_devices() { auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0); VERIFY(!address.is_null()); - VERIFY(m_framebuffer_stride != 0); + VERIFY(m_framebuffer_pitch != 0); VERIFY(m_framebuffer_height != 0); VERIFY(m_framebuffer_width != 0); - m_framebuffer = m_framebuffer = RawFramebufferDevice::create(*this, address, m_framebuffer_stride, m_framebuffer_width, m_framebuffer_height); + m_framebuffer_device = RawFramebufferDevice::create(*this, address, m_framebuffer_pitch, m_framebuffer_width, m_framebuffer_height); + m_framebuffer_device->initialize(); } } diff --git a/Kernel/Graphics/IntelNativeGraphicsAdapter.h b/Kernel/Graphics/IntelNativeGraphicsAdapter.h index a221929118..48cd0fac36 100644 --- a/Kernel/Graphics/IntelNativeGraphicsAdapter.h +++ b/Kernel/Graphics/IntelNativeGraphicsAdapter.h @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #include #include @@ -47,8 +47,7 @@ enum RegisterIndex { } class IntelNativeGraphicsAdapter final - : public GraphicsDevice - , public PCI::DeviceController { + : public VGACompatibleAdapter { AK_MAKE_ETERNAL public: struct PLLSettings { @@ -129,6 +128,7 @@ private: void enable_output(PhysicalAddress fb_address, size_t width); void disable_vga_emulation(); + void enable_vga_plane(); void disable_dac_output(); void enable_dac_output(); @@ -138,7 +138,9 @@ private: void disable_pipe_b(); void disable_dpll(); - void set_dpll_registers(const PLLSettings&, size_t dac_multiplier); + void set_dpll_registers(const PLLSettings&); + + void enable_dpll_without_vga(const PLLSettings&, size_t dac_multiplier); void set_display_timings(const Graphics::Modesetting&); void enable_pipe_a(); void set_framebuffer_parameters(size_t, size_t); @@ -166,15 +168,7 @@ private: Graphics::VideoInfoBlock m_crt_edid; const PhysicalAddress m_registers; const PhysicalAddress m_framebuffer_addr; - OwnPtr m_registers_region; - - size_t m_framebuffer_width { 0 }; - size_t m_framebuffer_height { 0 }; - size_t m_framebuffer_stride { 0 }; - -protected: - RefPtr m_framebuffer; }; } diff --git a/Kernel/Graphics/VGACompatibleAdapter.cpp b/Kernel/Graphics/VGACompatibleAdapter.cpp index 80b2e8b0d4..0fd8e647ae 100644 --- a/Kernel/Graphics/VGACompatibleAdapter.cpp +++ b/Kernel/Graphics/VGACompatibleAdapter.cpp @@ -4,7 +4,11 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include #include +#include namespace Kernel { @@ -13,19 +17,55 @@ UNMAP_AFTER_INIT NonnullRefPtr VGACompatibleAdapter::initi return adopt_ref(*new VGACompatibleAdapter(address, m_framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch)); } +UNMAP_AFTER_INIT NonnullRefPtr VGACompatibleAdapter::initialize(PCI::Address address) +{ + return adopt_ref(*new VGACompatibleAdapter(address)); +} + UNMAP_AFTER_INIT void VGACompatibleAdapter::initialize_framebuffer_devices() { + // We might not have any pre-set framebuffer, so if that's the case - don't try to initialize one. + if (m_framebuffer_address.is_null()) + return; + VERIFY(m_framebuffer_width); + VERIFY(m_framebuffer_width != 0); + VERIFY(m_framebuffer_height != 0); + VERIFY(m_framebuffer_pitch != 0); + m_framebuffer_device = RawFramebufferDevice::create(*this, m_framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch); + m_framebuffer_device->initialize(); } UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address) : PCI::DeviceController(address) { + m_framebuffer_console = Graphics::TextModeConsole::initialize(*this); + // FIXME: This is a very wrong way to do this... + GraphicsManagement::the().m_console = m_framebuffer_console; } UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address, PhysicalAddress framebuffer_address, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch) : PCI::DeviceController(address) + , m_framebuffer_address(framebuffer_address) + , m_framebuffer_width(framebuffer_width) + , m_framebuffer_height(framebuffer_height) + , m_framebuffer_pitch(framebuffer_pitch) { - m_framebuffer = RawFramebufferDevice::create(*this, framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch); + m_framebuffer_console = Graphics::FramebufferConsole::initialize(framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch); +} + +void VGACompatibleAdapter::enable_consoles() +{ + VERIFY(m_framebuffer_console); + if (m_framebuffer_device) + m_framebuffer_device->dectivate_writes(); + m_framebuffer_console->enable(); +} +void VGACompatibleAdapter::disable_consoles() +{ + VERIFY(m_framebuffer_device); + VERIFY(m_framebuffer_console); + m_framebuffer_console->disable(); + m_framebuffer_device->activate_writes(); } } diff --git a/Kernel/Graphics/VGACompatibleAdapter.h b/Kernel/Graphics/VGACompatibleAdapter.h index 75ed5ee946..c1b6572e05 100644 --- a/Kernel/Graphics/VGACompatibleAdapter.h +++ b/Kernel/Graphics/VGACompatibleAdapter.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -15,11 +16,14 @@ namespace Kernel { -class VGACompatibleAdapter final : public GraphicsDevice +class VGACompatibleAdapter : public GraphicsDevice , public PCI::DeviceController { AK_MAKE_ETERNAL public: static NonnullRefPtr initialize_with_preset_resolution(PCI::Address, PhysicalAddress, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch); + static NonnullRefPtr initialize(PCI::Address); + + virtual bool framebuffer_devices_initialized() const override { return !m_framebuffer_device.is_null(); } protected: explicit VGACompatibleAdapter(PCI::Address); @@ -31,8 +35,17 @@ private: virtual void initialize_framebuffer_devices() override; virtual Type type() const override { return Type::VGACompatible; } + virtual void enable_consoles() override; + virtual void disable_consoles() override; + protected: - RefPtr m_framebuffer; + PhysicalAddress m_framebuffer_address; + size_t m_framebuffer_width { 0 }; + size_t m_framebuffer_height { 0 }; + size_t m_framebuffer_pitch { 0 }; + + RefPtr m_framebuffer_device; + RefPtr m_framebuffer_console; }; } diff --git a/Kernel/Panic.cpp b/Kernel/Panic.cpp index 192b37ddf8..95593b58e9 100644 --- a/Kernel/Panic.cpp +++ b/Kernel/Panic.cpp @@ -13,7 +13,7 @@ namespace Kernel { void __panic(const char* file, unsigned int line, const char* function) { - dmesgln("at {}:{} in {}", file, line, function); + critical_dmesgln("at {}:{} in {}", file, line, function); dump_backtrace(); Processor::halt(); } diff --git a/Kernel/Panic.h b/Kernel/Panic.h index 490173a054..96f5034b8d 100644 --- a/Kernel/Panic.h +++ b/Kernel/Panic.h @@ -12,8 +12,8 @@ namespace Kernel { #define PANIC(...) \ do { \ - dmesgln("KERNEL PANIC! :^("); \ - dmesgln(__VA_ARGS__); \ + critical_dmesgln("KERNEL PANIC! :^("); \ + critical_dmesgln(__VA_ARGS__); \ __panic(__FILE__, __LINE__, __PRETTY_FUNCTION__); \ } while (0) diff --git a/Kernel/TTY/ConsoleManagement.cpp b/Kernel/TTY/ConsoleManagement.cpp new file mode 100644 index 0000000000..c9753578f7 --- /dev/null +++ b/Kernel/TTY/ConsoleManagement.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Kernel { + +static AK::Singleton s_the; + +bool ConsoleManagement::is_initialized() +{ + if (!s_the.is_initialized()) + return false; + if (s_the->m_consoles.is_empty()) + return false; + if (s_the->m_active_console.is_null()) + return false; + return true; +} + +ConsoleManagement& ConsoleManagement::the() +{ + return *s_the; +} + +UNMAP_AFTER_INIT ConsoleManagement::ConsoleManagement() +{ +} + +UNMAP_AFTER_INIT void ConsoleManagement::initialize() +{ + for (size_t index = 0; index < 4; index++) { + m_consoles.append(VirtualConsole::create(index)); + } + // Note: By default the active console is the first one. + m_active_console = m_consoles[0]; + ScopedSpinLock lock(m_lock); + m_active_console->set_active(true); +} + +void ConsoleManagement::switch_to(unsigned index) +{ + ScopedSpinLock lock(m_lock); + VERIFY(m_active_console); + VERIFY(index < m_consoles.size()); + if (m_active_console->index() == index) + return; + + bool was_graphical = m_active_console->is_graphical(); + m_active_console->set_active(false); + m_active_console = m_consoles[index]; + dbgln_if(VIRTUAL_CONSOLE_DEBUG, "Console: Switch to {}", index); + + // Before setting current console to be "active", switch between graphical mode to "textual" mode + // if needed. This will ensure we clear the screen and also that WindowServer won't print anything + // in between. + if (m_active_console->is_graphical() && !was_graphical) { + GraphicsManagement::the().activate_graphical_mode(); + } + if (!m_active_console->is_graphical() && was_graphical) { + GraphicsManagement::the().deactivate_graphical_mode(); + } + m_active_console->set_active(true); +} + +} diff --git a/Kernel/TTY/ConsoleManagement.h b/Kernel/TTY/ConsoleManagement.h new file mode 100644 index 0000000000..8cca80e860 --- /dev/null +++ b/Kernel/TTY/ConsoleManagement.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { + +class ConsoleManagement { + AK_MAKE_ETERNAL; + friend class VirtualConsole; + +public: + ConsoleManagement(); + + static bool is_initialized(); + static ConsoleManagement& the(); + + void switch_to(unsigned); + void initialize(); + + NonnullRefPtr first_tty() const { return m_consoles[0]; } + NonnullRefPtr debug_tty() const { return m_consoles[1]; } + + RecursiveSpinLock& tty_write_lock() { return m_tty_write_lock; } + +private: + NonnullRefPtrVector m_consoles; + RefPtr m_active_console; + SpinLock m_lock; + RecursiveSpinLock m_tty_write_lock; +}; + +}; diff --git a/Kernel/TTY/VirtualConsole.cpp b/Kernel/TTY/VirtualConsole.cpp index 5ac13edee4..f08284a339 100644 --- a/Kernel/TTY/VirtualConsole.cpp +++ b/Kernel/TTY/VirtualConsole.cpp @@ -1,59 +1,141 @@ /* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2020, Sergey Bugaev + * Copyright (c) 2021, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ #include "VirtualConsole.h" +#include #include #include +#include #include +#include #include #include #include +#include namespace Kernel { -static u8* s_vga_buffer; -static VirtualConsole* s_consoles[s_max_virtual_consoles]; -static int s_active_console; -static RecursiveSpinLock s_lock; - -void VirtualConsole::flush_vga_cursor() +ConsoleImpl::ConsoleImpl(VirtualConsole& client) + : Terminal(client) { - u16 value = m_current_vga_start_address + (m_terminal.cursor_row() * columns() + m_terminal.cursor_column()); - IO::out8(0x3d4, 0x0e); - IO::out8(0x3d5, MSB(value)); - IO::out8(0x3d4, 0x0f); - IO::out8(0x3d5, LSB(value)); } -UNMAP_AFTER_INIT void VirtualConsole::initialize() +void ConsoleImpl::invalidate_cursor() { - s_vga_buffer = (u8*)0xc00b8000; - s_active_console = -1; +} +void ConsoleImpl::clear() +{ + m_client.clear(); +} +void ConsoleImpl::clear_including_history() +{ +} + +void ConsoleImpl::set_size(u16 determined_columns, u16 determined_rows) +{ + VERIFY(determined_columns); + VERIFY(determined_rows); + + if (determined_columns == columns() && determined_rows == rows()) + return; + + m_columns = determined_columns; + m_rows = determined_rows; + + m_cursor_row = min((int)m_cursor_row, rows() - 1); + m_cursor_column = min((int)m_cursor_column, columns() - 1); + m_saved_cursor_row = min((int)m_saved_cursor_row, rows() - 1); + m_saved_cursor_column = min((int)m_saved_cursor_column, columns() - 1); + + m_horizontal_tabs.resize(determined_columns); + for (unsigned i = 0; i < determined_columns; ++i) + m_horizontal_tabs[i] = (i % 8) == 0; + // Rightmost column is always last tab on line. + m_horizontal_tabs[determined_columns - 1] = 1; + m_client.terminal_did_resize(m_columns, m_rows); +} +void ConsoleImpl::scroll_up() +{ + // NOTE: We have to invalidate the cursor first. + m_client.invalidate_cursor(m_cursor_row); + m_client.scroll_up(); +} +void ConsoleImpl::scroll_down() +{ +} +void ConsoleImpl::newline() +{ + u16 new_row = m_cursor_row; + u16 max_row = rows() - 1; + if (new_row == max_row) { + // NOTE: We have to invalidate the cursor first. + m_client.invalidate_cursor(new_row); + m_client.scroll_up(); + } else { + ++new_row; + } + set_cursor(new_row, 0); +} +void ConsoleImpl::put_character_at(unsigned row, unsigned column, u32 ch) +{ + m_client.put_character_at(row, column, ch, m_current_attribute); + m_last_code_point = ch; +} +void ConsoleImpl::set_window_title(const String&) +{ +} +void ConsoleImpl::ICH(Parameters) +{ + // FIXME: Implement this +} +void ConsoleImpl::IL(Parameters) +{ + // FIXME: Implement this +} +void ConsoleImpl::DCH(Parameters) +{ + // FIXME: Implement this +} +void ConsoleImpl::DL(Parameters) +{ + // FIXME: Implement this } void VirtualConsole::set_graphical(bool graphical) { - if (graphical) - set_vga_start_row(0); - m_graphical = graphical; } +UNMAP_AFTER_INIT NonnullRefPtr VirtualConsole::create(size_t index) +{ + return adopt_ref(*new VirtualConsole(index)); +} + UNMAP_AFTER_INIT VirtualConsole::VirtualConsole(const unsigned index) : TTY(4, index) , m_index(index) - , m_terminal(*this) + , m_console_impl(*this) { - VERIFY(index < s_max_virtual_consoles); - m_tty_name = String::formatted("/dev/tty{}", m_index); - m_terminal.set_size(80, 25); + VERIFY(GraphicsManagement::the().console()); + set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row()); + m_console_impl.set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row()); - s_consoles[index] = this; + // Allocate twice of the max row * max column * sizeof(Cell) to ensure we can some sort of history mechanism... + auto size = GraphicsManagement::the().console()->max_column() * GraphicsManagement::the().console()->max_row() * sizeof(Cell) * 2; + m_cells = MM.allocate_kernel_region(page_round_up(size), "Virtual Console Cells", Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow); + + // Add the lines, so we also ensure they will be flushed now + for (size_t row = 0; row < rows(); row++) { + m_lines.append({ true }); + } + clear(); + VERIFY(m_cells); } UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole() @@ -61,69 +143,6 @@ UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole() VERIFY_NOT_REACHED(); } -void VirtualConsole::switch_to(unsigned index) -{ - if ((int)index == s_active_console) - return; - VERIFY(index < s_max_virtual_consoles); - VERIFY(s_consoles[index]); - - ScopedSpinLock lock(s_lock); - if (s_active_console != -1) { - auto* active_console = s_consoles[s_active_console]; - // We won't know how to switch away from a graphical console until we - // can set the video mode on our own. Just stop anyone from trying for - // now. - if (active_console->is_graphical()) { - dbgln("Cannot switch away from graphical console yet :("); - return; - } - active_console->set_active(false); - } - dbgln("VC: Switch to {} ({})", index, s_consoles[index]); - s_active_console = index; - s_consoles[s_active_console]->set_active(true); -} - -void VirtualConsole::set_active(bool active) -{ - if (active == m_active) - return; - - ScopedSpinLock lock(s_lock); - - m_active = active; - - if (active) { - set_vga_start_row(0); - HIDManagement::the().set_client(this); - - m_terminal.m_need_full_flush = true; - flush_dirty_lines(); - } else { - HIDManagement::the().set_client(nullptr); - } -} - -enum class VGAColor : u8 { - Black = 0, - Blue, - Green, - Cyan, - Red, - Magenta, - Brown, - LightGray, - DarkGray, - BrightBlue, - BrightGreen, - BrightCyan, - BrightRed, - BrightMagenta, - Yellow, - White, -}; - enum class ANSIColor : u8 { Black = 0, Red, @@ -144,60 +163,53 @@ enum class ANSIColor : u8 { __Count, }; -static inline VGAColor ansi_color_to_vga(ANSIColor color) +static inline Graphics::Console::Color ansi_color_to_standard_vga_color(ANSIColor color) { switch (color) { case ANSIColor::Black: - return VGAColor::Black; + return Graphics::Console::Color::Black; case ANSIColor::Red: - return VGAColor::Red; + return Graphics::Console::Color::Red; case ANSIColor::Brown: - return VGAColor::Brown; + return Graphics::Console::Color::Brown; case ANSIColor::Blue: - return VGAColor::Blue; + return Graphics::Console::Color::Blue; case ANSIColor::Magenta: - return VGAColor::Magenta; + return Graphics::Console::Color::Magenta; case ANSIColor::Green: - return VGAColor::Green; + return Graphics::Console::Color::Green; case ANSIColor::Cyan: - return VGAColor::Cyan; + return Graphics::Console::Color::Cyan; case ANSIColor::LightGray: - return VGAColor::LightGray; + return Graphics::Console::Color::LightGray; case ANSIColor::DarkGray: - return VGAColor::DarkGray; + return Graphics::Console::Color::DarkGray; case ANSIColor::BrightRed: - return VGAColor::BrightRed; + return Graphics::Console::Color::BrightRed; case ANSIColor::BrightGreen: - return VGAColor::BrightGreen; + return Graphics::Console::Color::BrightGreen; case ANSIColor::Yellow: - return VGAColor::Yellow; + return Graphics::Console::Color::Yellow; case ANSIColor::BrightBlue: - return VGAColor::BrightBlue; + return Graphics::Console::Color::BrightBlue; case ANSIColor::BrightMagenta: - return VGAColor::BrightMagenta; + return Graphics::Console::Color::BrightMagenta; case ANSIColor::BrightCyan: - return VGAColor::BrightCyan; + return Graphics::Console::Color::BrightCyan; case ANSIColor::White: - return VGAColor::White; + return Graphics::Console::Color::White; default: VERIFY_NOT_REACHED(); } } -static inline u8 xterm_color_to_vga(u32 color) +static inline Graphics::Console::Color xterm_to_standard_color(u32 color) { for (u8 i = 0; i < (u8)ANSIColor::__Count; i++) { if (xterm_colors[i] == color) - return (u8)ansi_color_to_vga((ANSIColor)i); + return (Graphics::Console::Color)ansi_color_to_standard_vga_color((ANSIColor)i); } - return (u8)VGAColor::LightGray; -} - -void VirtualConsole::clear_vga_row(u16 row) -{ - u16* linemem = (u16*)&m_current_vga_window[row * 160]; - for (u16 i = 0; i < columns(); ++i) - linemem[i] = 0x0720; + return Graphics::Console::Color::LightGray; } void VirtualConsole::on_key_pressed(KeyEvent event) @@ -209,26 +221,18 @@ void VirtualConsole::on_key_pressed(KeyEvent event) if (!event.is_press()) return; - if (event.key == KeyCode::Key_PageUp && event.flags == Mod_Shift) { - // TODO: scroll up - return; - } - if (event.key == KeyCode::Key_PageDown && event.flags == Mod_Shift) { - // TODO: scroll down - return; - } - Processor::deferred_call_queue([this, event]() { - m_terminal.handle_key_press(event.key, event.code_point, event.flags); + m_console_impl.handle_key_press(event.key, event.code_point, event.flags); }); } ssize_t VirtualConsole::on_tty_write(const UserOrKernelBuffer& data, ssize_t size) { - ScopedSpinLock lock(s_lock); + ScopedSpinLock global_lock(ConsoleManagement::the().tty_write_lock()); + ScopedSpinLock lock(m_lock); auto result = data.read_buffered<512>((size_t)size, [&](u8 const* buffer, size_t buffer_bytes) { for (size_t i = 0; i < buffer_bytes; ++i) - m_terminal.on_input(buffer[i]); + m_console_impl.on_input(buffer[i]); return buffer_bytes; }); if (m_active) @@ -238,52 +242,51 @@ ssize_t VirtualConsole::on_tty_write(const UserOrKernelBuffer& data, ssize_t siz return (ssize_t)result.value(); } -void VirtualConsole::set_vga_start_row(u16 row) +void VirtualConsole::set_active(bool active) { - m_vga_start_row = row; - m_current_vga_start_address = row * columns(); - m_current_vga_window = s_vga_buffer + row * 160; - IO::out8(0x3d4, 0x0c); - IO::out8(0x3d5, MSB(m_current_vga_start_address)); - IO::out8(0x3d4, 0x0d); - IO::out8(0x3d5, LSB(m_current_vga_start_address)); + VERIFY(ConsoleManagement::the().m_lock.is_locked()); + VERIFY(m_active != active); + m_active = active; + + if (active) { + HIDManagement::the().set_client(this); + + m_console_impl.m_need_full_flush = true; + flush_dirty_lines(); + } else { + HIDManagement::the().set_client(nullptr); + } } -static inline u8 attribute_to_vga(const VT::Attribute& attribute) +void VirtualConsole::emit_char(char ch) { - u8 vga_attr = 0x07; - - if (attribute.flags & VT::Attribute::Bold) - vga_attr |= 0x08; - - // Background color - vga_attr &= ~0x70; - vga_attr |= xterm_color_to_vga(attribute.effective_background_color()) << 8; - - // Foreground color - vga_attr &= ~0x7; - vga_attr |= xterm_color_to_vga(attribute.effective_foreground_color()); - - return vga_attr; + echo(ch); } void VirtualConsole::flush_dirty_lines() { - for (u16 visual_row = 0; visual_row < m_terminal.rows(); ++visual_row) { - auto& line = m_terminal.visible_line(visual_row); - if (!line.is_dirty() && !m_terminal.m_need_full_flush) + VERIFY(GraphicsManagement::is_initialized()); + VERIFY(GraphicsManagement::the().console()); + for (u16 visual_row = 0; visual_row < rows(); ++visual_row) { + auto& line = m_lines[visual_row]; + if (!line.dirty && !m_console_impl.m_need_full_flush) continue; - for (size_t column = 0; column < line.length(); ++column) { - u32 code_point = line.code_point(column); - auto attribute = line.attribute_at(column); - u16 vga_index = (visual_row * 160) + (column * 2); - m_current_vga_window[vga_index] = code_point < 128 ? code_point : '?'; - m_current_vga_window[vga_index + 1] = attribute_to_vga(attribute); + for (size_t column = 0; column < columns(); ++column) { + auto& cell = cell_at(column, visual_row); + + auto foreground_color = xterm_to_standard_color(cell.attribute.effective_foreground_color()); + if (cell.attribute.flags & VT::Attribute::Flags::Bold) + foreground_color = (Graphics::Console::Color)((u8)foreground_color | 0x08); + GraphicsManagement::the().console()->write(column, + visual_row, + ((u8)cell.ch < 128 ? cell.ch : '?'), + xterm_to_standard_color(cell.attribute.effective_background_color()), + foreground_color); } - line.set_dirty(false); + line.dirty = false; } - flush_vga_cursor(); - m_terminal.m_need_full_flush = false; + GraphicsManagement::the().console()->set_cursor(m_console_impl.cursor_column(), m_console_impl.cursor_row()); + m_console_impl.m_need_full_flush = false; } void VirtualConsole::beep() @@ -304,9 +307,8 @@ void VirtualConsole::set_window_progress(int, int) void VirtualConsole::terminal_did_resize(u16 columns, u16 rows) { - VERIFY(columns == 80); - VERIFY(rows == 25); - set_size(columns, rows); + // FIXME: Allocate more Region(s) or deallocate them if needed... + dbgln("VC {}: Resized to {} x {}", index(), columns, rows); } void VirtualConsole::terminal_history_changed() @@ -333,4 +335,66 @@ void VirtualConsole::echo(u8 ch) } } +VirtualConsole::Cell& VirtualConsole::cell_at(size_t x, size_t y) +{ + auto* ptr = (VirtualConsole::Cell*)(m_cells->vaddr().as_ptr()); + ptr += (y * columns()) + x; + return *ptr; +} + +void VirtualConsole::clear() +{ + auto* cell = (Cell*)m_cells->vaddr().as_ptr(); + for (size_t y = 0; y < rows(); y++) { + m_lines[y].dirty = true; + for (size_t x = 0; x < columns(); x++) { + cell[x].clear(); + } + cell += columns(); + } + m_console_impl.set_cursor(0, 0); +} + +void VirtualConsole::scroll_up() +{ + memmove(m_cells->vaddr().as_ptr(), m_cells->vaddr().offset(columns() * sizeof(Cell)).as_ptr(), ((rows() - 1) * columns() * sizeof(Cell))); + clear_line(rows() - 1); + m_console_impl.m_need_full_flush = true; +} + +void VirtualConsole::newline() +{ +} + +void VirtualConsole::clear_line(size_t y_index) +{ + m_lines[y_index].dirty = true; + for (size_t x = 0; x < columns(); x++) { + auto& cell = cell_at(x, y_index); + cell.clear(); + } +} + +void VirtualConsole::put_character_at(unsigned row, unsigned column, u32 code_point, const VT::Attribute& attribute) +{ + VERIFY(row < rows()); + VERIFY(column < columns()); + auto& line = m_lines[row]; + auto& cell = cell_at(column, row); + cell.attribute.foreground_color = attribute.foreground_color; + cell.attribute.background_color = attribute.background_color; + cell.attribute.flags = attribute.flags; + if (code_point > 128) + cell.ch = ' '; + else + cell.ch = code_point; + cell.attribute.flags |= VT::Attribute::Flags::Touched; + line.dirty = true; +} + +void VirtualConsole::invalidate_cursor(size_t row) +{ + m_lines[row].dirty = true; +} + } diff --git a/Kernel/TTY/VirtualConsole.h b/Kernel/TTY/VirtualConsole.h index 10b04771f8..3ad2c0f703 100644 --- a/Kernel/TTY/VirtualConsole.h +++ b/Kernel/TTY/VirtualConsole.h @@ -1,35 +1,91 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2021, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include +#include +#include +#include +#include #include #include +#include #include +#include +#include #include namespace Kernel { -static constexpr unsigned s_max_virtual_consoles = 6; +class ConsoleManagement; +class VirtualConsole; +// FIXME: This implementation has no knowledge about keeping terminal history... +class ConsoleImpl final : public VT::Terminal { +public: + explicit ConsoleImpl(VirtualConsole&); + + virtual void set_size(u16 columns, u16 rows) override; + +private: + virtual void invalidate_cursor() override; + virtual void clear() override; + virtual void clear_including_history() override; + + virtual void scroll_up() override; + virtual void scroll_down() override; + virtual void newline() override; + virtual void put_character_at(unsigned row, unsigned column, u32 ch) override; + virtual void set_window_title(const String&) override; + + virtual void ICH(Parameters) override; + + virtual void IL(Parameters) override; + virtual void DCH(Parameters) override; + virtual void DL(Parameters) override; +}; class VirtualConsole final : public TTY , public KeyboardClient , public VT::TerminalClient { AK_MAKE_ETERNAL + friend class ConsoleManagement; + friend class ConsoleImpl; + friend class VT::Terminal; + public: - VirtualConsole(const unsigned index); + struct Line { + bool dirty; + }; + + struct Cell { + void clear() + { + ch = ' '; + attribute.reset(); + } + char ch; + VT::Attribute attribute; + }; + +public: + static NonnullRefPtr create(size_t index); + virtual ~VirtualConsole() override; - static void switch_to(unsigned); - static void initialize(); + size_t index() const { return m_index; } bool is_graphical() { return m_graphical; } void set_graphical(bool graphical); + void emit_char(char); + private: + VirtualConsole(const unsigned index); // ^KeyboardClient virtual void on_key_pressed(KeyEvent) override; @@ -53,23 +109,37 @@ private: virtual String device_name() const override; void set_active(bool); - - void flush_vga_cursor(); void flush_dirty_lines(); unsigned m_index; bool m_active { false }; bool m_graphical { false }; - void clear_vga_row(u16 row); - void set_vga_start_row(u16 row); - u16 m_vga_start_row { 0 }; - u16 m_current_vga_start_address { 0 }; - u8* m_current_vga_window { nullptr }; - - VT::Terminal m_terminal; - String m_tty_name; + RecursiveSpinLock m_lock; + +private: + void invalidate_cursor(size_t row); + + void clear(); + + void inject_string(const StringView&); + + Cell& cell_at(size_t column, size_t row); + + typedef Vector ParamVector; + + void on_code_point(u32); + + void scroll_down(); + void scroll_up(); + void newline(); + void clear_line(size_t index); + void put_character_at(unsigned row, unsigned column, u32 ch, const VT::Attribute&); + + OwnPtr m_cells; + Vector m_lines; + ConsoleImpl m_console_impl; }; } diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 855771e272..9f0fec02a9 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -138,12 +139,13 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init() for (ctor_func_t* ctor = &start_ctors; ctor < &end_ctors; ctor++) (*ctor)(); + ConsoleDevice::initialize(); + APIC::initialize(); InterruptManagement::initialize(); ACPI::initialize(); VFS::initialize(); - ConsoleDevice::initialize(); dmesgln("Starting SerenityOS..."); @@ -160,12 +162,11 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init() VMWareBackdoor::the(); // don't wait until first mouse packet HIDManagement::initialize(); - VirtualConsole::initialize(); - tty0 = new VirtualConsole(0); - for (unsigned i = 1; i < s_max_virtual_consoles; i++) { - new VirtualConsole(i); - } - VirtualConsole::switch_to(0); + + PCI::initialize(); + GraphicsManagement::the().initialize(); + ConsoleManagement::the().initialize(); + ConsoleManagement::the().switch_to(0); Thread::initialize(); Process::initialize(); @@ -230,11 +231,8 @@ void init_stage2(void*) SyncTask::spawn(); FinalizerTask::spawn(); - PCI::initialize(); auto boot_profiling = kernel_command_line().is_boot_profiling_enabled(); - GraphicsManagement::the().initialize(); - USB::UHCIController::detect(); DMIExpose::initialize(); @@ -274,7 +272,8 @@ void init_stage2(void*) int error; // FIXME: It would be nicer to set the mode from userspace. - tty0->set_graphical(!GraphicsManagement::the().is_text_mode_enabled()); + // FIXME: It would be smarter to not hardcode that the first tty is the only graphical one + ConsoleManagement::the().first_tty()->set_graphical(GraphicsManagement::the().framebuffer_devices_exist()); RefPtr thread; auto userspace_init = kernel_command_line().userspace_init(); auto init_args = kernel_command_line().userspace_init_args(); diff --git a/Kernel/kprintf.cpp b/Kernel/kprintf.cpp index fb3401e0eb..a01329abe0 100644 --- a/Kernel/kprintf.cpp +++ b/Kernel/kprintf.cpp @@ -7,9 +7,12 @@ #include #include #include +#include +#include #include #include #include +#include #include #include @@ -60,6 +63,20 @@ static void serial_putch(char ch) was_cr = false; } +static void critical_console_out(char ch) +{ + if (serial_debug) + serial_putch(ch); + // No need to output things to the real ConsoleDevice as no one is likely + // to read it (because we are in a fatal situation, so only print things and halt) + IO::out8(0xe9, ch); + // We emit chars directly to the string. this is necessary in few cases, + // especially when we want to avoid any memory allocations... + if (GraphicsManagement::is_initialized() && GraphicsManagement::the().console()) { + GraphicsManagement::the().console()->write(ch); + } +} + static void console_out(char ch) { if (serial_debug) @@ -72,6 +89,9 @@ static void console_out(char ch) } else { IO::out8(0xe9, ch); } + if (ConsoleManagement::is_initialized()) { + ConsoleManagement::the().debug_tty()->emit_char(ch); + } } static void buffer_putch(char*& bufptr, char ch) @@ -145,3 +165,12 @@ extern "C" void kernelputstr(const char* characters, size_t length) for (size_t i = 0; i < length; ++i) console_out(characters[i]); } + +extern "C" void kernelcriticalputstr(const char* characters, size_t length) +{ + if (!characters) + return; + ScopedSpinLock lock(s_log_lock); + for (size_t i = 0; i < length; ++i) + critical_console_out(characters[i]); +} diff --git a/Kernel/kstdio.h b/Kernel/kstdio.h index 2f997d1820..4f84e8b871 100644 --- a/Kernel/kstdio.h +++ b/Kernel/kstdio.h @@ -11,6 +11,7 @@ extern "C" { void dbgputstr(const char*, size_t); void kernelputstr(const char*, size_t); +void kernelcriticalputstr(const char*, size_t); int snprintf(char* buf, size_t, const char* fmt, ...) __attribute__((format(printf, 3, 4))); void set_serial_debug(bool on_or_off); int get_serial_debug(); diff --git a/Meta/CMake/all_the_debug_macros.cmake b/Meta/CMake/all_the_debug_macros.cmake index 037cbb8cab..cb8297a193 100644 --- a/Meta/CMake/all_the_debug_macros.cmake +++ b/Meta/CMake/all_the_debug_macros.cmake @@ -16,6 +16,7 @@ set(BXVGA_DEBUG ON) set(PS2MOUSE_DEBUG ON) set(MOUSE_DEBUG ON) set(VMWARE_BACKDOOR_DEBUG ON) +set(VIRTUAL_CONSOLE_DEBUG ON) set(FILEDESCRIPTION_DEBUG ON) set(PROCFS_DEBUG ON) set(VFS_DEBUG ON) diff --git a/Userland/Libraries/LibVT/Attribute.h b/Userland/Libraries/LibVT/Attribute.h new file mode 100644 index 0000000000..c5d154e5c0 --- /dev/null +++ b/Userland/Libraries/LibVT/Attribute.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace VT { + +struct Attribute { + Attribute() { reset(); } + + static const u32 default_foreground_color = xterm_colors[7]; + static const u32 default_background_color = xterm_colors[0]; + + void reset() + { + foreground_color = default_foreground_color; + background_color = default_background_color; + flags = Flags::NoAttributes; + } + u32 foreground_color {}; + u32 background_color {}; + + u32 effective_background_color() const { return flags & Negative ? foreground_color : background_color; } + u32 effective_foreground_color() const { return flags & Negative ? background_color : foreground_color; } + +#ifndef KERNEL + String href; + String href_id; +#endif + + enum Flags : u8 { + NoAttributes = 0x00, + Bold = 0x01, + Italic = 0x02, + Underline = 0x04, + Negative = 0x08, + Blink = 0x10, + Touched = 0x20, + }; + + bool is_untouched() const { return !(flags & Touched); } + + // TODO: it would be really nice if we had a helper for enums that + // exposed bit ops for class enums... + u8 flags = Flags::NoAttributes; + + bool operator==(const Attribute& other) const + { + return foreground_color == other.foreground_color && background_color == other.background_color && flags == other.flags; + } + bool operator!=(const Attribute& other) const + { + return !(*this == other); + } +}; + +} diff --git a/Userland/Libraries/LibVT/Line.h b/Userland/Libraries/LibVT/Line.h index ce6532b6e1..ecc9fdc19b 100644 --- a/Userland/Libraries/LibVT/Line.h +++ b/Userland/Libraries/LibVT/Line.h @@ -9,57 +9,11 @@ #include #include #include +#include #include namespace VT { -struct Attribute { - Attribute() { reset(); } - - static const u32 default_foreground_color = xterm_colors[7]; - static const u32 default_background_color = xterm_colors[0]; - - void reset() - { - foreground_color = default_foreground_color; - background_color = default_background_color; - flags = Flags::NoAttributes; - } - u32 foreground_color {}; - u32 background_color {}; - - u32 effective_background_color() const { return flags & Negative ? foreground_color : background_color; } - u32 effective_foreground_color() const { return flags & Negative ? background_color : foreground_color; } - - String href; - String href_id; - - enum Flags : u8 { - NoAttributes = 0x00, - Bold = 0x01, - Italic = 0x02, - Underline = 0x04, - Negative = 0x08, - Blink = 0x10, - Touched = 0x20, - }; - - bool is_untouched() const { return !(flags & Touched); } - - // TODO: it would be really nice if we had a helper for enums that - // exposed bit ops for class enums... - u8 flags = Flags::NoAttributes; - - bool operator==(const Attribute& other) const - { - return foreground_color == other.foreground_color && background_color == other.background_color && flags == other.flags; - } - bool operator!=(const Attribute& other) const - { - return !(*this == other); - } -}; - class Line { AK_MAKE_NONCOPYABLE(Line); AK_MAKE_NONMOVABLE(Line); diff --git a/Userland/Libraries/LibVT/Terminal.cpp b/Userland/Libraries/LibVT/Terminal.cpp index ccd66257cf..42ee743396 100644 --- a/Userland/Libraries/LibVT/Terminal.cpp +++ b/Userland/Libraries/LibVT/Terminal.cpp @@ -9,19 +9,23 @@ #include #include #include +#ifdef KERNEL +# include +#endif namespace VT { +#ifndef KERNEL Terminal::Terminal(TerminalClient& client) +#else +Terminal::Terminal(Kernel::VirtualConsole& client) +#endif : m_client(client) , m_parser(*this) { } -Terminal::~Terminal() -{ -} - +#ifndef KERNEL void Terminal::clear() { for (size_t i = 0; i < rows(); ++i) @@ -38,6 +42,7 @@ void Terminal::clear_including_history() m_client.terminal_history_changed(); } +#endif void Terminal::alter_mode(bool should_set, Parameters params, Intermediates intermediates) { @@ -445,6 +450,7 @@ void Terminal::SD(Parameters params) scroll_down(); } +#ifndef KERNEL void Terminal::IL(Parameters params) { int count = 1; @@ -461,12 +467,14 @@ void Terminal::IL(Parameters params) m_need_full_flush = true; } +#endif void Terminal::DA(Parameters) { emit_string("\033[?1;0c"); } +#ifndef KERNEL void Terminal::DL(Parameters params) { int count = 1; @@ -511,6 +519,7 @@ void Terminal::DCH(Parameters params) line.set_dirty(true); } +#endif void Terminal::newline() { @@ -527,6 +536,7 @@ void Terminal::carriage_return() set_cursor(m_cursor_row, 0); } +#ifndef KERNEL void Terminal::scroll_up() { // NOTE: We have to invalidate the cursor first. @@ -550,6 +560,20 @@ void Terminal::scroll_down() m_need_full_flush = true; } +void Terminal::put_character_at(unsigned row, unsigned column, u32 code_point) +{ + VERIFY(row < rows()); + VERIFY(column < columns()); + auto& line = m_lines[row]; + line.set_code_point(column, code_point); + line.attribute_at(column) = m_current_attribute; + line.attribute_at(column).flags |= Attribute::Touched; + line.set_dirty(true); + + m_last_code_point = code_point; +} +#endif + void Terminal::set_cursor(unsigned a_row, unsigned a_column) { unsigned row = min(a_row, m_rows - 1u); @@ -565,19 +589,6 @@ void Terminal::set_cursor(unsigned a_row, unsigned a_column) invalidate_cursor(); } -void Terminal::put_character_at(unsigned row, unsigned column, u32 code_point) -{ - VERIFY(row < rows()); - VERIFY(column < columns()); - auto& line = m_lines[row]; - line.set_code_point(column, code_point); - line.attribute_at(column) = m_current_attribute; - line.attribute_at(column).flags |= Attribute::Touched; - line.set_dirty(true); - - m_last_code_point = code_point; -} - void Terminal::NEL() { newline(); @@ -607,6 +618,7 @@ void Terminal::DSR(Parameters params) } } +#ifndef KERNEL void Terminal::ICH(Parameters params) { int num = 0; @@ -628,6 +640,7 @@ void Terminal::ICH(Parameters params) line.set_dirty(true); } +#endif void Terminal::on_input(u8 byte) { @@ -837,6 +850,7 @@ void Terminal::execute_osc_sequence(OscParameters parameters, u8 last_byte) // Should we expose the raw OSC string from the parser? Or join by semicolon? break; case 8: +#ifndef KERNEL if (parameters.size() < 2) { dbgln("Attempted to set href but gave too few parameters"); } else if (parameters[2].is_empty()) { @@ -847,6 +861,7 @@ void Terminal::execute_osc_sequence(OscParameters parameters, u8 last_byte) // FIXME: Respect the provided ID m_current_attribute.href_id = String::number(m_next_href_id++); } +#endif break; case 9: if (parameters.size() < 2 || parameters[1].is_empty() || parameters[2].is_empty()) @@ -1034,6 +1049,7 @@ void Terminal::unimplemented_osc_sequence(OscParameters parameters, u8 last_byte dbgln("{}", builder.string_view()); } +#ifndef KERNEL void Terminal::set_size(u16 columns, u16 rows) { if (!columns) @@ -1073,7 +1089,9 @@ void Terminal::set_size(u16 columns, u16 rows) m_client.terminal_did_resize(m_columns, m_rows); } +#endif +#ifndef KERNEL void Terminal::invalidate_cursor() { m_lines[m_cursor_row].set_dirty(true); @@ -1090,5 +1108,6 @@ Attribute Terminal::attribute_at(const Position& position) const return {}; return line.attribute_at(position.column()); } +#endif } diff --git a/Userland/Libraries/LibVT/Terminal.h b/Userland/Libraries/LibVT/Terminal.h index 7198059654..853319353b 100644 --- a/Userland/Libraries/LibVT/Terminal.h +++ b/Userland/Libraries/LibVT/Terminal.h @@ -12,9 +12,18 @@ #include #include #include -#include #include +#ifndef KERNEL +# include +# include +#else +namespace Kernel { +class VirtualConsole; +} +# include +#endif + namespace VT { class TerminalClient { @@ -31,24 +40,52 @@ public: class Terminal : public EscapeSequenceExecutor { public: +#ifndef KERNEL explicit Terminal(TerminalClient&); - ~Terminal(); +#else + explicit Terminal(Kernel::VirtualConsole&); +#endif + + virtual ~Terminal() + { + } bool m_need_full_flush { false }; +#ifndef KERNEL void invalidate_cursor(); +#else + virtual void invalidate_cursor() = 0; +#endif + void on_input(u8); + void set_cursor(unsigned row, unsigned column); + +#ifndef KERNEL void clear(); void clear_including_history(); +#else + virtual void clear() = 0; + virtual void clear_including_history() = 0; +#endif +#ifndef KERNEL void set_size(u16 columns, u16 rows); - u16 columns() const { return m_columns; } +#else + virtual void set_size(u16 columns, u16 rows) = 0; +#endif + + u16 columns() const + { + return m_columns; + } u16 rows() const { return m_rows; } u16 cursor_column() const { return m_cursor_column; } u16 cursor_row() const { return m_cursor_row; } +#ifndef KERNEL size_t line_count() const { return m_history.size() + m_lines.size(); @@ -100,13 +137,16 @@ public: m_max_history_lines = value; } size_t history_size() const { return m_history.size(); } +#endif void inject_string(const StringView&); void handle_key_press(KeyCode, u32, u8 flags); +#ifndef KERNEL Attribute attribute_at(const Position&) const; +#endif -private: +protected: // ^EscapeSequenceExecutor virtual void emit_code_point(u32) override; virtual void execute_control_code(u8) override; @@ -117,14 +157,20 @@ private: virtual void receive_dcs_char(u8 byte) override; virtual void execute_dcs_sequence() override; + void carriage_return(); +#ifndef KERNEL void scroll_up(); void scroll_down(); void newline(); - void carriage_return(); - - void set_cursor(unsigned row, unsigned column); void put_character_at(unsigned row, unsigned column, u32 ch); void set_window_title(const String&); +#else + virtual void scroll_up() = 0; + virtual void scroll_down() = 0; + virtual void newline() = 0; + virtual void put_character_at(unsigned row, unsigned column, u32 ch) = 0; + virtual void set_window_title(const String&) = 0; +#endif void unimplemented_control_code(u8); void unimplemented_escape_sequence(Intermediates, u8 last_byte); @@ -192,8 +238,12 @@ private: // DSR - Device Status Reports void DSR(Parameters); +#ifndef KERNEL // ICH - Insert Character void ICH(Parameters); +#else + virtual void ICH(Parameters) = 0; +#endif // SU - Scroll Up (called "Pan Down" in VT510) void SU(Parameters); @@ -201,14 +251,18 @@ private: // SD - Scroll Down (called "Pan Up" in VT510) void SD(Parameters); +#ifndef KERNEL // IL - Insert Line void IL(Parameters); - // DCH - Delete Character void DCH(Parameters); - // DL - Delete Line void DL(Parameters); +#else + virtual void IL(Parameters) = 0; + virtual void DCH(Parameters) = 0; + virtual void DL(Parameters) = 0; +#endif // CHA - Cursor Horizontal Absolute void CHA(Parameters); @@ -225,10 +279,14 @@ private: // FIXME: Find the right names for these. void XTERM_WM(Parameters); +#ifndef KERNEL TerminalClient& m_client; +#else + Kernel::VirtualConsole& m_client; +#endif EscapeSequenceParser m_parser; - +#ifndef KERNEL size_t m_history_start = 0; NonnullOwnPtrVector m_history; void add_line_to_history(NonnullOwnPtr&& line) @@ -246,6 +304,7 @@ private: } NonnullOwnPtrVector m_lines; +#endif size_t m_scroll_region_top { 0 }; size_t m_scroll_region_bottom { 0 }; @@ -263,7 +322,9 @@ private: Attribute m_current_attribute; Attribute m_saved_attribute; +#ifndef KERNEL u32 m_next_href_id { 0 }; +#endif Vector m_horizontal_tabs; u32 m_last_code_point { 0 }; diff --git a/Userland/Services/SystemServer/main.cpp b/Userland/Services/SystemServer/main.cpp index 9128fc68c9..948c4a4ceb 100644 --- a/Userland/Services/SystemServer/main.cpp +++ b/Userland/Services/SystemServer/main.cpp @@ -57,10 +57,19 @@ static void parse_boot_mode() const String cmdline = String::copy(f->read_all(), Chomp); dbgln("Read command line: {}", cmdline); - for (auto& part : cmdline.split_view(' ')) { - auto pair = part.split_view('=', 2); - if (pair.size() == 2 && pair[0] == "boot_mode") - g_boot_mode = pair[1]; + // FIXME: Support more than one framebuffer detection + struct stat file_state; + int rc = lstat("/dev/fb0", &file_state); + if (rc < 0) { + for (auto& part : cmdline.split_view(' ')) { + auto pair = part.split_view('=', 2); + if (pair.size() == 2 && pair[0] == "boot_mode") + g_boot_mode = pair[1]; + } + // We could boot into self-test which is not graphical too. + if (g_boot_mode == "self-test") + return; + g_boot_mode = "text"; } dbgln("Booting in {} mode", g_boot_mode); }