#include // VMware SVGA-II PCI display provider. This file owns adapter setup, FIFO command submission, framebuffer presentation, and cursor integration. #include #include "vmwaresvga.h" static DRIVER_OBJECT gVmwSvgaDriver; static const IO_DEVICE_ID gVmwSvgaIds[] = { { IoBusPci, { "PCI\\VEN_15AD&DEV_0405", sizeof("PCI\\VEN_15AD&DEV_0405") - 1 } }, }; static const IO_DRIVER_ID_TABLE gVmwSvgaIdTable = { gVmwSvgaIds, sizeof(gVmwSvgaIds) / sizeof(gVmwSvgaIds[0]), }; static KSTATUS VmwSvgaSyncDevice(VMWSVGA_DEVICE* v); static void VmwSvgaWakeFifo(VMWSVGA_DEVICE* v); // Reads a 32-bit SVGA register through the indexed I/O port pair. static uint32_t VmwSvgaReadReg(VMWSVGA_DEVICE* v, uint32_t reg) { __outdword((unsigned short)(v->IoBase + SVGA_INDEX_PORT), reg); return __indword((unsigned short)(v->IoBase + SVGA_VALUE_PORT)); } // Writes a 32-bit SVGA register through the indexed I/O port pair. static void VmwSvgaWriteReg(VMWSVGA_DEVICE* v, uint32_t reg, uint32_t value) { __outdword((unsigned short)(v->IoBase + SVGA_INDEX_PORT), reg); __outdword((unsigned short)(v->IoBase + SVGA_VALUE_PORT), value); } // Negotiates the highest SVGA device ID supported by the adapter. static uint32_t VmwSvgaProbeId(VMWSVGA_DEVICE* v) { for (uint32_t id = SVGA_ID_2; id >= SVGA_ID_0; id--) { VmwSvgaWriteReg(v, SVGA_REG_ID, id); uint32_t got = VmwSvgaReadReg(v, SVGA_REG_ID); if (got == id) return id; if (id == SVGA_ID_0) break; } return SVGA_ID_INVALID; } // Chooses the runtime display mode, preferring the current boot framebuffer size. static KSTATUS VmwSvgaChooseMode(uint32_t* outWidth, uint32_t* outHeight) { if (!outWidth || !outHeight) return NTLSTATUS_INVALID_PARAMETER; DISPLAY_MODE current; memset(¤t, 0, sizeof(current)); if (KSUCCESS(KeDisplayGetMode(¤t)) && current.Format == DisplayPixelFormatX8R8G8B8 && current.Width > 0 && current.Height > 0) { *outWidth = current.Width; *outHeight = current.Height; return NTLSTATUS_SUCCESS; } *outWidth = 1024; *outHeight = 768; return NTLSTATUS_SUCCESS; } // Programs the visible guest display rectangle used by the legacy framebuffer path. static void VmwSvgaProgramLegacyDisplayTopology(VMWSVGA_DEVICE* v, uint32_t width, uint32_t height, uint32_t caps) { if (!v || (caps & SVGA_CAP_DISPLAY_TOPOLOGY) == 0) return; // Legacy GFB rendering still needs one visible primary display rectangle. VmwSvgaWriteReg(v, SVGA_REG_NUM_GUEST_DISPLAYS, 1u); VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_ID, 0u); VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_IS_PRIMARY, 1u); VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_POSITION_X, 0u); VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_POSITION_Y, 0u); VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_WIDTH, width); VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_HEIGHT, height); } // Programs the SVGA device mode before FIFO and framebuffer use. static KSTATUS VmwSvgaSetMode(VMWSVGA_DEVICE* v) { if (!v) return NTLSTATUS_INVALID_PARAMETER; uint32_t desiredWidth = 0; uint32_t desiredHeight = 0; KSTATUS st = VmwSvgaChooseMode(&desiredWidth, &desiredHeight); if (!KSUCCESS(st)) return st; uint32_t maxWidth = VmwSvgaReadReg(v, SVGA_REG_MAX_WIDTH); uint32_t maxHeight = VmwSvgaReadReg(v, SVGA_REG_MAX_HEIGHT); uint32_t caps = VmwSvgaReadReg(v, SVGA_REG_CAPABILITIES); if (maxWidth && desiredWidth > maxWidth) desiredWidth = maxWidth; if (maxHeight && desiredHeight > maxHeight) desiredHeight = maxHeight; if (desiredWidth == 0 || desiredHeight == 0) return NTLSTATUS_DEVICE_ERROR; VmwSvgaWriteReg(v, SVGA_REG_CONFIG_DONE, 0u); VmwSvgaWriteReg(v, SVGA_REG_ENABLE, 0u); VmwSvgaWriteReg(v, SVGA_REG_WIDTH, desiredWidth); VmwSvgaWriteReg(v, SVGA_REG_HEIGHT, desiredHeight); VmwSvgaWriteReg(v, SVGA_REG_BITS_PER_PIXEL, 32u); if (caps & SVGA_CAP_PITCHLOCK) VmwSvgaWriteReg(v, SVGA_REG_PITCHLOCK, desiredWidth * sizeof(uint32_t)); #if !VMWSVGA_ENABLE_SCREEN_OBJECTS VmwSvgaProgramLegacyDisplayTopology(v, desiredWidth, desiredHeight, caps); #endif VmwSvgaWriteReg(v, SVGA_REG_ENABLE, 1u); DbgPrint("VMWSVGA: requested mode %ux%u max=%ux%u caps=0x%x\n", desiredWidth, desiredHeight, maxWidth, maxHeight, caps); return NTLSTATUS_SUCCESS; } // Clips a display rectangle to the current framebuffer bounds. static bool VmwSvgaClipRect(VMWSVGA_DEVICE* v, DISPLAY_RECT* r) { if (!v || !r || r->W <= 0 || r->H <= 0) return false; int64_t x0 = r->X; int64_t y0 = r->Y; int64_t x1 = x0 + r->W; int64_t y1 = y0 + r->H; if (x0 < 0) x0 = 0; if (y0 < 0) y0 = 0; if (x1 > (int64_t)v->Width) x1 = v->Width; if (y1 > (int64_t)v->Height) y1 = v->Height; if (x0 >= x1 || y0 >= y1) return false; r->X = (int32_t)x0; r->Y = (int32_t)y0; r->W = (int32_t)(x1 - x0); r->H = (int32_t)(y1 - y0); return true; } // Returns the next FIFO dword offset, wrapping at FIFO_MAX. static uint32_t VmwSvgaFifoNext(VMWSVGA_DEVICE* v, uint32_t offset) { uint32_t next = offset + sizeof(uint32_t); uint32_t min = v->Fifo[SVGA_FIFO_MIN]; uint32_t max = v->Fifo[SVGA_FIFO_MAX]; if (next >= max) next = min; return next; } // Writes one command dword into the SVGA FIFO and waits for space if needed. static KSTATUS VmwSvgaFifoWrite(VMWSVGA_DEVICE* v, uint32_t value) { if (!v || !v->FifoReady || !v->Fifo) return NTLSTATUS_NOT_SUPPORTED; uint32_t min = v->Fifo[SVGA_FIFO_MIN]; uint32_t max = v->Fifo[SVGA_FIFO_MAX]; if (min < SVGA_FIFO_NUM_LEGACY_REGS * sizeof(uint32_t) || max > v->FifoBytes || min >= max) return NTLSTATUS_DEVICE_ERROR; for (uint32_t guard = 0; guard < VMWSVGA_MAX_WAIT_ITERS; guard++) { uint32_t next = v->Fifo[SVGA_FIFO_NEXT_CMD]; uint32_t stop = v->Fifo[SVGA_FIFO_STOP]; if (next < min || next >= max || stop < min || stop >= max) return NTLSTATUS_DEVICE_ERROR; uint32_t nextAfter = VmwSvgaFifoNext(v, next); if (nextAfter != stop) { v->Fifo[next / sizeof(uint32_t)] = value; _ReadWriteBarrier(); v->Fifo[SVGA_FIFO_NEXT_CMD] = nextAfter; _ReadWriteBarrier(); return NTLSTATUS_SUCCESS; } KSTATUS syncSt = VmwSvgaSyncDevice(v); if (!KSUCCESS(syncSt)) return syncSt; } return NTLSTATUS_TIMEOUT; } // Checks whether an extended FIFO register is valid for this device. static bool VmwSvgaHasFifoRegister(VMWSVGA_DEVICE* v, uint32_t index) { if (!v || !v->Fifo || !v->FifoReady) return false; return v->FifoRegCount > index && v->Fifo[SVGA_FIFO_MIN] >= (index + 1u) * sizeof(uint32_t); } // Wakes the host side of the SVGA FIFO after new commands are queued. static void VmwSvgaWakeFifo(VMWSVGA_DEVICE* v) { if (!v || !v->FifoReady) return; _ReadWriteBarrier(); if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_BUSY)) { if (v->Fifo[SVGA_FIFO_BUSY]) return; v->Fifo[SVGA_FIFO_BUSY] = 1u; _ReadWriteBarrier(); } VmwSvgaWriteReg(v, SVGA_REG_SYNC, 1u); } static KSTATUS VmwSvgaUploadCursorImage(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels); // Implements the premultiply Argb For Bgra operation. static uint32_t VmwSvgaPremultiplyArgbForBgra(uint32_t argb) { uint32_t a = argb >> 24; if (a == 0) return 0u; if (a == 255u) return argb; uint32_t r = (argb >> 16) & 0xFFu; uint32_t g = (argb >> 8) & 0xFFu; uint32_t b = argb & 0xFFu; r = (r * a + 127u) / 255u; g = (g * a + 127u) / 255u; b = (b * a + 127u) / 255u; return (a << 24) | (r << 16) | (g << 8) | b; } // Implements the cursor Commit operation. static void VmwSvgaCursorCommit(VMWSVGA_DEVICE* v) { if (!v || !v->HardwareCursor || !v->CursorImageDefined) return; if (v->CursorBypass3 && VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_ON) && VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_X) && // Checks whether an extended FIFO register is valid for this device. VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_Y)) { v->Fifo[SVGA_FIFO_CURSOR_X] = (uint32_t)v->CursorX; v->Fifo[SVGA_FIFO_CURSOR_Y] = (uint32_t)v->CursorY; if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_SCREEN_ID)) v->Fifo[SVGA_FIFO_CURSOR_SCREEN_ID] = SVGA_ID_INVALID; v->Fifo[SVGA_FIFO_CURSOR_ON] = v->CursorVisible ? SVGA_CURSOR_ON_SHOW : SVGA_CURSOR_ON_HIDE; _ReadWriteBarrier(); if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_COUNT)) v->Fifo[SVGA_FIFO_CURSOR_COUNT]++; _ReadWriteBarrier(); VmwSvgaWakeFifo(v); return; } // Legacy QEMU SVGA cursors are frontend/host cursors. Re-defining the // cursor image from the input path can stall the FIFO/frontend after a // GTK focus/grab transition, so cursor activity must only update the // legacy position/visibility registers. The image is defined once when // SetCursorImage runs. VmwSvgaWriteReg(v, SVGA_REG_CURSOR_ID, 0u); VmwSvgaWriteReg(v, SVGA_REG_CURSOR_X, (uint32_t)v->CursorX); VmwSvgaWriteReg(v, SVGA_REG_CURSOR_Y, (uint32_t)v->CursorY); VmwSvgaWriteReg(v, SVGA_REG_CURSOR_ON, v->CursorVisible ? SVGA_CURSOR_ON_SHOW : SVGA_CURSOR_ON_HIDE); } // Implements the cursor Set Visible operation. static void VmwSvgaCursorSetVisible(VMWSVGA_DEVICE* v, bool visible) { if (!v || !v->HardwareCursor) return; v->CursorVisible = visible; VmwSvgaCursorCommit(v); } // Implements the cursor Set Position operation. static void VmwSvgaCursorSetPosition(VMWSVGA_DEVICE* v, int32_t x, int32_t y) { if (!v || !v->HardwareCursor) return; v->CursorX = x; v->CursorY = y; VmwSvgaCursorCommit(v); } // Implements the define Alpha Cursor operation. static KSTATUS VmwSvgaDefineAlphaCursor(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_DEFINE_ALPHA_CURSOR); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, 0u); // cursor id, reserved if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)hotX); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)hotY); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)width); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)height); if (!KSUCCESS(st)) return st; for (int32_t y = 0; y < height; y++) { for (int32_t x = 0; x < width; x++) { uint32_t p = argbPixels[(size_t)y * (size_t)width + (size_t)x]; st = VmwSvgaFifoWrite(v, VmwSvgaPremultiplyArgbForBgra(p)); if (!KSUCCESS(st)) return st; } } return VmwSvgaSyncDevice(v); } static uint32_t VmwSvgaPackMonoCursorDword(const uint8_t* bits, int32_t width, int32_t height, int32_t y, uint32_t dwordIndex) { // SVGA classic cursor scanlines are padded to 32 bits. QEMU stores // FIFO dwords in guest-endian order and then interprets the result as // bytes via cursor_set_mono(), whose first pixel is bit 0x80 of byte 0. // Therefore byte 0 of the numeric dword must contain pixels 0..7, // byte 1 pixels 8..15, and so on. uint8_t bytes[4] = { 0, 0, 0, 0 }; uint32_t baseX = dwordIndex * 32u; for (uint32_t bit = 0; bit < 32u; bit++) { uint32_t x = baseX + bit; if (y >= 0 && y < height && x < (uint32_t)width) { uint8_t v = bits[(size_t)y * (size_t)width + (size_t)x]; if (v) { uint32_t byteIndex = bit >> 3; uint8_t bitMask = (uint8_t)(0x80u >> (bit & 7u)); bytes[byteIndex] |= bitMask; } } } return ((uint32_t)bytes[0]) | ((uint32_t)bytes[1] << 8) | ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24); } // Implements the classic Cursor Width operation. static int32_t VmwSvgaClassicCursorWidth(int32_t width) { // QEMU's VMware SVGA implementation reads classic cursor masks as // SVGA dword-padded scanlines, then passes the raw byte stream to // cursor_set_mono(), which advances rows by ceil(width / 8) bytes. // For widths that are not a multiple of 32 pixels, those two row // strides differ and every other row is decoded from padding bytes. // Define the classic cursor with a 32-pixel-aligned visible width and // leave the extra pixels transparent. The real cursor image remains // at the left edge and the hotspot is unchanged. return (width + 31) & ~31; } static void VmwSvgaBuildMonoCursorBits(const uint32_t* argbPixels, int32_t sourceWidth, int32_t encodedWidth, int32_t height, uint8_t* andBits, uint8_t* xorBits) { // Classic SVGA cursors are AND/XOR masks. The most compatible path is // a 1bpp AND mask plus a 1bpp XOR mask: // transparent: AND=1 XOR=0 // black: AND=0 XOR=0 // white: AND=0 XOR=1 // This intentionally discards antialiasing, but avoids QEMU/GTK's // fragile 32bpp classic host-cursor path. for (int32_t y = 0; y < height; y++) { for (int32_t x = 0; x < encodedWidth; x++) { size_t out = (size_t)y * (size_t)encodedWidth + (size_t)x; if (x >= sourceWidth) { andBits[out] = 1u; xorBits[out] = 0u; continue; } size_t in = (size_t)y * (size_t)sourceWidth + (size_t)x; uint32_t p = argbPixels[in]; uint32_t a = p >> 24; uint32_t r = (p >> 16) & 0xFFu; uint32_t g = (p >> 8) & 0xFFu; uint32_t b = p & 0xFFu; if (a < 128u) { andBits[out] = 1u; xorBits[out] = 0u; } else { uint32_t luma = r * 30u + g * 59u + b * 11u; andBits[out] = 0u; xorBits[out] = (luma >= 12800u) ? 1u : 0u; } } } } // Implements the define Classic Cursor operation. static KSTATUS VmwSvgaDefineClassicCursor(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { int32_t encodedWidth = VmwSvgaClassicCursorWidth(width); uint32_t dwordsPerRow = ((uint32_t)encodedWidth + 31u) / 32u; size_t pixelCount = (size_t)encodedWidth * (size_t)height; if (encodedWidth > 256 || pixelCount > 256u * 256u) return NTLSTATUS_INVALID_PARAMETER; uint8_t* andBits = (uint8_t*)MmAllocatePool(pixelCount); uint8_t* xorBits = (uint8_t*)MmAllocatePool(pixelCount); if (!andBits || !xorBits) { if (andBits) MmFreePool(andBits); if (xorBits) MmFreePool(xorBits); return NTLSTATUS_NO_MEMORY; } VmwSvgaBuildMonoCursorBits(argbPixels, width, encodedWidth, height, andBits, xorBits); DbgPrint("VMWSVGA: classic cursor define source=%dx%d encoded=%dx%d hot=%d,%d\n", width, height, encodedWidth, height, hotX, hotY); KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_DEFINE_CURSOR); if (!KSUCCESS(st)) goto done; st = VmwSvgaFifoWrite(v, 0u); // cursor id, reserved if (!KSUCCESS(st)) goto done; st = VmwSvgaFifoWrite(v, (uint32_t)hotX); if (!KSUCCESS(st)) goto done; st = VmwSvgaFifoWrite(v, (uint32_t)hotY); if (!KSUCCESS(st)) goto done; st = VmwSvgaFifoWrite(v, (uint32_t)encodedWidth); if (!KSUCCESS(st)) goto done; st = VmwSvgaFifoWrite(v, (uint32_t)height); if (!KSUCCESS(st)) goto done; st = VmwSvgaFifoWrite(v, 1u); // AND mask depth if (!KSUCCESS(st)) goto done; st = VmwSvgaFifoWrite(v, 1u); // XOR mask depth if (!KSUCCESS(st)) goto done; for (int32_t y = 0; y < height; y++) { for (uint32_t d = 0; d < dwordsPerRow; d++) { st = VmwSvgaFifoWrite(v, VmwSvgaPackMonoCursorDword(andBits, encodedWidth, height, y, d)); if (!KSUCCESS(st)) goto done; } } for (int32_t y = 0; y < height; y++) { for (uint32_t d = 0; d < dwordsPerRow; d++) { st = VmwSvgaFifoWrite(v, VmwSvgaPackMonoCursorDword(xorBits, encodedWidth, height, y, d)); if (!KSUCCESS(st)) goto done; } } st = VmwSvgaSyncDevice(v); done: MmFreePool(andBits); MmFreePool(xorBits); return st; } // Implements the upload Cursor Image operation. static KSTATUS VmwSvgaUploadCursorImage(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { if (!v || !v->HardwareCursor || !argbPixels || width <= 0 || height <= 0) return NTLSTATUS_INVALID_PARAMETER; if (width > 256 || height > 256 || hotX < 0 || hotY < 0 || hotX >= width || hotY >= height) return NTLSTATUS_INVALID_PARAMETER; KSTATUS st; if (v->AlphaCursor) st = VmwSvgaDefineAlphaCursor(v, width, height, hotX, hotY, argbPixels); else st = VmwSvgaDefineClassicCursor(v, width, height, hotX, hotY, argbPixels); if (KSUCCESS(st)) VmwSvgaWriteReg(v, SVGA_REG_CURSOR_ID, 0u); return st; } // Implements the define Cursor Image operation. static KSTATUS VmwSvgaDefineCursorImage(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { if (!v || !v->HardwareCursor || !argbPixels || width <= 0 || height <= 0) return NTLSTATUS_INVALID_PARAMETER; if (width > 256 || height > 256 || hotX < 0 || hotY < 0 || hotX >= width || hotY >= height) return NTLSTATUS_INVALID_PARAMETER; size_t bytes = (size_t)width * (size_t)height * sizeof(uint32_t); uint32_t* copy = (uint32_t*)MmAllocatePool(bytes); if (!copy) return NTLSTATUS_NO_MEMORY; memcpy(copy, argbPixels, bytes); KSTATUS st = VmwSvgaUploadCursorImage(v, width, height, hotX, hotY, copy); if (!KSUCCESS(st)) { MmFreePool(copy); return st; } if (v->CursorImageArgb) MmFreePool(v->CursorImageArgb); v->CursorImageArgb = copy; v->CursorImageBytes = bytes; v->CursorImageWidth = width; v->CursorImageHeight = height; v->CursorHotX = hotX; v->CursorHotY = hotY; v->CursorImageDefined = true; return NTLSTATUS_SUCCESS; } // Implements the align Up32 operation. static uint32_t VmwSvgaAlignUp32(uint32_t value, uint32_t alignment) { return (value + alignment - 1u) & ~(alignment - 1u); } // Implements the define Screen Object operation. static KSTATUS VmwSvgaDefineScreenObject(VMWSVGA_DEVICE* v) { if (!v || !v->FifoReady || !v->UseScreenObject) return NTLSTATUS_INVALID_PARAMETER; KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_DEFINE_SCREEN); if (!KSUCCESS(st)) return st; // SVGAScreenObject, structSize = 11 dwords with backingStore+cloneCount. st = VmwSvgaFifoWrite(v, 44u); // structSize if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, v->ScreenId); // id if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, SVGA_SCREEN_MUST_BE_SET | SVGA_SCREEN_IS_PRIMARY); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, v->Width); // size.width if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, v->Height); // size.height if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, 0u); // root.x if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, 0u); // root.y if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, SVGA_GMR_FRAMEBUFFER); // backingStore.ptr.gmrId if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, v->ScreenBackingOffset); // backingStore.ptr.offset if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, v->BytesPerLine); // backingStore.pitch if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, 0u); // cloneCount if (!KSUCCESS(st)) return st; return VmwSvgaSyncDevice(v); } // Implements the define Gmrfb operation. static KSTATUS VmwSvgaDefineGmrfb(VMWSVGA_DEVICE* v) { if (!v || !v->FifoReady || !v->UseScreenObject) return NTLSTATUS_INVALID_PARAMETER; KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_DEFINE_GMRFB); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, SVGA_GMR_FRAMEBUFFER); // ptr.gmrId if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, v->ScreenSourceOffset); // ptr.offset if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, v->BytesPerLine); // bytesPerLine if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, SVGA_GMRFB_FORMAT_32BPP_24DEPTH); if (!KSUCCESS(st)) return st; return NTLSTATUS_SUCCESS; } // Blits gmrfb To Screen. static KSTATUS VmwSvgaBlitGmrfbToScreen(VMWSVGA_DEVICE* v, int32_t x, int32_t y, int32_t w, int32_t h) { DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_BLIT_GMRFB_TO_SCREEN); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)r.X); // srcOrigin.x if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)r.Y); // srcOrigin.y if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)r.X); // destRect.left if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)r.Y); // destRect.top if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)(r.X + r.W)); // destRect.right if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)(r.Y + r.H)); // destRect.bottom if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, v->ScreenId); // destScreenId if (!KSUCCESS(st)) return st; VmwSvgaWakeFifo(v); return NTLSTATUS_SUCCESS; } // Initializes screen Object. static KSTATUS VmwSvgaInitializeScreenObject(VMWSVGA_DEVICE* v) { if (!v || !v->FifoReady) return NTLSTATUS_INVALID_PARAMETER; DbgPrint("VMWSVGA: screen object probe caps=0x%x fifoCaps=0x%x\n", v->Capabilities, v->FifoCaps); bool hasScreenObject1 = (v->FifoCaps & SVGA_FIFO_CAP_SCREEN_OBJECT) != 0; bool hasScreenObject2 = ((v->FifoCaps & SVGA_FIFO_CAP_SCREEN_OBJECT_2) != 0) && ((v->Capabilities & SVGA_CAP_SCREEN_OBJECT_2) != 0); // Real VMware can advertise SCREEN_OBJECT_2 without also setting the // original FIFO_CAP_SCREEN_OBJECT bit. The command set is still the // Screen Object command set: DEFINE_SCREEN, DEFINE_GMRFB, and // BLIT_GMRFB_TO_SCREEN. Treat either capability generation as usable. if (!hasScreenObject1 && !hasScreenObject2) { DbgPrint("VMWSVGA: screen object unavailable: screenObject1=%u screenObject2=%u caps=0x%x fifoCaps=0x%x\n", hasScreenObject1 ? 1u : 0u, hasScreenObject2 ? 1u : 0u, v->Capabilities, v->FifoCaps); return NTLSTATUS_NOT_SUPPORTED; } DbgPrint("VMWSVGA: using screen object generation %u\n", hasScreenObject2 ? 2u : 1u); uint32_t sourceBytes = (uint32_t)VmwSvgaAlignUp32((uint32_t)v->BackbufferBytes, 4096u); uint32_t sourceOffset = VmwSvgaAlignUp32(v->FramebufferOffset, 4096u); uint32_t backingOffset = VmwSvgaAlignUp32(sourceOffset + sourceBytes, 4096u); uint32_t backingBytes = sourceBytes; if ((uint64_t)sourceOffset + sourceBytes > v->FramebufferBytes || (uint64_t)backingOffset + backingBytes > v->FramebufferBytes) { DbgPrint("VMWSVGA: screen object disabled; insufficient GFB source=0x%x backing=0x%x fbLen=0x%llx\n", sourceBytes, backingBytes, (unsigned long long)v->FramebufferBytes); return NTLSTATUS_NOT_SUPPORTED; } v->ScreenId = SVGA_SCREEN_OBJECT_ID_PRIMARY; v->ScreenSourceOffset = sourceOffset; v->ScreenSourceBytes = sourceBytes; v->ScreenBackingOffset = backingOffset; v->ScreenBackingBytes = backingBytes; v->FramebufferOffset = sourceOffset; v->Framebuffer = v->FramebufferMapping + sourceOffset; v->UseScreenObject = true; KSTATUS st = VmwSvgaDefineGmrfb(v); if (!KSUCCESS(st)) { v->UseScreenObject = false; return st; } st = VmwSvgaDefineScreenObject(v); if (!KSUCCESS(st)) { v->UseScreenObject = false; return st; } DbgPrint("VMWSVGA: screen object enabled id=%u sourceOff=0x%x backingOff=0x%x bytes=0x%x\n", v->ScreenId, v->ScreenSourceOffset, v->ScreenBackingOffset, v->ScreenSourceBytes); return NTLSTATUS_SUCCESS; } // Implements the legacy Update operation. static KSTATUS VmwSvgaLegacyUpdate(VMWSVGA_DEVICE* v, int32_t x, int32_t y, int32_t w, int32_t h) { DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_UPDATE); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)r.X); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)r.Y); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)r.W); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, (uint32_t)r.H); if (!KSUCCESS(st)) return st; VmwSvgaWakeFifo(v); return NTLSTATUS_SUCCESS; } // Updates the requested value. static KSTATUS VmwSvgaUpdate(VMWSVGA_DEVICE* v, int32_t x, int32_t y, int32_t w, int32_t h) { DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; v->Stats.DirtyRectCount++; if (v->UseScreenObject) { KSTATUS st = VmwSvgaBlitGmrfbToScreen(v, r.X, r.Y, r.W, r.H); KSTATUS up = VmwSvgaLegacyUpdate(v, r.X, r.Y, r.W, r.H); return KSUCCESS(st) ? up : st; } return VmwSvgaLegacyUpdate(v, r.X, r.Y, r.W, r.H); } // Waits until the SVGA host has consumed the queued FIFO work. static KSTATUS VmwSvgaSyncDevice(VMWSVGA_DEVICE* v) { if (!v) return NTLSTATUS_INVALID_PARAMETER; v->Stats.SyncCount++; VmwSvgaWriteReg(v, SVGA_REG_SYNC, 1u); for (uint32_t guard = 0; guard < VMWSVGA_MAX_WAIT_ITERS; guard++) { if (VmwSvgaReadReg(v, SVGA_REG_BUSY) == 0) { if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_BUSY)) v->Fifo[SVGA_FIFO_BUSY] = 0u; return NTLSTATUS_SUCCESS; } } return NTLSTATUS_TIMEOUT; } // Implements the blend Pixel operation. static uint32_t VmwSvgaBlendPixel(uint32_t dst, uint32_t src) { uint8_t a = (uint8_t)(src >> 24); if (a == 0) return dst; if (a == 255) return src & 0x00FFFFFFu; uint8_t sr = (uint8_t)(src >> 16); uint8_t sg = (uint8_t)(src >> 8); uint8_t sb = (uint8_t)src; uint8_t dr = (uint8_t)(dst >> 16); uint8_t dg = (uint8_t)(dst >> 8); uint8_t db = (uint8_t)dst; uint8_t na = (uint8_t)(255 - a); uint8_t rr = (uint8_t)((a * sr + na * dr) >> 8); uint8_t gg = (uint8_t)((a * sg + na * dg) >> 8); uint8_t bb = (uint8_t)((a * sb + na * db) >> 8); return ((uint32_t)rr << 16) | ((uint32_t)gg << 8) | bb; } // Implements the from Display operation. static VMWSVGA_DEVICE* VmwSvgaFromDisplay(DISPLAY_DEVICE* device) { return device ? (VMWSVGA_DEVICE*)device->DriverContext : NULL; } // Implements the display Get Mode operation. static KSTATUS VmwSvgaDisplayGetMode(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !outMode || !v->Framebuffer) return NTLSTATUS_INVALID_PARAMETER; outMode->Width = v->Width; outMode->Height = v->Height; outMode->Stride = v->Stride; outMode->Format = DisplayPixelFormatX8R8G8B8; return NTLSTATUS_SUCCESS; } // Streams one pixel row into the write-combined SVGA framebuffer. static void VmwSvgaCopyToFramebuffer(uint32_t* destination, const uint32_t* source, size_t pixelCount) { if (!destination || !source || pixelCount == 0) return; while (pixelCount != 0 && ((uintptr_t)destination & 15u) != 0) { *destination++ = *source++; pixelCount--; } while (pixelCount >= 16u) { __m128i v0 = _mm_loadu_si128((const __m128i*)(source + 0)); __m128i v1 = _mm_loadu_si128((const __m128i*)(source + 4)); __m128i v2 = _mm_loadu_si128((const __m128i*)(source + 8)); __m128i v3 = _mm_loadu_si128((const __m128i*)(source + 12)); _mm_stream_si128((__m128i*)(destination + 0), v0); _mm_stream_si128((__m128i*)(destination + 4), v1); _mm_stream_si128((__m128i*)(destination + 8), v2); _mm_stream_si128((__m128i*)(destination + 12), v3); source += 16; destination += 16; pixelCount -= 16; } while (pixelCount >= 4u) { __m128i value = _mm_loadu_si128((const __m128i*)source); _mm_stream_si128((__m128i*)destination, value); source += 4; destination += 4; pixelCount -= 4; } while (pixelCount != 0) { *destination++ = *source++; pixelCount--; } } // Copies one row into the retained SVGA shadow and framebuffer in one read pass. static void VmwSvgaCopyToShadowAndFramebuffer(uint32_t* shadow, uint32_t* framebuffer, const uint32_t* source, size_t pixelCount) { if (!shadow || !framebuffer || !source || pixelCount == 0) return; while (pixelCount != 0 && ((uintptr_t)framebuffer & 15u) != 0) { uint32_t value = *source++; *shadow++ = value; *framebuffer++ = value; pixelCount--; } while (pixelCount >= 16u) { __m128i v0 = _mm_loadu_si128((const __m128i*)(source + 0)); __m128i v1 = _mm_loadu_si128((const __m128i*)(source + 4)); __m128i v2 = _mm_loadu_si128((const __m128i*)(source + 8)); __m128i v3 = _mm_loadu_si128((const __m128i*)(source + 12)); _mm_storeu_si128((__m128i*)(shadow + 0), v0); _mm_storeu_si128((__m128i*)(shadow + 4), v1); _mm_storeu_si128((__m128i*)(shadow + 8), v2); _mm_storeu_si128((__m128i*)(shadow + 12), v3); _mm_stream_si128((__m128i*)(framebuffer + 0), v0); _mm_stream_si128((__m128i*)(framebuffer + 4), v1); _mm_stream_si128((__m128i*)(framebuffer + 8), v2); _mm_stream_si128((__m128i*)(framebuffer + 12), v3); source += 16; shadow += 16; framebuffer += 16; pixelCount -= 16; } while (pixelCount >= 4u) { __m128i value = _mm_loadu_si128((const __m128i*)source); _mm_storeu_si128((__m128i*)shadow, value); _mm_stream_si128((__m128i*)framebuffer, value); source += 4; shadow += 4; framebuffer += 4; pixelCount -= 4; } while (pixelCount != 0) { uint32_t value = *source++; *shadow++ = value; *framebuffer++ = value; pixelCount--; } } // Implements the display pitched blit operation. static KSTATUS VmwSvgaDisplayBlit(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Backbuffer || !pixels || w <= 0 || h <= 0 || pitchBytes < (uint32_t)w * sizeof(uint32_t)) return NTLSTATUS_INVALID_PARAMETER; DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; v->Stats.BlitCount++; v->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); int srcOffsetX = r.X - x; int srcOffsetY = r.Y - y; size_t bytesPerRow = (size_t)r.W * sizeof(uint32_t); for (int32_t row = 0; row < r.H; row++) { uint32_t* dst = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; const uint8_t* sourceRow = (const uint8_t*)pixels + (size_t)(srcOffsetY + row) * pitchBytes; const uint32_t* src = (const uint32_t*)sourceRow + srcOffsetX; memcpy(dst, src, bytesPerRow); } return NTLSTATUS_SUCCESS; } // Presents one pitched rectangle without a shadow-to-framebuffer second pass. static KSTATUS VmwSvgaDisplayPresentPitched(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Framebuffer || !v->Backbuffer || !pixels || w <= 0 || h <= 0 || pitchBytes < (uint32_t)w * sizeof(uint32_t)) return NTLSTATUS_INVALID_PARAMETER; DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; int srcOffsetX = r.X - x; int srcOffsetY = r.Y - y; size_t pixelCount = (size_t)r.W; uint64_t bytes = (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); v->Stats.BlitCount++; v->Stats.PresentCount++; v->Stats.PresentRectCount++; v->Stats.DirectPresentCount++; v->Stats.ScanoutBytes += bytes; v->Stats.BytesCopied += bytes * 2u; for (int32_t row = 0; row < r.H; row++) { const uint8_t* sourceRow = (const uint8_t*)pixels + (size_t)(srcOffsetY + row) * pitchBytes; const uint32_t* source = (const uint32_t*)sourceRow + srcOffsetX; uint32_t* shadow = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; uint32_t* framebuffer = (uint32_t*)(uintptr_t)(v->Framebuffer + ((size_t)(r.Y + row) * v->BytesPerLine) + ((size_t)r.X * sizeof(uint32_t))); VmwSvgaCopyToShadowAndFramebuffer(shadow, framebuffer, source, pixelCount); } _mm_sfence(); _ReadWriteBarrier(); KSTATUS st = VmwSvgaUpdate(v, r.X, r.Y, r.W, r.H); if (!KSUCCESS(st)) return st; return VmwSvgaSyncDevice(v); } // Implements the display Fill Rect operation. static KSTATUS VmwSvgaDisplayFillRect(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Backbuffer || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; v->Stats.FillRectCount++; v->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); for (int yy = r.Y; yy < r.Y + r.H; yy++) { uint32_t* dst = v->Backbuffer + ((size_t)yy * v->Stride) + r.X; for (int xx = 0; xx < r.W; xx++) *dst++ = color; } return NTLSTATUS_SUCCESS; } // Implements the display Copy Rect operation. static KSTATUS VmwSvgaDisplayCopyRect(DISPLAY_DEVICE* device, int32_t dstX, int32_t dstY, int32_t srcX, int32_t srcY, int32_t w, int32_t h) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Backbuffer || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; if (srcX < 0) { int d = -srcX; srcX = 0; dstX += d; w -= d; } if (srcY < 0) { int d = -srcY; srcY = 0; dstY += d; h -= d; } if (dstX < 0) { int d = -dstX; dstX = 0; srcX += d; w -= d; } if (dstY < 0) { int d = -dstY; dstY = 0; srcY += d; h -= d; } if (srcX + w > (int)v->Width) w = (int)v->Width - srcX; if (dstX + w > (int)v->Width) w = (int)v->Width - dstX; if (srcY + h > (int)v->Height) h = (int)v->Height - srcY; if (dstY + h > (int)v->Height) h = (int)v->Height - dstY; if (w <= 0 || h <= 0) return NTLSTATUS_SUCCESS; v->Stats.CopyRectCount++; v->Stats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t); size_t rowBytes = (size_t)w * sizeof(uint32_t); if (dstY > srcY) { for (int32_t row = h - 1; row >= 0; row--) { uint32_t* dst = v->Backbuffer + ((size_t)(dstY + row) * v->Stride) + dstX; uint32_t* src = v->Backbuffer + ((size_t)(srcY + row) * v->Stride) + srcX; memmove(dst, src, rowBytes); } } else { for (int32_t row = 0; row < h; row++) { uint32_t* dst = v->Backbuffer + ((size_t)(dstY + row) * v->Stride) + dstX; uint32_t* src = v->Backbuffer + ((size_t)(srcY + row) * v->Stride) + srcX; memmove(dst, src, rowBytes); } } return NTLSTATUS_SUCCESS; } // Implements the display Read Rect operation. static KSTATUS VmwSvgaDisplayReadRect(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t* outPixels, uint32_t outStride) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Backbuffer || !outPixels || outStride == 0 || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; v->Stats.ReadRectCount++; v->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); int dstOffsetX = r.X - x; int dstOffsetY = r.Y - y; for (int32_t row = 0; row < r.H; row++) { const uint32_t* src = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; uint32_t* dst = outPixels + ((size_t)(dstOffsetY + row) * outStride) + dstOffsetX; memcpy(dst, src, (size_t)r.W * sizeof(uint32_t)); } return NTLSTATUS_SUCCESS; } // Implements the display Present operation. static KSTATUS VmwSvgaDisplayPresent(DISPLAY_DEVICE* device, const DISPLAY_RECT* rects, size_t rectCount) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Framebuffer || !v->Backbuffer) return NTLSTATUS_INVALID_PARAMETER; if (!rects || rectCount == 0) return NTLSTATUS_SUCCESS; v->Stats.PresentCount++; v->Stats.PresentRectCount += rectCount; KSTATUS result = NTLSTATUS_SUCCESS; for (size_t i = 0; i < rectCount; i++) { DISPLAY_RECT r = rects[i]; if (!VmwSvgaClipRect(v, &r)) continue; uint64_t bytes = (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); v->Stats.BytesCopied += bytes; v->Stats.ScanoutBytes += bytes; size_t pixelCount = (size_t)r.W; for (int32_t row = 0; row < r.H; row++) { uint32_t* dst = (uint32_t*)(uintptr_t)(v->Framebuffer + ((size_t)(r.Y + row) * v->BytesPerLine) + ((size_t)r.X * sizeof(uint32_t))); uint32_t* src = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; VmwSvgaCopyToFramebuffer(dst, src, pixelCount); } _mm_sfence(); _ReadWriteBarrier(); KSTATUS st = VmwSvgaUpdate(v, r.X, r.Y, r.W, r.H); if (!KSUCCESS(st)) result = st; } if (KSUCCESS(result)) { KSTATUS syncSt = VmwSvgaSyncDevice(v); if (!KSUCCESS(syncSt)) result = syncSt; } return result; } // Implements the display Sync operation. static KSTATUS VmwSvgaDisplaySync(DISPLAY_DEVICE* device) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); return VmwSvgaSyncDevice(v); } // Implements the display Set Cursor Image operation. static KSTATUS VmwSvgaDisplaySetCursorImage(DISPLAY_DEVICE* device, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->HardwareCursor) return NTLSTATUS_NOT_SUPPORTED; return VmwSvgaDefineCursorImage(v, width, height, hotX, hotY, argbPixels); } // Implements the display Set Cursor Position operation. static KSTATUS VmwSvgaDisplaySetCursorPosition(DISPLAY_DEVICE* device, int32_t x, int32_t y) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v) return NTLSTATUS_INVALID_PARAMETER; v->CursorX = x; v->CursorY = y; v->Stats.CursorMoveCount++; if (v->HardwareCursor && v->CursorImageDefined) VmwSvgaCursorSetPosition(v, x, y); return NTLSTATUS_SUCCESS; } // Implements the restore Cursor operation. static void VmwSvgaRestoreCursor(VMWSVGA_DEVICE* v) { if (!v || !v->CursorDrawn || !v->Backbuffer || !v->Framebuffer) return; DISPLAY_RECT r = { v->CursorDrawX, v->CursorDrawY, VMWSVGA_CURSOR_W, VMWSVGA_CURSOR_H }; if (VmwSvgaClipRect(v, &r)) { for (int32_t row = 0; row < r.H; row++) { volatile uint8_t* dstBytes = v->Framebuffer + ((size_t)(r.Y + row) * v->BytesPerLine) + ((size_t)r.X * sizeof(uint32_t)); uint32_t* src = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; memcpy((void*)dstBytes, src, (size_t)r.W * sizeof(uint32_t)); } (void)VmwSvgaUpdate(v, r.X, r.Y, r.W, r.H); } v->CursorDrawn = false; v->CursorDrawX = -1; v->CursorDrawY = -1; } // Draws cursor. static void VmwSvgaDrawCursor(VMWSVGA_DEVICE* v) { if (!v || !v->Backbuffer || !v->Framebuffer) return; int32_t x = v->CursorX; int32_t y = v->CursorY; v->CursorDrawX = x; v->CursorDrawY = y; DISPLAY_RECT dirty = { x, y, VMWSVGA_CURSOR_W, VMWSVGA_CURSOR_H }; for (int32_t row = 0; row < VMWSVGA_CURSOR_H; row++) { int fy = y + row; if ((unsigned)fy >= v->Height) continue; for (int32_t col = 0; col < VMWSVGA_CURSOR_W; col++) { int fx = x + col; if ((unsigned)fx >= v->Width) continue; uint32_t src = gDisplayDefaultCursorArgb[row][col]; if ((src >> 24) == 0) continue; uint32_t dst = v->Backbuffer[(size_t)fy * v->Stride + fx]; uint32_t pixel = VmwSvgaBlendPixel(dst, src); volatile uint32_t* fb = (volatile uint32_t*)(v->Framebuffer + ((size_t)fy * v->BytesPerLine) + ((size_t)fx * sizeof(uint32_t))); *fb = pixel; } } v->CursorDrawn = true; if (VmwSvgaClipRect(v, &dirty)) (void)VmwSvgaUpdate(v, dirty.X, dirty.Y, dirty.W, dirty.H); } // Implements the display Show Cursor operation. static KSTATUS VmwSvgaDisplayShowCursor(DISPLAY_DEVICE* device, bool show) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v) return NTLSTATUS_INVALID_PARAMETER; if (v->HardwareCursor) { if (!v->CursorImageDefined) return NTLSTATUS_NOT_SUPPORTED; if (show) { v->Stats.CursorShowCount++; VmwSvgaCursorSetPosition(v, v->CursorX, v->CursorY); VmwSvgaCursorSetVisible(v, true); } else { v->Stats.CursorHideCount++; VmwSvgaCursorSetVisible(v, false); } return NTLSTATUS_SUCCESS; } if (!v->Framebuffer || !v->Backbuffer) return NTLSTATUS_NOT_SUPPORTED; if (show) { v->Stats.CursorShowCount++; if (v->CursorDrawn) VmwSvgaRestoreCursor(v); VmwSvgaDrawCursor(v); } else { v->Stats.CursorHideCount++; VmwSvgaRestoreCursor(v); } return NTLSTATUS_SUCCESS; } // Implements the display Query Stats operation. static KSTATUS VmwSvgaDisplayQueryStats(DISPLAY_DEVICE* device, DISPLAY_STATS* outStats) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !outStats) return NTLSTATUS_INVALID_PARAMETER; *outStats = v->Stats; return NTLSTATUS_SUCCESS; } static const DISPLAY_DEVICE_OPS gVmwSvgaDisplayOps = { .GetMode = VmwSvgaDisplayGetMode, .Blit = VmwSvgaDisplayBlit, .PresentPitched = VmwSvgaDisplayPresentPitched, .FillRect = VmwSvgaDisplayFillRect, .CopyRect = VmwSvgaDisplayCopyRect, .ReadRect = VmwSvgaDisplayReadRect, .Present = VmwSvgaDisplayPresent, .Sync = VmwSvgaDisplaySync, .SetCursorImage = VmwSvgaDisplaySetCursorImage, .SetCursorPosition = VmwSvgaDisplaySetCursorPosition, .ShowCursor = VmwSvgaDisplayShowCursor, .QueryStats = VmwSvgaDisplayQueryStats, }; // Implements the select Bars operation. static KSTATUS VmwSvgaSelectBars(VMWSVGA_DEVICE* v) { PCI_BAR bar; memset(&bar, 0, sizeof(bar)); KSTATUS st = PciGetBar(v->Pdo, 0, &bar); if (!KSUCCESS(st) || bar.Type != PciBarIo || bar.Base > 0xFFFFu) { DbgPrint("VMWSVGA: BAR0 is not a usable I/O register BAR st=0x%x type=%u base=0x%llx\n", st, bar.Type, (unsigned long long)bar.Base); return KSUCCESS(st) ? NTLSTATUS_NOT_SUPPORTED : st; } v->IoBar = bar; v->IoBase = (uint16_t)bar.Base; st = PciGetBar(v->Pdo, 1, &bar); if (!KSUCCESS(st) || (bar.Type != PciBarMemory32 && bar.Type != PciBarMemory64)) { DbgPrint("VMWSVGA: BAR1 is not a usable framebuffer BAR st=0x%x type=%u\n", st, bar.Type); return KSUCCESS(st) ? NTLSTATUS_NOT_SUPPORTED : st; } v->FramebufferBar = bar; st = PciGetBar(v->Pdo, 2, &bar); if (!KSUCCESS(st) || (bar.Type != PciBarMemory32 && bar.Type != PciBarMemory64)) { DbgPrint("VMWSVGA: BAR2 is not a usable FIFO BAR st=0x%x type=%u\n", st, bar.Type); return KSUCCESS(st) ? NTLSTATUS_NOT_SUPPORTED : st; } v->FifoBar = bar; return NTLSTATUS_SUCCESS; } // Returns the write-combined mapping flags used for the framebuffer aperture. static uint64_t VmwSvgaFramebufferPageFlags(void) { return PAGE_PRESENT | PAGE_WRITE | PAGE_CACHE_WC | PAGE_NX; } // Returns the uncached mapping flags used for device-control apertures. static uint64_t VmwSvgaRegisterPageFlags(void) { return PAGE_PRESENT | PAGE_WRITE | PAGE_CACHE_UC | PAGE_NX; } // Maps bars. static KSTATUS VmwSvgaMapBars(VMWSVGA_DEVICE* v) { KSTATUS st = PciMapBar(v->Pdo, 1, VmwSvgaFramebufferPageFlags(), (void**)&v->FramebufferMapping, &v->FramebufferBytes); if (!KSUCCESS(st) || !v->FramebufferMapping) { DbgPrint("VMWSVGA: PciMapBar framebuffer failed st=0x%x virt=0x%llx base=0x%llx len=0x%llx\n", st, (unsigned long long)(uintptr_t)v->FramebufferMapping, (unsigned long long)v->FramebufferBar.Base, (unsigned long long)v->FramebufferBar.Length); return KSUCCESS(st) ? NTLSTATUS_DEVICE_ERROR : st; } st = PciMapBar(v->Pdo, 2, VmwSvgaRegisterPageFlags(), (void**)&v->Fifo, &v->FifoBytes); if (!KSUCCESS(st) || !v->Fifo) { DbgPrint("VMWSVGA: PciMapBar FIFO failed st=0x%x virt=0x%llx base=0x%llx len=0x%llx\n", st, (unsigned long long)(uintptr_t)v->Fifo, (unsigned long long)v->FifoBar.Base, (unsigned long long)v->FifoBar.Length); return KSUCCESS(st) ? NTLSTATUS_DEVICE_ERROR : st; } DbgPrint("VMWSVGA: BARs io=0x%04x fbBase=0x%llx fbLen=0x%llx fbVirt=0x%llx fifoBase=0x%llx fifoLen=0x%llx fifoVirt=0x%llx\n", v->IoBase, (unsigned long long)v->FramebufferBar.Base, (unsigned long long)v->FramebufferBar.Length, (unsigned long long)(uintptr_t)v->FramebufferMapping, (unsigned long long)v->FifoBar.Base, (unsigned long long)v->FifoBar.Length, (unsigned long long)(uintptr_t)v->Fifo); return NTLSTATUS_SUCCESS; } // Returns mode. static KSTATUS VmwSvgaReadMode(VMWSVGA_DEVICE* v) { v->Width = VmwSvgaReadReg(v, SVGA_REG_WIDTH); v->Height = VmwSvgaReadReg(v, SVGA_REG_HEIGHT); v->BytesPerLine = VmwSvgaReadReg(v, SVGA_REG_BYTES_PER_LINE); v->BitsPerPixel = VmwSvgaReadReg(v, SVGA_REG_BITS_PER_PIXEL); if (v->BitsPerPixel == 0) v->BitsPerPixel = VmwSvgaReadReg(v, SVGA_REG_HOST_BITS_PER_PIXEL); uint32_t fbSize = VmwSvgaReadReg(v, SVGA_REG_FB_SIZE); uint32_t fifoSize = VmwSvgaReadReg(v, SVGA_REG_MEM_SIZE); uint32_t fbOffset = VmwSvgaReadReg(v, SVGA_REG_FB_OFFSET); uint32_t fbStart = VmwSvgaReadReg(v, SVGA_REG_FB_START); v->FifoDeviceBytes = fifoSize; v->Capabilities = VmwSvgaReadReg(v, SVGA_REG_CAPABILITIES); if (v->Width == 0 || v->Height == 0 || v->BytesPerLine == 0) { DbgPrint("VMWSVGA: invalid mode %ux%u pitch=%u\n", v->Width, v->Height, v->BytesPerLine); return NTLSTATUS_DEVICE_ERROR; } if (v->BitsPerPixel != 32) { DbgPrint("VMWSVGA: unsupported bpp=%u\n", v->BitsPerPixel); return NTLSTATUS_NOT_SUPPORTED; } if ((v->BytesPerLine & 3u) != 0) { DbgPrint("VMWSVGA: unsupported non-dword pitch=%u\n", v->BytesPerLine); return NTLSTATUS_NOT_SUPPORTED; } v->Stride = v->BytesPerLine / sizeof(uint32_t); if (v->Stride < v->Width) return NTLSTATUS_DEVICE_ERROR; if (!v->FramebufferMapping) return NTLSTATUS_DEVICE_ERROR; uint64_t needed = (uint64_t)v->BytesPerLine * (uint64_t)v->Height; if (fbSize && needed > fbSize) return NTLSTATUS_DEVICE_ERROR; if ((uint64_t)fbOffset > v->FramebufferBytes || needed > v->FramebufferBytes - (uint64_t)fbOffset) return NTLSTATUS_DEVICE_ERROR; v->FramebufferOffset = fbOffset; v->Framebuffer = v->FramebufferMapping + fbOffset; v->BackbufferBytes = (size_t)needed; v->Backbuffer = (uint32_t*)MmAllocatePool(v->BackbufferBytes); if (!v->Backbuffer) return NTLSTATUS_NO_MEMORY; memset(v->Backbuffer, 0, v->BackbufferBytes); DbgPrint("VMWSVGA: mode %ux%u stride=%u bpp=%u fbSize=0x%x fbOff=0x%x fbStart=0x%x fifoSize=0x%x caps=0x%x\n", v->Width, v->Height, v->Stride, v->BitsPerPixel, fbSize, fbOffset, fbStart, fifoSize, v->Capabilities); return NTLSTATUS_SUCCESS; } // Returns the active FIFO size reported by the device, not the larger PCI BAR aperture. static uint32_t VmwSvgaGetFifoMaxBytes(VMWSVGA_DEVICE* v) { uint64_t limit = v->FifoBytes; if (v->FifoDeviceBytes != 0 && (uint64_t)v->FifoDeviceBytes < limit) limit = v->FifoDeviceBytes; if (limit > 0xFFFFFFFFu) limit = 0xFFFFFFFFu; return (uint32_t)limit & ~3u; } // Initializes fifo. static KSTATUS VmwSvgaInitializeFifo(VMWSVGA_DEVICE* v) { if (!v->Fifo || v->FifoBytes < 4096u) return NTLSTATUS_DEVICE_ERROR; VmwSvgaWriteReg(v, SVGA_REG_CONFIG_DONE, 0u); uint32_t regs = VmwSvgaReadReg(v, SVGA_REG_MEM_REGS); if (regs < SVGA_FIFO_NUM_LEGACY_REGS || regs > 4096u) regs = SVGA_FIFO_NUM_REGS_FALLBACK; uint32_t min = regs * sizeof(uint32_t); uint32_t max = VmwSvgaGetFifoMaxBytes(v); if (max < (SVGA_FIFO_NUM_REGS_FALLBACK * sizeof(uint32_t)) + (10u * 1024u)) min = SVGA_FIFO_NUM_LEGACY_REGS * sizeof(uint32_t); if (max <= min + 32u) return NTLSTATUS_DEVICE_ERROR; v->FifoRegCount = min / sizeof(uint32_t); memset((void*)v->Fifo, 0, min); v->Fifo[SVGA_FIFO_MIN] = min; v->Fifo[SVGA_FIFO_MAX] = max; v->Fifo[SVGA_FIFO_NEXT_CMD] = min; v->Fifo[SVGA_FIFO_STOP] = min; _ReadWriteBarrier(); VmwSvgaWriteReg(v, SVGA_REG_CONFIG_DONE, 1u); v->FifoReady = true; if (v->FifoRegCount > SVGA_FIFO_CAPABILITIES) v->FifoCaps = v->Fifo[SVGA_FIFO_CAPABILITIES]; if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_BUSY)) v->Fifo[SVGA_FIFO_BUSY] = 0u; DbgPrint("VMWSVGA: FIFO init regs=%u min=0x%x max=0x%x fifoCaps=0x%x deviceBytes=0x%x barBytes=0x%llx\n", v->FifoRegCount, min, max, v->FifoCaps, v->FifoDeviceBytes, (unsigned long long)v->FifoBytes); return NTLSTATUS_SUCCESS; } // Starts device. static KSTATUS VmwSvgaStartDevice(VMWSVGA_DEVICE* v) { if (!v || v->Magic != VMWSVGA_DEVICE_MAGIC || !v->Fdo || !v->Pdo) return NTLSTATUS_INVALID_PARAMETER; if (v->Started) return NTLSTATUS_SUCCESS; KSTATUS st = PciGetDeviceInfo(v->Pdo, &v->PciInfo); if (!KSUCCESS(st)) return st; DbgPrint("VMWSVGA: controller at PCI %02x:%02x.%x vendor=%04x device=%04x\n", v->PciInfo.Address.Bus, v->PciInfo.Address.Device, v->PciInfo.Address.Function, v->PciInfo.VendorId, v->PciInfo.DeviceId); st = VmwSvgaSelectBars(v); if (!KSUCCESS(st)) return st; st = PciEnableDevice(v->Pdo, PCI_ENABLE_IO | PCI_ENABLE_MEMORY | PCI_ENABLE_BUSMASTER | PCI_ENABLE_INTX); if (!KSUCCESS(st)) return st; v->DeviceId = VmwSvgaProbeId(v); if (v->DeviceId == SVGA_ID_INVALID) { DbgPrint("VMWSVGA: SVGA ID probe failed\n"); return NTLSTATUS_NOT_SUPPORTED; } VmwSvgaWriteReg(v, SVGA_REG_GUEST_ID, 0u); st = VmwSvgaMapBars(v); if (!KSUCCESS(st)) return st; KeDisplaySuspendConsole(true); st = VmwSvgaSetMode(v); if (!KSUCCESS(st)) { KeDisplaySuspendConsole(false); return st; } st = VmwSvgaReadMode(v); if (!KSUCCESS(st)) { KeDisplaySuspendConsole(false); return st; } st = VmwSvgaInitializeFifo(v); if (!KSUCCESS(st)) { KeDisplaySuspendConsole(false); DbgPrint("VMWSVGA: FIFO init failed st=0x%x\n", st); return st; } #if VMWSVGA_ENABLE_SCREEN_OBJECTS KSTATUS screenSt = VmwSvgaInitializeScreenObject(v); if (!KSUCCESS(screenSt) && screenSt != NTLSTATUS_NOT_SUPPORTED) DbgPrint("VMWSVGA: screen object init failed st=0x%x; using legacy GFB UPDATE\n", screenSt); #else v->UseScreenObject = false; DbgPrint("VMWSVGA: screen objects disabled; using legacy GFB UPDATE path\n"); #endif v->CursorDrawX = -1; v->CursorDrawY = -1; bool cursorCap = (v->Capabilities & SVGA_CAP_CURSOR) != 0; v->AlphaCursor = (v->Capabilities & SVGA_CAP_ALPHA_CURSOR) != 0; v->HardwareCursor = cursorCap; v->CursorBypass3 = v->HardwareCursor && (v->FifoCaps & SVGA_FIFO_CAP_CURSOR_BYPASS_3) && VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_ON) && VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_X) && VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_Y); memset(&v->DisplayDevice, 0, sizeof(v->DisplayDevice)); v->DisplayDevice.Name = "vmwaresvga"; v->DisplayDevice.Caps = v->HardwareCursor ? VMWSVGA_CAPS_HW_CURSOR : VMWSVGA_CAPS_MINIMAL; v->DisplayDevice.DriverContext = v; v->DisplayDevice.Ops = &gVmwSvgaDisplayOps; DbgPrint("VMWSVGA: cursor caps cursor=%u alpha=%u bypass3=%u active=%u\n", cursorCap ? 1u : 0u, v->AlphaCursor ? 1u : 0u, v->CursorBypass3 ? 1u : 0u, v->HardwareCursor ? 1u : 0u); if (v->HardwareCursor) { st = VmwSvgaDefineCursorImage(v, DISPLAY_DEFAULT_CURSOR_W, DISPLAY_DEFAULT_CURSOR_H, 0, 0, &gDisplayDefaultCursorArgb[0][0]); if (!KSUCCESS(st)) { DbgPrint("VMWSVGA: hardware cursor init failed st=0x%x; using software cursor fallback\n", st); v->HardwareCursor = false; v->AlphaCursor = false; v->CursorBypass3 = false; v->DisplayDevice.Caps = VMWSVGA_CAPS_MINIMAL; } else { VmwSvgaCursorSetPosition(v, v->CursorX, v->CursorY); VmwSvgaCursorSetVisible(v, false); DbgPrint("VMWSVGA: hardware cursor ready alpha=%u bypass3=%u\n", v->AlphaCursor ? 1u : 0u, v->CursorBypass3 ? 1u : 0u); } } st = KeDisplayRegisterDevice(&v->DisplayDevice); if (!KSUCCESS(st) && st != NTLSTATUS_ALREADY_EXISTS) { KeDisplaySuspendConsole(false); return st; } st = KeDisplaySetPrimary(&v->DisplayDevice); KeDisplaySuspendConsole(false); if (!KSUCCESS(st)) { (void)KeDisplayUnregisterDevice(&v->DisplayDevice); return st; } v->Started = true; v->Fdo->PnpState = DevicePnpStarted; (void)VmwSvgaDisplaySync(&v->DisplayDevice); DbgPrint("VMWSVGA: initialized as primary display id=0x%x io=0x%04x\n", v->DeviceId, v->IoBase); return NTLSTATUS_SUCCESS; } // Dispatches pnp. KSTATUS VmwSvgaDispatchPnp(DEVICE_OBJECT* device, IRP* irp) { if (!device || !irp || !device->DeviceExtension) return NTLSTATUS_INVALID_PARAMETER; VMWSVGA_DEVICE* v = (VMWSVGA_DEVICE*)device->DeviceExtension; if (v->Magic != VMWSVGA_DEVICE_MAGIC) return NTLSTATUS_INVALID_PARAMETER; IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp); if (!ios || ios->MajorFunction != IRP_MJ_PNP) return NTLSTATUS_INVALID_PARAMETER; switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) { case IRP_MN_START_DEVICE: return VmwSvgaStartDevice(v); case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: return NTLSTATUS_SUCCESS; case IRP_MN_SURPRISE_REMOVAL: if (v->DisplayDevice.Ops) (void)KeDisplayUnregisterDevice(&v->DisplayDevice); v->SurpriseRemoved = true; device->PnpState = DevicePnpSurpriseRemoved; return NTLSTATUS_SUCCESS; case IRP_MN_REMOVE_DEVICE: if (v->DisplayDevice.Ops) (void)KeDisplayUnregisterDevice(&v->DisplayDevice); v->Removed = true; device->PnpState = DevicePnpDeleted; if (v->Backbuffer) { MmFreePool(v->Backbuffer); v->Backbuffer = NULL; } if (v->CursorImageArgb) { MmFreePool(v->CursorImageArgb); v->CursorImageArgb = NULL; v->CursorImageBytes = 0; } IoDetachDevice(device); IoDeleteDevice(device); return NTLSTATUS_SUCCESS; default: return IoForwardIrpToLower(device, irp); } } // Implements the add Device operation. KSTATUS VmwSvgaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) { if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical) return NTLSTATUS_INVALID_PARAMETER; DEVICE_OBJECT* fdo = NULL; KSTATUS st = IoCreateDevice(driver, sizeof(VMWSVGA_DEVICE), &fdo); if (!KSUCCESS(st)) return st; (void)IoSetDeviceName(fdo, SYS_SV_LITERAL("vmwaresvga")); VMWSVGA_DEVICE* v = (VMWSVGA_DEVICE*)fdo->DeviceExtension; memset(v, 0, sizeof(*v)); v->Magic = VMWSVGA_DEVICE_MAGIC; v->Fdo = fdo; v->Pdo = physicalDeviceObject; v->CursorDrawX = -1; v->CursorDrawY = -1; v->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject); if (!v->LowerDevice) { IoDeleteDevice(fdo); return NTLSTATUS_FAILURE; } DbgPrint("VMWSVGA: FDO attached to %v\n", physicalDeviceObject->Name); return NTLSTATUS_SUCCESS; } static KSTATUS DriverInitialize(NTL_MODULE* module) { (void)module; DbgPrint("VMWSVGA: driver loading...\n"); memset(&gVmwSvgaDriver, 0, sizeof(gVmwSvgaDriver)); IoInitializeDriverObject(&gVmwSvgaDriver, SYS_SV_LITERAL("vmwaresvga")); gVmwSvgaDriver.IdTable = &gVmwSvgaIdTable; gVmwSvgaDriver.DriverAddDevice = VmwSvgaAddDevice; gVmwSvgaDriver.MajorFunction[IRP_MJ_PNP] = VmwSvgaDispatchPnp; return NTLSTATUS_SUCCESS; } static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gVmwSvgaDriver }; NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);