diff --git a/kernel/include/kernel/Device/FramebufferDevice.h b/kernel/include/kernel/Device/FramebufferDevice.h index 313e42c5..a6c20eaf 100644 --- a/kernel/include/kernel/Device/FramebufferDevice.h +++ b/kernel/include/kernel/Device/FramebufferDevice.h @@ -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> 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 m_video_buffer; + + friend class FramebufferMemoryRegion; }; } \ No newline at end of file diff --git a/kernel/kernel/Device/FramebufferDevice.cpp b/kernel/kernel/Device/FramebufferDevice.cpp index 1d40f8d0..48d7dfc3 100644 --- a/kernel/kernel/Device/FramebufferDevice.cpp +++ b/kernel/kernel/Device/FramebufferDevice.cpp @@ -3,6 +3,9 @@ #include #include +#include +#include + namespace Kernel { @@ -190,4 +193,86 @@ namespace Kernel } } + class FramebufferMemoryRegion : public MemoryRegion + { + public: + static BAN::ErrorOr> create(PageTable& page_table, size_t size, AddressRange address_range, MemoryRegion::Type region_type, PageTable::flags_t page_flags, BAN::RefPtr 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::adopt(region_ptr); + + TRY(region->initialize(address_range)); + + return region; + } + + ~FramebufferMemoryRegion() + { + m_framebuffer->sync_pixels_full(); + } + + virtual BAN::ErrorOr 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((vaddr % bytes_per_pixel_internal) + size, bytes_per_pixel_internal) + ); + + return {}; + } + + virtual BAN::ErrorOr> 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 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 framebuffer) + : MemoryRegion(page_table, size, region_type, page_flags) + , m_framebuffer(framebuffer) + { } + + private: + BAN::RefPtr m_framebuffer; + }; + + BAN::ErrorOr> 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(BAN::move(region)); + } + } \ No newline at end of file