summaryrefslogtreecommitdiff
path: root/Kernel/Graphics
diff options
context:
space:
mode:
authorLiav A <liavalb@gmail.com>2021-03-05 14:23:08 +0200
committerAndreas Kling <kling@serenityos.org>2021-05-16 19:58:33 +0200
commit6a728e2d761601a9d21f2269e2febbfde55b3646 (patch)
tree5019dbb0de448bfa895d06818089ed765b047b59 /Kernel/Graphics
parent8515a1c49d0442f7ce23faec2c27a336d32ce7d6 (diff)
downloadserenity-6a728e2d761601a9d21f2269e2febbfde55b3646.zip
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.
Diffstat (limited to 'Kernel/Graphics')
-rw-r--r--Kernel/Graphics/Bochs.h27
-rw-r--r--Kernel/Graphics/BochsFramebufferDevice.cpp114
-rw-r--r--Kernel/Graphics/BochsFramebufferDevice.h41
-rw-r--r--Kernel/Graphics/BochsGraphicsAdapter.cpp126
-rw-r--r--Kernel/Graphics/BochsGraphicsAdapter.h49
-rw-r--r--Kernel/Graphics/FramebufferDevice.cpp102
-rw-r--r--Kernel/Graphics/FramebufferDevice.h49
-rw-r--r--Kernel/Graphics/GraphicsDevice.h33
-rw-r--r--Kernel/Graphics/GraphicsManagement.cpp68
-rw-r--r--Kernel/Graphics/GraphicsManagement.h38
-rw-r--r--Kernel/Graphics/RawFramebufferDevice.cpp20
-rw-r--r--Kernel/Graphics/RawFramebufferDevice.h31
-rw-r--r--Kernel/Graphics/VGACompatibleAdapter.cpp31
-rw-r--r--Kernel/Graphics/VGACompatibleAdapter.h38
14 files changed, 767 insertions, 0 deletions
diff --git a/Kernel/Graphics/Bochs.h b/Kernel/Graphics/Bochs.h
new file mode 100644
index 0000000000..19b9d08a4b
--- /dev/null
+++ b/Kernel/Graphics/Bochs.h
@@ -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
diff --git a/Kernel/Graphics/BochsFramebufferDevice.cpp b/Kernel/Graphics/BochsFramebufferDevice.cpp
new file mode 100644
index 0000000000..15af62d532
--- /dev/null
+++ b/Kernel/Graphics/BochsFramebufferDevice.cpp
@@ -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;
+ };
+}
+}
diff --git a/Kernel/Graphics/BochsFramebufferDevice.h b/Kernel/Graphics/BochsFramebufferDevice.h
new file mode 100644
index 0000000000..5133c3320f
--- /dev/null
+++ b/Kernel/Graphics/BochsFramebufferDevice.h
@@ -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;
+};
+
+}
diff --git a/Kernel/Graphics/BochsGraphicsAdapter.cpp b/Kernel/Graphics/BochsGraphicsAdapter.cpp
new file mode 100644
index 0000000000..d6f42b1662
--- /dev/null
+++ b/Kernel/Graphics/BochsGraphicsAdapter.cpp
@@ -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;
+}
+
+}
diff --git a/Kernel/Graphics/BochsGraphicsAdapter.h b/Kernel/Graphics/BochsGraphicsAdapter.h
new file mode 100644
index 0000000000..e2c2b1cdd5
--- /dev/null
+++ b/Kernel/Graphics/BochsGraphicsAdapter.h
@@ -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;
+};
+
+}
diff --git a/Kernel/Graphics/FramebufferDevice.cpp b/Kernel/Graphics/FramebufferDevice.cpp
new file mode 100644
index 0000000000..c6d45774cf
--- /dev/null
+++ b/Kernel/Graphics/FramebufferDevice.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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>
+
+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, framebuffer_size_in_bytes());
+ if (!vmobject)
+ return ENOMEM;
+ return process.space().allocate_region_with_vmobject(
+ range,
+ vmobject.release_nonnull(),
+ 0,
+ "Framebuffer",
+ prot,
+ shared);
+}
+
+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) {
+ 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;
+ };
+}
+
+}
diff --git a/Kernel/Graphics/FramebufferDevice.h b/Kernel/Graphics/FramebufferDevice.h
new file mode 100644
index 0000000000..104fc5739d
--- /dev/null
+++ b/Kernel/Graphics/FramebufferDevice.h
@@ -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/Devices/BlockDevice.h>
+#include <Kernel/Graphics/GraphicsDevice.h>
+#include <Kernel/PhysicalAddress.h>
+
+namespace Kernel {
+
+class FramebufferDevice : public BlockDevice {
+ AK_MAKE_ETERNAL
+public:
+ 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;
+
+ 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; }
+
+protected:
+ PhysicalAddress m_framebuffer_address;
+ size_t m_framebuffer_pitch { 0 };
+ size_t m_framebuffer_width { 0 };
+ size_t m_framebuffer_height { 0 };
+};
+
+}
diff --git a/Kernel/Graphics/GraphicsDevice.h b/Kernel/Graphics/GraphicsDevice.h
new file mode 100644
index 0000000000..f4d3ae7e29
--- /dev/null
+++ b/Kernel/Graphics/GraphicsDevice.h
@@ -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;
+};
+
+}
diff --git a/Kernel/Graphics/GraphicsManagement.cpp b/Kernel/Graphics/GraphicsManagement.cpp
new file mode 100644
index 0000000000..4fbef68629
--- /dev/null
+++ b/Kernel/Graphics/GraphicsManagement.cpp
@@ -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;
+}
+
+}
diff --git a/Kernel/Graphics/GraphicsManagement.h b/Kernel/Graphics/GraphicsManagement.h
new file mode 100644
index 0000000000..bcf0cded39
--- /dev/null
+++ b/Kernel/Graphics/GraphicsManagement.h
@@ -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;
+};
+
+}
diff --git a/Kernel/Graphics/RawFramebufferDevice.cpp b/Kernel/Graphics/RawFramebufferDevice.cpp
new file mode 100644
index 0000000000..f36a5cfa27
--- /dev/null
+++ b/Kernel/Graphics/RawFramebufferDevice.cpp
@@ -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)
+{
+}
+
+}
diff --git a/Kernel/Graphics/RawFramebufferDevice.h b/Kernel/Graphics/RawFramebufferDevice.h
new file mode 100644
index 0000000000..10ddf44ea8
--- /dev/null
+++ b/Kernel/Graphics/RawFramebufferDevice.h
@@ -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"; }
+};
+
+}
diff --git a/Kernel/Graphics/VGACompatibleAdapter.cpp b/Kernel/Graphics/VGACompatibleAdapter.cpp
new file mode 100644
index 0000000000..80b2e8b0d4
--- /dev/null
+++ b/Kernel/Graphics/VGACompatibleAdapter.cpp
@@ -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);
+}
+
+}
diff --git a/Kernel/Graphics/VGACompatibleAdapter.h b/Kernel/Graphics/VGACompatibleAdapter.h
new file mode 100644
index 0000000000..75ed5ee946
--- /dev/null
+++ b/Kernel/Graphics/VGACompatibleAdapter.h
@@ -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;
+};
+
+}