mirror of
https://github.com/RGBCube/serenity
synced 2025-05-19 17:25:07 +00:00

If we tried to change the resolution before of this patch, we triggered a kernel crash due to mmaping the framebuffer device again. Therefore, on mmaping of the framebuffer device, we create an entire new set of VMObjects and Regions for the new settings. Then, when we change the resolution, the framebuffersconsole needs to be updated with the new resolution and also to be refreshed with the new settings. To ensure we handle both shrinking of the resolution and growth of it, we only copy the right amount of available data from the cells Region.
155 lines
6.1 KiB
C++
155 lines
6.1 KiB
C++
/*
|
|
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/Checked.h>
|
|
#include <AK/Singleton.h>
|
|
#include <Kernel/Debug.h>
|
|
#include <Kernel/Graphics/FramebufferDevice.h>
|
|
#include <Kernel/Graphics/GraphicsManagement.h>
|
|
#include <Kernel/Process.h>
|
|
#include <Kernel/VM/AnonymousVMObject.h>
|
|
#include <Kernel/VM/MemoryManager.h>
|
|
#include <Kernel/VM/TypedMapping.h>
|
|
#include <LibC/errno_numbers.h>
|
|
#include <LibC/sys/ioctl_numbers.h>
|
|
|
|
#include <Kernel/Panic.h>
|
|
|
|
namespace Kernel {
|
|
|
|
KResultOr<Region*> FramebufferDevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
|
|
{
|
|
REQUIRE_PROMISE(video);
|
|
if (!shared)
|
|
return ENODEV;
|
|
if (offset != 0)
|
|
return ENXIO;
|
|
if (range.size() != page_round_up(framebuffer_size_in_bytes()))
|
|
return EOVERFLOW;
|
|
|
|
auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, page_round_up(framebuffer_size_in_bytes()));
|
|
if (!vmobject)
|
|
return ENOMEM;
|
|
m_userspace_real_framebuffer_vmobject = vmobject;
|
|
|
|
m_real_framebuffer_vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, page_round_up(framebuffer_size_in_bytes()));
|
|
m_swapped_framebuffer_vmobject = AnonymousVMObject::create_with_size(page_round_up(framebuffer_size_in_bytes()), AllocationStrategy::AllocateNow);
|
|
m_real_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, page_round_up(framebuffer_size_in_bytes()), "Framebuffer", Region::Access::Read | Region::Access::Write);
|
|
m_swapped_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, page_round_up(framebuffer_size_in_bytes()), "Framebuffer Swap (Blank)", Region::Access::Read | Region::Access::Write);
|
|
|
|
auto result = process.space().allocate_region_with_vmobject(
|
|
range,
|
|
vmobject.release_nonnull(),
|
|
0,
|
|
"Framebuffer",
|
|
prot,
|
|
shared);
|
|
if (!result.is_error()) {
|
|
m_userspace_framebuffer_region = result.value();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void FramebufferDevice::dectivate_writes()
|
|
{
|
|
ScopedSpinLock lock(m_activation_lock);
|
|
if (!m_userspace_framebuffer_region)
|
|
return;
|
|
memcpy(m_swapped_framebuffer_region->vaddr().as_ptr(), m_real_framebuffer_region->vaddr().as_ptr(), page_round_up(framebuffer_size_in_bytes()));
|
|
auto vmobject = m_swapped_framebuffer_vmobject;
|
|
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
|
m_userspace_framebuffer_region->remap();
|
|
}
|
|
void FramebufferDevice::activate_writes()
|
|
{
|
|
ScopedSpinLock lock(m_activation_lock);
|
|
if (!m_userspace_framebuffer_region || !m_real_framebuffer_vmobject)
|
|
return;
|
|
// restore the image we had in the void area
|
|
// FIXME: if we happen to have multiple Framebuffers that are writing to that location
|
|
// we will experience glitches...
|
|
memcpy(m_real_framebuffer_region->vaddr().as_ptr(), m_swapped_framebuffer_region->vaddr().as_ptr(), page_round_up(framebuffer_size_in_bytes()));
|
|
auto vmobject = m_userspace_real_framebuffer_vmobject;
|
|
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
|
m_userspace_framebuffer_region->remap();
|
|
}
|
|
|
|
String FramebufferDevice::device_name() const
|
|
{
|
|
return String::formatted("fb{}", minor());
|
|
}
|
|
|
|
UNMAP_AFTER_INIT void FramebufferDevice::initialize()
|
|
{
|
|
m_real_framebuffer_vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, page_round_up(framebuffer_size_in_bytes()));
|
|
VERIFY(m_real_framebuffer_vmobject);
|
|
m_real_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, page_round_up(framebuffer_size_in_bytes()), "Framebuffer", Region::Access::Read | Region::Access::Write);
|
|
VERIFY(m_real_framebuffer_region);
|
|
m_swapped_framebuffer_vmobject = AnonymousVMObject::create_with_size(page_round_up(framebuffer_size_in_bytes()), AllocationStrategy::AllocateNow);
|
|
VERIFY(m_swapped_framebuffer_vmobject);
|
|
m_swapped_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, page_round_up(framebuffer_size_in_bytes()), "Framebuffer Swap (Blank)", Region::Access::Read | Region::Access::Write);
|
|
VERIFY(m_swapped_framebuffer_region);
|
|
}
|
|
|
|
UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size_t width, size_t height, size_t pitch)
|
|
: BlockDevice(29, GraphicsManagement::the().allocate_minor_device_number())
|
|
, m_framebuffer_address(addr)
|
|
, m_framebuffer_pitch(pitch)
|
|
, m_framebuffer_width(width)
|
|
, m_framebuffer_height(height)
|
|
{
|
|
VERIFY(!m_framebuffer_address.is_null());
|
|
VERIFY(m_framebuffer_pitch);
|
|
VERIFY(m_framebuffer_width);
|
|
VERIFY(m_framebuffer_height);
|
|
dbgln("Framebuffer {}: address={}, pitch={}, width={}, height={}", minor(), addr, pitch, width, height);
|
|
}
|
|
|
|
bool FramebufferDevice::set_resolution(size_t, size_t, size_t)
|
|
{
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
int FramebufferDevice::ioctl(FileDescription&, unsigned request, FlatPtr arg)
|
|
{
|
|
REQUIRE_PROMISE(video);
|
|
switch (request) {
|
|
case FB_IOCTL_GET_SIZE_IN_BYTES: {
|
|
auto* out = (size_t*)arg;
|
|
size_t value = framebuffer_size_in_bytes();
|
|
if (!copy_to_user(out, &value))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
case FB_IOCTL_GET_BUFFER: {
|
|
return -ENOTIMPL;
|
|
}
|
|
case FB_IOCTL_GET_RESOLUTION: {
|
|
auto* user_resolution = (FBResolution*)arg;
|
|
FBResolution resolution;
|
|
resolution.pitch = m_framebuffer_pitch;
|
|
resolution.width = m_framebuffer_width;
|
|
resolution.height = m_framebuffer_height;
|
|
if (!copy_to_user(user_resolution, &resolution))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
case FB_IOCTL_SET_RESOLUTION: {
|
|
auto* user_resolution = (FBResolution*)arg;
|
|
FBResolution resolution;
|
|
resolution.pitch = m_framebuffer_pitch;
|
|
resolution.width = m_framebuffer_width;
|
|
resolution.height = m_framebuffer_height;
|
|
if (!copy_to_user(user_resolution, &resolution))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
};
|
|
}
|
|
|
|
}
|