/* * 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 namespace Kernel { UNMAP_AFTER_INIT LockRefPtr VMWareGraphicsAdapter::try_initialize(PCI::DeviceIdentifier const& pci_device_identifier) { PCI::HardwareID id = pci_device_identifier.hardware_id(); VERIFY(id.vendor_id == PCI::VendorID::VMWare); // Note: We only support VMWare SVGA II adapter if (id.device_id != 0x0405) return {}; auto registers_io_window = MUST(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); auto adapter = MUST(adopt_nonnull_lock_ref_or_enomem(new (nothrow) VMWareGraphicsAdapter(pci_device_identifier, move(registers_io_window)))); MUST(adapter->initialize_adapter()); return adapter; } UNMAP_AFTER_INIT VMWareGraphicsAdapter::VMWareGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr registers_io_window) : PCI::Device(pci_device_identifier.address()) , m_registers_io_window(move(registers_io_window)) { dbgln("VMWare SVGA @ {}, {}", pci_device_identifier.address(), m_registers_io_window); } u32 VMWareGraphicsAdapter::read_io_register(VMWareDisplayRegistersOffset register_offset) const { SpinlockLocker locker(m_io_access_lock); m_registers_io_window->write32(0, to_underlying(register_offset)); return m_registers_io_window->read32_unaligned(1); } void VMWareGraphicsAdapter::write_io_register(VMWareDisplayRegistersOffset register_offset, u32 value) { SpinlockLocker locker(m_io_access_lock); m_registers_io_window->write32(0, to_underlying(register_offset)); m_registers_io_window->write32_unaligned(1, value); } UNMAP_AFTER_INIT ErrorOr VMWareGraphicsAdapter::negotiate_device_version() { write_io_register(VMWareDisplayRegistersOffset::ID, vmware_svga_version_2_id); auto accepted_version = read_io_register(VMWareDisplayRegistersOffset::ID); dbgln("VMWare SVGA @ {}: Accepted version {}", pci_address(), accepted_version); if (read_io_register(VMWareDisplayRegistersOffset::ID) == vmware_svga_version_2_id) return {}; return Error::from_errno(ENOTSUP); } UNMAP_AFTER_INIT ErrorOr VMWareGraphicsAdapter::initialize_fifo_registers() { auto framebuffer_size = read_io_register(VMWareDisplayRegistersOffset::FB_SIZE); auto fifo_size = read_io_register(VMWareDisplayRegistersOffset::MEM_SIZE); auto fifo_physical_address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0); dbgln("VMWare SVGA @ {}: framebuffer size {} bytes, FIFO size {} bytes @ {}", pci_address(), framebuffer_size, fifo_size, fifo_physical_address); if (framebuffer_size < 0x100000 || fifo_size < 0x10000) { dbgln("VMWare SVGA @ {}: invalid framebuffer or fifo size", pci_address()); return Error::from_errno(ENOTSUP); } m_fifo_registers = TRY(Memory::map_typed(fifo_physical_address, fifo_size, Memory::Region::Access::ReadWrite)); m_fifo_registers->start = 16; m_fifo_registers->size = 16 + (10 * 1024); m_fifo_registers->next_command = 16; m_fifo_registers->stop = 16; return {}; } UNMAP_AFTER_INIT void VMWareGraphicsAdapter::print_svga_capabilities() const { auto svga_capabilities = read_io_register(VMWareDisplayRegistersOffset::CAPABILITIES); dbgln("VMWare SVGA capabilities (raw {:x}):", svga_capabilities); if (svga_capabilities & (1 << 1)) dbgln("\tRect copy"); if (svga_capabilities & (1 << 5)) dbgln("\tCursor"); if (svga_capabilities & (1 << 6)) dbgln("\tCursor Bypass"); if (svga_capabilities & (1 << 7)) dbgln("\tCursor Bypass 2"); if (svga_capabilities & (1 << 8)) dbgln("\t8 Bit emulation"); if (svga_capabilities & (1 << 9)) dbgln("\tAlpha Cursor"); if (svga_capabilities & (1 << 14)) dbgln("\t3D acceleration"); if (svga_capabilities & (1 << 15)) dbgln("\tExtended FIFO"); if (svga_capabilities & (1 << 16)) dbgln("\tMulti-monitor (legacy)"); if (svga_capabilities & (1 << 17)) dbgln("\tPitch lock"); if (svga_capabilities & (1 << 18)) dbgln("\tIRQ masking"); if (svga_capabilities & (1 << 19)) dbgln("\tDisplay topology"); if (svga_capabilities & (1 << 20)) dbgln("\tGMR"); if (svga_capabilities & (1 << 21)) dbgln("\tTraces"); if (svga_capabilities & (1 << 22)) dbgln("\tGMR2"); if (svga_capabilities & (1 << 23)) dbgln("\tScreen object 2"); } ErrorOr VMWareGraphicsAdapter::modeset_primary_screen_resolution(Badge, size_t width, size_t height) { auto max_width = read_io_register(VMWareDisplayRegistersOffset::MAX_WIDTH); auto max_height = read_io_register(VMWareDisplayRegistersOffset::MAX_HEIGHT); if (width > max_width || height > max_height) return Error::from_errno(ENOTSUP); modeset_primary_screen_resolution(width, height); return {}; } size_t VMWareGraphicsAdapter::primary_screen_width(Badge) const { SpinlockLocker locker(m_operation_lock); return read_io_register(VMWareDisplayRegistersOffset::WIDTH); } size_t VMWareGraphicsAdapter::primary_screen_height(Badge) const { SpinlockLocker locker(m_operation_lock); return read_io_register(VMWareDisplayRegistersOffset::HEIGHT); } size_t VMWareGraphicsAdapter::primary_screen_pitch(Badge) const { SpinlockLocker locker(m_operation_lock); return read_io_register(VMWareDisplayRegistersOffset::BYTES_PER_LINE); } void VMWareGraphicsAdapter::primary_screen_flush(Badge, size_t current_width, size_t current_height) { SpinlockLocker locker(m_operation_lock); m_fifo_registers->start = 16; m_fifo_registers->size = 16 + (10 * 1024); m_fifo_registers->next_command = 16 + 4 * 5; m_fifo_registers->stop = 16; m_fifo_registers->commands[0] = 1; m_fifo_registers->commands[1] = 0; m_fifo_registers->commands[2] = 0; m_fifo_registers->commands[3] = current_width; m_fifo_registers->commands[4] = current_height; write_io_register(VMWareDisplayRegistersOffset::SYNC, 1); } void VMWareGraphicsAdapter::modeset_primary_screen_resolution(size_t width, size_t height) { SpinlockLocker locker(m_operation_lock); write_io_register(VMWareDisplayRegistersOffset::ENABLE, 0); write_io_register(VMWareDisplayRegistersOffset::WIDTH, width); write_io_register(VMWareDisplayRegistersOffset::HEIGHT, height); write_io_register(VMWareDisplayRegistersOffset::BITS_PER_PIXEL, 32); write_io_register(VMWareDisplayRegistersOffset::ENABLE, 1); write_io_register(VMWareDisplayRegistersOffset::CONFIG_DONE, 1); } UNMAP_AFTER_INIT ErrorOr VMWareGraphicsAdapter::initialize_adapter() { TRY(negotiate_device_version()); print_svga_capabilities(); TRY(initialize_fifo_registers()); // Note: enable the device by modesetting the primary screen resolution modeset_primary_screen_resolution(640, 480); auto bar1_space_size = PCI::get_BAR_space_size(pci_address(), PCI::HeaderType0BaseRegister::BAR1); m_display_connector = VMWareDisplayConnector::must_create(*this, PhysicalAddress(PCI::get_BAR1(pci_address()) & 0xfffffff0), bar1_space_size); TRY(m_display_connector->set_safe_mode_setting()); return {}; } }