Serenity Operating System
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}