Kernel: Implement mmaping for framebuffer device

This commit is contained in:
Bananymous 2023-11-28 23:51:56 +02:00
parent 4275d2ce48
commit cc572af390
2 changed files with 89 additions and 0 deletions

View File

@ -21,6 +21,8 @@ namespace Kernel
void sync_pixels_linear(uint32_t first_pixel, uint32_t pixel_count);
void sync_pixels_rectangle(uint32_t top_right_x, uint32_t top_right_y, uint32_t width, uint32_t height);
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> mmap_region(PageTable&, off_t offset, size_t len, AddressRange, MemoryRegion::Type, PageTable::flags_t) override;
virtual dev_t rdev() const override { return m_rdev; }
virtual BAN::StringView name() const override { return m_name.sv(); }
@ -44,6 +46,8 @@ namespace Kernel
const uint8_t m_bpp;
BAN::UniqPtr<VirtualRange> m_video_buffer;
friend class FramebufferMemoryRegion;
};
}

View File

@ -3,6 +3,9 @@
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/Memory/Heap.h>
#include <sys/framebuffer.h>
#include <sys/mman.h>
namespace Kernel
{
@ -190,4 +193,86 @@ namespace Kernel
}
}
class FramebufferMemoryRegion : public MemoryRegion
{
public:
static BAN::ErrorOr<BAN::UniqPtr<FramebufferMemoryRegion>> create(PageTable& page_table, size_t size, AddressRange address_range, MemoryRegion::Type region_type, PageTable::flags_t page_flags, BAN::RefPtr<FramebufferDevice> framebuffer)
{
auto* region_ptr = new FramebufferMemoryRegion(page_table, size, region_type, page_flags, framebuffer);
if (region_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto region = BAN::UniqPtr<FramebufferMemoryRegion>::adopt(region_ptr);
TRY(region->initialize(address_range));
return region;
}
~FramebufferMemoryRegion()
{
m_framebuffer->sync_pixels_full();
}
virtual BAN::ErrorOr<void> msync(vaddr_t vaddr, size_t size, int flags) override
{
if (flags != MS_SYNC)
return BAN::Error::from_errno(ENOTSUP);
if (vaddr < m_vaddr)
vaddr = m_vaddr;
if (vaddr + size > m_vaddr + m_size)
size = (vaddr - m_vaddr) + m_size;
m_framebuffer->sync_pixels_linear(
(vaddr - m_vaddr) / bytes_per_pixel_internal,
BAN::Math::div_round_up<uint32_t>((vaddr % bytes_per_pixel_internal) + size, bytes_per_pixel_internal)
);
return {};
}
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override
{
return BAN::Error::from_errno(ENOTSUP);
}
protected:
// Returns error if no memory was available
// Returns true if page was succesfully allocated
// Returns false if page was already allocated
virtual BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr) override
{
vaddr &= PAGE_ADDR_MASK;
if (m_page_table.physical_address_of(vaddr))
return false;
paddr_t paddr = PageTable::kernel().physical_address_of(m_framebuffer->m_video_buffer->vaddr() + (vaddr - m_vaddr));
m_page_table.map_page_at(paddr, vaddr, m_flags);
return true;
}
private:
FramebufferMemoryRegion(PageTable& page_table, size_t size, MemoryRegion::Type region_type, PageTable::flags_t page_flags, BAN::RefPtr<FramebufferDevice> framebuffer)
: MemoryRegion(page_table, size, region_type, page_flags)
, m_framebuffer(framebuffer)
{ }
private:
BAN::RefPtr<FramebufferDevice> m_framebuffer;
};
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> FramebufferDevice::mmap_region(PageTable& page_table, off_t offset, size_t len, AddressRange address_range, MemoryRegion::Type region_type, PageTable::flags_t page_flags)
{
if (offset != 0)
return BAN::Error::from_errno(EINVAL);
if (len > m_video_buffer->size())
return BAN::Error::from_errno(EINVAL);
if (region_type != MemoryRegion::Type::SHARED)
return BAN::Error::from_errno(EINVAL);
auto region = TRY(FramebufferMemoryRegion::create(page_table, len, address_range, region_type, page_flags, this));
return BAN::UniqPtr<MemoryRegion>(BAN::move(region));
}
}