Kernel: Add very simple BGA driver

Only thing this is currently helpful is qemu when booting with -kernel
flag as that does not provide framebuffer info in multiboot headers
This commit is contained in:
2026-08-24 01:35:06 +03:00
parent 9a9a4fe8c0
commit cd6269d406
11 changed files with 442 additions and 37 deletions
+69 -11
View File
@@ -2,6 +2,7 @@
#include <kernel/Device/DeviceNumbers.h>
#include <kernel/Device/FramebufferDevice.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/Graphics/BGA.h>
#include <kernel/Memory/Heap.h>
#include <kernel/Terminal/FramebufferTerminal.h>
@@ -26,19 +27,14 @@ namespace Kernel
return s_boot_framebuffer;
}
BAN::ErrorOr<BAN::RefPtr<FramebufferDevice>> FramebufferDevice::create_from_boot_framebuffer()
BAN::ErrorOr<BAN::RefPtr<FramebufferDevice>> FramebufferDevice::create(paddr_t paddr, uint32_t width, uint32_t height, uint32_t pitch, uint8_t bpp)
{
ASSERT(g_boot_info.framebuffer.type == FramebufferInfo::Type::RGB);
if (g_boot_info.framebuffer.bpp != 24 && g_boot_info.framebuffer.bpp != 32)
if (bpp != 24 && bpp != 32)
return BAN::Error::from_errno(ENOTSUP);
auto* device_ptr = new FramebufferDevice(
0660, 0, 900,
makedev(DeviceNumber::Framebuffer, get_framebuffer_device_index()),
g_boot_info.framebuffer.address,
g_boot_info.framebuffer.width,
g_boot_info.framebuffer.height,
g_boot_info.framebuffer.pitch,
g_boot_info.framebuffer.bpp
paddr, width, height, pitch, bpp
);
if (device_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
@@ -49,6 +45,33 @@ namespace Kernel
return device;
}
BAN::ErrorOr<BAN::RefPtr<FramebufferDevice>> FramebufferDevice::create(BAN::RefPtr<BGAController> bga_controller)
{
const auto fix_info = bga_controller->get_fb_fix_info();
const auto var_info = bga_controller->get_fb_var_info();
auto controller = TRY(create(
fix_info.mem_start,
var_info.xres,
var_info.yres,
fix_info.pitch,
var_info.bpp
));
controller->m_bga_controller = bga_controller;
return controller;
}
BAN::ErrorOr<BAN::RefPtr<FramebufferDevice>> FramebufferDevice::create_from_boot_framebuffer()
{
s_boot_framebuffer = TRY(create(
g_boot_info.framebuffer.address,
g_boot_info.framebuffer.width,
g_boot_info.framebuffer.height,
g_boot_info.framebuffer.pitch,
g_boot_info.framebuffer.bpp
));
return s_boot_framebuffer;
}
FramebufferDevice::FramebufferDevice(mode_t mode, uid_t uid, gid_t gid, dev_t rdev, paddr_t paddr, uint32_t width, uint32_t height, uint32_t pitch, uint8_t bpp)
: CharacterDevice(mode, uid, gid)
, m_name(MUST(BAN::String::formatted("fb{}", minor(rdev))))
@@ -65,16 +88,20 @@ namespace Kernel
{
if (m_video_memory_vaddr == 0)
return;
size_t video_memory_pages = range_page_count(m_video_memory_paddr, m_height * m_pitch);
const size_t video_memory_bytes = m_bga_controller ? m_bga_controller->get_fb_fix_info().mem_size : m_height * m_pitch;
const size_t video_memory_pages = range_page_count(m_video_memory_paddr, video_memory_bytes);
PageTable::kernel().unmap_range(m_video_memory_vaddr, video_memory_pages * PAGE_SIZE);
}
BAN::ErrorOr<void> FramebufferDevice::initialize()
{
size_t video_memory_pages = range_page_count(m_video_memory_paddr, m_height * m_pitch);
const size_t video_memory_bytes = m_bga_controller ? m_bga_controller->get_fb_fix_info().mem_size : m_height * m_pitch;
const size_t video_memory_pages = range_page_count(m_video_memory_paddr, video_memory_bytes);
m_video_memory_vaddr = PageTable::kernel().reserve_free_contiguous_pages(video_memory_pages, KERNEL_OFFSET);
if (m_video_memory_vaddr == 0)
return BAN::Error::from_errno(ENOMEM);
PageTable::kernel().map_range_at(
m_video_memory_paddr & PAGE_ADDR_MASK,
m_video_memory_vaddr,
@@ -86,7 +113,7 @@ namespace Kernel
m_video_buffer = TRY(VirtualRange::create_to_vaddr_range(
PageTable::kernel(),
{ KERNEL_OFFSET, UINTPTR_MAX },
BAN::Math::div_round_up<size_t>(m_width * m_height * (BANAN_FB_BPP / 8), PAGE_SIZE) * PAGE_SIZE,
video_memory_pages * PAGE_SIZE,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
false
));
@@ -94,6 +121,37 @@ namespace Kernel
return {};
}
BAN::ErrorOr<void> FramebufferDevice::set_bga_controller(BAN::RefPtr<BGAController> bga_controller)
{
ASSERT(!m_bga_controller);
if (auto var_info = bga_controller->get_fb_var_info(); var_info.bpp != 32)
{
var_info.bpp = 32;
TRY(bga_controller->set_fb_var_info(var_info));
}
const auto fix_info = bga_controller->get_fb_fix_info();
const auto var_info = bga_controller->get_fb_var_info();
ASSERT(m_video_memory_vaddr);
PageTable::kernel().unmap_range(m_video_memory_vaddr, m_height * m_pitch);
m_video_memory_vaddr = 0;
m_video_memory_paddr = fix_info.mem_start;
m_width = var_info.xres;
m_height = var_info.yres;
m_pitch = fix_info.pitch;
m_bpp = var_info.bpp;
m_bga_controller = bga_controller;
TRY(initialize());
return {};
}
BAN::ErrorOr<size_t> FramebufferDevice::read_impl(off_t offset, BAN::ByteSpan buffer)
{
// Reading from negative offset will fill buffer with framebuffer info