Kernel: Add support for text mode terminal

This probably won't be used at all but it was so simple and made me do
really nice refactorings so i decided to add it :)
This commit is contained in:
Bananymous 2025-04-18 02:45:06 +03:00
parent 40d1d20cd6
commit 7c6966a9c4
8 changed files with 202 additions and 2 deletions

View File

@ -90,6 +90,7 @@ set(KERNEL_SOURCES
kernel/Terminal/PseudoTerminal.cpp kernel/Terminal/PseudoTerminal.cpp
kernel/Terminal/Serial.cpp kernel/Terminal/Serial.cpp
kernel/Terminal/TerminalDriver.cpp kernel/Terminal/TerminalDriver.cpp
kernel/Terminal/TextModeTerminal.cpp
kernel/Terminal/TTY.cpp kernel/Terminal/TTY.cpp
kernel/Terminal/VirtualTTY.cpp kernel/Terminal/VirtualTTY.cpp
kernel/Thread.cpp kernel/Thread.cpp

View File

@ -4,6 +4,7 @@
#define BANAN_BOOTLOADER_MAGIC 0xD3C60CFF #define BANAN_BOOTLOADER_MAGIC 0xD3C60CFF
#define BANAN_BOOTLOADER_FB_RGB 1 #define BANAN_BOOTLOADER_FB_RGB 1
#define BANAN_BOOTLOADER_FB_TEXT 2
struct BananBootFramebufferInfo struct BananBootFramebufferInfo
{ {

View File

@ -15,6 +15,7 @@ namespace Kernel
None, None,
Unknown, Unknown,
RGB, RGB,
Text,
}; };
paddr_t address; paddr_t address;

View File

@ -0,0 +1,42 @@
#pragma once
#include <kernel/Terminal/TerminalDriver.h>
namespace Kernel
{
class TextModeTerminalDriver final : public TerminalDriver
{
public:
static BAN::ErrorOr<BAN::RefPtr<TextModeTerminalDriver>> create_from_boot_info();
~TextModeTerminalDriver();
uint32_t width() const override { return m_width; }
uint32_t height() const override { return m_height; }
void putchar_at(uint16_t, uint32_t, uint32_t, Color, Color) override;
void clear(Color) override;
void set_cursor_shown(bool) override;
void set_cursor_position(uint32_t, uint32_t) override;
private:
TextModeTerminalDriver(paddr_t paddr, uint32_t width, uint32_t height, uint32_t pitch)
: m_paddr(paddr)
, m_width(width)
, m_height(height)
, m_pitch(pitch)
{}
BAN::ErrorOr<void> initialize();
private:
const paddr_t m_paddr;
const uint32_t m_width;
const uint32_t m_height;
const uint32_t m_pitch;
vaddr_t m_vaddr { 0 };
static constexpr Color s_cursor_color = TerminalColor::BRIGHT_WHITE;
};
}

View File

@ -12,6 +12,7 @@
#define MULTIBOOT2_TAG_NEW_RSDP 15 #define MULTIBOOT2_TAG_NEW_RSDP 15
#define MULTIBOOT2_FRAMEBUFFER_TYPE_RGB 1 #define MULTIBOOT2_FRAMEBUFFER_TYPE_RGB 1
#define MULTIBOOT2_FRAMEBUFFER_TYPE_TEXT 2
#define MULTIBOOT2_MAGIC 0x36d76289 #define MULTIBOOT2_MAGIC 0x36d76289

View File

@ -41,6 +41,8 @@ namespace Kernel
g_boot_info.framebuffer.bpp = framebuffer_tag.framebuffer_bpp; g_boot_info.framebuffer.bpp = framebuffer_tag.framebuffer_bpp;
if (framebuffer_tag.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_RGB) if (framebuffer_tag.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_RGB)
g_boot_info.framebuffer.type = FramebufferInfo::Type::RGB; g_boot_info.framebuffer.type = FramebufferInfo::Type::RGB;
else if (framebuffer_tag.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_TEXT)
g_boot_info.framebuffer.type = FramebufferInfo::Type::Text;
else else
g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown; g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown;
} }
@ -107,6 +109,8 @@ namespace Kernel
g_boot_info.framebuffer.bpp = framebuffer.bpp; g_boot_info.framebuffer.bpp = framebuffer.bpp;
if (framebuffer.type == BANAN_BOOTLOADER_FB_RGB) if (framebuffer.type == BANAN_BOOTLOADER_FB_RGB)
g_boot_info.framebuffer.type = FramebufferInfo::Type::RGB; g_boot_info.framebuffer.type = FramebufferInfo::Type::RGB;
else if (framebuffer.type == BANAN_BOOTLOADER_FB_TEXT)
g_boot_info.framebuffer.type = FramebufferInfo::Type::Text;
else else
g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown; g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown;

View File

@ -1,5 +1,6 @@
#include <kernel/BootInfo.h> #include <kernel/BootInfo.h>
#include <kernel/Terminal/FramebufferTerminal.h> #include <kernel/Terminal/FramebufferTerminal.h>
#include <kernel/Terminal/TextModeTerminal.h>
namespace Kernel namespace Kernel
{ {
@ -18,6 +19,9 @@ namespace Kernel
TRY(FramebufferDevice::create_from_boot_framebuffer()) TRY(FramebufferDevice::create_from_boot_framebuffer())
)); ));
break; break;
case FramebufferInfo::Type::Text:
g_terminal_driver = TRY(TextModeTerminalDriver::create_from_boot_info());
break;
} }
return {}; return {};
} }

View File

@ -0,0 +1,146 @@
#include <kernel/BootInfo.h>
#include <kernel/IO.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/MMIO.h>
#include <kernel/Terminal/TextModeTerminal.h>
namespace Kernel
{
static constexpr TerminalDriver::Color s_palette[] {
TerminalColor::BLACK,
TerminalColor::BLUE,
TerminalColor::GREEN,
TerminalColor::CYAN,
TerminalColor::RED,
TerminalColor::MAGENTA,
TerminalColor::YELLOW,
TerminalColor::WHITE,
TerminalColor::BRIGHT_BLACK,
TerminalColor::BRIGHT_BLUE,
TerminalColor::BRIGHT_GREEN,
TerminalColor::BRIGHT_CYAN,
TerminalColor::BRIGHT_RED,
TerminalColor::BRIGHT_MAGENTA,
TerminalColor::BRIGHT_YELLOW,
TerminalColor::BRIGHT_WHITE,
};
static constexpr uint8_t color_to_text_mode_color(TerminalDriver::Color color)
{
uint32_t min_diff = BAN::numeric_limits<uint32_t>::max();
uint8_t closest = 0;
static_assert(sizeof(s_palette) / sizeof(*s_palette) == 16);
for (size_t i = 0; i < 16; i++)
{
const auto rdiff = color.red() - s_palette[i].red();
const auto gdiff = color.green() - s_palette[i].green();
const auto bdiff = color.blue() - s_palette[i].blue();
const uint32_t diff = rdiff*rdiff + gdiff*gdiff + bdiff*bdiff;
if (diff >= min_diff)
continue;
min_diff = diff;
closest = i;
}
return closest;
}
BAN::ErrorOr<BAN::RefPtr<TextModeTerminalDriver>> TextModeTerminalDriver::create_from_boot_info()
{
ASSERT(g_boot_info.framebuffer.type == FramebufferInfo::Type::Text);
if (g_boot_info.framebuffer.bpp != 16)
return BAN::Error::from_errno(ENOTSUP);
auto* driver_ptr = new TextModeTerminalDriver(
g_boot_info.framebuffer.address,
g_boot_info.framebuffer.width,
g_boot_info.framebuffer.height,
g_boot_info.framebuffer.pitch
);
if (driver_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto driver = BAN::RefPtr<TextModeTerminalDriver>::adopt(driver_ptr);
TRY(driver->initialize());
return driver;
}
BAN::ErrorOr<void> TextModeTerminalDriver::initialize()
{
const size_t page_count = range_page_count(m_paddr, m_height * m_pitch);
const vaddr_t vaddr = PageTable::kernel().reserve_free_contiguous_pages(page_count, KERNEL_OFFSET);
if (vaddr == 0)
return BAN::Error::from_errno(ENOMEM);
PageTable::kernel().map_range_at(
m_paddr & PAGE_ADDR_MASK,
vaddr,
page_count * PAGE_SIZE,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
PageTable::MemoryType::WriteCombining
);
m_vaddr = vaddr + (m_paddr % PAGE_SIZE);
set_cursor_position(0, 0);
clear(TerminalColor::BLACK);
return {};
}
TextModeTerminalDriver::~TextModeTerminalDriver()
{
if (m_vaddr == 0)
return;
const size_t page_count = range_page_count(m_paddr, m_height * m_pitch);
PageTable::kernel().unmap_range(m_vaddr & PAGE_ADDR_MASK, page_count * PAGE_SIZE);
}
void TextModeTerminalDriver::putchar_at(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg)
{
if (x >= m_width || y >= m_height)
return;
if (ch >= 0x100)
ch = '?';
const uint8_t color =
(color_to_text_mode_color(bg) << 4) |
(color_to_text_mode_color(fg) << 0);
MMIO::write16(m_vaddr + y * m_pitch + 2 * x, ch | (color << 8));
}
void TextModeTerminalDriver::clear(Color color)
{
for (uint32_t y = 0; y < m_height; y++)
for (uint32_t x = 0; x < m_width; x++)
putchar_at(' ', x, y, TerminalColor::BRIGHT_WHITE, color);
}
void TextModeTerminalDriver::set_cursor_shown(bool shown)
{
if (shown)
{
IO::outb(0x3D4, 0x0A);
IO::outb(0x3D5, (IO::inb(0x3D5) & 0xC0) | 14);
IO::outb(0x3D4, 0x0B);
IO::outb(0x3D5, (IO::inb(0x3D5) & 0xE0) | 15);
}
else
{
IO::outb(0x3D4, 0x0A);
IO::outb(0x3D5, 0x20);
}
}
void TextModeTerminalDriver::set_cursor_position(uint32_t x, uint32_t y)
{
const uint16_t pos = y * m_width + x;
IO::outb(0x3D4, 0x0F);
IO::outb(0x3D5, pos & 0xFF);
IO::outb(0x3D4, 0x0E);
IO::outb(0x3D5, pos >> 8);
}
}