From 6a728e2d761601a9d21f2269e2febbfde55b3646 Mon Sep 17 00:00:00 2001 From: Liav A Date: Fri, 5 Mar 2021 14:23:08 +0200 Subject: [PATCH] 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. --- Kernel/CMakeLists.txt | 8 +- Kernel/Devices/BXVGADevice.cpp | 290 ------------------ Kernel/Devices/BXVGADevice.h | 64 ---- Kernel/Graphics/Bochs.h | 27 ++ Kernel/Graphics/BochsFramebufferDevice.cpp | 114 +++++++ Kernel/Graphics/BochsFramebufferDevice.h | 41 +++ Kernel/Graphics/BochsGraphicsAdapter.cpp | 126 ++++++++ Kernel/Graphics/BochsGraphicsAdapter.h | 49 +++ .../FramebufferDevice.cpp} | 64 ++-- .../FramebufferDevice.h} | 29 +- Kernel/Graphics/GraphicsDevice.h | 33 ++ Kernel/Graphics/GraphicsManagement.cpp | 68 ++++ Kernel/Graphics/GraphicsManagement.h | 38 +++ Kernel/Graphics/RawFramebufferDevice.cpp | 20 ++ Kernel/Graphics/RawFramebufferDevice.h | 31 ++ Kernel/Graphics/VGACompatibleAdapter.cpp | 31 ++ Kernel/Graphics/VGACompatibleAdapter.h | 38 +++ Kernel/init.cpp | 29 +- 18 files changed, 672 insertions(+), 428 deletions(-) delete mode 100644 Kernel/Devices/BXVGADevice.cpp delete mode 100644 Kernel/Devices/BXVGADevice.h create mode 100644 Kernel/Graphics/Bochs.h create mode 100644 Kernel/Graphics/BochsFramebufferDevice.cpp create mode 100644 Kernel/Graphics/BochsFramebufferDevice.h create mode 100644 Kernel/Graphics/BochsGraphicsAdapter.cpp create mode 100644 Kernel/Graphics/BochsGraphicsAdapter.h rename Kernel/{Devices/MBVGADevice.cpp => Graphics/FramebufferDevice.cpp} (68%) rename Kernel/{Devices/MBVGADevice.h => Graphics/FramebufferDevice.h} (65%) create mode 100644 Kernel/Graphics/GraphicsDevice.h create mode 100644 Kernel/Graphics/GraphicsManagement.cpp create mode 100644 Kernel/Graphics/GraphicsManagement.h create mode 100644 Kernel/Graphics/RawFramebufferDevice.cpp create mode 100644 Kernel/Graphics/RawFramebufferDevice.h create mode 100644 Kernel/Graphics/VGACompatibleAdapter.cpp create mode 100644 Kernel/Graphics/VGACompatibleAdapter.h diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 103ba80609..e0e6be75d8 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -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 diff --git a/Kernel/Devices/BXVGADevice.cpp b/Kernel/Devices/BXVGADevice.cpp deleted file mode 100644 index 06be3bb785..0000000000 --- a/Kernel/Devices/BXVGADevice.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 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(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(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::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 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; - }; -} - -} diff --git a/Kernel/Devices/BXVGADevice.h b/Kernel/Devices/BXVGADevice.h deleted file mode 100644 index b1692dc9f7..0000000000 --- a/Kernel/Devices/BXVGADevice.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -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 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 read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override { return EINVAL; } - virtual KResultOr 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 }; -}; - -} 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. + * + * 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +UNMAP_AFTER_INIT NonnullRefPtr 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { + +class BochsFramebufferDevice final : public FramebufferDevice { + AK_MAKE_ETERNAL + friend class BochsGraphicsAdapter; + +public: + static NonnullRefPtr 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 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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::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(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::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(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(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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +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 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 m_framebuffer; +}; + +} diff --git a/Kernel/Devices/MBVGADevice.cpp b/Kernel/Graphics/FramebufferDevice.cpp similarity index 68% rename from Kernel/Devices/MBVGADevice.cpp rename to Kernel/Graphics/FramebufferDevice.cpp index bfefe387d8..c6d45774cf 100644 --- a/Kernel/Devices/MBVGADevice.cpp +++ b/Kernel/Graphics/FramebufferDevice.cpp @@ -1,37 +1,24 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2021, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ -#include +#include +#include +#include +#include +#include #include #include #include +#include #include #include 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 MBVGADevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared) +KResultOr FramebufferDevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared) { REQUIRE_PROMISE(video); if (!shared) @@ -48,12 +35,32 @@ KResultOr 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()); -} - } diff --git a/Kernel/Devices/MBVGADevice.h b/Kernel/Graphics/FramebufferDevice.h similarity index 65% rename from Kernel/Devices/MBVGADevice.h rename to Kernel/Graphics/FramebufferDevice.h index db473c5443..104fc5739d 100644 --- a/Kernel/Devices/MBVGADevice.h +++ b/Kernel/Graphics/FramebufferDevice.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2021, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,17 +9,14 @@ #include #include #include +#include #include 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 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 read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override { return -EINVAL; } virtual KResultOr 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 }; 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { +class FramebufferDevice; +class GraphicsDevice : public RefCounted { +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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +static AK::Singleton 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 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +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 determine_graphics_device(PCI::Address address, PCI::ID id) const; + + NonnullRefPtrVector 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Kernel { + +UNMAP_AFTER_INIT NonnullRefPtr 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { + +class RawFramebufferDevice : public FramebufferDevice { + AK_MAKE_ETERNAL + friend class GraphicsDevice; + +public: + static NonnullRefPtr 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Kernel { + +UNMAP_AFTER_INIT NonnullRefPtr 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. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class VGACompatibleAdapter final : public GraphicsDevice + , public PCI::DeviceController { + AK_MAKE_ETERNAL +public: + static NonnullRefPtr 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 m_framebuffer; +}; + +} diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 1918aa1dc8..e497688b56 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -12,10 +12,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -26,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -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; auto userspace_init = kernel_command_line().userspace_init(); auto init_args = kernel_command_line().userspace_init_args();