Serenity Operating System
at master 193 lines 8.2 kB view raw
1/* 2 * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Atomic.h> 8#include <AK/Checked.h> 9#include <AK/Try.h> 10#include <Kernel/Bus/PCI/API.h> 11#include <Kernel/Bus/PCI/IDs.h> 12#include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h> 13#include <Kernel/Graphics/GraphicsManagement.h> 14#include <Kernel/Graphics/VMWare/Definitions.h> 15#include <Kernel/Graphics/VMWare/DisplayConnector.h> 16#include <Kernel/Graphics/VMWare/GraphicsAdapter.h> 17#include <Kernel/IOWindow.h> 18#include <Kernel/Memory/TypedMapping.h> 19#include <Kernel/Sections.h> 20 21namespace Kernel { 22 23ErrorOr<bool> VMWareGraphicsAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier) 24{ 25 PCI::HardwareID id = pci_device_identifier.hardware_id(); 26 // Note: We only support VMWare SVGA II adapter 27 return id.vendor_id == PCI::VendorID::VMWare && id.device_id == 0x0405; 28} 29 30ErrorOr<NonnullLockRefPtr<GenericGraphicsAdapter>> VMWareGraphicsAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier) 31{ 32 auto registers_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); 33 auto adapter = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) VMWareGraphicsAdapter(pci_device_identifier, move(registers_io_window)))); 34 TRY(adapter->initialize_adapter()); 35 return adapter; 36} 37 38UNMAP_AFTER_INIT VMWareGraphicsAdapter::VMWareGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr<IOWindow> registers_io_window) 39 : PCI::Device(const_cast<PCI::DeviceIdentifier&>(pci_device_identifier)) 40 , m_registers_io_window(move(registers_io_window)) 41{ 42 dbgln("VMWare SVGA @ {}, {}", pci_device_identifier.address(), m_registers_io_window); 43} 44 45u32 VMWareGraphicsAdapter::read_io_register(VMWareDisplayRegistersOffset register_offset) const 46{ 47 SpinlockLocker locker(m_io_access_lock); 48 m_registers_io_window->write32(0, to_underlying(register_offset)); 49 return m_registers_io_window->read32_unaligned(1); 50} 51void VMWareGraphicsAdapter::write_io_register(VMWareDisplayRegistersOffset register_offset, u32 value) 52{ 53 SpinlockLocker locker(m_io_access_lock); 54 m_registers_io_window->write32(0, to_underlying(register_offset)); 55 m_registers_io_window->write32_unaligned(1, value); 56} 57 58UNMAP_AFTER_INIT ErrorOr<void> VMWareGraphicsAdapter::negotiate_device_version() 59{ 60 write_io_register(VMWareDisplayRegistersOffset::ID, vmware_svga_version_2_id); 61 auto accepted_version = read_io_register(VMWareDisplayRegistersOffset::ID); 62 dbgln("VMWare SVGA @ {}: Accepted version {}", device_identifier().address(), accepted_version); 63 if (read_io_register(VMWareDisplayRegistersOffset::ID) == vmware_svga_version_2_id) 64 return {}; 65 return Error::from_errno(ENOTSUP); 66} 67 68UNMAP_AFTER_INIT ErrorOr<void> VMWareGraphicsAdapter::initialize_fifo_registers() 69{ 70 auto framebuffer_size = read_io_register(VMWareDisplayRegistersOffset::FB_SIZE); 71 auto fifo_size = read_io_register(VMWareDisplayRegistersOffset::MEM_SIZE); 72 auto fifo_physical_address = PhysicalAddress(PCI::get_BAR2(device_identifier()) & 0xfffffff0); 73 74 dbgln("VMWare SVGA @ {}: framebuffer size {} bytes, FIFO size {} bytes @ {}", device_identifier().address(), framebuffer_size, fifo_size, fifo_physical_address); 75 if (framebuffer_size < 0x100000 || fifo_size < 0x10000) { 76 dbgln("VMWare SVGA @ {}: invalid framebuffer or fifo size", device_identifier().address()); 77 return Error::from_errno(ENOTSUP); 78 } 79 80 m_fifo_registers = TRY(Memory::map_typed<VMWareDisplayFIFORegisters volatile>(fifo_physical_address, fifo_size, Memory::Region::Access::ReadWrite)); 81 m_fifo_registers->start = 16; 82 m_fifo_registers->size = 16 + (10 * 1024); 83 m_fifo_registers->next_command = 16; 84 m_fifo_registers->stop = 16; 85 return {}; 86} 87 88UNMAP_AFTER_INIT void VMWareGraphicsAdapter::print_svga_capabilities() const 89{ 90 auto svga_capabilities = read_io_register(VMWareDisplayRegistersOffset::CAPABILITIES); 91 dbgln("VMWare SVGA capabilities (raw {:x}):", svga_capabilities); 92 if (svga_capabilities & (1 << 1)) 93 dbgln("\tRect copy"); 94 if (svga_capabilities & (1 << 5)) 95 dbgln("\tCursor"); 96 if (svga_capabilities & (1 << 6)) 97 dbgln("\tCursor Bypass"); 98 if (svga_capabilities & (1 << 7)) 99 dbgln("\tCursor Bypass 2"); 100 if (svga_capabilities & (1 << 8)) 101 dbgln("\t8 Bit emulation"); 102 if (svga_capabilities & (1 << 9)) 103 dbgln("\tAlpha Cursor"); 104 if (svga_capabilities & (1 << 14)) 105 dbgln("\t3D acceleration"); 106 if (svga_capabilities & (1 << 15)) 107 dbgln("\tExtended FIFO"); 108 if (svga_capabilities & (1 << 16)) 109 dbgln("\tMulti-monitor (legacy)"); 110 if (svga_capabilities & (1 << 17)) 111 dbgln("\tPitch lock"); 112 if (svga_capabilities & (1 << 18)) 113 dbgln("\tIRQ masking"); 114 if (svga_capabilities & (1 << 19)) 115 dbgln("\tDisplay topology"); 116 if (svga_capabilities & (1 << 20)) 117 dbgln("\tGMR"); 118 if (svga_capabilities & (1 << 21)) 119 dbgln("\tTraces"); 120 if (svga_capabilities & (1 << 22)) 121 dbgln("\tGMR2"); 122 if (svga_capabilities & (1 << 23)) 123 dbgln("\tScreen object 2"); 124} 125 126ErrorOr<void> VMWareGraphicsAdapter::modeset_primary_screen_resolution(Badge<VMWareDisplayConnector>, size_t width, size_t height) 127{ 128 auto max_width = read_io_register(VMWareDisplayRegistersOffset::MAX_WIDTH); 129 auto max_height = read_io_register(VMWareDisplayRegistersOffset::MAX_HEIGHT); 130 if (width > max_width || height > max_height) 131 return Error::from_errno(ENOTSUP); 132 modeset_primary_screen_resolution(width, height); 133 return {}; 134} 135 136size_t VMWareGraphicsAdapter::primary_screen_width(Badge<VMWareDisplayConnector>) const 137{ 138 SpinlockLocker locker(m_operation_lock); 139 return read_io_register(VMWareDisplayRegistersOffset::WIDTH); 140} 141size_t VMWareGraphicsAdapter::primary_screen_height(Badge<VMWareDisplayConnector>) const 142{ 143 SpinlockLocker locker(m_operation_lock); 144 return read_io_register(VMWareDisplayRegistersOffset::HEIGHT); 145} 146size_t VMWareGraphicsAdapter::primary_screen_pitch(Badge<VMWareDisplayConnector>) const 147{ 148 SpinlockLocker locker(m_operation_lock); 149 return read_io_register(VMWareDisplayRegistersOffset::BYTES_PER_LINE); 150} 151 152void VMWareGraphicsAdapter::primary_screen_flush(Badge<VMWareDisplayConnector>, size_t current_width, size_t current_height) 153{ 154 SpinlockLocker locker(m_operation_lock); 155 m_fifo_registers->start = 16; 156 m_fifo_registers->size = 16 + (10 * 1024); 157 m_fifo_registers->next_command = 16 + 4 * 5; 158 m_fifo_registers->stop = 16; 159 m_fifo_registers->commands[0] = 1; 160 m_fifo_registers->commands[1] = 0; 161 m_fifo_registers->commands[2] = 0; 162 m_fifo_registers->commands[3] = current_width; 163 m_fifo_registers->commands[4] = current_height; 164 write_io_register(VMWareDisplayRegistersOffset::SYNC, 1); 165} 166 167void VMWareGraphicsAdapter::modeset_primary_screen_resolution(size_t width, size_t height) 168{ 169 SpinlockLocker locker(m_operation_lock); 170 write_io_register(VMWareDisplayRegistersOffset::ENABLE, 0); 171 write_io_register(VMWareDisplayRegistersOffset::WIDTH, width); 172 write_io_register(VMWareDisplayRegistersOffset::HEIGHT, height); 173 write_io_register(VMWareDisplayRegistersOffset::BITS_PER_PIXEL, 32); 174 write_io_register(VMWareDisplayRegistersOffset::ENABLE, 1); 175 write_io_register(VMWareDisplayRegistersOffset::CONFIG_DONE, 1); 176} 177 178UNMAP_AFTER_INIT ErrorOr<void> VMWareGraphicsAdapter::initialize_adapter() 179{ 180 TRY(negotiate_device_version()); 181 print_svga_capabilities(); 182 TRY(initialize_fifo_registers()); 183 // Note: enable the device by modesetting the primary screen resolution 184 modeset_primary_screen_resolution(640, 480); 185 186 auto bar1_space_size = PCI::get_BAR_space_size(device_identifier(), PCI::HeaderType0BaseRegister::BAR1); 187 188 m_display_connector = VMWareDisplayConnector::must_create(*this, PhysicalAddress(PCI::get_BAR1(device_identifier()) & 0xfffffff0), bar1_space_size); 189 TRY(m_display_connector->set_safe_mode_setting()); 190 return {}; 191} 192 193}