Pārlūkot izejas kodu

Kernel/Graphics + SystemServer: Support text mode properly

As we removed the support of VBE modesetting that was done by GRUB early
on boot, we need to determine if we can modeset the resolution with our
drivers, and if not, we should enable text mode and ensure that
SystemServer knows about it too.

Also, SystemServer should first check if there's a framebuffer device
node, which is an indication that text mode was not even if it was
requested. Then, if it doesn't find it, it should check what boot_mode
argument the user specified (in case it's self-test). This way if we
try to use bochs-display device (which is not VGA compatible) and
request a text mode, it will not honor the request and will continue
with graphical mode.

Also try to print critical messages with mininum memory allocations
possible.

In LibVT, We make the implementation flexible for kernel-specific
methods that are implemented in ConsoleImpl class.
Liav A 4 gadi atpakaļ
vecāks
revīzija
20743e8aed
41 mainītis faili ar 1828 papildinājumiem un 317 dzēšanām
  1. 23 0
      AK/Format.cpp
  2. 11 0
      AK/Format.h
  3. 2 2
      Kernel/Arch/i386/CPU.cpp
  4. 4 0
      Kernel/CMakeLists.txt
  5. 4 4
      Kernel/CommandLine.cpp
  6. 2 2
      Kernel/CommandLine.h
  7. 4 0
      Kernel/Debug.h.in
  8. 2 2
      Kernel/Devices/HID/PS2KeyboardDevice.cpp
  9. 34 2
      Kernel/Graphics/BochsGraphicsAdapter.cpp
  10. 9 1
      Kernel/Graphics/BochsGraphicsAdapter.h
  11. 82 0
      Kernel/Graphics/Console/Console.h
  12. 348 0
      Kernel/Graphics/Console/FramebufferConsole.cpp
  13. 50 0
      Kernel/Graphics/Console/FramebufferConsole.h
  14. 203 0
      Kernel/Graphics/Console/TextModeConsole.cpp
  15. 50 0
      Kernel/Graphics/Console/TextModeConsole.h
  16. 19 0
      Kernel/Graphics/Console/VGAConsole.cpp
  17. 39 0
      Kernel/Graphics/Console/VGAConsole.h
  18. 55 2
      Kernel/Graphics/FramebufferDevice.cpp
  19. 17 0
      Kernel/Graphics/FramebufferDevice.h
  20. 6 0
      Kernel/Graphics/GraphicsDevice.h
  21. 92 12
      Kernel/Graphics/GraphicsManagement.cpp
  22. 27 3
      Kernel/Graphics/GraphicsManagement.h
  23. 34 8
      Kernel/Graphics/IntelNativeGraphicsAdapter.cpp
  24. 6 12
      Kernel/Graphics/IntelNativeGraphicsAdapter.h
  25. 41 1
      Kernel/Graphics/VGACompatibleAdapter.cpp
  26. 15 2
      Kernel/Graphics/VGACompatibleAdapter.h
  27. 1 1
      Kernel/Panic.cpp
  28. 2 2
      Kernel/Panic.h
  29. 72 0
      Kernel/TTY/ConsoleManagement.cpp
  30. 41 0
      Kernel/TTY/ConsoleManagement.h
  31. 223 159
      Kernel/TTY/VirtualConsole.cpp
  32. 83 13
      Kernel/TTY/VirtualConsole.h
  33. 10 11
      Kernel/init.cpp
  34. 29 0
      Kernel/kprintf.cpp
  35. 1 0
      Kernel/kstdio.h
  36. 1 0
      Meta/CMake/all_the_debug_macros.cmake
  37. 65 0
      Userland/Libraries/LibVT/Attribute.h
  38. 1 47
      Userland/Libraries/LibVT/Line.h
  39. 36 17
      Userland/Libraries/LibVT/Terminal.cpp
  40. 71 10
      Userland/Libraries/LibVT/Terminal.h
  41. 13 4
      Userland/Services/SystemServer/main.cpp

+ 23 - 0
AK/Format.cpp

@@ -677,6 +677,29 @@ void vdmesgln(StringView fmtstr, TypeErasedFormatParams params)
     const auto string = builder.string_view();
     const auto string = builder.string_view();
     kernelputstr(string.characters_without_null_termination(), string.length());
     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
 #endif
 
 
 template struct Formatter<unsigned char, void>;
 template struct Formatter<unsigned char, void>;

+ 11 - 0
AK/Format.h

@@ -436,6 +436,16 @@ void dmesgln(CheckedFormatString<Parameters...>&& fmt, const Parameters&... para
 {
 {
     vdmesgln(fmt.view(), VariadicFormatParams { parameters... });
     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<typename... Parameters>
+void critical_dmesgln(CheckedFormatString<Parameters...>&& fmt, const Parameters&... parameters)
+{
+    v_critical_dmesgln(fmt.view(), VariadicFormatParams { parameters... });
+}
 #endif
 #endif
 
 
 template<typename T, typename = void>
 template<typename T, typename = void>
@@ -492,6 +502,7 @@ struct Formatter<FormatString> : Formatter<String> {
 } // namespace AK
 } // namespace AK
 
 
 #ifdef KERNEL
 #ifdef KERNEL
+using AK::critical_dmesgln;
 using AK::dmesgln;
 using AK::dmesgln;
 #else
 #else
 using AK::out;
 using AK::out;

+ 2 - 2
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)
 void __assertion_failed(const char* msg, const char* file, unsigned line, const char* func)
 {
 {
     asm volatile("cli");
     asm volatile("cli");
-    dmesgln("ASSERTION FAILED: {}", msg);
-    dmesgln("{}:{} in {}", file, line, func);
+    critical_dmesgln("ASSERTION FAILED: {}", msg);
+    critical_dmesgln("{}:{} in {}", file, line, func);
 
 
     abort();
     abort();
 }
 }

+ 4 - 0
Kernel/CMakeLists.txt

@@ -50,6 +50,9 @@ set(KERNEL_SOURCES
     Devices/HID/PS2KeyboardDevice.cpp
     Devices/HID/PS2KeyboardDevice.cpp
     Devices/HID/PS2MouseDevice.cpp
     Devices/HID/PS2MouseDevice.cpp
     Devices/HID/VMWareMouseDevice.cpp
     Devices/HID/VMWareMouseDevice.cpp
+    Graphics/Console/FramebufferConsole.cpp
+    Graphics/Console/TextModeConsole.cpp
+    Graphics/Console/VGAConsole.cpp
     Graphics/BochsFramebufferDevice.cpp
     Graphics/BochsFramebufferDevice.cpp
     Graphics/BochsGraphicsAdapter.cpp
     Graphics/BochsGraphicsAdapter.cpp
     Graphics/FramebufferDevice.cpp
     Graphics/FramebufferDevice.cpp
@@ -205,6 +208,7 @@ set(KERNEL_SOURCES
     Syscalls/waitid.cpp
     Syscalls/waitid.cpp
     Syscalls/inode_watcher.cpp
     Syscalls/inode_watcher.cpp
     Syscalls/write.cpp
     Syscalls/write.cpp
+    TTY/ConsoleManagement.cpp
     TTY/MasterPTY.cpp
     TTY/MasterPTY.cpp
     TTY/PTYMultiplexer.cpp
     TTY/PTYMultiplexer.cpp
     TTY/SlavePTY.cpp
     TTY/SlavePTY.cpp

+ 4 - 4
Kernel/CommandLine.cpp

@@ -171,8 +171,8 @@ UNMAP_AFTER_INIT AHCIResetMode CommandLine::ahci_reset_mode() const
 UNMAP_AFTER_INIT BootMode CommandLine::boot_mode() const
 UNMAP_AFTER_INIT BootMode CommandLine::boot_mode() const
 {
 {
     const auto boot_mode = lookup("boot_mode").value_or("graphical");
     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") {
     } else if (boot_mode == "self-test") {
         return BootMode::SelfTest;
         return BootMode::SelfTest;
     } else if (boot_mode == "graphical") {
     } else if (boot_mode == "graphical") {
@@ -181,10 +181,10 @@ UNMAP_AFTER_INIT BootMode CommandLine::boot_mode() const
     PANIC("Unknown BootMode: {}", boot_mode);
     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();
     const auto mode = boot_mode();
-    return mode == BootMode::Text || mode == BootMode::SelfTest;
+    return mode == BootMode::NoFramebufferDevices || mode == BootMode::SelfTest;
 }
 }
 
 
 String CommandLine::userspace_init() const
 String CommandLine::userspace_init() const

+ 2 - 2
Kernel/CommandLine.h

@@ -14,7 +14,7 @@
 namespace Kernel {
 namespace Kernel {
 
 
 enum class BootMode {
 enum class BootMode {
-    Text,
+    NoFramebufferDevices,
     SelfTest,
     SelfTest,
     Graphical
     Graphical
 };
 };
@@ -59,7 +59,7 @@ public:
     [[nodiscard]] bool is_vmmouse_enabled() const;
     [[nodiscard]] bool is_vmmouse_enabled() const;
     [[nodiscard]] PCIAccessLevel pci_access_level() const;
     [[nodiscard]] PCIAccessLevel pci_access_level() const;
     [[nodiscard]] bool is_legacy_time_enabled() 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]] bool is_force_pio() const;
     [[nodiscard]] AcpiFeatureLevel acpi_feature_level() const;
     [[nodiscard]] AcpiFeatureLevel acpi_feature_level() const;
     [[nodiscard]] BootMode boot_mode() const;
     [[nodiscard]] BootMode boot_mode() const;

+ 4 - 0
Kernel/Debug.h.in

@@ -306,6 +306,10 @@
 #cmakedefine01 VIRTIO_DEBUG
 #cmakedefine01 VIRTIO_DEBUG
 #endif
 #endif
 
 
+#ifndef VIRTUAL_CONSOLE_DEBUG
+#cmakedefine01 VIRTUAL_CONSOLE_DEBUG
+#endif
+
 #ifndef VRA_DEBUG
 #ifndef VRA_DEBUG
 #cmakedefine01 VRA_DEBUG
 #cmakedefine01 VRA_DEBUG
 #endif
 #endif

+ 2 - 2
Kernel/Devices/HID/PS2KeyboardDevice.cpp

@@ -15,7 +15,7 @@
 #include <Kernel/Devices/HID/HIDManagement.h>
 #include <Kernel/Devices/HID/HIDManagement.h>
 #include <Kernel/Devices/HID/PS2KeyboardDevice.h>
 #include <Kernel/Devices/HID/PS2KeyboardDevice.h>
 #include <Kernel/IO.h>
 #include <Kernel/IO.h>
-#include <Kernel/TTY/VirtualConsole.h>
+#include <Kernel/TTY/ConsoleManagement.h>
 
 
 namespace Kernel {
 namespace Kernel {
 
 
@@ -69,7 +69,7 @@ void PS2KeyboardDevice::irq_handle_byte_read(u8 byte)
         if (m_modifiers & Mod_Alt) {
         if (m_modifiers & Mod_Alt) {
             switch (ch) {
             switch (ch) {
             case 0x02 ... 0x07: // 1 to 6
             case 0x02 ... 0x07: // 1 to 6
-                VirtualConsole::switch_to(ch - 0x02);
+                ConsoleManagement::the().switch_to(ch - 0x02);
                 break;
                 break;
             default:
             default:
                 key_state_changed(ch, pressed);
                 key_state_changed(ch, pressed);

+ 34 - 2
Kernel/Graphics/BochsGraphicsAdapter.cpp

@@ -11,6 +11,8 @@
 #include <Kernel/Graphics/Bochs.h>
 #include <Kernel/Graphics/Bochs.h>
 #include <Kernel/Graphics/BochsFramebufferDevice.h>
 #include <Kernel/Graphics/BochsFramebufferDevice.h>
 #include <Kernel/Graphics/BochsGraphicsAdapter.h>
 #include <Kernel/Graphics/BochsGraphicsAdapter.h>
+#include <Kernel/Graphics/Console/FramebufferConsole.h>
+#include <Kernel/Graphics/GraphicsManagement.h>
 #include <Kernel/IO.h>
 #include <Kernel/IO.h>
 #include <Kernel/PCI/Access.h>
 #include <Kernel/PCI/Access.h>
 #include <Kernel/Process.h>
 #include <Kernel/Process.h>
@@ -52,19 +54,24 @@ UNMAP_AFTER_INIT BochsGraphicsAdapter::BochsGraphicsAdapter(PCI::Address pci_add
     , m_mmio_registers(PCI::get_BAR2(pci_address) & 0xfffffff0)
     , m_mmio_registers(PCI::get_BAR2(pci_address) & 0xfffffff0)
 {
 {
     set_safe_resolution();
     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()
 UNMAP_AFTER_INIT void BochsGraphicsAdapter::initialize_framebuffer_devices()
 {
 {
     // FIXME: Find a better way to determine default resolution...
     // 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
 GraphicsDevice::Type BochsGraphicsAdapter::type() const
 {
 {
     if (PCI::get_class(pci_address()) == 0x3 && PCI::get_subclass(pci_address()) == 0x0)
     if (PCI::get_class(pci_address()) == 0x3 && PCI::get_subclass(pci_address()) == 0x0)
         return Type::VGACompatible;
         return Type::VGACompatible;
-    return Type::Bochs; 
+    return Type::Bochs;
 }
 }
 
 
 void BochsGraphicsAdapter::set_safe_resolution()
 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)
 void BochsGraphicsAdapter::set_y_offset(size_t y_offset)
 {
 {
+    if (m_console_enabled)
+        return;
     auto registers = map_typed_writable<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
     auto registers = map_typed_writable<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
     registers->bochs_regs.y_offset = y_offset;
     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<volatile BochsDisplayMMIORegisters>(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<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
+    registers->bochs_regs.y_offset = 0;
+    m_framebuffer_console->disable();
+    m_framebuffer_device->activate_writes();
+}
+
 }
 }

+ 9 - 1
Kernel/Graphics/BochsGraphicsAdapter.h

@@ -8,6 +8,7 @@
 
 
 #include <AK/String.h>
 #include <AK/String.h>
 #include <AK/Types.h>
 #include <AK/Types.h>
+#include <Kernel/Graphics/Console/FramebufferConsole.h>
 #include <Kernel/Graphics/GraphicsDevice.h>
 #include <Kernel/Graphics/GraphicsDevice.h>
 #include <Kernel/PCI/DeviceController.h>
 #include <Kernel/PCI/DeviceController.h>
 #include <Kernel/PhysicalAddress.h>
 #include <Kernel/PhysicalAddress.h>
@@ -25,12 +26,16 @@ class BochsGraphicsAdapter final : public GraphicsDevice
 public:
 public:
     static NonnullRefPtr<BochsGraphicsAdapter> initialize(PCI::Address);
     static NonnullRefPtr<BochsGraphicsAdapter> initialize(PCI::Address);
     virtual ~BochsGraphicsAdapter() = default;
     virtual ~BochsGraphicsAdapter() = default;
+    virtual bool framebuffer_devices_initialized() const override { return !m_framebuffer_device.is_null(); }
 
 
 private:
 private:
     // ^GraphicsDevice
     // ^GraphicsDevice
     virtual void initialize_framebuffer_devices() override;
     virtual void initialize_framebuffer_devices() override;
     virtual Type type() const override;
     virtual Type type() const override;
 
 
+    virtual void enable_consoles() override;
+    virtual void disable_consoles() override;
+
     explicit BochsGraphicsAdapter(PCI::Address);
     explicit BochsGraphicsAdapter(PCI::Address);
 
 
     void set_safe_resolution();
     void set_safe_resolution();
@@ -43,7 +48,10 @@ private:
     void set_y_offset(size_t);
     void set_y_offset(size_t);
 
 
     PhysicalAddress m_mmio_registers;
     PhysicalAddress m_mmio_registers;
-    RefPtr<BochsFramebufferDevice> m_framebuffer;
+    RefPtr<BochsFramebufferDevice> m_framebuffer_device;
+    RefPtr<Graphics::FramebufferConsole> m_framebuffer_console;
+    SpinLock<u8> m_console_mode_switch_lock;
+    bool m_console_enabled { false };
 };
 };
 
 
 }
 }

+ 82 - 0
Kernel/Graphics/Console/Console.h

@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <AK/String.h>
+#include <AK/Types.h>
+#include <Kernel/Graphics/GraphicsDevice.h>
+
+namespace Kernel::Graphics {
+
+class Console : public RefCounted<Console> {
+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<bool> 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 };
+};
+}

+ 348 - 0
Kernel/Graphics/Console/FramebufferConsole.cpp

@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Graphics/Console/FramebufferConsole.h>
+
+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> 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);
+}
+
+}

+ 50 - 0
Kernel/Graphics/Console/FramebufferConsole.h

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <AK/Types.h>
+#include <Kernel/Graphics/Console/Console.h>
+#include <Kernel/PhysicalAddress.h>
+
+namespace Kernel::Graphics {
+class FramebufferConsole final : public Console {
+public:
+    static NonnullRefPtr<FramebufferConsole> 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<Region> m_framebuffer_region;
+    PhysicalAddress m_framebuffer_address;
+    size_t m_pitch;
+    mutable SpinLock<u8> m_lock;
+};
+}

+ 203 - 0
Kernel/Graphics/Console/TextModeConsole.cpp

@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Graphics/Console/TextModeConsole.h>
+#include <Kernel/Graphics/GraphicsManagement.h>
+#include <Kernel/IO.h>
+
+namespace Kernel::Graphics {
+
+UNMAP_AFTER_INIT NonnullRefPtr<TextModeConsole> 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);
+}
+
+}

+ 50 - 0
Kernel/Graphics/Console/TextModeConsole.h

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <AK/Types.h>
+#include <Kernel/Graphics/Console/VGAConsole.h>
+#include <Kernel/SpinLock.h>
+
+namespace Kernel::Graphics {
+class TextModeConsole final : public VGAConsole {
+public:
+    static NonnullRefPtr<TextModeConsole> 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<u8> 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 };
+};
+}

+ 19 - 0
Kernel/Graphics/Console/VGAConsole.cpp

@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Graphics/Console/VGAConsole.h>
+
+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)
+{
+}
+
+}

+ 39 - 0
Kernel/Graphics/Console/VGAConsole.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <AK/Types.h>
+#include <Kernel/Graphics/Console/Console.h>
+#include <Kernel/Graphics/VGACompatibleAdapter.h>
+
+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<VGAConsole> 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<Region> m_vga_region;
+    NonnullRefPtr<VGACompatibleAdapter> m_adapter;
+    const Mode m_mode;
+};
+}

+ 55 - 2
Kernel/Graphics/FramebufferDevice.cpp

@@ -16,6 +16,8 @@
 #include <LibC/errno_numbers.h>
 #include <LibC/errno_numbers.h>
 #include <LibC/sys/ioctl_numbers.h>
 #include <LibC/sys/ioctl_numbers.h>
 
 
+#include <Kernel/Panic.h>
+
 namespace Kernel {
 namespace Kernel {
 
 
 KResultOr<Region*> FramebufferDevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
 KResultOr<Region*> FramebufferDevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
@@ -28,16 +30,51 @@ KResultOr<Region*> FramebufferDevice::mmap(Process& process, FileDescription&, c
     if (range.size() != page_round_up(framebuffer_size_in_bytes()))
     if (range.size() != page_round_up(framebuffer_size_in_bytes()))
         return EOVERFLOW;
         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)
     if (!vmobject)
         return ENOMEM;
         return ENOMEM;
-    return process.space().allocate_region_with_vmobject(
+    m_userspace_real_framebuffer_vmobject = vmobject;
+
+    auto result = process.space().allocate_region_with_vmobject(
         range,
         range,
         vmobject.release_nonnull(),
         vmobject.release_nonnull(),
         0,
         0,
         "Framebuffer",
         "Framebuffer",
         prot,
         prot,
         shared);
         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
 String FramebufferDevice::device_name() const
@@ -45,6 +82,18 @@ String FramebufferDevice::device_name() const
     return String::formatted("fb{}", minor());
     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)
 UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size_t pitch, size_t width, size_t height)
     : BlockDevice(29, GraphicsManagement::the().current_minor_number())
     : BlockDevice(29, GraphicsManagement::the().current_minor_number())
     , m_framebuffer_address(addr)
     , m_framebuffer_address(addr)
@@ -52,6 +101,10 @@ UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size
     , m_framebuffer_width(width)
     , m_framebuffer_width(width)
     , m_framebuffer_height(height)
     , 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);
     dbgln("Framebuffer {}: address={}, pitch={}, width={}, height={}", minor(), addr, pitch, width, height);
 }
 }
 
 

+ 17 - 0
Kernel/Graphics/FramebufferDevice.h

@@ -6,11 +6,14 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AK/NonnullOwnPtr.h>
 #include <AK/String.h>
 #include <AK/String.h>
 #include <AK/Types.h>
 #include <AK/Types.h>
 #include <Kernel/Devices/BlockDevice.h>
 #include <Kernel/Devices/BlockDevice.h>
 #include <Kernel/Graphics/GraphicsDevice.h>
 #include <Kernel/Graphics/GraphicsDevice.h>
 #include <Kernel/PhysicalAddress.h>
 #include <Kernel/PhysicalAddress.h>
+#include <Kernel/SpinLock.h>
+#include <Kernel/VM/AnonymousVMObject.h>
 
 
 namespace Kernel {
 namespace Kernel {
 
 
@@ -24,9 +27,12 @@ public:
     virtual mode_t required_mode() const override { return 0660; }
     virtual mode_t required_mode() const override { return 0660; }
     virtual String device_name() const override;
     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 size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height; }
 
 
     virtual ~FramebufferDevice() {};
     virtual ~FramebufferDevice() {};
+    void initialize();
 
 
 protected:
 protected:
     virtual bool set_resolution(size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch);
     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_pitch { 0 };
     size_t m_framebuffer_width { 0 };
     size_t m_framebuffer_width { 0 };
     size_t m_framebuffer_height { 0 };
     size_t m_framebuffer_height { 0 };
+
+private:
+    SpinLock<u8> m_activation_lock;
+
+    RefPtr<AnonymousVMObject> m_real_framebuffer_vmobject;
+    RefPtr<AnonymousVMObject> m_swapped_framebuffer_vmobject;
+    OwnPtr<Region> m_real_framebuffer_region;
+    OwnPtr<Region> m_swapped_framebuffer_region;
+
+    RefPtr<AnonymousVMObject> m_userspace_real_framebuffer_vmobject;
+    Region* m_userspace_framebuffer_region { nullptr };
 };
 };
 
 
 }
 }

+ 6 - 0
Kernel/Graphics/GraphicsDevice.h

@@ -25,9 +25,15 @@ public:
     virtual ~GraphicsDevice() = default;
     virtual ~GraphicsDevice() = default;
     virtual void initialize_framebuffer_devices() = 0;
     virtual void initialize_framebuffer_devices() = 0;
     virtual Type type() const = 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:
 protected:
     GraphicsDevice() = default;
     GraphicsDevice() = default;
+
+    bool m_consoles_enabled { false };
 };
 };
 
 
 }
 }

+ 92 - 12
Kernel/Graphics/GraphicsManagement.cpp

@@ -9,10 +9,14 @@
 #include <Kernel/CommandLine.h>
 #include <Kernel/CommandLine.h>
 #include <Kernel/Debug.h>
 #include <Kernel/Debug.h>
 #include <Kernel/Graphics/BochsGraphicsAdapter.h>
 #include <Kernel/Graphics/BochsGraphicsAdapter.h>
+#include <Kernel/Graphics/Console/FramebufferConsole.h>
+#include <Kernel/Graphics/Console/TextModeConsole.h>
 #include <Kernel/Graphics/GraphicsManagement.h>
 #include <Kernel/Graphics/GraphicsManagement.h>
 #include <Kernel/Graphics/IntelNativeGraphicsAdapter.h>
 #include <Kernel/Graphics/IntelNativeGraphicsAdapter.h>
 #include <Kernel/Graphics/VGACompatibleAdapter.h>
 #include <Kernel/Graphics/VGACompatibleAdapter.h>
+#include <Kernel/IO.h>
 #include <Kernel/Multiboot.h>
 #include <Kernel/Multiboot.h>
+#include <Kernel/VM/AnonymousVMObject.h>
 
 
 namespace Kernel {
 namespace Kernel {
 
 
@@ -29,10 +33,24 @@ bool GraphicsManagement::is_initialized()
 }
 }
 
 
 UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement()
 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<GraphicsDevice> GraphicsManagement::determine_graphics_device(PCI::Address address, PCI::ID id) const
 UNMAP_AFTER_INIT RefPtr<GraphicsDevice> 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)) {
     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<GraphicsDevice> GraphicsManagement::determine_graphics_d
             if (!adapter.is_null())
             if (!adapter.is_null())
                 return adapter;
                 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 {};
     return {};
 }
 }
 
 
 UNMAP_AFTER_INIT bool GraphicsManagement::initialize()
 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) {
     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);
         auto adapter = determine_graphics_device(address, id);
         if (!adapter)
         if (!adapter)
             return;
             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;
     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;
+}
 }
 }

+ 27 - 3
Kernel/Graphics/GraphicsManagement.h

@@ -6,16 +6,26 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AK/NonnullOwnPtr.h>
 #include <AK/NonnullRefPtr.h>
 #include <AK/NonnullRefPtr.h>
 #include <AK/NonnullRefPtrVector.h>
 #include <AK/NonnullRefPtrVector.h>
 #include <AK/Types.h>
 #include <AK/Types.h>
+#include <Kernel/Graphics/Console/Console.h>
 #include <Kernel/Graphics/GraphicsDevice.h>
 #include <Kernel/Graphics/GraphicsDevice.h>
+#include <Kernel/Graphics/VGACompatibleAdapter.h>
 #include <Kernel/PCI/Definitions.h>
 #include <Kernel/PCI/Definitions.h>
+#include <Kernel/VM/Region.h>
 
 
 namespace Kernel {
 namespace Kernel {
 
 
+class BochsGraphicsAdapter;
+class IntelNativeGraphicsAdapter;
+class VGACompatibleAdapter;
 class GraphicsManagement {
 class GraphicsManagement {
-    AK_MAKE_ETERNAL;
+    friend class BochsGraphicsAdapter;
+    friend class IntelNativeGraphicsAdapter;
+    friend class VGACompatibleAdapter;
+    AK_MAKE_ETERNAL
 
 
 public:
 public:
     static GraphicsManagement& the();
     static GraphicsManagement& the();
@@ -25,14 +35,28 @@ public:
     unsigned current_minor_number() { return m_current_minor_number++; };
     unsigned current_minor_number() { return m_current_minor_number++; };
     GraphicsManagement();
     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<u8>& main_vga_lock() { return m_main_vga_lock; }
+    RefPtr<Graphics::Console> console() const { return m_console; }
+
+    void deactivate_graphical_mode();
+    void activate_graphical_mode();
 
 
 private:
 private:
     RefPtr<GraphicsDevice> determine_graphics_device(PCI::Address address, PCI::ID id) const;
     RefPtr<GraphicsDevice> determine_graphics_device(PCI::Address address, PCI::ID id) const;
 
 
     NonnullRefPtrVector<GraphicsDevice> m_graphics_devices;
     NonnullRefPtrVector<GraphicsDevice> m_graphics_devices;
+    NonnullOwnPtr<Region> m_vga_font_region;
+    RefPtr<Graphics::Console> m_console;
+
+    // Note: there could be multiple VGA adapters, but only one can operate in VGA mode
+    RefPtr<VGACompatibleAdapter> m_vga_adapter;
     unsigned m_current_minor_number { 0 };
     unsigned m_current_minor_number { 0 };
-    bool m_textmode_enabled;
+    const bool m_framebuffer_devices_allowed;
+
+    SpinLock<u8> m_main_vga_lock;
 };
 };
 
 
 }
 }

+ 34 - 8
Kernel/Graphics/IntelNativeGraphicsAdapter.cpp

@@ -4,7 +4,9 @@
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
+#include <Kernel/Graphics/Console/FramebufferConsole.h>
 #include <Kernel/Graphics/Definitions.h>
 #include <Kernel/Graphics/Definitions.h>
+#include <Kernel/Graphics/GraphicsManagement.h>
 #include <Kernel/Graphics/IntelNativeGraphicsAdapter.h>
 #include <Kernel/Graphics/IntelNativeGraphicsAdapter.h>
 #include <Kernel/IO.h>
 #include <Kernel/IO.h>
 #include <Kernel/PhysicalAddress.h>
 #include <Kernel/PhysicalAddress.h>
@@ -165,7 +167,7 @@ Optional<IntelNativeGraphicsAdapter::PLLSettings> IntelNativeGraphicsAdapter::cr
 }
 }
 
 
 IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
 IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
-    : PCI::DeviceController(address)
+    : VGACompatibleAdapter(address)
     , m_registers(PCI::get_BAR0(address) & 0xfffffffc)
     , m_registers(PCI::get_BAR0(address) & 0xfffffffc)
     , m_framebuffer_addr(PCI::get_BAR2(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);
         set_gmbus_pin_pair(GMBusPinPair::DedicatedAnalog);
     }
     }
     gmbus_read_edid();
     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);
     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)
 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());
     VERIFY(m_control_lock.is_locked());
     return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30);
     return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30);
 }
 }
+
 bool IntelNativeGraphicsAdapter::pipe_b_enabled() const
 bool IntelNativeGraphicsAdapter::pipe_b_enabled() const
 {
 {
     VERIFY(m_control_lock.is_locked());
     VERIFY(m_control_lock.is_locked());
@@ -396,7 +413,7 @@ bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height)
         VERIFY_NOT_REACHED();
         VERIFY_NOT_REACHED();
     auto settings = pll_settings.value();
     auto settings = pll_settings.value();
     dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
     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);
     set_display_timings(modesetting);
     auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
     auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
     VERIFY(!address.is_null());
     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_width = width;
     m_framebuffer_height = height;
     m_framebuffer_height = height;
-    m_framebuffer_stride = width * 4;
+    m_framebuffer_pitch = width * 4;
 
 
     return true;
     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));
     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_control_lock.is_locked());
     VERIFY(m_modeset_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::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);
     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);
     IO::delay(200);
 
 
@@ -597,9 +622,10 @@ void IntelNativeGraphicsAdapter::initialize_framebuffer_devices()
 {
 {
     auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
     auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
     VERIFY(!address.is_null());
     VERIFY(!address.is_null());
-    VERIFY(m_framebuffer_stride != 0);
+    VERIFY(m_framebuffer_pitch != 0);
     VERIFY(m_framebuffer_height != 0);
     VERIFY(m_framebuffer_height != 0);
     VERIFY(m_framebuffer_width != 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();
 }
 }
 }
 }

+ 6 - 12
Kernel/Graphics/IntelNativeGraphicsAdapter.h

@@ -9,8 +9,8 @@
 #include <AK/String.h>
 #include <AK/String.h>
 #include <AK/Types.h>
 #include <AK/Types.h>
 #include <Kernel/Graphics/Definitions.h>
 #include <Kernel/Graphics/Definitions.h>
-#include <Kernel/Graphics/GraphicsDevice.h>
 #include <Kernel/Graphics/RawFramebufferDevice.h>
 #include <Kernel/Graphics/RawFramebufferDevice.h>
+#include <Kernel/Graphics/VGACompatibleAdapter.h>
 #include <Kernel/PCI/DeviceController.h>
 #include <Kernel/PCI/DeviceController.h>
 #include <Kernel/PhysicalAddress.h>
 #include <Kernel/PhysicalAddress.h>
 
 
@@ -47,8 +47,7 @@ enum RegisterIndex {
 }
 }
 
 
 class IntelNativeGraphicsAdapter final
 class IntelNativeGraphicsAdapter final
-    : public GraphicsDevice
-    , public PCI::DeviceController {
+    : public VGACompatibleAdapter {
     AK_MAKE_ETERNAL
     AK_MAKE_ETERNAL
 public:
 public:
     struct PLLSettings {
     struct PLLSettings {
@@ -129,6 +128,7 @@ private:
     void enable_output(PhysicalAddress fb_address, size_t width);
     void enable_output(PhysicalAddress fb_address, size_t width);
 
 
     void disable_vga_emulation();
     void disable_vga_emulation();
+    void enable_vga_plane();
 
 
     void disable_dac_output();
     void disable_dac_output();
     void enable_dac_output();
     void enable_dac_output();
@@ -138,7 +138,9 @@ private:
     void disable_pipe_b();
     void disable_pipe_b();
     void disable_dpll();
     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 set_display_timings(const Graphics::Modesetting&);
     void enable_pipe_a();
     void enable_pipe_a();
     void set_framebuffer_parameters(size_t, size_t);
     void set_framebuffer_parameters(size_t, size_t);
@@ -166,15 +168,7 @@ private:
     Graphics::VideoInfoBlock m_crt_edid;
     Graphics::VideoInfoBlock m_crt_edid;
     const PhysicalAddress m_registers;
     const PhysicalAddress m_registers;
     const PhysicalAddress m_framebuffer_addr;
     const PhysicalAddress m_framebuffer_addr;
-
     OwnPtr<Region> m_registers_region;
     OwnPtr<Region> m_registers_region;
-
-    size_t m_framebuffer_width { 0 };
-    size_t m_framebuffer_height { 0 };
-    size_t m_framebuffer_stride { 0 };
-
-protected:
-    RefPtr<RawFramebufferDevice> m_framebuffer;
 };
 };
 
 
 }
 }

+ 41 - 1
Kernel/Graphics/VGACompatibleAdapter.cpp

@@ -4,7 +4,11 @@
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
+#include <Kernel/Graphics/Console/FramebufferConsole.h>
+#include <Kernel/Graphics/Console/TextModeConsole.h>
+#include <Kernel/Graphics/GraphicsManagement.h>
 #include <Kernel/Graphics/VGACompatibleAdapter.h>
 #include <Kernel/Graphics/VGACompatibleAdapter.h>
+#include <Kernel/IO.h>
 
 
 namespace Kernel {
 namespace Kernel {
 
 
@@ -13,19 +17,55 @@ UNMAP_AFTER_INIT NonnullRefPtr<VGACompatibleAdapter> VGACompatibleAdapter::initi
     return adopt_ref(*new VGACompatibleAdapter(address, m_framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch));
     return adopt_ref(*new VGACompatibleAdapter(address, m_framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch));
 }
 }
 
 
+UNMAP_AFTER_INIT NonnullRefPtr<VGACompatibleAdapter> VGACompatibleAdapter::initialize(PCI::Address address)
+{
+    return adopt_ref(*new VGACompatibleAdapter(address));
+}
+
 UNMAP_AFTER_INIT void VGACompatibleAdapter::initialize_framebuffer_devices()
 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)
 UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address)
     : PCI::DeviceController(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)
 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)
     : 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_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()
 {
 {
-    m_framebuffer = RawFramebufferDevice::create(*this, framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch);
+    VERIFY(m_framebuffer_device);
+    VERIFY(m_framebuffer_console);
+    m_framebuffer_console->disable();
+    m_framebuffer_device->activate_writes();
 }
 }
 
 
 }
 }

+ 15 - 2
Kernel/Graphics/VGACompatibleAdapter.h

@@ -8,6 +8,7 @@
 
 
 #include <AK/String.h>
 #include <AK/String.h>
 #include <AK/Types.h>
 #include <AK/Types.h>
+#include <Kernel/Graphics/Console/Console.h>
 #include <Kernel/Graphics/GraphicsDevice.h>
 #include <Kernel/Graphics/GraphicsDevice.h>
 #include <Kernel/Graphics/RawFramebufferDevice.h>
 #include <Kernel/Graphics/RawFramebufferDevice.h>
 #include <Kernel/PCI/DeviceController.h>
 #include <Kernel/PCI/DeviceController.h>
@@ -15,11 +16,14 @@
 
 
 namespace Kernel {
 namespace Kernel {
 
 
-class VGACompatibleAdapter final : public GraphicsDevice
+class VGACompatibleAdapter : public GraphicsDevice
     , public PCI::DeviceController {
     , public PCI::DeviceController {
     AK_MAKE_ETERNAL
     AK_MAKE_ETERNAL
 public:
 public:
     static NonnullRefPtr<VGACompatibleAdapter> initialize_with_preset_resolution(PCI::Address, PhysicalAddress, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch);
     static NonnullRefPtr<VGACompatibleAdapter> initialize_with_preset_resolution(PCI::Address, PhysicalAddress, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch);
+    static NonnullRefPtr<VGACompatibleAdapter> initialize(PCI::Address);
+
+    virtual bool framebuffer_devices_initialized() const override { return !m_framebuffer_device.is_null(); }
 
 
 protected:
 protected:
     explicit VGACompatibleAdapter(PCI::Address);
     explicit VGACompatibleAdapter(PCI::Address);
@@ -31,8 +35,17 @@ private:
     virtual void initialize_framebuffer_devices() override;
     virtual void initialize_framebuffer_devices() override;
     virtual Type type() const override { return Type::VGACompatible; }
     virtual Type type() const override { return Type::VGACompatible; }
 
 
+    virtual void enable_consoles() override;
+    virtual void disable_consoles() override;
+
 protected:
 protected:
-    RefPtr<RawFramebufferDevice> 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<RawFramebufferDevice> m_framebuffer_device;
+    RefPtr<Graphics::Console> m_framebuffer_console;
 };
 };
 
 
 }
 }

+ 1 - 1
Kernel/Panic.cpp

@@ -13,7 +13,7 @@ namespace Kernel {
 
 
 void __panic(const char* file, unsigned int line, const char* function)
 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();
     dump_backtrace();
     Processor::halt();
     Processor::halt();
 }
 }

+ 2 - 2
Kernel/Panic.h

@@ -12,8 +12,8 @@ namespace Kernel {
 
 
 #define PANIC(...)                                        \
 #define PANIC(...)                                        \
     do {                                                  \
     do {                                                  \
-        dmesgln("KERNEL PANIC! :^(");                     \
-        dmesgln(__VA_ARGS__);                             \
+        critical_dmesgln("KERNEL PANIC! :^(");            \
+        critical_dmesgln(__VA_ARGS__);                    \
         __panic(__FILE__, __LINE__, __PRETTY_FUNCTION__); \
         __panic(__FILE__, __LINE__, __PRETTY_FUNCTION__); \
     } while (0)
     } while (0)
 
 

+ 72 - 0
Kernel/TTY/ConsoleManagement.cpp

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Singleton.h>
+#include <Kernel/Debug.h>
+#include <Kernel/Graphics/GraphicsManagement.h>
+#include <Kernel/TTY/ConsoleManagement.h>
+
+namespace Kernel {
+
+static AK::Singleton<ConsoleManagement> 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);
+}
+
+}

+ 41 - 0
Kernel/TTY/ConsoleManagement.h

@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtr.h>
+#include <AK/NonnullRefPtrVector.h>
+#include <AK/Types.h>
+#include <Kernel/TTY/VirtualConsole.h>
+
+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<VirtualConsole> first_tty() const { return m_consoles[0]; }
+    NonnullRefPtr<VirtualConsole> debug_tty() const { return m_consoles[1]; }
+
+    RecursiveSpinLock& tty_write_lock() { return m_tty_write_lock; }
+
+private:
+    NonnullRefPtrVector<VirtualConsole> m_consoles;
+    RefPtr<VirtualConsole> m_active_console;
+    SpinLock<u8> m_lock;
+    RecursiveSpinLock m_tty_write_lock;
+};
+
+};

+ 223 - 159
Kernel/TTY/VirtualConsole.cpp

@@ -1,128 +1,147 @@
 /*
 /*
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
  * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
  *
  *
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
 #include "VirtualConsole.h"
 #include "VirtualConsole.h"
+#include <AK/StdLibExtras.h>
 #include <AK/String.h>
 #include <AK/String.h>
 #include <Kernel/Arch/x86/CPU.h>
 #include <Kernel/Arch/x86/CPU.h>
+#include <Kernel/Debug.h>
 #include <Kernel/Devices/HID/HIDManagement.h>
 #include <Kernel/Devices/HID/HIDManagement.h>
+#include <Kernel/Graphics/GraphicsManagement.h>
 #include <Kernel/Heap/kmalloc.h>
 #include <Kernel/Heap/kmalloc.h>
 #include <Kernel/IO.h>
 #include <Kernel/IO.h>
 #include <Kernel/StdLib.h>
 #include <Kernel/StdLib.h>
+#include <Kernel/TTY/ConsoleManagement.h>
 
 
 namespace Kernel {
 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 VirtualConsole::set_graphical(bool graphical)
+void ConsoleImpl::clear()
+{
+    m_client.clear();
+}
+void ConsoleImpl::clear_including_history()
 {
 {
-    if (graphical)
-        set_vga_start_row(0);
-
-    m_graphical = graphical;
 }
 }
 
 
-UNMAP_AFTER_INIT VirtualConsole::VirtualConsole(const unsigned index)
-    : TTY(4, index)
-    , m_index(index)
-    , m_terminal(*this)
+void ConsoleImpl::set_size(u16 determined_columns, u16 determined_rows)
 {
 {
-    VERIFY(index < s_max_virtual_consoles);
+    VERIFY(determined_columns);
+    VERIFY(determined_rows);
 
 
-    m_tty_name = String::formatted("/dev/tty{}", m_index);
-    m_terminal.set_size(80, 25);
+    if (determined_columns == columns() && determined_rows == rows())
+        return;
 
 
-    s_consoles[index] = this;
-}
+    m_columns = determined_columns;
+    m_rows = determined_rows;
 
 
-UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole()
+    m_cursor_row = min<size_t>((int)m_cursor_row, rows() - 1);
+    m_cursor_column = min<size_t>((int)m_cursor_column, columns() - 1);
+    m_saved_cursor_row = min<size_t>((int)m_saved_cursor_row, rows() - 1);
+    m_saved_cursor_column = min<size_t>((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()
 {
 {
-    VERIFY_NOT_REACHED();
+    // NOTE: We have to invalidate the cursor first.
+    m_client.invalidate_cursor(m_cursor_row);
+    m_client.scroll_up();
 }
 }
-
-void VirtualConsole::switch_to(unsigned index)
+void ConsoleImpl::scroll_down()
 {
 {
-    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);
+}
+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;
     }
     }
-    dbgln("VC: Switch to {} ({})", index, s_consoles[index]);
-    s_active_console = index;
-    s_consoles[s_active_console]->set_active(true);
+    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_active(bool active)
+void VirtualConsole::set_graphical(bool graphical)
 {
 {
-    if (active == m_active)
-        return;
+    m_graphical = graphical;
+}
 
 
-    ScopedSpinLock lock(s_lock);
+UNMAP_AFTER_INIT NonnullRefPtr<VirtualConsole> VirtualConsole::create(size_t index)
+{
+    return adopt_ref(*new VirtualConsole(index));
+}
 
 
-    m_active = active;
+UNMAP_AFTER_INIT VirtualConsole::VirtualConsole(const unsigned index)
+    : TTY(4, index)
+    , m_index(index)
+    , m_console_impl(*this)
+{
+    m_tty_name = String::formatted("/dev/tty{}", m_index);
+    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());
 
 
-    if (active) {
-        set_vga_start_row(0);
-        HIDManagement::the().set_client(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);
 
 
-        m_terminal.m_need_full_flush = true;
-        flush_dirty_lines();
-    } else {
-        HIDManagement::the().set_client(nullptr);
+    // 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);
 }
 }
 
 
-enum class VGAColor : u8 {
-    Black = 0,
-    Blue,
-    Green,
-    Cyan,
-    Red,
-    Magenta,
-    Brown,
-    LightGray,
-    DarkGray,
-    BrightBlue,
-    BrightGreen,
-    BrightCyan,
-    BrightRed,
-    BrightMagenta,
-    Yellow,
-    White,
-};
+UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole()
+{
+    VERIFY_NOT_REACHED();
+}
 
 
 enum class ANSIColor : u8 {
 enum class ANSIColor : u8 {
     Black = 0,
     Black = 0,
@@ -144,60 +163,53 @@ enum class ANSIColor : u8 {
     __Count,
     __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) {
     switch (color) {
     case ANSIColor::Black:
     case ANSIColor::Black:
-        return VGAColor::Black;
+        return Graphics::Console::Color::Black;
     case ANSIColor::Red:
     case ANSIColor::Red:
-        return VGAColor::Red;
+        return Graphics::Console::Color::Red;
     case ANSIColor::Brown:
     case ANSIColor::Brown:
-        return VGAColor::Brown;
+        return Graphics::Console::Color::Brown;
     case ANSIColor::Blue:
     case ANSIColor::Blue:
-        return VGAColor::Blue;
+        return Graphics::Console::Color::Blue;
     case ANSIColor::Magenta:
     case ANSIColor::Magenta:
-        return VGAColor::Magenta;
+        return Graphics::Console::Color::Magenta;
     case ANSIColor::Green:
     case ANSIColor::Green:
-        return VGAColor::Green;
+        return Graphics::Console::Color::Green;
     case ANSIColor::Cyan:
     case ANSIColor::Cyan:
-        return VGAColor::Cyan;
+        return Graphics::Console::Color::Cyan;
     case ANSIColor::LightGray:
     case ANSIColor::LightGray:
-        return VGAColor::LightGray;
+        return Graphics::Console::Color::LightGray;
     case ANSIColor::DarkGray:
     case ANSIColor::DarkGray:
-        return VGAColor::DarkGray;
+        return Graphics::Console::Color::DarkGray;
     case ANSIColor::BrightRed:
     case ANSIColor::BrightRed:
-        return VGAColor::BrightRed;
+        return Graphics::Console::Color::BrightRed;
     case ANSIColor::BrightGreen:
     case ANSIColor::BrightGreen:
-        return VGAColor::BrightGreen;
+        return Graphics::Console::Color::BrightGreen;
     case ANSIColor::Yellow:
     case ANSIColor::Yellow:
-        return VGAColor::Yellow;
+        return Graphics::Console::Color::Yellow;
     case ANSIColor::BrightBlue:
     case ANSIColor::BrightBlue:
-        return VGAColor::BrightBlue;
+        return Graphics::Console::Color::BrightBlue;
     case ANSIColor::BrightMagenta:
     case ANSIColor::BrightMagenta:
-        return VGAColor::BrightMagenta;
+        return Graphics::Console::Color::BrightMagenta;
     case ANSIColor::BrightCyan:
     case ANSIColor::BrightCyan:
-        return VGAColor::BrightCyan;
+        return Graphics::Console::Color::BrightCyan;
     case ANSIColor::White:
     case ANSIColor::White:
-        return VGAColor::White;
+        return Graphics::Console::Color::White;
     default:
     default:
         VERIFY_NOT_REACHED();
         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++) {
     for (u8 i = 0; i < (u8)ANSIColor::__Count; i++) {
         if (xterm_colors[i] == color)
         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)
 void VirtualConsole::on_key_pressed(KeyEvent event)
@@ -209,26 +221,18 @@ void VirtualConsole::on_key_pressed(KeyEvent event)
     if (!event.is_press())
     if (!event.is_press())
         return;
         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]() {
     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)
 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) {
     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)
         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;
         return buffer_bytes;
     });
     });
     if (m_active)
     if (m_active)
@@ -238,52 +242,51 @@ ssize_t VirtualConsole::on_tty_write(const UserOrKernelBuffer& data, ssize_t siz
     return (ssize_t)result.value();
     return (ssize_t)result.value();
 }
 }
 
 
-void VirtualConsole::set_vga_start_row(u16 row)
-{
-    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));
-}
-
-static inline u8 attribute_to_vga(const VT::Attribute& attribute)
+void VirtualConsole::set_active(bool active)
 {
 {
-    u8 vga_attr = 0x07;
-
-    if (attribute.flags & VT::Attribute::Bold)
-        vga_attr |= 0x08;
+    VERIFY(ConsoleManagement::the().m_lock.is_locked());
+    VERIFY(m_active != active);
+    m_active = active;
 
 
-    // Background color
-    vga_attr &= ~0x70;
-    vga_attr |= xterm_color_to_vga(attribute.effective_background_color()) << 8;
+    if (active) {
+        HIDManagement::the().set_client(this);
 
 
-    // Foreground color
-    vga_attr &= ~0x7;
-    vga_attr |= xterm_color_to_vga(attribute.effective_foreground_color());
+        m_console_impl.m_need_full_flush = true;
+        flush_dirty_lines();
+    } else {
+        HIDManagement::the().set_client(nullptr);
+    }
+}
 
 
-    return vga_attr;
+void VirtualConsole::emit_char(char ch)
+{
+    echo(ch);
 }
 }
 
 
 void VirtualConsole::flush_dirty_lines()
 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;
             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()
 void VirtualConsole::beep()
@@ -304,9 +307,8 @@ void VirtualConsole::set_window_progress(int, int)
 
 
 void VirtualConsole::terminal_did_resize(u16 columns, u16 rows)
 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()
 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;
+}
+
 }
 }

+ 83 - 13
Kernel/TTY/VirtualConsole.h

@@ -1,35 +1,91 @@
 /*
 /*
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
  *
  *
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
 #pragma once
 #pragma once
 
 
+#include <AK/Noncopyable.h>
+#include <AK/NonnullOwnPtrVector.h>
+#include <AK/String.h>
+#include <AK/Vector.h>
+#include <Kernel/API/KeyCode.h>
 #include <Kernel/ConsoleDevice.h>
 #include <Kernel/ConsoleDevice.h>
 #include <Kernel/Devices/HID/HIDManagement.h>
 #include <Kernel/Devices/HID/HIDManagement.h>
+#include <Kernel/Graphics/Console/Console.h>
 #include <Kernel/TTY/TTY.h>
 #include <Kernel/TTY/TTY.h>
+#include <LibVT/Attribute.h>
+#include <LibVT/Position.h>
 #include <LibVT/Terminal.h>
 #include <LibVT/Terminal.h>
 
 
 namespace Kernel {
 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
 class VirtualConsole final : public TTY
     , public KeyboardClient
     , public KeyboardClient
     , public VT::TerminalClient {
     , public VT::TerminalClient {
     AK_MAKE_ETERNAL
     AK_MAKE_ETERNAL
+    friend class ConsoleManagement;
+    friend class ConsoleImpl;
+    friend class VT::Terminal;
+
 public:
 public:
-    VirtualConsole(const unsigned index);
+    struct Line {
+        bool dirty;
+    };
+
+    struct Cell {
+        void clear()
+        {
+            ch = ' ';
+            attribute.reset();
+        }
+        char ch;
+        VT::Attribute attribute;
+    };
+
+public:
+    static NonnullRefPtr<VirtualConsole> create(size_t index);
+
     virtual ~VirtualConsole() override;
     virtual ~VirtualConsole() override;
 
 
-    static void switch_to(unsigned);
-    static void initialize();
+    size_t index() const { return m_index; }
 
 
     bool is_graphical() { return m_graphical; }
     bool is_graphical() { return m_graphical; }
     void set_graphical(bool graphical);
     void set_graphical(bool graphical);
 
 
+    void emit_char(char);
+
 private:
 private:
+    VirtualConsole(const unsigned index);
     // ^KeyboardClient
     // ^KeyboardClient
     virtual void on_key_pressed(KeyEvent) override;
     virtual void on_key_pressed(KeyEvent) override;
 
 
@@ -53,23 +109,37 @@ private:
     virtual String device_name() const override;
     virtual String device_name() const override;
 
 
     void set_active(bool);
     void set_active(bool);
-
-    void flush_vga_cursor();
     void flush_dirty_lines();
     void flush_dirty_lines();
 
 
     unsigned m_index;
     unsigned m_index;
     bool m_active { false };
     bool m_active { false };
     bool m_graphical { 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 };
+    String m_tty_name;
+    RecursiveSpinLock m_lock;
 
 
-    VT::Terminal m_terminal;
+private:
+    void invalidate_cursor(size_t row);
 
 
-    String m_tty_name;
+    void clear();
+
+    void inject_string(const StringView&);
+
+    Cell& cell_at(size_t column, size_t row);
+
+    typedef Vector<unsigned, 4> 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<Region> m_cells;
+    Vector<VirtualConsole::Line> m_lines;
+    ConsoleImpl m_console_impl;
 };
 };
 
 
 }
 }

+ 10 - 11
Kernel/init.cpp

@@ -45,6 +45,7 @@
 #include <Kernel/Random.h>
 #include <Kernel/Random.h>
 #include <Kernel/Scheduler.h>
 #include <Kernel/Scheduler.h>
 #include <Kernel/Storage/StorageManagement.h>
 #include <Kernel/Storage/StorageManagement.h>
+#include <Kernel/TTY/ConsoleManagement.h>
 #include <Kernel/TTY/PTYMultiplexer.h>
 #include <Kernel/TTY/PTYMultiplexer.h>
 #include <Kernel/TTY/VirtualConsole.h>
 #include <Kernel/TTY/VirtualConsole.h>
 #include <Kernel/Tasks/FinalizerTask.h>
 #include <Kernel/Tasks/FinalizerTask.h>
@@ -138,12 +139,13 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init()
     for (ctor_func_t* ctor = &start_ctors; ctor < &end_ctors; ctor++)
     for (ctor_func_t* ctor = &start_ctors; ctor < &end_ctors; ctor++)
         (*ctor)();
         (*ctor)();
 
 
+    ConsoleDevice::initialize();
+
     APIC::initialize();
     APIC::initialize();
     InterruptManagement::initialize();
     InterruptManagement::initialize();
     ACPI::initialize();
     ACPI::initialize();
 
 
     VFS::initialize();
     VFS::initialize();
-    ConsoleDevice::initialize();
 
 
     dmesgln("Starting SerenityOS...");
     dmesgln("Starting SerenityOS...");
 
 
@@ -160,12 +162,11 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init()
 
 
     VMWareBackdoor::the(); // don't wait until first mouse packet
     VMWareBackdoor::the(); // don't wait until first mouse packet
     HIDManagement::initialize();
     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();
     Thread::initialize();
     Process::initialize();
     Process::initialize();
@@ -230,11 +231,8 @@ void init_stage2(void*)
     SyncTask::spawn();
     SyncTask::spawn();
     FinalizerTask::spawn();
     FinalizerTask::spawn();
 
 
-    PCI::initialize();
     auto boot_profiling = kernel_command_line().is_boot_profiling_enabled();
     auto boot_profiling = kernel_command_line().is_boot_profiling_enabled();
 
 
-    GraphicsManagement::the().initialize();
-
     USB::UHCIController::detect();
     USB::UHCIController::detect();
 
 
     DMIExpose::initialize();
     DMIExpose::initialize();
@@ -274,7 +272,8 @@ void init_stage2(void*)
     int error;
     int error;
 
 
     // FIXME: It would be nicer to set the mode from userspace.
     // 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> thread;
     RefPtr<Thread> thread;
     auto userspace_init = kernel_command_line().userspace_init();
     auto userspace_init = kernel_command_line().userspace_init();
     auto init_args = kernel_command_line().userspace_init_args();
     auto init_args = kernel_command_line().userspace_init_args();

+ 29 - 0
Kernel/kprintf.cpp

@@ -7,9 +7,12 @@
 #include <AK/PrintfImplementation.h>
 #include <AK/PrintfImplementation.h>
 #include <AK/Types.h>
 #include <AK/Types.h>
 #include <Kernel/ConsoleDevice.h>
 #include <Kernel/ConsoleDevice.h>
+#include <Kernel/Graphics/Console/Console.h>
+#include <Kernel/Graphics/GraphicsManagement.h>
 #include <Kernel/IO.h>
 #include <Kernel/IO.h>
 #include <Kernel/Process.h>
 #include <Kernel/Process.h>
 #include <Kernel/SpinLock.h>
 #include <Kernel/SpinLock.h>
+#include <Kernel/TTY/ConsoleManagement.h>
 #include <Kernel/kstdio.h>
 #include <Kernel/kstdio.h>
 
 
 #include <LibC/stdarg.h>
 #include <LibC/stdarg.h>
@@ -60,6 +63,20 @@ static void serial_putch(char ch)
         was_cr = false;
         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)
 static void console_out(char ch)
 {
 {
     if (serial_debug)
     if (serial_debug)
@@ -72,6 +89,9 @@ static void console_out(char ch)
     } else {
     } else {
         IO::out8(0xe9, ch);
         IO::out8(0xe9, ch);
     }
     }
+    if (ConsoleManagement::is_initialized()) {
+        ConsoleManagement::the().debug_tty()->emit_char(ch);
+    }
 }
 }
 
 
 static void buffer_putch(char*& bufptr, 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)
     for (size_t i = 0; i < length; ++i)
         console_out(characters[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]);
+}

+ 1 - 0
Kernel/kstdio.h

@@ -11,6 +11,7 @@
 extern "C" {
 extern "C" {
 void dbgputstr(const char*, size_t);
 void dbgputstr(const char*, size_t);
 void kernelputstr(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)));
 int snprintf(char* buf, size_t, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
 void set_serial_debug(bool on_or_off);
 void set_serial_debug(bool on_or_off);
 int get_serial_debug();
 int get_serial_debug();

+ 1 - 0
Meta/CMake/all_the_debug_macros.cmake

@@ -16,6 +16,7 @@ set(BXVGA_DEBUG ON)
 set(PS2MOUSE_DEBUG ON)
 set(PS2MOUSE_DEBUG ON)
 set(MOUSE_DEBUG ON)
 set(MOUSE_DEBUG ON)
 set(VMWARE_BACKDOOR_DEBUG ON)
 set(VMWARE_BACKDOOR_DEBUG ON)
+set(VIRTUAL_CONSOLE_DEBUG ON)
 set(FILEDESCRIPTION_DEBUG ON)
 set(FILEDESCRIPTION_DEBUG ON)
 set(PROCFS_DEBUG ON)
 set(PROCFS_DEBUG ON)
 set(VFS_DEBUG ON)
 set(VFS_DEBUG ON)

+ 65 - 0
Userland/Libraries/LibVT/Attribute.h

@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Noncopyable.h>
+#include <AK/String.h>
+#include <AK/Vector.h>
+#include <LibVT/XtermColors.h>
+
+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);
+    }
+};
+
+}

+ 1 - 47
Userland/Libraries/LibVT/Line.h

@@ -9,57 +9,11 @@
 #include <AK/Noncopyable.h>
 #include <AK/Noncopyable.h>
 #include <AK/String.h>
 #include <AK/String.h>
 #include <AK/Vector.h>
 #include <AK/Vector.h>
+#include <LibVT/Attribute.h>
 #include <LibVT/XtermColors.h>
 #include <LibVT/XtermColors.h>
 
 
 namespace VT {
 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 {
 class Line {
     AK_MAKE_NONCOPYABLE(Line);
     AK_MAKE_NONCOPYABLE(Line);
     AK_MAKE_NONMOVABLE(Line);
     AK_MAKE_NONMOVABLE(Line);

+ 36 - 17
Userland/Libraries/LibVT/Terminal.cpp

@@ -9,19 +9,23 @@
 #include <AK/StringBuilder.h>
 #include <AK/StringBuilder.h>
 #include <AK/StringView.h>
 #include <AK/StringView.h>
 #include <LibVT/Terminal.h>
 #include <LibVT/Terminal.h>
+#ifdef KERNEL
+#    include <Kernel/TTY/VirtualConsole.h>
+#endif
 
 
 namespace VT {
 namespace VT {
 
 
+#ifndef KERNEL
 Terminal::Terminal(TerminalClient& client)
 Terminal::Terminal(TerminalClient& client)
+#else
+Terminal::Terminal(Kernel::VirtualConsole& client)
+#endif
     : m_client(client)
     : m_client(client)
     , m_parser(*this)
     , m_parser(*this)
 {
 {
 }
 }
 
 
-Terminal::~Terminal()
-{
-}
-
+#ifndef KERNEL
 void Terminal::clear()
 void Terminal::clear()
 {
 {
     for (size_t i = 0; i < rows(); ++i)
     for (size_t i = 0; i < rows(); ++i)
@@ -38,6 +42,7 @@ void Terminal::clear_including_history()
 
 
     m_client.terminal_history_changed();
     m_client.terminal_history_changed();
 }
 }
+#endif
 
 
 void Terminal::alter_mode(bool should_set, Parameters params, Intermediates intermediates)
 void Terminal::alter_mode(bool should_set, Parameters params, Intermediates intermediates)
 {
 {
@@ -445,6 +450,7 @@ void Terminal::SD(Parameters params)
         scroll_down();
         scroll_down();
 }
 }
 
 
+#ifndef KERNEL
 void Terminal::IL(Parameters params)
 void Terminal::IL(Parameters params)
 {
 {
     int count = 1;
     int count = 1;
@@ -461,12 +467,14 @@ void Terminal::IL(Parameters params)
 
 
     m_need_full_flush = true;
     m_need_full_flush = true;
 }
 }
+#endif
 
 
 void Terminal::DA(Parameters)
 void Terminal::DA(Parameters)
 {
 {
     emit_string("\033[?1;0c");
     emit_string("\033[?1;0c");
 }
 }
 
 
+#ifndef KERNEL
 void Terminal::DL(Parameters params)
 void Terminal::DL(Parameters params)
 {
 {
     int count = 1;
     int count = 1;
@@ -511,6 +519,7 @@ void Terminal::DCH(Parameters params)
 
 
     line.set_dirty(true);
     line.set_dirty(true);
 }
 }
+#endif
 
 
 void Terminal::newline()
 void Terminal::newline()
 {
 {
@@ -527,6 +536,7 @@ void Terminal::carriage_return()
     set_cursor(m_cursor_row, 0);
     set_cursor(m_cursor_row, 0);
 }
 }
 
 
+#ifndef KERNEL
 void Terminal::scroll_up()
 void Terminal::scroll_up()
 {
 {
     // NOTE: We have to invalidate the cursor first.
     // NOTE: We have to invalidate the cursor first.
@@ -550,6 +560,20 @@ void Terminal::scroll_down()
     m_need_full_flush = true;
     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)
 void Terminal::set_cursor(unsigned a_row, unsigned a_column)
 {
 {
     unsigned row = min(a_row, m_rows - 1u);
     unsigned row = min(a_row, m_rows - 1u);
@@ -565,19 +589,6 @@ void Terminal::set_cursor(unsigned a_row, unsigned a_column)
     invalidate_cursor();
     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()
 void Terminal::NEL()
 {
 {
     newline();
     newline();
@@ -607,6 +618,7 @@ void Terminal::DSR(Parameters params)
     }
     }
 }
 }
 
 
+#ifndef KERNEL
 void Terminal::ICH(Parameters params)
 void Terminal::ICH(Parameters params)
 {
 {
     int num = 0;
     int num = 0;
@@ -628,6 +640,7 @@ void Terminal::ICH(Parameters params)
 
 
     line.set_dirty(true);
     line.set_dirty(true);
 }
 }
+#endif
 
 
 void Terminal::on_input(u8 byte)
 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?
                 // Should we expose the raw OSC string from the parser? Or join by semicolon?
                 break;
                 break;
             case 8:
             case 8:
+#ifndef KERNEL
                 if (parameters.size() < 2) {
                 if (parameters.size() < 2) {
                     dbgln("Attempted to set href but gave too few parameters");
                     dbgln("Attempted to set href but gave too few parameters");
                 } else if (parameters[2].is_empty()) {
                 } 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
                     // FIXME: Respect the provided ID
                     m_current_attribute.href_id = String::number(m_next_href_id++);
                     m_current_attribute.href_id = String::number(m_next_href_id++);
                 }
                 }
+#endif
                 break;
                 break;
             case 9:
             case 9:
                 if (parameters.size() < 2 || parameters[1].is_empty() || parameters[2].is_empty())
                 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());
     dbgln("{}", builder.string_view());
 }
 }
 
 
+#ifndef KERNEL
 void Terminal::set_size(u16 columns, u16 rows)
 void Terminal::set_size(u16 columns, u16 rows)
 {
 {
     if (!columns)
     if (!columns)
@@ -1073,7 +1089,9 @@ void Terminal::set_size(u16 columns, u16 rows)
 
 
     m_client.terminal_did_resize(m_columns, m_rows);
     m_client.terminal_did_resize(m_columns, m_rows);
 }
 }
+#endif
 
 
+#ifndef KERNEL
 void Terminal::invalidate_cursor()
 void Terminal::invalidate_cursor()
 {
 {
     m_lines[m_cursor_row].set_dirty(true);
     m_lines[m_cursor_row].set_dirty(true);
@@ -1090,5 +1108,6 @@ Attribute Terminal::attribute_at(const Position& position) const
         return {};
         return {};
     return line.attribute_at(position.column());
     return line.attribute_at(position.column());
 }
 }
+#endif
 
 
 }
 }

+ 71 - 10
Userland/Libraries/LibVT/Terminal.h

@@ -12,9 +12,18 @@
 #include <AK/Vector.h>
 #include <AK/Vector.h>
 #include <Kernel/API/KeyCode.h>
 #include <Kernel/API/KeyCode.h>
 #include <LibVT/EscapeSequenceParser.h>
 #include <LibVT/EscapeSequenceParser.h>
-#include <LibVT/Line.h>
 #include <LibVT/Position.h>
 #include <LibVT/Position.h>
 
 
+#ifndef KERNEL
+#    include <LibVT/Attribute.h>
+#    include <LibVT/Line.h>
+#else
+namespace Kernel {
+class VirtualConsole;
+}
+#    include <LibVT/Attribute.h>
+#endif
+
 namespace VT {
 namespace VT {
 
 
 class TerminalClient {
 class TerminalClient {
@@ -31,24 +40,52 @@ public:
 
 
 class Terminal : public EscapeSequenceExecutor {
 class Terminal : public EscapeSequenceExecutor {
 public:
 public:
+#ifndef KERNEL
     explicit Terminal(TerminalClient&);
     explicit Terminal(TerminalClient&);
-    ~Terminal();
+#else
+    explicit Terminal(Kernel::VirtualConsole&);
+#endif
+
+    virtual ~Terminal()
+    {
+    }
 
 
     bool m_need_full_flush { false };
     bool m_need_full_flush { false };
 
 
+#ifndef KERNEL
     void invalidate_cursor();
     void invalidate_cursor();
+#else
+    virtual void invalidate_cursor() = 0;
+#endif
+
     void on_input(u8);
     void on_input(u8);
 
 
+    void set_cursor(unsigned row, unsigned column);
+
+#ifndef KERNEL
     void clear();
     void clear();
     void clear_including_history();
     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);
     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 rows() const { return m_rows; }
 
 
     u16 cursor_column() const { return m_cursor_column; }
     u16 cursor_column() const { return m_cursor_column; }
     u16 cursor_row() const { return m_cursor_row; }
     u16 cursor_row() const { return m_cursor_row; }
 
 
+#ifndef KERNEL
     size_t line_count() const
     size_t line_count() const
     {
     {
         return m_history.size() + m_lines.size();
         return m_history.size() + m_lines.size();
@@ -100,13 +137,16 @@ public:
         m_max_history_lines = value;
         m_max_history_lines = value;
     }
     }
     size_t history_size() const { return m_history.size(); }
     size_t history_size() const { return m_history.size(); }
+#endif
 
 
     void inject_string(const StringView&);
     void inject_string(const StringView&);
     void handle_key_press(KeyCode, u32, u8 flags);
     void handle_key_press(KeyCode, u32, u8 flags);
 
 
+#ifndef KERNEL
     Attribute attribute_at(const Position&) const;
     Attribute attribute_at(const Position&) const;
+#endif
 
 
-private:
+protected:
     // ^EscapeSequenceExecutor
     // ^EscapeSequenceExecutor
     virtual void emit_code_point(u32) override;
     virtual void emit_code_point(u32) override;
     virtual void execute_control_code(u8) override;
     virtual void execute_control_code(u8) override;
@@ -117,14 +157,20 @@ private:
     virtual void receive_dcs_char(u8 byte) override;
     virtual void receive_dcs_char(u8 byte) override;
     virtual void execute_dcs_sequence() override;
     virtual void execute_dcs_sequence() override;
 
 
+    void carriage_return();
+#ifndef KERNEL
     void scroll_up();
     void scroll_up();
     void scroll_down();
     void scroll_down();
     void newline();
     void newline();
-    void carriage_return();
-
-    void set_cursor(unsigned row, unsigned column);
     void put_character_at(unsigned row, unsigned column, u32 ch);
     void put_character_at(unsigned row, unsigned column, u32 ch);
     void set_window_title(const String&);
     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_control_code(u8);
     void unimplemented_escape_sequence(Intermediates, u8 last_byte);
     void unimplemented_escape_sequence(Intermediates, u8 last_byte);
@@ -192,8 +238,12 @@ private:
     // DSR - Device Status Reports
     // DSR - Device Status Reports
     void DSR(Parameters);
     void DSR(Parameters);
 
 
+#ifndef KERNEL
     // ICH - Insert Character
     // ICH - Insert Character
     void ICH(Parameters);
     void ICH(Parameters);
+#else
+    virtual void ICH(Parameters) = 0;
+#endif
 
 
     // SU - Scroll Up (called "Pan Down" in VT510)
     // SU - Scroll Up (called "Pan Down" in VT510)
     void SU(Parameters);
     void SU(Parameters);
@@ -201,14 +251,18 @@ private:
     // SD - Scroll Down (called "Pan Up" in VT510)
     // SD - Scroll Down (called "Pan Up" in VT510)
     void SD(Parameters);
     void SD(Parameters);
 
 
+#ifndef KERNEL
     // IL - Insert Line
     // IL - Insert Line
     void IL(Parameters);
     void IL(Parameters);
-
     // DCH - Delete Character
     // DCH - Delete Character
     void DCH(Parameters);
     void DCH(Parameters);
-
     // DL - Delete Line
     // DL - Delete Line
     void DL(Parameters);
     void DL(Parameters);
+#else
+    virtual void IL(Parameters) = 0;
+    virtual void DCH(Parameters) = 0;
+    virtual void DL(Parameters) = 0;
+#endif
 
 
     // CHA - Cursor Horizontal Absolute
     // CHA - Cursor Horizontal Absolute
     void CHA(Parameters);
     void CHA(Parameters);
@@ -225,10 +279,14 @@ private:
     // FIXME: Find the right names for these.
     // FIXME: Find the right names for these.
     void XTERM_WM(Parameters);
     void XTERM_WM(Parameters);
 
 
+#ifndef KERNEL
     TerminalClient& m_client;
     TerminalClient& m_client;
+#else
+    Kernel::VirtualConsole& m_client;
+#endif
 
 
     EscapeSequenceParser m_parser;
     EscapeSequenceParser m_parser;
-
+#ifndef KERNEL
     size_t m_history_start = 0;
     size_t m_history_start = 0;
     NonnullOwnPtrVector<Line> m_history;
     NonnullOwnPtrVector<Line> m_history;
     void add_line_to_history(NonnullOwnPtr<Line>&& line)
     void add_line_to_history(NonnullOwnPtr<Line>&& line)
@@ -246,6 +304,7 @@ private:
     }
     }
 
 
     NonnullOwnPtrVector<Line> m_lines;
     NonnullOwnPtrVector<Line> m_lines;
+#endif
 
 
     size_t m_scroll_region_top { 0 };
     size_t m_scroll_region_top { 0 };
     size_t m_scroll_region_bottom { 0 };
     size_t m_scroll_region_bottom { 0 };
@@ -263,7 +322,9 @@ private:
     Attribute m_current_attribute;
     Attribute m_current_attribute;
     Attribute m_saved_attribute;
     Attribute m_saved_attribute;
 
 
+#ifndef KERNEL
     u32 m_next_href_id { 0 };
     u32 m_next_href_id { 0 };
+#endif
 
 
     Vector<bool> m_horizontal_tabs;
     Vector<bool> m_horizontal_tabs;
     u32 m_last_code_point { 0 };
     u32 m_last_code_point { 0 };

+ 13 - 4
Userland/Services/SystemServer/main.cpp

@@ -57,10 +57,19 @@ static void parse_boot_mode()
     const String cmdline = String::copy(f->read_all(), Chomp);
     const String cmdline = String::copy(f->read_all(), Chomp);
     dbgln("Read command line: {}", cmdline);
     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);
     dbgln("Booting in {} mode", g_boot_mode);
 }
 }