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.
This commit is contained in:
Liav A 2021-04-16 22:58:51 +03:00 committed by Andreas Kling
parent dac129e10b
commit 20743e8aed
Notes: sideshowbarker 2024-07-18 18:00:39 +09:00
41 changed files with 1832 additions and 321 deletions

View file

@ -677,6 +677,29 @@ void vdmesgln(StringView fmtstr, TypeErasedFormatParams params)
const auto string = builder.string_view();
kernelputstr(string.characters_without_null_termination(), string.length());
}
void v_critical_dmesgln(StringView fmtstr, TypeErasedFormatParams params)
{
// FIXME: Try to avoid memory allocations further to prevent faulting
// at OOM conditions.
StringBuilder builder;
# ifdef __serenity__
if (Kernel::Processor::is_initialized() && Kernel::Thread::current()) {
auto& thread = *Kernel::Thread::current();
builder.appendff("[{}({}:{})]: ", thread.process().name(), thread.pid().value(), thread.tid().value());
} else {
builder.appendff("[Kernel]: ");
}
# endif
vformat(builder, fmtstr, params);
builder.append('\n');
const auto string = builder.string_view();
kernelcriticalputstr(string.characters_without_null_termination(), string.length());
}
#endif
template struct Formatter<unsigned char, void>;

View file

@ -436,6 +436,16 @@ void dmesgln(CheckedFormatString<Parameters...>&& fmt, const Parameters&... para
{
vdmesgln(fmt.view(), VariadicFormatParams { parameters... });
}
void v_critical_dmesgln(StringView fmtstr, TypeErasedFormatParams);
// be very careful to not cause any allocations here, since we could be in
// a very unstable situation
template<typename... Parameters>
void critical_dmesgln(CheckedFormatString<Parameters...>&& fmt, const Parameters&... parameters)
{
v_critical_dmesgln(fmt.view(), VariadicFormatParams { parameters... });
}
#endif
template<typename T, typename = void>
@ -492,6 +502,7 @@ struct Formatter<FormatString> : Formatter<String> {
} // namespace AK
#ifdef KERNEL
using AK::critical_dmesgln;
using AK::dmesgln;
#else
using AK::out;

View file

@ -2455,8 +2455,8 @@ void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, con
void __assertion_failed(const char* msg, const char* file, unsigned line, const char* func)
{
asm volatile("cli");
dmesgln("ASSERTION FAILED: {}", msg);
dmesgln("{}:{} in {}", file, line, func);
critical_dmesgln("ASSERTION FAILED: {}", msg);
critical_dmesgln("{}:{} in {}", file, line, func);
abort();
}

View file

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

View file

@ -171,8 +171,8 @@ UNMAP_AFTER_INIT AHCIResetMode CommandLine::ahci_reset_mode() const
UNMAP_AFTER_INIT BootMode CommandLine::boot_mode() const
{
const auto boot_mode = lookup("boot_mode").value_or("graphical");
if (boot_mode == "text") {
return BootMode::Text;
if (boot_mode == "no-fbdev") {
return BootMode::NoFramebufferDevices;
} else if (boot_mode == "self-test") {
return BootMode::SelfTest;
} else if (boot_mode == "graphical") {
@ -181,10 +181,10 @@ UNMAP_AFTER_INIT BootMode CommandLine::boot_mode() const
PANIC("Unknown BootMode: {}", boot_mode);
}
UNMAP_AFTER_INIT bool CommandLine::is_text_mode() const
UNMAP_AFTER_INIT bool CommandLine::is_no_framebuffer_devices_mode() const
{
const auto mode = boot_mode();
return mode == BootMode::Text || mode == BootMode::SelfTest;
return mode == BootMode::NoFramebufferDevices || mode == BootMode::SelfTest;
}
String CommandLine::userspace_init() const

View file

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

View file

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

View file

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

View file

@ -11,6 +11,8 @@
#include <Kernel/Graphics/Bochs.h>
#include <Kernel/Graphics/BochsFramebufferDevice.h>
#include <Kernel/Graphics/BochsGraphicsAdapter.h>
#include <Kernel/Graphics/Console/FramebufferConsole.h>
#include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/IO.h>
#include <Kernel/PCI/Access.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)
{
set_safe_resolution();
// We assume safe resolutio is 1024x768x32
m_framebuffer_console = Graphics::FramebufferConsole::initialize(PhysicalAddress(PCI::get_BAR0(pci_address) & 0xfffffff0), 1024, 768, 1024 * sizeof(u32));
// FIXME: This is a very wrong way to do this...
GraphicsManagement::the().m_console = m_framebuffer_console;
}
UNMAP_AFTER_INIT void BochsGraphicsAdapter::initialize_framebuffer_devices()
{
// FIXME: Find a better way to determine default resolution...
m_framebuffer = BochsFramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024 * 4, 1024, 768);
m_framebuffer_device = BochsFramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024 * 4, 1024, 768);
m_framebuffer_device->initialize();
}
GraphicsDevice::Type BochsGraphicsAdapter::type() const
{
if (PCI::get_class(pci_address()) == 0x3 && PCI::get_subclass(pci_address()) == 0x0)
return Type::VGACompatible;
return Type::Bochs;
return Type::Bochs;
}
void BochsGraphicsAdapter::set_safe_resolution()
@ -119,8 +126,33 @@ bool BochsGraphicsAdapter::validate_setup_resolution(size_t width, size_t height
void BochsGraphicsAdapter::set_y_offset(size_t y_offset)
{
if (m_console_enabled)
return;
auto registers = map_typed_writable<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
registers->bochs_regs.y_offset = y_offset;
}
void BochsGraphicsAdapter::enable_consoles()
{
ScopedSpinLock lock(m_console_mode_switch_lock);
VERIFY(m_framebuffer_console);
m_console_enabled = true;
auto registers = map_typed_writable<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();
}
}

View file

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

View file

@ -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 };
};
}

View file

@ -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);
}
}

View file

@ -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;
};
}

View file

@ -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);
}
}

View file

@ -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 };
};
}

View file

@ -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)
{
}
}

View file

@ -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;
};
}

View file

@ -16,6 +16,8 @@
#include <LibC/errno_numbers.h>
#include <LibC/sys/ioctl_numbers.h>
#include <Kernel/Panic.h>
namespace Kernel {
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()))
return EOVERFLOW;
auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes());
// FIXME: We rely on the fact that only the WindowServer will mmap the framebuffer
// and only once when starting to work with it. If other program wants to do so, we need to fix this.
VERIFY(!m_userspace_framebuffer_region);
VERIFY(!m_userspace_real_framebuffer_vmobject);
auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, page_round_up(framebuffer_size_in_bytes()));
if (!vmobject)
return ENOMEM;
return process.space().allocate_region_with_vmobject(
m_userspace_real_framebuffer_vmobject = vmobject;
auto result = process.space().allocate_region_with_vmobject(
range,
vmobject.release_nonnull(),
0,
"Framebuffer",
prot,
shared);
if (!result.is_error()) {
m_userspace_framebuffer_region = result.value();
}
return result;
}
void FramebufferDevice::dectivate_writes()
{
ScopedSpinLock lock(m_activation_lock);
if (!m_userspace_framebuffer_region)
return;
memcpy(m_swapped_framebuffer_region->vaddr().as_ptr(), m_real_framebuffer_region->vaddr().as_ptr(), page_round_up(framebuffer_size_in_bytes()));
auto vmobject = m_swapped_framebuffer_vmobject;
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
m_userspace_framebuffer_region->remap();
}
void FramebufferDevice::activate_writes()
{
ScopedSpinLock lock(m_activation_lock);
if (!m_userspace_framebuffer_region || !m_real_framebuffer_vmobject)
return;
// restore the image we had in the void area
// FIXME: if we happen to have multiple Framebuffers that are writing to that location
// we will experience glitches...
memcpy(m_real_framebuffer_region->vaddr().as_ptr(), m_swapped_framebuffer_region->vaddr().as_ptr(), page_round_up(framebuffer_size_in_bytes()));
auto vmobject = m_userspace_real_framebuffer_vmobject;
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
m_userspace_framebuffer_region->remap();
}
String FramebufferDevice::device_name() const
@ -45,6 +82,18 @@ String FramebufferDevice::device_name() const
return String::formatted("fb{}", minor());
}
UNMAP_AFTER_INIT void FramebufferDevice::initialize()
{
m_real_framebuffer_vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, page_round_up(framebuffer_size_in_bytes()));
VERIFY(m_real_framebuffer_vmobject);
m_real_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, framebuffer_size_in_bytes(), "Framebuffer", Region::Access::Read | Region::Access::Write);
VERIFY(m_real_framebuffer_region);
m_swapped_framebuffer_vmobject = AnonymousVMObject::create_with_size(page_round_up(framebuffer_size_in_bytes()), AllocationStrategy::AllocateNow);
VERIFY(m_swapped_framebuffer_vmobject);
m_swapped_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, page_round_up(framebuffer_size_in_bytes()), "Framebuffer Swap (Blank)", Region::Access::Read | Region::Access::Write);
VERIFY(m_swapped_framebuffer_region);
}
UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size_t pitch, size_t width, size_t height)
: BlockDevice(29, GraphicsManagement::the().current_minor_number())
, m_framebuffer_address(addr)
@ -52,6 +101,10 @@ UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size
, m_framebuffer_width(width)
, m_framebuffer_height(height)
{
VERIFY(!m_framebuffer_address.is_null());
VERIFY(m_framebuffer_pitch);
VERIFY(m_framebuffer_width);
VERIFY(m_framebuffer_height);
dbgln("Framebuffer {}: address={}, pitch={}, width={}, height={}", minor(), addr, pitch, width, height);
}

View file

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

View file

@ -25,9 +25,15 @@ public:
virtual ~GraphicsDevice() = default;
virtual void initialize_framebuffer_devices() = 0;
virtual Type type() const = 0;
virtual void enable_consoles() = 0;
virtual void disable_consoles() = 0;
bool consoles_enabled() const { return m_consoles_enabled; }
virtual bool framebuffer_devices_initialized() const = 0;
protected:
GraphicsDevice() = default;
bool m_consoles_enabled { false };
};
}

View file

@ -9,10 +9,14 @@
#include <Kernel/CommandLine.h>
#include <Kernel/Debug.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/IntelNativeGraphicsAdapter.h>
#include <Kernel/Graphics/VGACompatibleAdapter.h>
#include <Kernel/IO.h>
#include <Kernel/Multiboot.h>
#include <Kernel/VM/AnonymousVMObject.h>
namespace Kernel {
@ -29,10 +33,24 @@ bool GraphicsManagement::is_initialized()
}
UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement()
: m_textmode_enabled(kernel_command_line().is_text_mode())
: m_vga_font_region(MM.allocate_kernel_region(PAGE_SIZE, "VGA font", Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow).release_nonnull())
, m_framebuffer_devices_allowed(!kernel_command_line().is_no_framebuffer_devices_mode())
{
}
void GraphicsManagement::deactivate_graphical_mode()
{
for (auto& graphics_device : m_graphics_devices) {
graphics_device.enable_consoles();
}
}
void GraphicsManagement::activate_graphical_mode()
{
for (auto& graphics_device : m_graphics_devices) {
graphics_device.disable_consoles();
}
}
UNMAP_AFTER_INIT RefPtr<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)) {
@ -44,31 +62,93 @@ UNMAP_AFTER_INIT RefPtr<GraphicsDevice> GraphicsManagement::determine_graphics_d
if (!adapter.is_null())
return adapter;
}
VERIFY(multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB || multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT);
return VGACompatibleAdapter::initialize_with_preset_resolution(address,
PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)),
multiboot_info_ptr->framebuffer_pitch,
multiboot_info_ptr->framebuffer_width,
multiboot_info_ptr->framebuffer_height);
if (multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
dmesgln("Graphics: Using a preset resolution from the bootloader");
return VGACompatibleAdapter::initialize_with_preset_resolution(address,
PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)),
multiboot_info_ptr->framebuffer_pitch,
multiboot_info_ptr->framebuffer_width,
multiboot_info_ptr->framebuffer_height);
}
return VGACompatibleAdapter::initialize(address);
}
return {};
}
UNMAP_AFTER_INIT bool GraphicsManagement::initialize()
{
if (kernel_command_line().is_text_mode()) {
dbgln("Text mode enabled");
return false;
/* Explanation on the flow when not requesting to force not creating any
* framebuffer devices:
* If the user wants to use a Console instead of the graphical environment,
* they doesn't need to request text mode.
* Graphical mode might not be accessible on bare-metal hardware because
* the bootloader didn't set a framebuffer and we don't have a native driver
* to set a framebuffer for it. We don't have VBE modesetting capabilities
* in the kernel yet, so what will happen is one of the following situations:
* 1. The bootloader didn't specify settings of a pre-set framebuffer. The
* kernel has a native driver for a detected display adapter, therefore
* the kernel can still set a framebuffer.
* 2. The bootloader specified settings of a pre-set framebuffer, and the
* kernel has a native driver for a detected display adapter, therefore
* the kernel can still set a framebuffer and change the settings of it.
* In that situation, the kernel will simply ignore the Multiboot pre-set
* framebuffer.
* 2. The bootloader specified settings of a pre-set framebuffer, and the
* kernel does not have a native driver for a detected display adapter,
* therefore the kernel will use the pre-set framebuffer. Modesetting is not
* availabe in this situation.
* 3. The bootloader didn't specify settings of a pre-set framebuffer, and
* the kernel does not have a native driver for a detected display adapter,
* therefore the kernel will try to initialize a VGA text mode console.
* In that situation, the kernel will assume that VGA text mode was already
* initialized, but will still try to modeset it. No switching to graphical
* enviroment is allowed in this case.
*
* By default, the kernel assumes that no framebuffer was created until it
* was proven that there's an existing framebuffer or we can modeset the
* screen resolution to create a framebuffer.
*
* If the user requests to force no initialization of framebuffer devices
* the same flow above will happen, except that no framebuffer device will
* be created, so SystemServer will not try to initialize WindowServer.
*/
if (kernel_command_line().is_no_framebuffer_devices_mode()) {
dbgln("Forcing no initialization of framebuffer devices");
}
PCI::enumerate([&](const PCI::Address& address, PCI::ID id) {
// Note: Each graphics controller will try to set its native screen resolution
// upon creation. Later on, if we don't want to have framebuffer devices, a
// framebuffer console will take the control instead.
auto adapter = determine_graphics_device(address, id);
if (!adapter)
return;
adapter->initialize_framebuffer_devices();
m_graphics_devices.append(adapter.release_nonnull());
// If IO space is enabled, this VGA adapter is operating in VGA mode.
if (adapter->type() == GraphicsDevice::Type::VGACompatible && PCI::is_io_space_enabled(address)) {
VERIFY(m_vga_adapter.is_null());
dbgln("Graphics adapter @ {} is operating in VGA mode", address);
m_vga_adapter = adapter;
}
auto display_adapter = adapter.release_nonnull();
m_graphics_devices.append(display_adapter);
if (!m_framebuffer_devices_allowed) {
display_adapter->enable_consoles();
return;
}
display_adapter->initialize_framebuffer_devices();
});
return true;
}
bool GraphicsManagement::framebuffer_devices_exist() const
{
for (auto& graphics_device : m_graphics_devices) {
if (graphics_device.framebuffer_devices_initialized())
return true;
}
return false;
}
}

View file

@ -6,16 +6,26 @@
#pragma once
#include <AK/NonnullOwnPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/NonnullRefPtrVector.h>
#include <AK/Types.h>
#include <Kernel/Graphics/Console/Console.h>
#include <Kernel/Graphics/GraphicsDevice.h>
#include <Kernel/Graphics/VGACompatibleAdapter.h>
#include <Kernel/PCI/Definitions.h>
#include <Kernel/VM/Region.h>
namespace Kernel {
class BochsGraphicsAdapter;
class IntelNativeGraphicsAdapter;
class VGACompatibleAdapter;
class GraphicsManagement {
AK_MAKE_ETERNAL;
friend class BochsGraphicsAdapter;
friend class IntelNativeGraphicsAdapter;
friend class VGACompatibleAdapter;
AK_MAKE_ETERNAL
public:
static GraphicsManagement& the();
@ -25,14 +35,28 @@ public:
unsigned current_minor_number() { return m_current_minor_number++; };
GraphicsManagement();
bool is_text_mode_enabled() const { return m_textmode_enabled; }
bool framebuffer_devices_allowed() const { return m_framebuffer_devices_allowed; }
bool framebuffer_devices_exist() const;
SpinLock<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:
RefPtr<GraphicsDevice> determine_graphics_device(PCI::Address address, PCI::ID id) const;
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 };
bool m_textmode_enabled;
const bool m_framebuffer_devices_allowed;
SpinLock<u8> m_main_vga_lock;
};
}

View file

@ -4,7 +4,9 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Graphics/Console/FramebufferConsole.h>
#include <Kernel/Graphics/Definitions.h>
#include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/Graphics/IntelNativeGraphicsAdapter.h>
#include <Kernel/IO.h>
#include <Kernel/PhysicalAddress.h>
@ -165,7 +167,7 @@ Optional<IntelNativeGraphicsAdapter::PLLSettings> IntelNativeGraphicsAdapter::cr
}
IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
: PCI::DeviceController(address)
: VGACompatibleAdapter(address)
, m_registers(PCI::get_BAR0(address) & 0xfffffffc)
, m_framebuffer_addr(PCI::get_BAR2(address) & 0xfffffffc)
{
@ -182,10 +184,24 @@ IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
set_gmbus_pin_pair(GMBusPinPair::DedicatedAnalog);
}
gmbus_read_edid();
auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 0);
dmesgln("Intel Native Graphics Adapter @ {}, preferred resolution is {:d}x{:d}", address, modesetting.horizontal.active, modesetting.vertical.active);
auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 0);
dmesgln("Intel Native Graphics Adapter @ {}, preferred resolution is {:d}x{:d}", pci_address(), modesetting.horizontal.active, modesetting.vertical.active);
set_crt_resolution(modesetting.horizontal.active, modesetting.vertical.active);
auto framebuffer_address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
VERIFY(!framebuffer_address.is_null());
VERIFY(m_framebuffer_pitch != 0);
VERIFY(m_framebuffer_height != 0);
VERIFY(m_framebuffer_width != 0);
m_framebuffer_console = Graphics::FramebufferConsole::initialize(framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
// FIXME: This is a very wrong way to do this...
GraphicsManagement::the().m_console = m_framebuffer_console;
}
void IntelNativeGraphicsAdapter::enable_vga_plane()
{
VERIFY(m_control_lock.is_locked());
VERIFY(m_modeset_lock.is_locked());
}
static inline const char* convert_register_index_to_string(IntelGraphics::RegisterIndex index)
@ -269,6 +285,7 @@ bool IntelNativeGraphicsAdapter::pipe_a_enabled() const
VERIFY(m_control_lock.is_locked());
return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30);
}
bool IntelNativeGraphicsAdapter::pipe_b_enabled() const
{
VERIFY(m_control_lock.is_locked());
@ -396,7 +413,7 @@ bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height)
VERIFY_NOT_REACHED();
auto settings = pll_settings.value();
dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
set_dpll_registers(pll_settings.value(), dac_multiplier);
enable_dpll_without_vga(pll_settings.value(), dac_multiplier);
set_display_timings(modesetting);
auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
VERIFY(!address.is_null());
@ -404,7 +421,7 @@ bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height)
m_framebuffer_width = width;
m_framebuffer_height = height;
m_framebuffer_stride = width * 4;
m_framebuffer_pitch = width * 4;
return true;
}
@ -538,7 +555,7 @@ void IntelNativeGraphicsAdapter::enable_primary_plane(PhysicalAddress fb_address
write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, (read_from_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl) & (~(0b1111 << 26))) | (0b0110 << 26) | (1 << 31));
}
void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings, size_t dac_multiplier)
void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings)
{
VERIFY(m_control_lock.is_locked());
VERIFY(m_modeset_lock.is_locked());
@ -546,6 +563,14 @@ void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings,
write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA1, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16));
write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & ~0x80000000);
}
void IntelNativeGraphicsAdapter::enable_dpll_without_vga(const PLLSettings& settings, size_t dac_multiplier)
{
VERIFY(m_control_lock.is_locked());
VERIFY(m_modeset_lock.is_locked());
set_dpll_registers(settings);
IO::delay(200);
@ -597,9 +622,10 @@ void IntelNativeGraphicsAdapter::initialize_framebuffer_devices()
{
auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
VERIFY(!address.is_null());
VERIFY(m_framebuffer_stride != 0);
VERIFY(m_framebuffer_pitch != 0);
VERIFY(m_framebuffer_height != 0);
VERIFY(m_framebuffer_width != 0);
m_framebuffer = m_framebuffer = RawFramebufferDevice::create(*this, address, m_framebuffer_stride, m_framebuffer_width, m_framebuffer_height);
m_framebuffer_device = RawFramebufferDevice::create(*this, address, m_framebuffer_pitch, m_framebuffer_width, m_framebuffer_height);
m_framebuffer_device->initialize();
}
}

View file

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

View file

@ -4,7 +4,11 @@
* 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/IO.h>
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));
}
UNMAP_AFTER_INIT NonnullRefPtr<VGACompatibleAdapter> VGACompatibleAdapter::initialize(PCI::Address address)
{
return adopt_ref(*new VGACompatibleAdapter(address));
}
UNMAP_AFTER_INIT void VGACompatibleAdapter::initialize_framebuffer_devices()
{
// We might not have any pre-set framebuffer, so if that's the case - don't try to initialize one.
if (m_framebuffer_address.is_null())
return;
VERIFY(m_framebuffer_width);
VERIFY(m_framebuffer_width != 0);
VERIFY(m_framebuffer_height != 0);
VERIFY(m_framebuffer_pitch != 0);
m_framebuffer_device = RawFramebufferDevice::create(*this, m_framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
m_framebuffer_device->initialize();
}
UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address)
: PCI::DeviceController(address)
{
m_framebuffer_console = Graphics::TextModeConsole::initialize(*this);
// FIXME: This is a very wrong way to do this...
GraphicsManagement::the().m_console = m_framebuffer_console;
}
UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address, PhysicalAddress framebuffer_address, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch)
: PCI::DeviceController(address)
, m_framebuffer_address(framebuffer_address)
, m_framebuffer_width(framebuffer_width)
, m_framebuffer_height(framebuffer_height)
, m_framebuffer_pitch(framebuffer_pitch)
{
m_framebuffer = RawFramebufferDevice::create(*this, framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch);
m_framebuffer_console = Graphics::FramebufferConsole::initialize(framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch);
}
void VGACompatibleAdapter::enable_consoles()
{
VERIFY(m_framebuffer_console);
if (m_framebuffer_device)
m_framebuffer_device->dectivate_writes();
m_framebuffer_console->enable();
}
void VGACompatibleAdapter::disable_consoles()
{
VERIFY(m_framebuffer_device);
VERIFY(m_framebuffer_console);
m_framebuffer_console->disable();
m_framebuffer_device->activate_writes();
}
}

View file

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

View file

@ -13,7 +13,7 @@ namespace Kernel {
void __panic(const char* file, unsigned int line, const char* function)
{
dmesgln("at {}:{} in {}", file, line, function);
critical_dmesgln("at {}:{} in {}", file, line, function);
dump_backtrace();
Processor::halt();
}

View file

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

View file

@ -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);
}
}

View file

@ -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;
};
};

View file

@ -1,59 +1,141 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@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
*/
#include "VirtualConsole.h"
#include <AK/StdLibExtras.h>
#include <AK/String.h>
#include <Kernel/Arch/x86/CPU.h>
#include <Kernel/Debug.h>
#include <Kernel/Devices/HID/HIDManagement.h>
#include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/Heap/kmalloc.h>
#include <Kernel/IO.h>
#include <Kernel/StdLib.h>
#include <Kernel/TTY/ConsoleManagement.h>
namespace Kernel {
static u8* s_vga_buffer;
static VirtualConsole* s_consoles[s_max_virtual_consoles];
static int s_active_console;
static RecursiveSpinLock s_lock;
void VirtualConsole::flush_vga_cursor()
ConsoleImpl::ConsoleImpl(VirtualConsole& client)
: Terminal(client)
{
u16 value = m_current_vga_start_address + (m_terminal.cursor_row() * columns() + m_terminal.cursor_column());
IO::out8(0x3d4, 0x0e);
IO::out8(0x3d5, MSB(value));
IO::out8(0x3d4, 0x0f);
IO::out8(0x3d5, LSB(value));
}
UNMAP_AFTER_INIT void VirtualConsole::initialize()
void ConsoleImpl::invalidate_cursor()
{
s_vga_buffer = (u8*)0xc00b8000;
s_active_console = -1;
}
void ConsoleImpl::clear()
{
m_client.clear();
}
void ConsoleImpl::clear_including_history()
{
}
void ConsoleImpl::set_size(u16 determined_columns, u16 determined_rows)
{
VERIFY(determined_columns);
VERIFY(determined_rows);
if (determined_columns == columns() && determined_rows == rows())
return;
m_columns = determined_columns;
m_rows = determined_rows;
m_cursor_row = min<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()
{
// NOTE: We have to invalidate the cursor first.
m_client.invalidate_cursor(m_cursor_row);
m_client.scroll_up();
}
void ConsoleImpl::scroll_down()
{
}
void ConsoleImpl::newline()
{
u16 new_row = m_cursor_row;
u16 max_row = rows() - 1;
if (new_row == max_row) {
// NOTE: We have to invalidate the cursor first.
m_client.invalidate_cursor(new_row);
m_client.scroll_up();
} else {
++new_row;
}
set_cursor(new_row, 0);
}
void ConsoleImpl::put_character_at(unsigned row, unsigned column, u32 ch)
{
m_client.put_character_at(row, column, ch, m_current_attribute);
m_last_code_point = ch;
}
void ConsoleImpl::set_window_title(const String&)
{
}
void ConsoleImpl::ICH(Parameters)
{
// FIXME: Implement this
}
void ConsoleImpl::IL(Parameters)
{
// FIXME: Implement this
}
void ConsoleImpl::DCH(Parameters)
{
// FIXME: Implement this
}
void ConsoleImpl::DL(Parameters)
{
// FIXME: Implement this
}
void VirtualConsole::set_graphical(bool graphical)
{
if (graphical)
set_vga_start_row(0);
m_graphical = graphical;
}
UNMAP_AFTER_INIT NonnullRefPtr<VirtualConsole> VirtualConsole::create(size_t index)
{
return adopt_ref(*new VirtualConsole(index));
}
UNMAP_AFTER_INIT VirtualConsole::VirtualConsole(const unsigned index)
: TTY(4, index)
, m_index(index)
, m_terminal(*this)
, m_console_impl(*this)
{
VERIFY(index < s_max_virtual_consoles);
m_tty_name = String::formatted("/dev/tty{}", m_index);
m_terminal.set_size(80, 25);
VERIFY(GraphicsManagement::the().console());
set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row());
m_console_impl.set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row());
s_consoles[index] = this;
// Allocate twice of the max row * max column * sizeof(Cell) to ensure we can some sort of history mechanism...
auto size = GraphicsManagement::the().console()->max_column() * GraphicsManagement::the().console()->max_row() * sizeof(Cell) * 2;
m_cells = MM.allocate_kernel_region(page_round_up(size), "Virtual Console Cells", Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow);
// Add the lines, so we also ensure they will be flushed now
for (size_t row = 0; row < rows(); row++) {
m_lines.append({ true });
}
clear();
VERIFY(m_cells);
}
UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole()
@ -61,69 +143,6 @@ UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole()
VERIFY_NOT_REACHED();
}
void VirtualConsole::switch_to(unsigned index)
{
if ((int)index == s_active_console)
return;
VERIFY(index < s_max_virtual_consoles);
VERIFY(s_consoles[index]);
ScopedSpinLock lock(s_lock);
if (s_active_console != -1) {
auto* active_console = s_consoles[s_active_console];
// We won't know how to switch away from a graphical console until we
// can set the video mode on our own. Just stop anyone from trying for
// now.
if (active_console->is_graphical()) {
dbgln("Cannot switch away from graphical console yet :(");
return;
}
active_console->set_active(false);
}
dbgln("VC: Switch to {} ({})", index, s_consoles[index]);
s_active_console = index;
s_consoles[s_active_console]->set_active(true);
}
void VirtualConsole::set_active(bool active)
{
if (active == m_active)
return;
ScopedSpinLock lock(s_lock);
m_active = active;
if (active) {
set_vga_start_row(0);
HIDManagement::the().set_client(this);
m_terminal.m_need_full_flush = true;
flush_dirty_lines();
} else {
HIDManagement::the().set_client(nullptr);
}
}
enum class VGAColor : u8 {
Black = 0,
Blue,
Green,
Cyan,
Red,
Magenta,
Brown,
LightGray,
DarkGray,
BrightBlue,
BrightGreen,
BrightCyan,
BrightRed,
BrightMagenta,
Yellow,
White,
};
enum class ANSIColor : u8 {
Black = 0,
Red,
@ -144,60 +163,53 @@ enum class ANSIColor : u8 {
__Count,
};
static inline VGAColor ansi_color_to_vga(ANSIColor color)
static inline Graphics::Console::Color ansi_color_to_standard_vga_color(ANSIColor color)
{
switch (color) {
case ANSIColor::Black:
return VGAColor::Black;
return Graphics::Console::Color::Black;
case ANSIColor::Red:
return VGAColor::Red;
return Graphics::Console::Color::Red;
case ANSIColor::Brown:
return VGAColor::Brown;
return Graphics::Console::Color::Brown;
case ANSIColor::Blue:
return VGAColor::Blue;
return Graphics::Console::Color::Blue;
case ANSIColor::Magenta:
return VGAColor::Magenta;
return Graphics::Console::Color::Magenta;
case ANSIColor::Green:
return VGAColor::Green;
return Graphics::Console::Color::Green;
case ANSIColor::Cyan:
return VGAColor::Cyan;
return Graphics::Console::Color::Cyan;
case ANSIColor::LightGray:
return VGAColor::LightGray;
return Graphics::Console::Color::LightGray;
case ANSIColor::DarkGray:
return VGAColor::DarkGray;
return Graphics::Console::Color::DarkGray;
case ANSIColor::BrightRed:
return VGAColor::BrightRed;
return Graphics::Console::Color::BrightRed;
case ANSIColor::BrightGreen:
return VGAColor::BrightGreen;
return Graphics::Console::Color::BrightGreen;
case ANSIColor::Yellow:
return VGAColor::Yellow;
return Graphics::Console::Color::Yellow;
case ANSIColor::BrightBlue:
return VGAColor::BrightBlue;
return Graphics::Console::Color::BrightBlue;
case ANSIColor::BrightMagenta:
return VGAColor::BrightMagenta;
return Graphics::Console::Color::BrightMagenta;
case ANSIColor::BrightCyan:
return VGAColor::BrightCyan;
return Graphics::Console::Color::BrightCyan;
case ANSIColor::White:
return VGAColor::White;
return Graphics::Console::Color::White;
default:
VERIFY_NOT_REACHED();
}
}
static inline u8 xterm_color_to_vga(u32 color)
static inline Graphics::Console::Color xterm_to_standard_color(u32 color)
{
for (u8 i = 0; i < (u8)ANSIColor::__Count; i++) {
if (xterm_colors[i] == color)
return (u8)ansi_color_to_vga((ANSIColor)i);
return (Graphics::Console::Color)ansi_color_to_standard_vga_color((ANSIColor)i);
}
return (u8)VGAColor::LightGray;
}
void VirtualConsole::clear_vga_row(u16 row)
{
u16* linemem = (u16*)&m_current_vga_window[row * 160];
for (u16 i = 0; i < columns(); ++i)
linemem[i] = 0x0720;
return Graphics::Console::Color::LightGray;
}
void VirtualConsole::on_key_pressed(KeyEvent event)
@ -209,26 +221,18 @@ void VirtualConsole::on_key_pressed(KeyEvent event)
if (!event.is_press())
return;
if (event.key == KeyCode::Key_PageUp && event.flags == Mod_Shift) {
// TODO: scroll up
return;
}
if (event.key == KeyCode::Key_PageDown && event.flags == Mod_Shift) {
// TODO: scroll down
return;
}
Processor::deferred_call_queue([this, event]() {
m_terminal.handle_key_press(event.key, event.code_point, event.flags);
m_console_impl.handle_key_press(event.key, event.code_point, event.flags);
});
}
ssize_t VirtualConsole::on_tty_write(const UserOrKernelBuffer& data, ssize_t size)
{
ScopedSpinLock lock(s_lock);
ScopedSpinLock global_lock(ConsoleManagement::the().tty_write_lock());
ScopedSpinLock lock(m_lock);
auto result = data.read_buffered<512>((size_t)size, [&](u8 const* buffer, size_t buffer_bytes) {
for (size_t i = 0; i < buffer_bytes; ++i)
m_terminal.on_input(buffer[i]);
m_console_impl.on_input(buffer[i]);
return buffer_bytes;
});
if (m_active)
@ -238,52 +242,51 @@ ssize_t VirtualConsole::on_tty_write(const UserOrKernelBuffer& data, ssize_t siz
return (ssize_t)result.value();
}
void VirtualConsole::set_vga_start_row(u16 row)
void VirtualConsole::set_active(bool active)
{
m_vga_start_row = row;
m_current_vga_start_address = row * columns();
m_current_vga_window = s_vga_buffer + row * 160;
IO::out8(0x3d4, 0x0c);
IO::out8(0x3d5, MSB(m_current_vga_start_address));
IO::out8(0x3d4, 0x0d);
IO::out8(0x3d5, LSB(m_current_vga_start_address));
VERIFY(ConsoleManagement::the().m_lock.is_locked());
VERIFY(m_active != active);
m_active = active;
if (active) {
HIDManagement::the().set_client(this);
m_console_impl.m_need_full_flush = true;
flush_dirty_lines();
} else {
HIDManagement::the().set_client(nullptr);
}
}
static inline u8 attribute_to_vga(const VT::Attribute& attribute)
void VirtualConsole::emit_char(char ch)
{
u8 vga_attr = 0x07;
if (attribute.flags & VT::Attribute::Bold)
vga_attr |= 0x08;
// Background color
vga_attr &= ~0x70;
vga_attr |= xterm_color_to_vga(attribute.effective_background_color()) << 8;
// Foreground color
vga_attr &= ~0x7;
vga_attr |= xterm_color_to_vga(attribute.effective_foreground_color());
return vga_attr;
echo(ch);
}
void VirtualConsole::flush_dirty_lines()
{
for (u16 visual_row = 0; visual_row < m_terminal.rows(); ++visual_row) {
auto& line = m_terminal.visible_line(visual_row);
if (!line.is_dirty() && !m_terminal.m_need_full_flush)
VERIFY(GraphicsManagement::is_initialized());
VERIFY(GraphicsManagement::the().console());
for (u16 visual_row = 0; visual_row < rows(); ++visual_row) {
auto& line = m_lines[visual_row];
if (!line.dirty && !m_console_impl.m_need_full_flush)
continue;
for (size_t column = 0; column < line.length(); ++column) {
u32 code_point = line.code_point(column);
auto attribute = line.attribute_at(column);
u16 vga_index = (visual_row * 160) + (column * 2);
m_current_vga_window[vga_index] = code_point < 128 ? code_point : '?';
m_current_vga_window[vga_index + 1] = attribute_to_vga(attribute);
for (size_t column = 0; column < columns(); ++column) {
auto& cell = cell_at(column, visual_row);
auto foreground_color = xterm_to_standard_color(cell.attribute.effective_foreground_color());
if (cell.attribute.flags & VT::Attribute::Flags::Bold)
foreground_color = (Graphics::Console::Color)((u8)foreground_color | 0x08);
GraphicsManagement::the().console()->write(column,
visual_row,
((u8)cell.ch < 128 ? cell.ch : '?'),
xterm_to_standard_color(cell.attribute.effective_background_color()),
foreground_color);
}
line.set_dirty(false);
line.dirty = false;
}
flush_vga_cursor();
m_terminal.m_need_full_flush = false;
GraphicsManagement::the().console()->set_cursor(m_console_impl.cursor_column(), m_console_impl.cursor_row());
m_console_impl.m_need_full_flush = false;
}
void VirtualConsole::beep()
@ -304,9 +307,8 @@ void VirtualConsole::set_window_progress(int, int)
void VirtualConsole::terminal_did_resize(u16 columns, u16 rows)
{
VERIFY(columns == 80);
VERIFY(rows == 25);
set_size(columns, rows);
// FIXME: Allocate more Region(s) or deallocate them if needed...
dbgln("VC {}: Resized to {} x {}", index(), columns, rows);
}
void VirtualConsole::terminal_history_changed()
@ -333,4 +335,66 @@ void VirtualConsole::echo(u8 ch)
}
}
VirtualConsole::Cell& VirtualConsole::cell_at(size_t x, size_t y)
{
auto* ptr = (VirtualConsole::Cell*)(m_cells->vaddr().as_ptr());
ptr += (y * columns()) + x;
return *ptr;
}
void VirtualConsole::clear()
{
auto* cell = (Cell*)m_cells->vaddr().as_ptr();
for (size_t y = 0; y < rows(); y++) {
m_lines[y].dirty = true;
for (size_t x = 0; x < columns(); x++) {
cell[x].clear();
}
cell += columns();
}
m_console_impl.set_cursor(0, 0);
}
void VirtualConsole::scroll_up()
{
memmove(m_cells->vaddr().as_ptr(), m_cells->vaddr().offset(columns() * sizeof(Cell)).as_ptr(), ((rows() - 1) * columns() * sizeof(Cell)));
clear_line(rows() - 1);
m_console_impl.m_need_full_flush = true;
}
void VirtualConsole::newline()
{
}
void VirtualConsole::clear_line(size_t y_index)
{
m_lines[y_index].dirty = true;
for (size_t x = 0; x < columns(); x++) {
auto& cell = cell_at(x, y_index);
cell.clear();
}
}
void VirtualConsole::put_character_at(unsigned row, unsigned column, u32 code_point, const VT::Attribute& attribute)
{
VERIFY(row < rows());
VERIFY(column < columns());
auto& line = m_lines[row];
auto& cell = cell_at(column, row);
cell.attribute.foreground_color = attribute.foreground_color;
cell.attribute.background_color = attribute.background_color;
cell.attribute.flags = attribute.flags;
if (code_point > 128)
cell.ch = ' ';
else
cell.ch = code_point;
cell.attribute.flags |= VT::Attribute::Flags::Touched;
line.dirty = true;
}
void VirtualConsole::invalidate_cursor(size_t row)
{
m_lines[row].dirty = true;
}
}

View file

@ -1,35 +1,91 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#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/Devices/HID/HIDManagement.h>
#include <Kernel/Graphics/Console/Console.h>
#include <Kernel/TTY/TTY.h>
#include <LibVT/Attribute.h>
#include <LibVT/Position.h>
#include <LibVT/Terminal.h>
namespace Kernel {
static constexpr unsigned s_max_virtual_consoles = 6;
class ConsoleManagement;
class VirtualConsole;
// FIXME: This implementation has no knowledge about keeping terminal history...
class ConsoleImpl final : public VT::Terminal {
public:
explicit ConsoleImpl(VirtualConsole&);
virtual void set_size(u16 columns, u16 rows) override;
private:
virtual void invalidate_cursor() override;
virtual void clear() override;
virtual void clear_including_history() override;
virtual void scroll_up() override;
virtual void scroll_down() override;
virtual void newline() override;
virtual void put_character_at(unsigned row, unsigned column, u32 ch) override;
virtual void set_window_title(const String&) override;
virtual void ICH(Parameters) override;
virtual void IL(Parameters) override;
virtual void DCH(Parameters) override;
virtual void DL(Parameters) override;
};
class VirtualConsole final : public TTY
, public KeyboardClient
, public VT::TerminalClient {
AK_MAKE_ETERNAL
friend class ConsoleManagement;
friend class ConsoleImpl;
friend class VT::Terminal;
public:
VirtualConsole(const unsigned index);
struct Line {
bool dirty;
};
struct Cell {
void clear()
{
ch = ' ';
attribute.reset();
}
char ch;
VT::Attribute attribute;
};
public:
static NonnullRefPtr<VirtualConsole> create(size_t index);
virtual ~VirtualConsole() override;
static void switch_to(unsigned);
static void initialize();
size_t index() const { return m_index; }
bool is_graphical() { return m_graphical; }
void set_graphical(bool graphical);
void emit_char(char);
private:
VirtualConsole(const unsigned index);
// ^KeyboardClient
virtual void on_key_pressed(KeyEvent) override;
@ -53,23 +109,37 @@ private:
virtual String device_name() const override;
void set_active(bool);
void flush_vga_cursor();
void flush_dirty_lines();
unsigned m_index;
bool m_active { false };
bool m_graphical { false };
void clear_vga_row(u16 row);
void set_vga_start_row(u16 row);
u16 m_vga_start_row { 0 };
u16 m_current_vga_start_address { 0 };
u8* m_current_vga_window { nullptr };
VT::Terminal m_terminal;
String m_tty_name;
RecursiveSpinLock m_lock;
private:
void invalidate_cursor(size_t row);
void clear();
void inject_string(const StringView&);
Cell& cell_at(size_t column, size_t row);
typedef Vector<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;
};
}

View file

@ -45,6 +45,7 @@
#include <Kernel/Random.h>
#include <Kernel/Scheduler.h>
#include <Kernel/Storage/StorageManagement.h>
#include <Kernel/TTY/ConsoleManagement.h>
#include <Kernel/TTY/PTYMultiplexer.h>
#include <Kernel/TTY/VirtualConsole.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++)
(*ctor)();
ConsoleDevice::initialize();
APIC::initialize();
InterruptManagement::initialize();
ACPI::initialize();
VFS::initialize();
ConsoleDevice::initialize();
dmesgln("Starting SerenityOS...");
@ -160,12 +162,11 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init()
VMWareBackdoor::the(); // don't wait until first mouse packet
HIDManagement::initialize();
VirtualConsole::initialize();
tty0 = new VirtualConsole(0);
for (unsigned i = 1; i < s_max_virtual_consoles; i++) {
new VirtualConsole(i);
}
VirtualConsole::switch_to(0);
PCI::initialize();
GraphicsManagement::the().initialize();
ConsoleManagement::the().initialize();
ConsoleManagement::the().switch_to(0);
Thread::initialize();
Process::initialize();
@ -230,11 +231,8 @@ void init_stage2(void*)
SyncTask::spawn();
FinalizerTask::spawn();
PCI::initialize();
auto boot_profiling = kernel_command_line().is_boot_profiling_enabled();
GraphicsManagement::the().initialize();
USB::UHCIController::detect();
DMIExpose::initialize();
@ -274,7 +272,8 @@ void init_stage2(void*)
int error;
// FIXME: It would be nicer to set the mode from userspace.
tty0->set_graphical(!GraphicsManagement::the().is_text_mode_enabled());
// FIXME: It would be smarter to not hardcode that the first tty is the only graphical one
ConsoleManagement::the().first_tty()->set_graphical(GraphicsManagement::the().framebuffer_devices_exist());
RefPtr<Thread> thread;
auto userspace_init = kernel_command_line().userspace_init();
auto init_args = kernel_command_line().userspace_init_args();

View file

@ -7,9 +7,12 @@
#include <AK/PrintfImplementation.h>
#include <AK/Types.h>
#include <Kernel/ConsoleDevice.h>
#include <Kernel/Graphics/Console/Console.h>
#include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/IO.h>
#include <Kernel/Process.h>
#include <Kernel/SpinLock.h>
#include <Kernel/TTY/ConsoleManagement.h>
#include <Kernel/kstdio.h>
#include <LibC/stdarg.h>
@ -60,6 +63,20 @@ static void serial_putch(char ch)
was_cr = false;
}
static void critical_console_out(char ch)
{
if (serial_debug)
serial_putch(ch);
// No need to output things to the real ConsoleDevice as no one is likely
// to read it (because we are in a fatal situation, so only print things and halt)
IO::out8(0xe9, ch);
// We emit chars directly to the string. this is necessary in few cases,
// especially when we want to avoid any memory allocations...
if (GraphicsManagement::is_initialized() && GraphicsManagement::the().console()) {
GraphicsManagement::the().console()->write(ch);
}
}
static void console_out(char ch)
{
if (serial_debug)
@ -72,6 +89,9 @@ static void console_out(char ch)
} else {
IO::out8(0xe9, ch);
}
if (ConsoleManagement::is_initialized()) {
ConsoleManagement::the().debug_tty()->emit_char(ch);
}
}
static void buffer_putch(char*& bufptr, char ch)
@ -145,3 +165,12 @@ extern "C" void kernelputstr(const char* characters, size_t length)
for (size_t i = 0; i < length; ++i)
console_out(characters[i]);
}
extern "C" void kernelcriticalputstr(const char* characters, size_t length)
{
if (!characters)
return;
ScopedSpinLock lock(s_log_lock);
for (size_t i = 0; i < length; ++i)
critical_console_out(characters[i]);
}

View file

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

View file

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

View file

@ -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);
}
};
}

View file

@ -9,57 +9,11 @@
#include <AK/Noncopyable.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibVT/Attribute.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; }
String href;
String href_id;
enum Flags : u8 {
NoAttributes = 0x00,
Bold = 0x01,
Italic = 0x02,
Underline = 0x04,
Negative = 0x08,
Blink = 0x10,
Touched = 0x20,
};
bool is_untouched() const { return !(flags & Touched); }
// TODO: it would be really nice if we had a helper for enums that
// exposed bit ops for class enums...
u8 flags = Flags::NoAttributes;
bool operator==(const Attribute& other) const
{
return foreground_color == other.foreground_color && background_color == other.background_color && flags == other.flags;
}
bool operator!=(const Attribute& other) const
{
return !(*this == other);
}
};
class Line {
AK_MAKE_NONCOPYABLE(Line);
AK_MAKE_NONMOVABLE(Line);

View file

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

View file

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

View file

@ -57,10 +57,19 @@ static void parse_boot_mode()
const String cmdline = String::copy(f->read_all(), Chomp);
dbgln("Read command line: {}", cmdline);
for (auto& part : cmdline.split_view(' ')) {
auto pair = part.split_view('=', 2);
if (pair.size() == 2 && pair[0] == "boot_mode")
g_boot_mode = pair[1];
// FIXME: Support more than one framebuffer detection
struct stat file_state;
int rc = lstat("/dev/fb0", &file_state);
if (rc < 0) {
for (auto& part : cmdline.split_view(' ')) {
auto pair = part.split_view('=', 2);
if (pair.size() == 2 && pair[0] == "boot_mode")
g_boot_mode = pair[1];
}
// We could boot into self-test which is not graphical too.
if (g_boot_mode == "self-test")
return;
g_boot_mode = "text";
}
dbgln("Booting in {} mode", g_boot_mode);
}