The Pedigree Project 0.1
modules/drivers/x86/vmware-gfx/main.cc
1/*
2 * Copyright (c) 2008-2026, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/LockGuard.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/Service.h"
23#include "pedigree/kernel/ServiceFeatures.h"
24#include "pedigree/kernel/ServiceManager.h"
25#include "pedigree/kernel/graphics/Graphics.h"
26#include "pedigree/kernel/graphics/GraphicsService.h"
27#include "pedigree/kernel/machine/Device.h"
28#include "pedigree/kernel/machine/Display.h"
29#include "pedigree/kernel/machine/Framebuffer.h"
30#include "pedigree/kernel/panic.h"
31#include "pedigree/kernel/process/Mutex.h"
32#include "pedigree/kernel/process/OperationBarrier.h"
33#include "pedigree/kernel/processor/IoBase.h"
34#include "pedigree/kernel/processor/MemoryMappedIo.h"
35#include "pedigree/kernel/processor/Processor.h"
36#include "pedigree/kernel/processor/types.h"
37#include "pedigree/kernel/time/Time.h"
38#include "pedigree/kernel/utilities/List.h"
39#include "pedigree/kernel/utilities/String.h"
40#include "pedigree/kernel/utilities/new"
41#include "pedigree/kernel/utilities/utility.h"
42
43#include "VmwareSvgaState.h"
44#include "modules/Module.h"
45#include "svga_reg.h"
46#include "vm_device_version.h"
47
48namespace {
49struct Mode {
50 size_t id;
51 size_t width;
52 size_t height;
53 Graphics::PixelFormat format;
54};
55
56Mode g_Modes[] = {
57 {0x117, 1024, 768, Graphics::Bits16_Rgb555},
58 {0, 80, 25, Graphics::Bits8_Idx},
59};
60
61constexpr size_t ModeCount = sizeof(g_Modes) / sizeof(g_Modes[0]);
62constexpr size_t LargestCommandWords = 7;
63constexpr size_t MinimumFifoBytes = 10 * 1024;
64} // namespace
65
66class VmwareGraphics : public Display {
67 public:
68 explicit VmwareGraphics(Device* pDev)
69 : Display(pDev),
70 m_pIo(nullptr),
71 m_Framebuffer(nullptr),
72 m_CommandRegion(nullptr),
73 m_pFramebuffer(nullptr),
74 m_pProvider(nullptr),
75 m_DeviceLock(),
76 m_Capabilities(0),
77 m_MaxWidth(0),
78 m_MaxHeight(0),
79 m_FramebufferSize(0),
80 m_CommandRegionSize(0),
81 m_FifoHeaderBytes(0),
82 m_HardwareAccepted(false),
83 m_Online(false),
84 m_FifoRunning(false),
85 m_Stopping(false),
86 m_ProviderRegistered(false),
87 m_ShutdownComplete(false) {}
88
89 virtual ~VmwareGraphics();
90
91 void shutdown();
92
93 bool initialise() {
94 {
95 LockGuard<Mutex> guard(m_DeviceLock);
96 if (!initialiseHardwareLocked()) {
97 disableHardwareLocked();
98 return false;
99 }
100
101 m_pFramebuffer = new VmwareFramebuffer(0, this);
102 m_pProvider = new GraphicsService::GraphicsProvider;
103 if (!m_pFramebuffer || !m_pProvider) {
104 markOfflineLocked("could not allocate provider state");
105 return false;
106 }
107
108 m_pProvider->pDisplay = this;
109 m_pProvider->pFramebuffer = m_pFramebuffer;
110 m_pProvider->maxWidth = m_MaxWidth;
111 m_pProvider->maxHeight = m_MaxHeight;
112 m_pProvider->maxTextWidth = 0;
113 m_pProvider->maxTextHeight = 0;
114 m_pProvider->maxDepth = 32;
115 m_pProvider->bHardwareAccel = m_Capabilities & SVGA_CAP_RECT_COPY;
116 m_pProvider->bTextModes = false;
117 m_Online = true;
118 }
119
120 if (!registerProvider()) {
121 LockGuard<Mutex> guard(m_DeviceLock);
122 markOfflineLocked("could not register with the graphics service");
123 return false;
124 }
125
126 return true;
127 }
128
129 virtual void getName(String& str) {
130 str.assign("vmware-gfx", 11);
131 }
132
133 virtual void dump(String& str) {
134 str.assign("vmware guest tools, graphics card", 34);
135 }
136
139 return false;
140 LockGuard<Mutex> guard(m_DeviceLock);
141 if (!availableLocked())
142 return false;
143
144 sm.width = readRegisterLocked(SVGA_REG_WIDTH);
145 sm.height = readRegisterLocked(SVGA_REG_HEIGHT);
146 sm.pf.nBpp = readRegisterLocked(SVGA_REG_BITS_PER_PIXEL);
147
148 const uint32_t redMask = readRegisterLocked(SVGA_REG_RED_MASK);
149 const uint32_t greenMask = readRegisterLocked(SVGA_REG_GREEN_MASK);
150 const uint32_t blueMask = readRegisterLocked(SVGA_REG_BLUE_MASK);
151 sm.pf2 = pixelFormat(sm.pf.nBpp, redMask, greenMask, blueMask);
152 sm.bytesPerPixel = (sm.pf.nBpp + 7) / 8;
153 sm.bytesPerLine = readRegisterLocked(SVGA_REG_BYTES_PER_LINE);
154 return true;
155 }
156
159 return false;
160 LockGuard<Mutex> guard(m_DeviceLock);
161 if (!availableLocked())
162 return false;
163
164 for (size_t i = 0; i < ModeCount; ++i) {
166 pMode->id = g_Modes[i].id;
167 pMode->width = g_Modes[i].width;
168 pMode->height = g_Modes[i].height;
169 pMode->pf2 = g_Modes[i].format;
170 sms.pushBack(pMode);
171 }
172
173 return true;
174 }
175
178 return false;
179 LockGuard<Mutex> guard(m_DeviceLock);
180 if (!availableLocked())
181 return false;
182
183 if (!sm.id) {
184 disableDisplayLocked();
185 return true;
186 }
187
188 if (!setModeLocked(sm.width, sm.height, Graphics::bitsPerPixel(sm.pf2)))
189 return false;
190
191 return true;
192 }
193
194 virtual bool setScreenMode(size_t modeId) {
195 // panic() disables interrupts before asking the active display to
196 // return to mode zero. Never wait on a mutex in that path.
197 if (!modeId && !Processor::getInterrupts()) {
198 if (!m_DeviceLock.tryAcquire())
199 return false;
200 const bool available = availableLocked();
201 if (available)
202 disableDisplayLocked();
203 m_DeviceLock.release();
204 return available;
205 }
206
207 if (!modeId) {
208 LockGuard<Mutex> guard(m_DeviceLock);
209 if (!availableLocked())
210 return false;
211 disableDisplayLocked();
212 return true;
213 }
214
215 return Display::setScreenMode(modeId);
216 }
217
218 virtual bool setScreenMode(size_t width, size_t height, size_t bpp) {
220 return false;
221 LockGuard<Mutex> guard(m_DeviceLock);
222 if (!availableLocked())
223 return false;
224
225 if (width > m_MaxWidth)
226 width = m_MaxWidth;
227 if (height > m_MaxHeight)
228 height = m_MaxHeight;
229 return setModeLocked(width, height, bpp);
230 }
231
232 bool redraw(size_t x, size_t y, size_t width, size_t height) {
234 return false;
235 LockGuard<Mutex> guard(m_DeviceLock);
236 if (!availableLocked() || !m_FifoRunning || !m_pFramebuffer)
237 return false;
238
239 const size_t screenWidth = m_pFramebuffer->getWidth();
240 const size_t screenHeight = m_pFramebuffer->getHeight();
241 if ((x >= screenWidth) || (y >= screenHeight))
242 return true;
243 if (width > (screenWidth - x))
244 width = screenWidth - x;
245 if (height > (screenHeight - y))
246 height = screenHeight - y;
247 if (!width || !height)
248 return true;
249
250 const uint32_t command[] = {SVGA_CMD_UPDATE, static_cast<uint32_t>(x), static_cast<uint32_t>(y),
251 static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
252 return submitFifoLocked(command, sizeof(command) / sizeof(command[0]));
253 }
254
255 bool copy(size_t sourceX, size_t sourceY, size_t destinationX, size_t destinationY, size_t width,
256 size_t height, bool softwareFallback) {
258 return false;
259 LockGuard<Mutex> guard(m_DeviceLock);
260 if (!availableLocked() || !m_pFramebuffer || !m_pFramebuffer->getActive())
261 return false;
262
263 const size_t screenWidth = m_pFramebuffer->getWidth();
264 const size_t screenHeight = m_pFramebuffer->getHeight();
265 if ((sourceX >= screenWidth) || (destinationX >= screenWidth) || (sourceY >= screenHeight) ||
266 (destinationY >= screenHeight))
267 return false;
268
269 if (width > (screenWidth - sourceX))
270 width = screenWidth - sourceX;
271 if (width > (screenWidth - destinationX))
272 width = screenWidth - destinationX;
273 if (height > (screenHeight - sourceY))
274 height = screenHeight - sourceY;
275 if (height > (screenHeight - destinationY))
276 height = screenHeight - destinationY;
277 if (!width || !height)
278 return true;
279
280 if (m_FifoRunning && (m_Capabilities & SVGA_CAP_RECT_COPY)) {
281 const uint32_t command[] = {SVGA_CMD_RECT_COPY,
282 static_cast<uint32_t>(sourceX),
283 static_cast<uint32_t>(sourceY),
284 static_cast<uint32_t>(destinationX),
285 static_cast<uint32_t>(destinationY),
286 static_cast<uint32_t>(width),
287 static_cast<uint32_t>(height)};
288 if (submitFifoLocked(command, sizeof(command) / sizeof(command[0])))
289 return true;
290 if (!availableLocked())
291 return false;
292 }
293
294 if (!softwareFallback)
295 return false;
296 m_pFramebuffer->softwareCopy(sourceX, sourceY, destinationX, destinationY, width, height);
297 return true;
298 }
299
301 public:
302 VmwareFramebuffer(uintptr_t framebuffer, VmwareGraphics* pDisplay)
303 : Framebuffer(), m_CallbackOperations(), m_pDisplay(pDisplay) {
304 setFramebuffer(framebuffer);
305 }
306
307 virtual ~VmwareFramebuffer() = default;
308
309 void closeCallbacks() {
310 m_CallbackOperations.close();
311 }
312
313 void detach() {
314 m_CallbackOperations.wait();
315 m_pDisplay = nullptr;
316 setActive(false);
317 setFramebuffer(0);
318 }
319
320 virtual void hwRedraw(size_t x = ~0UL, size_t y = ~0UL, size_t width = ~0UL,
321 size_t height = ~0UL) {
323 return;
325 if (!m_CallbackOperations.tryAcquire(callback))
326 return;
327 if (!m_pDisplay)
328 return;
329 if (x == ~0UL)
330 x = 0;
331 if (y == ~0UL)
332 y = 0;
333 m_pDisplay->redraw(x, y, width, height);
334 }
335
336 virtual void copy(size_t sourceX, size_t sourceY, size_t destinationX, size_t destinationY,
337 size_t width, size_t height, bool bLowestCall = true) {
339 return;
341 if (!m_CallbackOperations.tryAcquire(callback))
342 return;
343 if (m_pDisplay)
344 m_pDisplay->copy(sourceX, sourceY, destinationX, destinationY, width, height, bLowestCall);
345 }
346
347 void softwareCopy(size_t sourceX, size_t sourceY, size_t destinationX, size_t destinationY,
348 size_t width, size_t height) {
349 swCopy(sourceX, sourceY, destinationX, destinationY, width, height);
350 }
351
352 private:
353 OperationBarrier m_CallbackOperations;
354 VmwareGraphics* m_pDisplay;
355 };
356
357 private:
358 bool availableLocked() const {
359 return m_Online && !m_Stopping;
360 }
361
362 static Graphics::PixelFormat pixelFormat(size_t bpp, uint32_t redMask, uint32_t greenMask,
363 uint32_t blueMask) {
364 switch (bpp) {
365 case 24:
366 return redMask > blueMask ? Graphics::Bits24_Rgb : Graphics::Bits24_Bgr;
367 case 16:
368 if ((redMask == greenMask) && (greenMask == blueMask)) {
369 return blueMask == 0xF ? Graphics::Bits16_Argb : Graphics::Bits16_Rgb555;
370 }
371 return Graphics::Bits16_Rgb565;
372 default:
373 return Graphics::Bits32_Argb;
374 }
375 }
376
377 bool initialiseHardwareLocked() {
378 if (m_Addresses.count() < 3) {
379 WARNING("vmware-gfx has an incomplete PCI BAR set");
380 return false;
381 }
382
383 if (!m_Addresses[0]) {
384 WARNING("vmware-gfx has no register I/O BAR");
385 return false;
386 }
387
388 m_pIo = m_Addresses[0]->m_Io;
389 if (!m_pIo || !(*m_pIo)) {
390 WARNING("vmware-gfx has no usable register I/O BAR");
391 return false;
392 }
393
394 writeRegisterLocked(SVGA_REG_ID, SVGA_MAKE_ID(2));
395 if (readRegisterLocked(SVGA_REG_ID) != SVGA_MAKE_ID(2)) {
396 WARNING("vmware-gfx is not a compatible SVGA2 device");
397 return false;
398 }
399 m_HardwareAccepted = true;
400
401 const uintptr_t fbBase = readRegisterLocked(SVGA_REG_FB_START);
402 const size_t vramSize = readRegisterLocked(SVGA_REG_VRAM_SIZE);
403 const size_t framebufferSize = readRegisterLocked(SVGA_REG_FB_SIZE);
404 const uintptr_t commandBase = readRegisterLocked(SVGA_REG_MEM_START);
405 const size_t commandSize = readRegisterLocked(SVGA_REG_MEM_SIZE);
406 m_Capabilities = readRegisterLocked(SVGA_REG_CAPABILITIES);
407 m_MaxWidth = readRegisterLocked(SVGA_REG_MAX_WIDTH);
408 m_MaxHeight = readRegisterLocked(SVGA_REG_MAX_HEIGHT);
409
410 Device::Address* pFramebufferAddress = nullptr;
411 Device::Address* pCommandAddress = nullptr;
412 for (size_t i = 1; i < m_Addresses.count(); ++i) {
413 Device::Address* pAddress = m_Addresses[i];
414 if (!pAddress || pAddress->m_IsIoSpace)
415 continue;
416 if (pAddress->m_Address == fbBase)
417 pFramebufferAddress = pAddress;
418 if (pAddress->m_Address == commandBase)
419 pCommandAddress = pAddress;
420 }
421
422 if (!pFramebufferAddress || !pCommandAddress || (pFramebufferAddress == pCommandAddress) ||
423 !pFramebufferAddress->m_Io || !pCommandAddress->m_Io || !m_MaxWidth || !m_MaxHeight ||
424 !commandSize || (commandSize > pCommandAddress->m_Size) ||
425 (commandSize > static_cast<size_t>(~static_cast<uint32_t>(0)))) {
426 WARNING("vmware-gfx reported an invalid framebuffer/FIFO layout");
427 return false;
428 }
429
430 m_Framebuffer = static_cast<MemoryMappedIo*>(pFramebufferAddress->m_Io);
431 m_CommandRegion = static_cast<MemoryMappedIo*>(pCommandAddress->m_Io);
432 m_FramebufferSize = pFramebufferAddress->m_Size;
433 if (framebufferSize && (framebufferSize < m_FramebufferSize))
434 m_FramebufferSize = framebufferSize;
435 m_CommandRegionSize = commandSize;
436 if (!m_FramebufferSize) {
437 WARNING("vmware-gfx reported an empty framebuffer aperture");
438 return false;
439 }
440
441 // Address::map() is one-shot, so map the complete usable aperture now
442 // rather than assuming a later, larger mode can grow the mapping.
443 pFramebufferAddress->map(m_FramebufferSize, true, true);
444 if (!m_Framebuffer->virtualAddress() || (m_Framebuffer->size() < m_FramebufferSize)) {
445 WARNING("vmware-gfx could not map its framebuffer aperture");
446 return false;
447 }
448
449 pCommandAddress->map(m_CommandRegionSize);
450 if (!m_CommandRegion->virtualAddress() || (m_CommandRegion->size() < m_CommandRegionSize)) {
451 WARNING("vmware-gfx could not map its FIFO aperture");
452 return false;
453 }
454
455 uint32_t fifoMin = 4 * sizeof(uint32_t);
456 const uint32_t fifoMax = static_cast<uint32_t>(commandSize) & ~static_cast<uint32_t>(3);
457 if (m_Capabilities & SVGA_CAP_EXTENDED_FIFO) {
458 const size_t fifoRegisters = readRegisterLocked(SVGA_REG_MEM_REGS);
459 if ((fifoRegisters < SVGA_FIFO_NUM_REGS) ||
460 (fifoRegisters > (static_cast<size_t>(~static_cast<uint32_t>(0)) / sizeof(uint32_t)))) {
461 WARNING("vmware-gfx reported an invalid extended FIFO header");
462 return false;
463 }
464 fifoMin = static_cast<uint32_t>(fifoRegisters * sizeof(uint32_t));
465 }
466
467 if ((fifoMax <= fifoMin) || ((fifoMax - fifoMin) < MinimumFifoBytes)) {
468 WARNING("vmware-gfx FIFO is too small for supported commands");
469 return false;
470 }
471 m_FifoHeaderBytes = fifoMin;
472
473 disableDisplayLocked();
474 volatile uint32_t* fifo = fifoLocked();
475 fifo[SVGA_FIFO_MIN] = fifoMin;
476 fifo[SVGA_FIFO_MAX] = fifoMax;
477 fifo[SVGA_FIFO_NEXT_CMD] = fifoMin;
478 fifo[SVGA_FIFO_STOP] = fifoMin;
479 asm volatile("" : : : "memory");
480
481 writeRegisterLocked(SVGA_REG_GUEST_ID, 0x500A);
482 NOTICE("vmware-gfx found, caps=" << Hex << m_Capabilities << ", maximum resolution is " << Dec
483 << m_MaxWidth << "x" << m_MaxHeight << Hex);
484 NOTICE("vmware-gfx framebuffer at " << Hex << fbBase << " - " << (fbBase + vramSize)
485 << ", command FIFO at " << commandBase);
486 if (m_Capabilities & SVGA_CAP_EXTENDED_FIFO) {
487 NOTICE("vmware-gfx using extended fifo, caps=" << fifo[SVGA_FIFO_CAPABILITIES]
488 << ", flags=" << fifo[SVGA_FIFO_FLAGS]);
489 }
490
491 return true;
492 }
493
494 bool registerProvider() {
495 ServiceFeatures* pFeatures = ServiceManager::instance().enumerateOperations(String("graphics"));
496 Service* pService = ServiceManager::instance().getService(String("graphics"));
497 if (!pFeatures || !pService || !pFeatures->provides(ServiceFeatures::touch) ||
498 !pService->serve(ServiceFeatures::touch, static_cast<void*>(m_pProvider),
499 sizeof(*m_pProvider)))
500 return false;
501
502 LockGuard<Mutex> guard(m_DeviceLock);
503 m_ProviderRegistered = true;
504 return true;
505 }
506
507 void unregisterProvider() {
508 GraphicsService::GraphicsProvider* pProvider = nullptr;
509 {
510 LockGuard<Mutex> guard(m_DeviceLock);
511 if (!m_ProviderRegistered)
512 return;
513 pProvider = m_pProvider;
514 }
515
516 ServiceFeatures* pFeatures = ServiceManager::instance().enumerateOperations(String("graphics"));
517 Service* pService = ServiceManager::instance().getService(String("graphics"));
518 if (!pFeatures || !pService || !pFeatures->provides(ServiceFeatures::withdraw) ||
519 !pService->serve(ServiceFeatures::withdraw, static_cast<void*>(pProvider),
520 sizeof(*pProvider))) {
521 panic("vmware-gfx could not withdraw its graphics provider");
522 }
523
524 LockGuard<Mutex> guard(m_DeviceLock);
525 m_ProviderRegistered = false;
526 }
527
528 bool setModeLocked(size_t width, size_t height, size_t bpp) {
529 if (!availableLocked() || !width || !height || !bpp || (width > m_MaxWidth) ||
530 (height > m_MaxHeight) || (bpp > 32))
531 return false;
532
533 if (m_FifoRunning && !syncFifoLocked()) {
534 markOfflineLocked("timed out draining FIFO before a mode change");
535 return false;
536 }
537
538 writeRegisterLocked(SVGA_REG_CONFIG_DONE, 0);
539 m_FifoRunning = false;
540 writeRegisterLocked(SVGA_REG_ENABLE, 1);
541 writeRegisterLocked(SVGA_REG_WIDTH, static_cast<uint32_t>(width));
542 writeRegisterLocked(SVGA_REG_HEIGHT, static_cast<uint32_t>(height));
543 writeRegisterLocked(SVGA_REG_BITS_PER_PIXEL, static_cast<uint32_t>(bpp));
544
545 const size_t actualWidth = readRegisterLocked(SVGA_REG_WIDTH);
546 const size_t actualHeight = readRegisterLocked(SVGA_REG_HEIGHT);
547 const size_t actualDepth = readRegisterLocked(SVGA_REG_DEPTH);
548 const size_t fbOffset = readRegisterLocked(SVGA_REG_FB_OFFSET);
549 const uintptr_t fbBase = readRegisterLocked(SVGA_REG_FB_START);
550 const uint32_t redMask = readRegisterLocked(SVGA_REG_RED_MASK);
551 const uint32_t greenMask = readRegisterLocked(SVGA_REG_GREEN_MASK);
552 const uint32_t blueMask = readRegisterLocked(SVGA_REG_BLUE_MASK);
553 const size_t bytesPerLine = readRegisterLocked(SVGA_REG_BYTES_PER_LINE);
554
555 if (!actualWidth || !actualHeight || !bytesPerLine || (actualWidth > m_MaxWidth) ||
556 (actualHeight > m_MaxHeight) ||
557 (actualWidth > ((~static_cast<size_t>(0) - 7) / static_cast<size_t>(bpp))) ||
558 (bytesPerLine < ((actualWidth * bpp + 7) / 8)) ||
559 (actualHeight > (~static_cast<size_t>(0) / bytesPerLine))) {
560 markOfflineLocked("device returned an invalid mode layout");
561 return false;
562 }
563
564 const size_t framebufferBytes = actualHeight * bytesPerLine;
565 if ((fbOffset > m_FramebufferSize) || (framebufferBytes > (m_FramebufferSize - fbOffset))) {
566 markOfflineLocked("mode exceeds the framebuffer aperture");
567 return false;
568 }
569
570 const size_t requiredMapping = fbOffset + framebufferBytes;
571 if (!m_Framebuffer->virtualAddress() || (m_Framebuffer->size() < requiredMapping)) {
572 markOfflineLocked("could not map the complete framebuffer mode");
573 return false;
574 }
575
576 m_pFramebuffer->setWidth(actualWidth);
577 m_pFramebuffer->setHeight(actualHeight);
578 m_pFramebuffer->setBytesPerPixel((bpp + 7) / 8);
579 m_pFramebuffer->setBytesPerLine(bytesPerLine);
580 m_pFramebuffer->setFormat(pixelFormat(bpp, redMask, greenMask, blueMask));
581 m_pFramebuffer->setXPos(0);
582 m_pFramebuffer->setYPos(0);
583 m_pFramebuffer->setParent(nullptr);
584 m_pFramebuffer->setFramebuffer(reinterpret_cast<uintptr_t>(m_Framebuffer->virtualAddress()) +
585 fbOffset);
586 m_pFramebuffer->setActive(true);
587 m_pFramebuffer->rect(0, 0, actualWidth, actualHeight, 0);
588
589 asm volatile("" : : : "memory");
590 writeRegisterLocked(SVGA_REG_CONFIG_DONE, 1);
591 m_FifoRunning = true;
592 NOTICE("vmware-gfx entered mode " << Dec << actualWidth << "x" << actualHeight << "x"
593 << actualDepth << Hex << ", mode framebuffer is "
594 << (fbBase + fbOffset));
595 return true;
596 }
597
598 bool submitFifoLocked(const uint32_t* pCommand, size_t words) {
599 if (!availableLocked() || !m_FifoRunning || !pCommand || (words > LargestCommandWords))
600 return false;
601
603 if (!prepareFifoLocked(words, layout))
604 return false;
605
606 volatile uint32_t* fifo = fifoLocked();
607 uint32_t next = layout.next;
608 for (size_t i = 0; i < words; ++i) {
609 fifo[next / sizeof(uint32_t)] = pCommand[i];
610 next += sizeof(uint32_t);
611 if (next == layout.max)
612 next = layout.min;
613 }
614
615 // Publish NEXT_CMD only after every word of the command is visible.
616 asm volatile("" : : : "memory");
617 fifo[SVGA_FIFO_NEXT_CMD] = next;
618 asm volatile("" : : : "memory");
619 return true;
620 }
621
622 bool prepareFifoLocked(size_t words, VmwareSvgaState::FifoLayout& layout) {
623 layout = readFifoLayoutLocked();
624 if (!VmwareSvgaState::valid(layout, m_FifoHeaderBytes, m_CommandRegionSize)) {
625 markOfflineLocked("device returned an invalid FIFO cursor");
626 return false;
627 }
628 if (VmwareSvgaState::canFit(layout, words))
629 return true;
630
631 if (!syncFifoLocked()) {
632 markOfflineLocked("timed out waiting for FIFO space");
633 return false;
634 }
635
636 layout = readFifoLayoutLocked();
637 if (!VmwareSvgaState::valid(layout, m_FifoHeaderBytes, m_CommandRegionSize) ||
638 !VmwareSvgaState::canFit(layout, words)) {
639 markOfflineLocked("FIFO remained full after a completed sync");
640 return false;
641 }
642 return true;
643 }
644
645 bool syncFifoLocked() {
646 writeRegisterLocked(SVGA_REG_SYNC, 1);
647 VmwareSvgaState::PollBudget budget(Time::getTicks());
648 while (readRegisterLocked(SVGA_REG_BUSY)) {
649 if (!budget.keepPolling(Time::getTicks()))
650 return false;
652 }
653 return true;
654 }
655
656 VmwareSvgaState::FifoLayout readFifoLayoutLocked() const {
657 volatile uint32_t* fifo = fifoLocked();
658 return {fifo[SVGA_FIFO_MIN], fifo[SVGA_FIFO_MAX], fifo[SVGA_FIFO_NEXT_CMD],
659 fifo[SVGA_FIFO_STOP]};
660 }
661
662 volatile uint32_t* fifoLocked() const {
663 return reinterpret_cast<volatile uint32_t*>(m_CommandRegion->virtualAddress());
664 }
665
666 uint32_t readRegisterLocked(size_t offset) const {
667 m_pIo->write32(static_cast<uint32_t>(offset), SVGA_INDEX_PORT);
668 return m_pIo->read32(SVGA_VALUE_PORT);
669 }
670
671 void writeRegisterLocked(size_t offset, uint32_t value) const {
672 m_pIo->write32(static_cast<uint32_t>(offset), SVGA_INDEX_PORT);
673 m_pIo->write32(value, SVGA_VALUE_PORT);
674 }
675
676 void disableDisplayLocked() {
677 if (!m_HardwareAccepted)
678 return;
679 writeRegisterLocked(SVGA_REG_CONFIG_DONE, 0);
680 writeRegisterLocked(SVGA_REG_ENABLE, 0);
681 m_FifoRunning = false;
682 if (m_pFramebuffer)
683 m_pFramebuffer->setActive(false);
684 }
685
686 void disableHardwareLocked() {
687 disableDisplayLocked();
688 m_Online = false;
689 }
690
691 void markOfflineLocked(const char* reason) {
692 WARNING("vmware-gfx offline: " << reason);
693 disableHardwareLocked();
694 }
695
696 IoBase* m_pIo;
697 MemoryMappedIo* m_Framebuffer;
698 MemoryMappedIo* m_CommandRegion;
699 VmwareFramebuffer* m_pFramebuffer;
701 Mutex m_DeviceLock;
702 uint32_t m_Capabilities;
703 size_t m_MaxWidth;
704 size_t m_MaxHeight;
705 size_t m_FramebufferSize;
706 size_t m_CommandRegionSize;
707 size_t m_FifoHeaderBytes;
708 bool m_HardwareAccepted;
709 bool m_Online;
710 bool m_FifoRunning;
711 bool m_Stopping;
712 bool m_ProviderRegistered;
713 bool m_ShutdownComplete;
714};
715
716VmwareGraphics::~VmwareGraphics() {
717 shutdown();
718}
719
720void VmwareGraphics::shutdown() {
721 VmwareFramebuffer* pFramebuffer = nullptr;
722 {
723 LockGuard<Mutex> guard(m_DeviceLock);
724 if (m_ShutdownComplete)
725 return;
726 if (m_Stopping)
727 panic("vmware-gfx encountered concurrent teardown");
728 m_Stopping = true;
729 pFramebuffer = m_pFramebuffer;
730 }
731
732 // Reject new callbacks before the provider disappears from discovery.
733 // Operations admitted before the close are drained before state is freed.
734 if (pFramebuffer)
735 pFramebuffer->closeCallbacks();
736 unregisterProvider();
737 if (pFramebuffer)
738 pFramebuffer->detach();
739
740 GraphicsService::GraphicsProvider* pProvider = nullptr;
741 {
742 LockGuard<Mutex> guard(m_DeviceLock);
743 if (m_FifoRunning && !syncFifoLocked())
744 WARNING("vmware-gfx timed out draining FIFO during teardown");
745 disableHardwareLocked();
746 pFramebuffer = m_pFramebuffer;
747 pProvider = m_pProvider;
748 m_pFramebuffer = nullptr;
749 m_pProvider = nullptr;
750 m_ShutdownComplete = true;
751 }
752
753 delete pFramebuffer;
754 delete pProvider;
755}
756
757static bool g_Found = false;
758static List<VmwareGraphics*> g_Displays;
759
760static void probeDevice(Device* pDevice) {
761 Device* pParent = pDevice->getParent();
762 if (!pParent)
763 panic("vmware-gfx found a detached PCI device");
764 if (pDevice->getNumChildren()) {
765 ERROR("vmware-gfx cannot bind a non-leaf PCI device");
766 return;
767 }
768
769 VmwareGraphics* pGraphics = new VmwareGraphics(pDevice);
770 if (!pGraphics) {
771 ERROR("vmware-gfx could not allocate device state");
772 return;
773 }
774 pGraphics->setParent(pParent);
775 pParent->replaceChild(pDevice, pGraphics);
776 // Device(Device *) recreated every BAR and invalidated the source mappings.
777 // The leaf check makes deleting the now-inert source safe.
778 delete pDevice;
779
780 g_Displays.pushBack(pGraphics);
781 g_Found = true;
782 if (!pGraphics->initialise())
783 ERROR("vmware-gfx device initialisation failed; device left offline");
784}
785
786static bool entry() {
787 g_Found = false;
788 Device::searchByVendorIdAndDeviceId(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_SVGA2,
789 probeDevice);
790 return g_Found;
791}
792
793static void exit() {
794 auto restoreDisplay = [](Device* pDevice, Device* pTarget, bool* pRestored) -> Device* {
795 if (pDevice != pTarget)
796 return pDevice;
797 *pRestored = true;
798 return new Device(pTarget);
799 };
800 auto callback = pedigree_std::make_callable(restoreDisplay);
801 while (g_Displays.count()) {
802 VmwareGraphics* pDisplay = g_Displays.popFront();
803 // Device(Device *) destroys the source I/O objects while recreating
804 // the BARs, so no driver callback may still be admitted at that point.
805 pDisplay->shutdown();
806 bool restored = false;
807 Device::foreach (callback, nullptr, pDisplay, &restored);
808 if (!restored)
809 delete pDisplay;
810 }
811 g_Found = false;
812}
813
814MODULE_INFO("vmware-gfx", &entry, &exit, "pci");
IoBase * m_Io
Definition Device.h:115
size_t m_Size
Definition Device.h:109
void map(size_t forcedSize=0, bool bUser=false, bool bWriteCombine=false, bool bWriteThrough=false)
Definition Device.cc:346
uintptr_t m_Address
Definition Device.h:107
bool m_IsIoSpace
Definition Device.h:112
size_t getNumChildren()
Definition Device.cc:143
static void foreach(Callback callback, Device *root=0)
Definition Device.cc:107
Vector< Address * > m_Addresses
Definition Device.h:349
Device * getParent() const
Definition Device.h:166
void setParent(Device *p)
Definition Device.h:170
void replaceChild(Device *src, Device *dest)
Definition Device.cc:166
virtual bool setScreenMode(ScreenMode sm)
Definition Display.cc:90
void swCopy(size_t srcx, size_t srcy, size_t destx, size_t desty, size_t w, size_t h)
virtual void rect(size_t x, size_t y, size_t width, size_t height, uint32_t colour, Graphics::PixelFormat format=Graphics::Bits32_Argb, bool bLowestCall=true)
Abstrace base class for hardware I/O capabilities.
Definition IoBase.h:32
virtual void write32(uint32_t value, size_t offset=0)=0
virtual uint32_t read32(size_t offset=0)=0
Definition List.h:61
Memory mapped I/O range.
virtual size_t size() const
void * virtualAddress() const
Definition Mutex.h:56
MUST_USE_RESULT bool tryAcquire(Lease &lease)
static bool getInterrupts()
static void pause()
void release(size_t n=1)
Definition Semaphore.cc:549
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:484
virtual bool provides(Type service)
Service * getService(const String &serviceName)
ServiceFeatures * enumerateOperations(const String &serviceName)
virtual bool serve(ServiceFeatures::Type type, void *pData, size_t dataLen)=0
virtual void hwRedraw(size_t x=~0UL, size_t y=~0UL, size_t width=~0UL, size_t height=~0UL)
Inherited by drivers that provide a hardware redraw function.
virtual void copy(size_t sourceX, size_t sourceY, size_t destinationX, size_t destinationY, size_t width, size_t height, bool bLowestCall=true)
virtual bool setScreenMode(Display::ScreenMode sm)
virtual void getName(String &str)
virtual bool getScreenModes(List< Display::ScreenMode * > &sms)
virtual bool setScreenMode(size_t width, size_t height, size_t bpp)
virtual bool getCurrentScreenMode(Display::ScreenMode &sm)
virtual bool setScreenMode(size_t modeId)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
void pushBack(const T &value)
Definition List.h:216
uint8_t nBpp
Bits per pixel (total).
Definition Display.h:62