diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 2e52cea77c..2e4bfe317c 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -181,6 +181,7 @@ set(KERNEL_SOURCES Memory/RingBuffer.cpp Memory/ScatterGatherList.cpp Memory/ScopedAddressSpaceSwitcher.cpp + Memory/SharedFramebufferVMObject.cpp Memory/SharedInodeVMObject.cpp Memory/VMObject.cpp Memory/VirtualRange.cpp diff --git a/Kernel/Forward.h b/Kernel/Forward.h index 7fcedb1d2a..667fd605bf 100644 --- a/Kernel/Forward.h +++ b/Kernel/Forward.h @@ -25,6 +25,7 @@ class DiskCache; class DoubleBuffer; class File; class OpenFileDescription; +class DisplayConnector; class FileSystem; class FutexQueue; class IPv4Socket; diff --git a/Kernel/Memory/SharedFramebufferVMObject.cpp b/Kernel/Memory/SharedFramebufferVMObject.cpp new file mode 100644 index 0000000000..f0ea27e874 --- /dev/null +++ b/Kernel/Memory/SharedFramebufferVMObject.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2022, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel::Memory { + +ErrorOr> SharedFramebufferVMObject::try_create_for_physical_range(PhysicalAddress paddr, size_t size) +{ + auto real_framebuffer_vmobject = TRY(AnonymousVMObject::try_create_for_physical_range(paddr, size)); + auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); + auto committed_pages = TRY(MM.commit_user_physical_pages(ceil_div(size, static_cast(PAGE_SIZE)))); + auto vm_object = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) SharedFramebufferVMObject(move(new_physical_pages), move(committed_pages), real_framebuffer_vmobject))); + TRY(vm_object->create_fake_writes_framebuffer_vm_object()); + TRY(vm_object->create_real_writes_framebuffer_vm_object()); + return vm_object; +} + +ErrorOr> SharedFramebufferVMObject::try_create_at_arbitrary_physical_range(size_t size) +{ + auto real_framebuffer_vmobject = TRY(AnonymousVMObject::try_create_with_size(size, AllocationStrategy::AllocateNow)); + auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); + auto committed_pages = TRY(MM.commit_user_physical_pages(ceil_div(size, static_cast(PAGE_SIZE)))); + auto vm_object = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) SharedFramebufferVMObject(move(new_physical_pages), move(committed_pages), real_framebuffer_vmobject))); + TRY(vm_object->create_fake_writes_framebuffer_vm_object()); + TRY(vm_object->create_real_writes_framebuffer_vm_object()); + return vm_object; +} + +ErrorOr> SharedFramebufferVMObject::FakeWritesFramebufferVMObject::try_create(Badge, SharedFramebufferVMObject const& parent_object) +{ + auto new_physical_pages = TRY(VMObject::try_create_physical_pages(0)); + return adopt_nonnull_ref_or_enomem(new (nothrow) FakeWritesFramebufferVMObject(parent_object, move(new_physical_pages))); +} + +ErrorOr> SharedFramebufferVMObject::RealWritesFramebufferVMObject::try_create(Badge, SharedFramebufferVMObject const& parent_object) +{ + auto new_physical_pages = TRY(VMObject::try_create_physical_pages(0)); + return adopt_nonnull_ref_or_enomem(new (nothrow) RealWritesFramebufferVMObject(parent_object, move(new_physical_pages))); +} + +ErrorOr SharedFramebufferVMObject::create_fake_writes_framebuffer_vm_object() +{ + m_fake_writes_framebuffer_vmobject = TRY(FakeWritesFramebufferVMObject::try_create({}, *this)); + return {}; +} + +ErrorOr SharedFramebufferVMObject::create_real_writes_framebuffer_vm_object() +{ + m_real_writes_framebuffer_vmobject = TRY(RealWritesFramebufferVMObject::try_create({}, *this)); + return {}; +} + +Span> SharedFramebufferVMObject::real_framebuffer_physical_pages() +{ + return m_real_framebuffer_vmobject->physical_pages(); +} +Span const> SharedFramebufferVMObject::real_framebuffer_physical_pages() const +{ + return m_real_framebuffer_vmobject->physical_pages(); +} + +Span> SharedFramebufferVMObject::fake_sink_framebuffer_physical_pages() +{ + return m_physical_pages.span(); +} + +Span const> SharedFramebufferVMObject::fake_sink_framebuffer_physical_pages() const +{ + return m_physical_pages.span(); +} + +void SharedFramebufferVMObject::switch_to_fake_sink_framebuffer_writes(Badge) +{ + SpinlockLocker locker(m_writes_state_lock); + m_writes_are_faked = true; + for_each_region([](Region& region) { + region.remap(); + }); +} +void SharedFramebufferVMObject::switch_to_real_framebuffer_writes(Badge) +{ + SpinlockLocker locker(m_writes_state_lock); + m_writes_are_faked = false; + for_each_region([](Region& region) { + region.remap(); + }); +} + +Span const> SharedFramebufferVMObject::physical_pages() const +{ + SpinlockLocker locker(m_writes_state_lock); + if (m_writes_are_faked) + return VMObject::physical_pages(); + return m_real_framebuffer_vmobject->physical_pages(); +} +Span> SharedFramebufferVMObject::physical_pages() +{ + SpinlockLocker locker(m_writes_state_lock); + if (m_writes_are_faked) + return VMObject::physical_pages(); + return m_real_framebuffer_vmobject->physical_pages(); +} + +SharedFramebufferVMObject::SharedFramebufferVMObject(FixedArray>&& new_physical_pages, CommittedPhysicalPageSet committed_pages, AnonymousVMObject& real_framebuffer_vmobject) + : VMObject(move(new_physical_pages)) + , m_real_framebuffer_vmobject(real_framebuffer_vmobject) + , m_committed_pages(move(committed_pages)) +{ + // Allocate all pages right now. We know we can get all because we committed the amount needed + for (size_t i = 0; i < page_count(); ++i) + m_physical_pages[i] = m_committed_pages.take_one(); +} + +} diff --git a/Kernel/Memory/SharedFramebufferVMObject.h b/Kernel/Memory/SharedFramebufferVMObject.h new file mode 100644 index 0000000000..90e195aa34 --- /dev/null +++ b/Kernel/Memory/SharedFramebufferVMObject.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Kernel::Memory { + +class SharedFramebufferVMObject final : public VMObject { +public: + class FakeWritesFramebufferVMObject final : public VMObject { + public: + static ErrorOr> try_create(Badge, SharedFramebufferVMObject const& parent_object); + + private: + FakeWritesFramebufferVMObject(SharedFramebufferVMObject const& parent_object, FixedArray>&& new_physical_pages) + : VMObject(move(new_physical_pages)) + , m_parent_object(parent_object) + { + } + virtual StringView class_name() const override { return "FakeWritesFramebufferVMObject"sv; } + virtual ErrorOr> try_clone() override { return Error::from_errno(ENOTIMPL); } + virtual Span const> physical_pages() const override { return m_parent_object->fake_sink_framebuffer_physical_pages(); } + virtual Span> physical_pages() override { return m_parent_object->fake_sink_framebuffer_physical_pages(); } + NonnullRefPtr m_parent_object; + }; + + class RealWritesFramebufferVMObject final : public VMObject { + public: + static ErrorOr> try_create(Badge, SharedFramebufferVMObject const& parent_object); + + private: + RealWritesFramebufferVMObject(SharedFramebufferVMObject const& parent_object, FixedArray>&& new_physical_pages) + : VMObject(move(new_physical_pages)) + , m_parent_object(parent_object) + { + } + virtual StringView class_name() const override { return "RealWritesFramebufferVMObject"sv; } + virtual ErrorOr> try_clone() override { return Error::from_errno(ENOTIMPL); } + virtual Span const> physical_pages() const override { return m_parent_object->real_framebuffer_physical_pages(); } + virtual Span> physical_pages() override { return m_parent_object->real_framebuffer_physical_pages(); } + NonnullRefPtr m_parent_object; + }; + + virtual ~SharedFramebufferVMObject() override = default; + + static ErrorOr> try_create_for_physical_range(PhysicalAddress paddr, size_t size); + static ErrorOr> try_create_at_arbitrary_physical_range(size_t size); + virtual ErrorOr> try_clone() override { return Error::from_errno(ENOTIMPL); } + + void switch_to_fake_sink_framebuffer_writes(Badge); + void switch_to_real_framebuffer_writes(Badge); + + virtual Span const> physical_pages() const override; + virtual Span> physical_pages() override; + + Span> fake_sink_framebuffer_physical_pages(); + Span const> fake_sink_framebuffer_physical_pages() const; + + Span> real_framebuffer_physical_pages(); + Span const> real_framebuffer_physical_pages() const; + + FakeWritesFramebufferVMObject const& fake_writes_framebuffer_vmobject() const { return *m_fake_writes_framebuffer_vmobject; } + FakeWritesFramebufferVMObject& fake_writes_framebuffer_vmobject() { return *m_fake_writes_framebuffer_vmobject; } + + RealWritesFramebufferVMObject const& real_writes_framebuffer_vmobject() const { return *m_real_writes_framebuffer_vmobject; } + RealWritesFramebufferVMObject& real_writes_framebuffer_vmobject() { return *m_real_writes_framebuffer_vmobject; } + +private: + SharedFramebufferVMObject(FixedArray>&& new_physical_pages, CommittedPhysicalPageSet, AnonymousVMObject& real_framebuffer_vmobject); + + virtual StringView class_name() const override { return "SharedFramebufferVMObject"sv; } + + ErrorOr create_fake_writes_framebuffer_vm_object(); + ErrorOr create_real_writes_framebuffer_vm_object(); + + NonnullRefPtr m_real_framebuffer_vmobject; + RefPtr m_fake_writes_framebuffer_vmobject; + RefPtr m_real_writes_framebuffer_vmobject; + bool m_writes_are_faked { false }; + mutable RecursiveSpinlock m_writes_state_lock; + CommittedPhysicalPageSet m_committed_pages; +}; + +} diff --git a/Kernel/Memory/VMObject.h b/Kernel/Memory/VMObject.h index 24898722a8..7432181ecf 100644 --- a/Kernel/Memory/VMObject.h +++ b/Kernel/Memory/VMObject.h @@ -34,8 +34,9 @@ public: virtual bool is_private_inode() const { return false; } size_t page_count() const { return m_physical_pages.size(); } - Span const> physical_pages() const { return m_physical_pages.span(); } - Span> physical_pages() { return m_physical_pages.span(); } + + virtual Span const> physical_pages() const { return m_physical_pages.span(); } + virtual Span> physical_pages() { return m_physical_pages.span(); } size_t size() const { return m_physical_pages.size() * PAGE_SIZE; }