Kernel: Introduce a new graphics subsystem

This new subsystem is replacing the old code that was used to
create device nodes of framebuffer devices in /dev.

This subsystem includes for now 3 roles:
1. GraphicsManagement singleton object that is used in the boot
process to enumerate and initialize display devices.
2. GraphicsDevice(s) that are used to control the display adapter.
3. FramebufferDevice(s) that are used to control the device node in
/dev.

For now, we support the Bochs display adapter and any other
generic VGA compatible adapter that was configured by the boot
loader to a known and fixed resolution.

Two improvements in the Bochs display adapter code are that
we can support native bochs-display device (this device doesn't
expose any VGA capabilities) and also that we use the MMIO region,
to configure the device, instead of setting IO ports for such tasks.
This commit is contained in:
Liav A 2021-03-05 14:23:08 +02:00 committed by Andreas Kling
parent 8515a1c49d
commit 6a728e2d76
Notes: sideshowbarker 2024-07-18 18:01:09 +09:00
18 changed files with 672 additions and 428 deletions

View file

@ -27,12 +27,10 @@ set(KERNEL_SOURCES
CoreDump.cpp
DMI.cpp
Devices/AsyncDeviceRequest.cpp
Devices/BXVGADevice.cpp
Devices/BlockDevice.cpp
Devices/CharacterDevice.cpp
Devices/Device.cpp
Devices/FullDevice.cpp
Devices/MBVGADevice.cpp
Devices/MemoryDevice.cpp
Devices/NullDevice.cpp
Devices/PCSpeaker.cpp
@ -52,6 +50,12 @@ set(KERNEL_SOURCES
Devices/HID/PS2KeyboardDevice.cpp
Devices/HID/PS2MouseDevice.cpp
Devices/HID/VMWareMouseDevice.cpp
Graphics/BochsFramebufferDevice.cpp
Graphics/BochsGraphicsAdapter.cpp
Graphics/FramebufferDevice.cpp
Graphics/GraphicsManagement.cpp
Graphics/RawFramebufferDevice.cpp
Graphics/VGACompatibleAdapter.cpp
Storage/Partition/DiskPartition.cpp
Storage/Partition/DiskPartitionMetadata.cpp
Storage/Partition/EBRPartitionTable.cpp

View file

@ -1,290 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Checked.h>
#include <AK/Format.h>
#include <AK/Singleton.h>
#include <Kernel/Debug.h>
#include <Kernel/Devices/BXVGADevice.h>
#include <Kernel/IO.h>
#include <Kernel/PCI/Access.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>
namespace Kernel {
#define MAX_RESOLUTION_WIDTH 4096
#define MAX_RESOLUTION_HEIGHT 2160
#define VBE_DISPI_IOPORT_INDEX 0x01CE
#define VBE_DISPI_IOPORT_DATA 0x01CF
#define VBE_DISPI_INDEX_ID 0x0
#define VBE_DISPI_INDEX_XRES 0x1
#define VBE_DISPI_INDEX_YRES 0x2
#define VBE_DISPI_INDEX_BPP 0x3
#define VBE_DISPI_INDEX_ENABLE 0x4
#define VBE_DISPI_INDEX_BANK 0x5
#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
#define VBE_DISPI_INDEX_X_OFFSET 0x8
#define VBE_DISPI_INDEX_Y_OFFSET 0x9
#define VBE_DISPI_DISABLED 0x00
#define VBE_DISPI_ENABLED 0x01
#define VBE_DISPI_LFB_ENABLED 0x40
static AK::Singleton<BXVGADevice> s_the;
UNMAP_AFTER_INIT void BXVGADevice::initialize()
{
s_the.ensure_instance();
}
BXVGADevice& BXVGADevice::the()
{
return *s_the;
}
UNMAP_AFTER_INIT BXVGADevice::BXVGADevice()
: BlockDevice(29, 0)
{
m_framebuffer_address = PhysicalAddress(find_framebuffer_address());
m_mmio_registers = find_mmio_region();
m_vga_compatible = is_vga_compatible();
set_register(VBE_DISPI_INDEX_ID, 0xB0C0);
dmesgln("BXVGA: ID {}", get_register(VBE_DISPI_INDEX_ID));
set_safe_resolution();
}
void BXVGADevice::set_safe_resolution()
{
m_framebuffer_width = 1024;
m_framebuffer_height = 768;
m_framebuffer_pitch = m_framebuffer_width * sizeof(u32);
set_resolution(m_framebuffer_width, m_framebuffer_height);
}
void BXVGADevice::set_register(u16 index, u16 data)
{
if (m_vga_compatible) {
IO::out16(VBE_DISPI_IOPORT_INDEX, index);
IO::out16(VBE_DISPI_IOPORT_DATA, data);
return;
}
auto reg = map_typed_writable<u16>(m_mmio_registers.offset(index * 2));
*(reg.ptr()) = data;
}
u16 BXVGADevice::get_register(u16 index)
{
if (m_vga_compatible) {
IO::out16(VBE_DISPI_IOPORT_INDEX, index);
return IO::in16(VBE_DISPI_IOPORT_DATA);
}
auto reg = map_typed_writable<u16>(m_mmio_registers.offset(index * 2));
return *(reg.ptr());
}
void BXVGADevice::revert_resolution()
{
set_resolution_registers(m_framebuffer_width, m_framebuffer_height);
VERIFY(validate_setup_resolution(m_framebuffer_width, m_framebuffer_height));
}
void BXVGADevice::set_resolution_registers(size_t width, size_t height)
{
dbgln_if(BXVGA_DEBUG, "BXVGADevice resolution registers set to - {}x{}", width, height);
set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
set_register(VBE_DISPI_INDEX_XRES, (u16)width);
set_register(VBE_DISPI_INDEX_YRES, (u16)height);
set_register(VBE_DISPI_INDEX_VIRT_WIDTH, (u16)width);
set_register(VBE_DISPI_INDEX_VIRT_HEIGHT, (u16)height * 2);
set_register(VBE_DISPI_INDEX_BPP, 32);
set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
set_register(VBE_DISPI_INDEX_BANK, 0);
}
bool BXVGADevice::test_resolution(size_t width, size_t height)
{
dbgln_if(BXVGA_DEBUG, "BXVGADevice resolution test - {}x{}", width, height);
set_resolution_registers(width, height);
bool resolution_changed = validate_setup_resolution(width, height);
revert_resolution();
return resolution_changed;
}
bool BXVGADevice::set_resolution(size_t width, size_t height)
{
if (Checked<size_t>::multiplication_would_overflow(width, height, sizeof(u32)))
return false;
if (!test_resolution(width, height))
return false;
set_resolution_registers(width, height);
dbgln("BXVGADevice resolution set to {}x{} (pitch={})", width, height, m_framebuffer_pitch);
m_framebuffer_width = width;
m_framebuffer_height = height;
m_framebuffer_pitch = width * sizeof(u32);
return true;
}
bool BXVGADevice::validate_setup_resolution(size_t width, size_t height)
{
if ((u16)width != get_register(VBE_DISPI_INDEX_XRES) || (u16)height != get_register(VBE_DISPI_INDEX_YRES)) {
return false;
}
return true;
}
void BXVGADevice::set_y_offset(size_t y_offset)
{
VERIFY(y_offset == 0 || y_offset == m_framebuffer_height);
m_y_offset = y_offset;
set_register(VBE_DISPI_INDEX_Y_OFFSET, (u16)y_offset);
}
UNMAP_AFTER_INIT u32 BXVGADevice::find_framebuffer_address()
{
// NOTE: The QEMU card has the same PCI ID as the Bochs one.
static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 };
static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef };
u32 framebuffer_address = 0;
PCI::enumerate([&framebuffer_address](const PCI::Address& address, PCI::ID id) {
if (id == bochs_vga_id || id == virtualbox_vga_id) {
framebuffer_address = PCI::get_BAR0(address) & 0xfffffff0;
dbgln("BXVGA: framebuffer @ {}", PhysicalAddress(framebuffer_address));
}
});
return framebuffer_address;
}
UNMAP_AFTER_INIT PhysicalAddress BXVGADevice::find_mmio_region()
{
// NOTE: The QEMU card has the same PCI ID as the Bochs one.
static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 };
static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef };
u32 mmio_region = 0;
PCI::enumerate([&mmio_region](const PCI::Address& address, PCI::ID id) {
if (id == bochs_vga_id || id == virtualbox_vga_id) {
mmio_region = PCI::get_BAR1(address) & 0xfffffff0;
dmesgln("BXVGA: mmio region @ {}", PhysicalAddress(mmio_region));
}
});
return PhysicalAddress(mmio_region);
}
UNMAP_AFTER_INIT bool BXVGADevice::is_vga_compatible()
{
// NOTE: The QEMU card has the same PCI ID as the Bochs one.
static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 };
static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef };
bool vga_compatible = true;
PCI::enumerate([&vga_compatible](const PCI::Address& address, PCI::ID id) {
if (id == bochs_vga_id || id == virtualbox_vga_id) {
if (PCI::get_subclass(address) != 0)
vga_compatible = false;
}
});
return vga_compatible;
}
KResultOr<Region*> BXVGADevice::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, framebuffer_size_in_bytes());
if (!vmobject)
return ENOMEM;
return process.space().allocate_region_with_vmobject(
range,
vmobject.release_nonnull(),
0,
"BXVGA Framebuffer",
prot,
shared);
}
String BXVGADevice::device_name() const
{
return String::formatted("fb{}", minor());
}
int BXVGADevice::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: {
auto* index = (int*)arg;
int value = m_y_offset == 0 ? 0 : 1;
if (!copy_to_user(index, &value))
return -EFAULT;
return 0;
}
case FB_IOCTL_SET_BUFFER: {
if (arg != 0 && arg != 1)
return -EINVAL;
set_y_offset(arg == 0 ? 0 : m_framebuffer_height);
return 0;
}
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;
if (!copy_from_user(&resolution, user_resolution))
return -EFAULT;
if (resolution.width > MAX_RESOLUTION_WIDTH || resolution.height > MAX_RESOLUTION_HEIGHT)
return -EINVAL;
if (!set_resolution(resolution.width, resolution.height)) {
dbgln_if(BXVGA_DEBUG, "Reverting resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
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 -EINVAL;
}
dbgln_if(BXVGA_DEBUG, "New resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
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;
};
}
}

View file

@ -1,64 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/Types.h>
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/PhysicalAddress.h>
namespace Kernel {
class BXVGADevice final : public BlockDevice {
AK_MAKE_ETERNAL
public:
static void initialize();
static BXVGADevice& the();
BXVGADevice();
virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override;
virtual KResultOr<Region*> mmap(Process&, FileDescription&, const Range&, u64 offset, int prot, bool shared) override;
// ^Device
virtual mode_t required_mode() const override { return 0660; }
virtual String device_name() const override;
private:
virtual const char* class_name() const override { return "BXVGA"; }
virtual bool can_read(const FileDescription&, size_t) const override { return true; }
virtual bool can_write(const FileDescription&, size_t) const override { return true; }
virtual void start_request(AsyncBlockDeviceRequest& request) override { request.complete(AsyncDeviceRequest::Failure); }
virtual KResultOr<size_t> read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override { return EINVAL; }
virtual KResultOr<size_t> write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return EINVAL; }
PhysicalAddress find_mmio_region();
bool is_vga_compatible();
void set_safe_resolution();
void set_register(u16 index, u16 value);
u16 get_register(u16 index);
bool validate_setup_resolution(size_t width, size_t height);
u32 find_framebuffer_address();
void revert_resolution();
bool test_resolution(size_t width, size_t height);
size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height * 2; }
bool set_resolution(size_t width, size_t height);
void set_resolution_registers(size_t width, size_t height);
void set_y_offset(size_t);
PhysicalAddress m_framebuffer_address;
PhysicalAddress m_mmio_registers;
bool m_vga_compatible { true };
size_t m_framebuffer_pitch { 0 };
size_t m_framebuffer_width { 0 };
size_t m_framebuffer_height { 0 };
size_t m_y_offset { 0 };
};
}

27
Kernel/Graphics/Bochs.h Normal file
View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#define MAX_RESOLUTION_WIDTH 4096
#define MAX_RESOLUTION_HEIGHT 2160
#define VBE_DISPI_IOPORT_INDEX 0x01CE
#define VBE_DISPI_IOPORT_DATA 0x01CF
#define VBE_DISPI_INDEX_ID 0x0
#define VBE_DISPI_INDEX_XRES 0x1
#define VBE_DISPI_INDEX_YRES 0x2
#define VBE_DISPI_INDEX_BPP 0x3
#define VBE_DISPI_INDEX_ENABLE 0x4
#define VBE_DISPI_INDEX_BANK 0x5
#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
#define VBE_DISPI_INDEX_X_OFFSET 0x8
#define VBE_DISPI_INDEX_Y_OFFSET 0x9
#define VBE_DISPI_DISABLED 0x00
#define VBE_DISPI_ENABLED 0x01
#define VBE_DISPI_LFB_ENABLED 0x40

View file

@ -0,0 +1,114 @@
/*
* 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/Bochs.h>
#include <Kernel/Graphics/BochsFramebufferDevice.h>
#include <Kernel/IO.h>
#include <Kernel/PCI/Access.h>
#include <Kernel/Process.h>
#include <LibC/errno_numbers.h>
#include <LibC/sys/ioctl_numbers.h>
namespace Kernel {
UNMAP_AFTER_INIT NonnullRefPtr<BochsFramebufferDevice> BochsFramebufferDevice::create(const BochsGraphicsAdapter& adapter, PhysicalAddress framebuffer_address, size_t pitch, size_t width, size_t height)
{
return adopt_ref(*new BochsFramebufferDevice(adapter, framebuffer_address, pitch, width, height));
}
UNMAP_AFTER_INIT BochsFramebufferDevice::BochsFramebufferDevice(const BochsGraphicsAdapter& adapter, PhysicalAddress framebuffer_address, size_t pitch, size_t width, size_t height)
: FramebufferDevice(framebuffer_address, pitch, width, height)
, m_bochs_adapter(adapter)
{
m_bochs_adapter->set_safe_resolution();
m_framebuffer_width = 1024;
m_framebuffer_height = 768;
m_framebuffer_pitch = m_framebuffer_width * sizeof(u32);
}
void BochsFramebufferDevice::set_y_offset(size_t y_offset)
{
VERIFY(y_offset == 0 || y_offset == m_framebuffer_height);
m_y_offset = y_offset;
m_bochs_adapter->set_y_offset(y_offset);
}
int BochsFramebufferDevice::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: {
auto* index = (int*)arg;
int value = m_y_offset == 0 ? 0 : 1;
if (!copy_to_user(index, &value))
return -EFAULT;
return 0;
}
case FB_IOCTL_SET_BUFFER: {
if (arg != 0 && arg != 1)
return -EINVAL;
set_y_offset(arg == 0 ? 0 : m_framebuffer_height);
return 0;
}
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;
if (!copy_from_user(&resolution, user_resolution))
return -EFAULT;
if (resolution.width > MAX_RESOLUTION_WIDTH || resolution.height > MAX_RESOLUTION_HEIGHT)
return -EINVAL;
if (!m_bochs_adapter->set_resolution(resolution.width, resolution.height)) {
m_framebuffer_pitch = m_framebuffer_width * sizeof(u32);
dbgln_if(BXVGA_DEBUG, "Reverting resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
// Note: We try to revert everything back, and if it doesn't work, just assert.
if (!m_bochs_adapter->set_resolution(m_framebuffer_width, m_framebuffer_height)) {
VERIFY_NOT_REACHED();
}
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 -EINVAL;
}
m_framebuffer_width = resolution.width;
m_framebuffer_height = resolution.height;
m_framebuffer_pitch = m_framebuffer_width * sizeof(u32);
dbgln_if(BXVGA_DEBUG, "New resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
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;
};
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/Types.h>
#include <Kernel/Graphics/BochsGraphicsAdapter.h>
#include <Kernel/Graphics/FramebufferDevice.h>
#include <Kernel/PhysicalAddress.h>
namespace Kernel {
class BochsFramebufferDevice final : public FramebufferDevice {
AK_MAKE_ETERNAL
friend class BochsGraphicsAdapter;
public:
static NonnullRefPtr<BochsFramebufferDevice> create(const BochsGraphicsAdapter&, PhysicalAddress, size_t, size_t, size_t);
virtual size_t framebuffer_size_in_bytes() const override { return m_framebuffer_pitch * m_framebuffer_height * 2; }
virtual ~BochsFramebufferDevice() = default;
private:
virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override;
BochsFramebufferDevice(const BochsGraphicsAdapter&, PhysicalAddress, size_t, size_t, size_t);
virtual const char* class_name() const override { return "BXVGA"; }
void set_y_offset(size_t);
size_t m_y_offset { 0 };
NonnullRefPtr<BochsGraphicsAdapter> m_bochs_adapter;
};
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Atomic.h>
#include <AK/Checked.h>
#include <AK/Singleton.h>
#include <Kernel/Debug.h>
#include <Kernel/Graphics/Bochs.h>
#include <Kernel/Graphics/BochsFramebufferDevice.h>
#include <Kernel/Graphics/BochsGraphicsAdapter.h>
#include <Kernel/IO.h>
#include <Kernel/PCI/Access.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>
namespace Kernel {
struct [[gnu::packed]] DISPIInterface {
u16 index_id;
u16 xres;
u16 yres;
u16 bpp;
u16 enable;
u16 bank;
u16 virt_width;
u16 virt_height;
u16 x_offset;
u16 y_offset;
};
struct [[gnu::packed]] BochsDisplayMMIORegisters {
u8 edid_data[0x400];
u16 vga_ioports[0x10];
u8 reserved[0xE0];
DISPIInterface bochs_regs;
};
UNMAP_AFTER_INIT NonnullRefPtr<BochsGraphicsAdapter> BochsGraphicsAdapter::initialize(PCI::Address address)
{
return adopt_ref(*new BochsGraphicsAdapter(address));
}
UNMAP_AFTER_INIT BochsGraphicsAdapter::BochsGraphicsAdapter(PCI::Address pci_address)
: PCI::DeviceController(pci_address)
, m_mmio_registers(PCI::get_BAR2(pci_address) & 0xfffffff0)
{
set_safe_resolution();
}
UNMAP_AFTER_INIT void BochsGraphicsAdapter::initialize_framebuffer_devices()
{
// FIXME: Find a better way to determine default resolution...
m_framebuffer = BochsFramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024 * 4, 1024, 768);
}
GraphicsDevice::Type BochsGraphicsAdapter::type() const
{
if (PCI::get_class(pci_address()) == 0x3 && PCI::get_subclass(pci_address()) == 0x0)
return Type::VGACompatible;
return Type::Bochs;
}
void BochsGraphicsAdapter::set_safe_resolution()
{
set_resolution(1024, 768);
}
void BochsGraphicsAdapter::set_resolution_registers(size_t width, size_t height)
{
dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter resolution registers set to - {}x{}", width, height);
auto registers = map_typed_writable<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
registers->bochs_regs.enable = VBE_DISPI_DISABLED;
full_memory_barrier();
registers->bochs_regs.xres = width;
registers->bochs_regs.yres = height;
registers->bochs_regs.virt_width = width;
registers->bochs_regs.virt_height = height * 2;
registers->bochs_regs.bpp = 32;
full_memory_barrier();
registers->bochs_regs.enable = VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED;
full_memory_barrier();
registers->bochs_regs.bank = 0;
}
bool BochsGraphicsAdapter::try_to_set_resolution(size_t width, size_t height)
{
dbgln_if(BXVGA_DEBUG, "BochsGraphicsAdapter resolution test - {}x{}", width, height);
set_resolution_registers(width, height);
return validate_setup_resolution(width, height);
}
bool BochsGraphicsAdapter::set_resolution(size_t width, size_t height)
{
if (Checked<size_t>::multiplication_would_overflow(width, height, sizeof(u32)))
return false;
if (!try_to_set_resolution(width, height))
return false;
dbgln("BochsGraphicsAdapter: resolution set to {}x{}", width, height);
return true;
}
bool BochsGraphicsAdapter::validate_setup_resolution(size_t width, size_t height)
{
auto registers = map_typed_writable<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
if ((u16)width != registers->bochs_regs.xres || (u16)height != registers->bochs_regs.yres) {
return false;
}
return true;
}
void BochsGraphicsAdapter::set_y_offset(size_t y_offset)
{
auto registers = map_typed_writable<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
registers->bochs_regs.y_offset = y_offset;
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/Types.h>
#include <Kernel/Graphics/GraphicsDevice.h>
#include <Kernel/PCI/DeviceController.h>
#include <Kernel/PhysicalAddress.h>
namespace Kernel {
class BochsFramebufferDevice;
class GraphicsManagement;
class BochsGraphicsAdapter final : public GraphicsDevice
, public PCI::DeviceController {
AK_MAKE_ETERNAL
friend class BochsFramebufferDevice;
friend class GraphicsManagement;
public:
static NonnullRefPtr<BochsGraphicsAdapter> initialize(PCI::Address);
virtual ~BochsGraphicsAdapter() = default;
private:
// ^GraphicsDevice
virtual void initialize_framebuffer_devices() override;
virtual Type type() const override;
explicit BochsGraphicsAdapter(PCI::Address);
void set_safe_resolution();
bool validate_setup_resolution(size_t width, size_t height);
u32 find_framebuffer_address();
bool try_to_set_resolution(size_t width, size_t height);
bool set_resolution(size_t width, size_t height);
void set_resolution_registers(size_t width, size_t height);
void set_y_offset(size_t);
PhysicalAddress m_mmio_registers;
RefPtr<BochsFramebufferDevice> m_framebuffer;
};
}

View file

@ -1,37 +1,24 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Devices/MBVGADevice.h>
#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>
namespace Kernel {
static MBVGADevice* s_the;
MBVGADevice& MBVGADevice::the()
{
return *s_the;
}
UNMAP_AFTER_INIT MBVGADevice::MBVGADevice(PhysicalAddress addr, size_t pitch, size_t width, size_t height)
: BlockDevice(29, 0)
, m_framebuffer_address(addr)
, m_framebuffer_pitch(pitch)
, m_framebuffer_width(width)
, m_framebuffer_height(height)
{
dbgln("MBVGADevice address={}, pitch={}, width={}, height={}", addr, pitch, width, height);
s_the = this;
}
KResultOr<Region*> MBVGADevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
KResultOr<Region*> FramebufferDevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
{
REQUIRE_PROMISE(video);
if (!shared)
@ -48,12 +35,32 @@ KResultOr<Region*> MBVGADevice::mmap(Process& process, FileDescription&, const R
range,
vmobject.release_nonnull(),
0,
"MBVGA Framebuffer",
"Framebuffer",
prot,
shared);
}
int MBVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg)
String FramebufferDevice::device_name() const
{
return String::formatted("fb{}", minor());
}
UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size_t pitch, size_t width, size_t height)
: BlockDevice(29, GraphicsManagement::the().current_minor_number())
, m_framebuffer_address(addr)
, m_framebuffer_pitch(pitch)
, m_framebuffer_width(width)
, m_framebuffer_height(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) {
@ -65,11 +72,7 @@ int MBVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg)
return 0;
}
case FB_IOCTL_GET_BUFFER: {
auto* index = (int*)arg;
int value = 0;
if (!copy_to_user(index, &value))
return -EFAULT;
return 0;
return -ENOTIMPL;
}
case FB_IOCTL_GET_RESOLUTION: {
auto* user_resolution = (FBResolution*)arg;
@ -96,9 +99,4 @@ int MBVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg)
};
}
String MBVGADevice::device_name() const
{
return String::formatted("fb{}", minor());
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -9,17 +9,14 @@
#include <AK/String.h>
#include <AK/Types.h>
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/Graphics/GraphicsDevice.h>
#include <Kernel/PhysicalAddress.h>
namespace Kernel {
class MBVGADevice final : public BlockDevice {
class FramebufferDevice : public BlockDevice {
AK_MAKE_ETERNAL
public:
static MBVGADevice& the();
MBVGADevice(PhysicalAddress addr, size_t pitch, size_t width, size_t height);
virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override;
virtual KResultOr<Region*> mmap(Process&, FileDescription&, const Range&, u64 offset, int prot, bool shared) override;
@ -27,16 +24,22 @@ public:
virtual mode_t required_mode() const override { return 0660; }
virtual String device_name() const override;
private:
virtual const char* class_name() const override { return "MBVGA"; }
virtual bool can_read(const FileDescription&, size_t) const override { return true; }
virtual bool can_write(const FileDescription&, size_t) const override { return true; }
virtual size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height; }
virtual ~FramebufferDevice() {};
protected:
virtual bool set_resolution(size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch);
FramebufferDevice(PhysicalAddress, size_t, size_t, size_t);
virtual bool can_read(const FileDescription&, size_t) const override final { return true; }
virtual bool can_write(const FileDescription&, size_t) const override final { return true; }
virtual void start_request(AsyncBlockDeviceRequest& request) override final { request.complete(AsyncDeviceRequest::Failure); }
virtual KResultOr<size_t> read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override { return -EINVAL; }
virtual KResultOr<size_t> write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return -EINVAL; }
virtual void start_request(AsyncBlockDeviceRequest& request) override { request.complete(AsyncDeviceRequest::Failure); }
size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height; }
protected:
PhysicalAddress m_framebuffer_address;
size_t m_framebuffer_pitch { 0 };
size_t m_framebuffer_width { 0 };

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/Types.h>
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/Graphics/FramebufferDevice.h>
#include <Kernel/PhysicalAddress.h>
namespace Kernel {
class FramebufferDevice;
class GraphicsDevice : public RefCounted<GraphicsDevice> {
public:
enum class Type {
VGACompatible,
Bochs,
SVGA,
Raw
};
virtual ~GraphicsDevice() = default;
virtual void initialize_framebuffer_devices() = 0;
virtual Type type() const = 0;
protected:
GraphicsDevice() = default;
};
}

View file

@ -0,0 +1,68 @@
/*
* 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/CommandLine.h>
#include <Kernel/Debug.h>
#include <Kernel/Graphics/BochsGraphicsAdapter.h>
#include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/Graphics/VGACompatibleAdapter.h>
#include <Kernel/Multiboot.h>
namespace Kernel {
static AK::Singleton<GraphicsManagement> s_the;
GraphicsManagement& GraphicsManagement::the()
{
return *s_the;
}
bool GraphicsManagement::is_initialized()
{
return s_the.is_initialized();
}
UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement()
: m_textmode_enabled(kernel_command_line().is_text_mode())
{
}
UNMAP_AFTER_INIT RefPtr<GraphicsDevice> GraphicsManagement::determine_graphics_device(PCI::Address address, PCI::ID id) const
{
if ((id.vendor_id == 0x1234 && id.device_id == 0x1111) || (id.vendor_id == 0x80ee && id.device_id == 0xbeef)) {
return BochsGraphicsAdapter::initialize(address);
}
if (PCI::get_class(address) == 0x3 && PCI::get_subclass(address) == 0x0) {
VERIFY(multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB || multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT);
return VGACompatibleAdapter::initialize_with_preset_resolution(address,
PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)),
multiboot_info_ptr->framebuffer_pitch,
multiboot_info_ptr->framebuffer_width,
multiboot_info_ptr->framebuffer_height);
}
return {};
}
UNMAP_AFTER_INIT bool GraphicsManagement::initialize()
{
if (kernel_command_line().is_text_mode()) {
dbgln("Text mode enabled");
return false;
}
PCI::enumerate([&](const PCI::Address& address, PCI::ID id) {
auto adapter = determine_graphics_device(address, id);
if (!adapter)
return;
adapter->initialize_framebuffer_devices();
m_graphics_devices.append(adapter.release_nonnull());
});
return true;
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullRefPtr.h>
#include <AK/NonnullRefPtrVector.h>
#include <AK/Types.h>
#include <Kernel/Graphics/GraphicsDevice.h>
#include <Kernel/PCI/Definitions.h>
namespace Kernel {
class GraphicsManagement {
AK_MAKE_ETERNAL;
public:
static GraphicsManagement& the();
static bool is_initialized();
bool initialize();
unsigned current_minor_number() { return m_current_minor_number++; };
GraphicsManagement();
bool is_text_mode_enabled() const { return m_textmode_enabled; }
private:
RefPtr<GraphicsDevice> determine_graphics_device(PCI::Address address, PCI::ID id) const;
NonnullRefPtrVector<GraphicsDevice> m_graphics_devices;
unsigned m_current_minor_number { 0 };
bool m_textmode_enabled;
};
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Graphics/RawFramebufferDevice.h>
namespace Kernel {
UNMAP_AFTER_INIT NonnullRefPtr<RawFramebufferDevice> RawFramebufferDevice::create(const GraphicsDevice&, PhysicalAddress framebuffer_address, size_t pitch, size_t width, size_t height)
{
return adopt_ref(*new RawFramebufferDevice(framebuffer_address, pitch, width, height));
}
UNMAP_AFTER_INIT RawFramebufferDevice::RawFramebufferDevice(PhysicalAddress framebuffer_address, size_t pitch, size_t width, size_t height)
: FramebufferDevice(framebuffer_address, pitch, width, height)
{
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/Types.h>
#include <Kernel/Graphics/FramebufferDevice.h>
#include <Kernel/Graphics/GraphicsDevice.h>
#include <Kernel/PhysicalAddress.h>
namespace Kernel {
class RawFramebufferDevice : public FramebufferDevice {
AK_MAKE_ETERNAL
friend class GraphicsDevice;
public:
static NonnullRefPtr<RawFramebufferDevice> create(const GraphicsDevice&, PhysicalAddress, size_t pitch, size_t width, size_t height);
virtual ~RawFramebufferDevice() {};
private:
RawFramebufferDevice(PhysicalAddress, size_t pitch, size_t width, size_t height);
virtual const char* class_name() const override { return "RawFramebuffer"; }
};
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Graphics/VGACompatibleAdapter.h>
namespace Kernel {
UNMAP_AFTER_INIT NonnullRefPtr<VGACompatibleAdapter> VGACompatibleAdapter::initialize_with_preset_resolution(PCI::Address address, PhysicalAddress m_framebuffer_address, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch)
{
return adopt_ref(*new VGACompatibleAdapter(address, m_framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch));
}
UNMAP_AFTER_INIT void VGACompatibleAdapter::initialize_framebuffer_devices()
{
}
UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address)
: PCI::DeviceController(address)
{
}
UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address, PhysicalAddress framebuffer_address, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch)
: PCI::DeviceController(address)
{
m_framebuffer = RawFramebufferDevice::create(*this, framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/Types.h>
#include <Kernel/Graphics/GraphicsDevice.h>
#include <Kernel/Graphics/RawFramebufferDevice.h>
#include <Kernel/PCI/DeviceController.h>
#include <Kernel/PhysicalAddress.h>
namespace Kernel {
class VGACompatibleAdapter final : public GraphicsDevice
, public PCI::DeviceController {
AK_MAKE_ETERNAL
public:
static NonnullRefPtr<VGACompatibleAdapter> initialize_with_preset_resolution(PCI::Address, PhysicalAddress, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch);
protected:
explicit VGACompatibleAdapter(PCI::Address);
private:
VGACompatibleAdapter(PCI::Address, PhysicalAddress, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch);
// ^GraphicsDevice
virtual void initialize_framebuffer_devices() override;
virtual Type type() const override { return Type::VGACompatible; }
protected:
RefPtr<RawFramebufferDevice> m_framebuffer;
};
}

View file

@ -12,10 +12,8 @@
#include <Kernel/CMOS.h>
#include <Kernel/CommandLine.h>
#include <Kernel/DMI.h>
#include <Kernel/Devices/BXVGADevice.h>
#include <Kernel/Devices/FullDevice.h>
#include <Kernel/Devices/HID/HIDManagement.h>
#include <Kernel/Devices/MBVGADevice.h>
#include <Kernel/Devices/MemoryDevice.h>
#include <Kernel/Devices/NullDevice.h>
#include <Kernel/Devices/RandomDevice.h>
@ -26,6 +24,7 @@
#include <Kernel/Devices/ZeroDevice.h>
#include <Kernel/FileSystem/Ext2FileSystem.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/Heap/SlabAllocator.h>
#include <Kernel/Heap/kmalloc.h>
#include <Kernel/Interrupts/APIC.h>
@ -233,30 +232,8 @@ void init_stage2(void*)
PCI::initialize();
auto boot_profiling = kernel_command_line().is_boot_profiling_enabled();
auto is_text_mode = kernel_command_line().is_text_mode();
if (is_text_mode) {
dbgln("Text mode enabled");
} else {
bool bxvga_found = false;
PCI::enumerate([&](const PCI::Address&, PCI::ID id) {
if ((id.vendor_id == 0x1234 && id.device_id == 0x1111) || (id.vendor_id == 0x80ee && id.device_id == 0xbeef))
bxvga_found = true;
});
if (bxvga_found) {
BXVGADevice::initialize();
} else {
if (multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB || multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT) {
new MBVGADevice(
PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)),
multiboot_info_ptr->framebuffer_pitch,
multiboot_info_ptr->framebuffer_width,
multiboot_info_ptr->framebuffer_height);
} else {
BXVGADevice::initialize();
}
}
}
GraphicsManagement::the().initialize();
USB::UHCIController::detect();
@ -297,7 +274,7 @@ void init_stage2(void*)
int error;
// FIXME: It would be nicer to set the mode from userspace.
tty0->set_graphical(!is_text_mode);
tty0->set_graphical(!GraphicsManagement::the().is_text_mode_enabled());
RefPtr<Thread> thread;
auto userspace_init = kernel_command_line().userspace_init();
auto init_args = kernel_command_line().userspace_init_args();