From 7c6966a9c45a8a53f37d3c64c3b7333634ea234c Mon Sep 17 00:00:00 2001 From: Bananymous Date: Fri, 18 Apr 2025 02:45:06 +0300 Subject: [PATCH] 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 :) --- kernel/CMakeLists.txt | 1 + kernel/include/kernel/BananBootloader.h | 3 +- kernel/include/kernel/BootInfo.h | 1 + .../kernel/Terminal/TextModeTerminal.h | 42 +++++ kernel/include/kernel/multiboot2.h | 3 +- kernel/kernel/BootInfo.cpp | 4 + kernel/kernel/Terminal/TerminalDriver.cpp | 4 + kernel/kernel/Terminal/TextModeTerminal.cpp | 146 ++++++++++++++++++ 8 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 kernel/include/kernel/Terminal/TextModeTerminal.h create mode 100644 kernel/kernel/Terminal/TextModeTerminal.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index b03718ad..4bb961bb 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -90,6 +90,7 @@ set(KERNEL_SOURCES kernel/Terminal/PseudoTerminal.cpp kernel/Terminal/Serial.cpp kernel/Terminal/TerminalDriver.cpp + kernel/Terminal/TextModeTerminal.cpp kernel/Terminal/TTY.cpp kernel/Terminal/VirtualTTY.cpp kernel/Thread.cpp diff --git a/kernel/include/kernel/BananBootloader.h b/kernel/include/kernel/BananBootloader.h index 8991b0ca..d81f29e9 100644 --- a/kernel/include/kernel/BananBootloader.h +++ b/kernel/include/kernel/BananBootloader.h @@ -3,7 +3,8 @@ #include #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 { diff --git a/kernel/include/kernel/BootInfo.h b/kernel/include/kernel/BootInfo.h index 452f9b3d..9f5b28ce 100644 --- a/kernel/include/kernel/BootInfo.h +++ b/kernel/include/kernel/BootInfo.h @@ -15,6 +15,7 @@ namespace Kernel None, Unknown, RGB, + Text, }; paddr_t address; diff --git a/kernel/include/kernel/Terminal/TextModeTerminal.h b/kernel/include/kernel/Terminal/TextModeTerminal.h new file mode 100644 index 00000000..0dcf88fb --- /dev/null +++ b/kernel/include/kernel/Terminal/TextModeTerminal.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +namespace Kernel +{ + + class TextModeTerminalDriver final : public TerminalDriver + { + public: + static BAN::ErrorOr> 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 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; + }; + +} diff --git a/kernel/include/kernel/multiboot2.h b/kernel/include/kernel/multiboot2.h index 6723a8de..8ae7c9eb 100644 --- a/kernel/include/kernel/multiboot2.h +++ b/kernel/include/kernel/multiboot2.h @@ -11,7 +11,8 @@ #define MULTIBOOT2_TAG_OLD_RSDP 14 #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 diff --git a/kernel/kernel/BootInfo.cpp b/kernel/kernel/BootInfo.cpp index d9712783..56bd5779 100644 --- a/kernel/kernel/BootInfo.cpp +++ b/kernel/kernel/BootInfo.cpp @@ -41,6 +41,8 @@ namespace Kernel g_boot_info.framebuffer.bpp = framebuffer_tag.framebuffer_bpp; if (framebuffer_tag.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_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 g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown; } @@ -107,6 +109,8 @@ namespace Kernel g_boot_info.framebuffer.bpp = framebuffer.bpp; if (framebuffer.type == BANAN_BOOTLOADER_FB_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 g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown; diff --git a/kernel/kernel/Terminal/TerminalDriver.cpp b/kernel/kernel/Terminal/TerminalDriver.cpp index 799469d7..11316c21 100644 --- a/kernel/kernel/Terminal/TerminalDriver.cpp +++ b/kernel/kernel/Terminal/TerminalDriver.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace Kernel { @@ -18,6 +19,9 @@ namespace Kernel TRY(FramebufferDevice::create_from_boot_framebuffer()) )); break; + case FramebufferInfo::Type::Text: + g_terminal_driver = TRY(TextModeTerminalDriver::create_from_boot_info()); + break; } return {}; } diff --git a/kernel/kernel/Terminal/TextModeTerminal.cpp b/kernel/kernel/Terminal/TextModeTerminal.cpp new file mode 100644 index 00000000..63d30665 --- /dev/null +++ b/kernel/kernel/Terminal/TextModeTerminal.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +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::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> 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::adopt(driver_ptr); + TRY(driver->initialize()); + return driver; + } + + BAN::ErrorOr 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); + } + +}