554e85328d
BZ#1540919 spice server crashes if live migration happens immediately after login or after driver upgrade/downgrade
881 lines
33 KiB
C++
Executable File
881 lines
33 KiB
C++
Executable File
/*
|
|
* Copyright 2013-2016 Red Hat, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
*
|
|
* You may obtain a copy of the License at
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*/
|
|
|
|
#pragma once
|
|
#include "baseobject.h"
|
|
#include "qxl_dev.h"
|
|
#include "qxl_windows.h"
|
|
#include "mspace.h"
|
|
|
|
#define MAX_CHILDREN 1
|
|
#define MAX_VIEWS 1
|
|
#define BITS_PER_BYTE 8
|
|
|
|
#define POINTER_SIZE 64
|
|
#define MIN_WIDTH_SIZE 1024
|
|
#define MIN_HEIGHT_SIZE 768
|
|
#define QXL_BPP 32
|
|
#define VGA_BPP 24
|
|
|
|
#define QXL_NON_PAGED __declspec(code_seg(".text"))
|
|
|
|
extern BOOLEAN g_bSupportVSync;
|
|
|
|
typedef struct _QXL_FLAGS
|
|
{
|
|
UINT DriverStarted : 1; // ( 1) 1 after StartDevice and 0 after StopDevice
|
|
UINT Unused : 31;
|
|
} QXL_FLAGS;
|
|
|
|
// For the following macros, c must be a UCHAR.
|
|
#define UPPER_6_BITS(c) (((c) & rMaskTable[6 - 1]) >> 2)
|
|
#define UPPER_5_BITS(c) (((c) & rMaskTable[5 - 1]) >> 3)
|
|
#define LOWER_6_BITS(c) (((BYTE)(c)) & lMaskTable[BITS_PER_BYTE - 6])
|
|
#define LOWER_5_BITS(c) (((BYTE)(c)) & lMaskTable[BITS_PER_BYTE - 5])
|
|
|
|
|
|
#define SHIFT_FOR_UPPER_5_IN_565 (6 + 5)
|
|
#define SHIFT_FOR_MIDDLE_6_IN_565 (5)
|
|
#define SHIFT_UPPER_5_IN_565_BACK ((BITS_PER_BYTE * 2) + (BITS_PER_BYTE - 5))
|
|
#define SHIFT_MIDDLE_6_IN_565_BACK ((BITS_PER_BYTE * 1) + (BITS_PER_BYTE - 6))
|
|
#define SHIFT_LOWER_5_IN_565_BACK ((BITS_PER_BYTE * 0) + (BITS_PER_BYTE - 5))
|
|
|
|
#pragma pack(push)
|
|
#pragma pack(1)
|
|
|
|
typedef struct
|
|
{
|
|
CHAR Signature[4];
|
|
USHORT Version;
|
|
ULONG OemStringPtr;
|
|
LONG Capabilities;
|
|
ULONG VideoModePtr;
|
|
USHORT TotalMemory;
|
|
USHORT OemSoftwareRevision;
|
|
ULONG OemVendorNamePtr;
|
|
ULONG OemProductNamePtr;
|
|
ULONG OemProductRevPtr;
|
|
CHAR Reserved[222];
|
|
} VBE_INFO, *PVBE_INFO;
|
|
|
|
typedef struct
|
|
{
|
|
/* Mandatory information for all VBE revisions */
|
|
USHORT ModeAttributes;
|
|
UCHAR WinAAttributes;
|
|
UCHAR WinBAttributes;
|
|
USHORT WinGranularity;
|
|
USHORT WinSize;
|
|
USHORT WinASegment;
|
|
USHORT WinBSegment;
|
|
ULONG WinFuncPtr;
|
|
USHORT BytesPerScanLine;
|
|
/* Mandatory information for VBE 1.2 and above */
|
|
USHORT XResolution;
|
|
USHORT YResolution;
|
|
UCHAR XCharSize;
|
|
UCHAR YCharSize;
|
|
UCHAR NumberOfPlanes;
|
|
UCHAR BitsPerPixel;
|
|
UCHAR NumberOfBanks;
|
|
UCHAR MemoryModel;
|
|
UCHAR BankSize;
|
|
UCHAR NumberOfImagePages;
|
|
UCHAR Reserved1;
|
|
/* Direct Color fields (required for Direct/6 and YUV/7 memory models) */
|
|
UCHAR RedMaskSize;
|
|
UCHAR RedFieldPosition;
|
|
UCHAR GreenMaskSize;
|
|
UCHAR GreenFieldPosition;
|
|
UCHAR BlueMaskSize;
|
|
UCHAR BlueFieldPosition;
|
|
UCHAR ReservedMaskSize;
|
|
UCHAR ReservedFieldPosition;
|
|
UCHAR DirectColorModeInfo;
|
|
/* Mandatory information for VBE 2.0 and above */
|
|
ULONG PhysBasePtr;
|
|
ULONG Reserved2;
|
|
USHORT Reserved3;
|
|
/* Mandatory information for VBE 3.0 and above */
|
|
USHORT LinBytesPerScanLine;
|
|
UCHAR BnkNumberOfImagePages;
|
|
UCHAR LinNumberOfImagePages;
|
|
UCHAR LinRedMaskSize;
|
|
UCHAR LinRedFieldPosition;
|
|
UCHAR LinGreenMaskSize;
|
|
UCHAR LinGreenFieldPosition;
|
|
UCHAR LinBlueMaskSize;
|
|
UCHAR LinBlueFieldPosition;
|
|
UCHAR LinReservedMaskSize;
|
|
UCHAR LinReservedFieldPosition;
|
|
ULONG MaxPixelClock;
|
|
CHAR Reserved4[189];
|
|
} VBE_MODEINFO, *PVBE_MODEINFO;
|
|
|
|
#pragma pack(pop)
|
|
|
|
typedef struct _X86BIOS_REGISTERS // invented names
|
|
{
|
|
ULONG Eax;
|
|
ULONG Ecx;
|
|
ULONG Edx;
|
|
ULONG Ebx;
|
|
ULONG Ebp;
|
|
ULONG Esi;
|
|
ULONG Edi;
|
|
USHORT SegDs;
|
|
USHORT SegEs;
|
|
} X86BIOS_REGISTERS, *PX86BIOS_REGISTERS;
|
|
|
|
/* Undocumented imports from the HAL */
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
NTHALAPI BOOLEAN x86BiosCall (ULONG, PX86BIOS_REGISTERS);
|
|
|
|
NTHALAPI NTSTATUS x86BiosAllocateBuffer (ULONG *, USHORT *, USHORT *);
|
|
NTHALAPI NTSTATUS x86BiosFreeBuffer (USHORT, USHORT);
|
|
|
|
NTHALAPI NTSTATUS x86BiosReadMemory (USHORT, USHORT, PVOID, ULONG);
|
|
NTHALAPI NTSTATUS x86BiosWriteMemory (USHORT, USHORT, PVOID, ULONG);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
struct DoPresentMemory
|
|
{
|
|
PVOID DstAddr;
|
|
UINT DstStride;
|
|
ULONG DstBitPerPixel;
|
|
UINT SrcWidth;
|
|
UINT SrcHeight;
|
|
BYTE* SrcAddr;
|
|
LONG SrcPitch;
|
|
ULONG NumMoves; // in: Number of screen to screen moves
|
|
D3DKMT_MOVE_RECT* Moves; // in: Point to the list of moves
|
|
ULONG NumDirtyRects; // in: Number of direct rects
|
|
RECT* DirtyRect; // in: Point to the list of dirty rects
|
|
D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation;
|
|
D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceID;
|
|
HANDLE hAdapter;
|
|
PMDL Mdl;
|
|
PVOID DisplaySource;
|
|
};
|
|
|
|
typedef struct _BLT_INFO
|
|
{
|
|
PVOID pBits;
|
|
UINT Pitch;
|
|
UINT BitsPerPel;
|
|
POINT Offset; // To unrotated top-left of dirty rects
|
|
D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation;
|
|
UINT Width; // For the unrotated image
|
|
UINT Height; // For the unrotated image
|
|
} BLT_INFO;
|
|
|
|
// Represents the current mode, may not always be set (i.e. frame buffer mapped) if representing the mode passed in on single mode setups.
|
|
typedef struct _CURRENT_BDD_MODE
|
|
{
|
|
// The source mode currently set for HW Framebuffer
|
|
// For sample driver this info filled in StartDevice by the OS and never changed.
|
|
DXGK_DISPLAY_INFORMATION DispInfo;
|
|
|
|
// The rotation of the current mode. Rotation is performed in software during Present call
|
|
D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation;
|
|
|
|
D3DKMDT_VIDPN_PRESENT_PATH_SCALING Scaling;
|
|
// This mode might be different from one which are supported for HW frame buffer
|
|
// Scaling/displasment might be needed (if supported)
|
|
UINT SrcModeWidth;
|
|
UINT SrcModeHeight;
|
|
|
|
// Various boolean flags the struct uses
|
|
struct _CURRENT_BDD_MODE_FLAGS
|
|
{
|
|
UINT SourceNotVisible : 1; // 0 if source is visible
|
|
UINT FullscreenPresent : 1; // 0 if should use dirty rects for present
|
|
UINT FrameBufferIsActive : 1; // 0 if not currently active (i.e. target not connected to source)
|
|
UINT DoNotMapOrUnmap : 1; // 1 if the FrameBuffer should not be (un)mapped during normal execution
|
|
UINT IsInternal : 1; // 1 if it was determined (i.e. through ACPI) that an internal panel is being driven
|
|
UINT Unused : 27;
|
|
} Flags;
|
|
|
|
// The start and end of physical memory known to be all zeroes. Used to optimize the BlackOutScreen function to not write
|
|
// zeroes to memory already known to be zero. (Physical address is located in DispInfo)
|
|
PHYSICAL_ADDRESS ZeroedOutStart;
|
|
PHYSICAL_ADDRESS ZeroedOutEnd;
|
|
|
|
// Linear frame buffer pointer
|
|
// A union with a ULONG64 is used here to ensure this struct looks the same on 32bit and 64bit builds
|
|
// since the size of a VOID* changes depending on the build.
|
|
union
|
|
{
|
|
VOID* Ptr;
|
|
ULONG64 Force8Bytes;
|
|
} FrameBuffer;
|
|
} CURRENT_BDD_MODE;
|
|
|
|
#if DBG
|
|
class TimeMeasurement
|
|
{
|
|
public:
|
|
TimeMeasurement()
|
|
{
|
|
Start();
|
|
}
|
|
void Start()
|
|
{
|
|
KeQuerySystemTime(&li1);
|
|
}
|
|
void Stop()
|
|
{
|
|
KeQuerySystemTime(&li2);
|
|
}
|
|
ULONG Diff()
|
|
{
|
|
return (ULONG)((li2.QuadPart - li1.QuadPart) / 10000);
|
|
}
|
|
protected:
|
|
LARGE_INTEGER li1;
|
|
LARGE_INTEGER li2;
|
|
};
|
|
#else
|
|
class TimeMeasurement
|
|
{
|
|
public:
|
|
TimeMeasurement() {}
|
|
void Start() {}
|
|
void Stop() {}
|
|
ULONG Diff() { return 0; }
|
|
};
|
|
#endif
|
|
|
|
struct DelayedChunk
|
|
{
|
|
LIST_ENTRY list;
|
|
QXLDataChunk chunk;
|
|
};
|
|
|
|
class QxlDod;
|
|
|
|
class HwDeviceInterface {
|
|
public:
|
|
virtual ~HwDeviceInterface() {;}
|
|
virtual NTSTATUS QueryCurrentMode(PVIDEO_MODE RequestedMode) = 0;
|
|
virtual NTSTATUS SetCurrentMode(ULONG Mode) = 0;
|
|
virtual NTSTATUS GetCurrentMode(ULONG* Mode) = 0;
|
|
virtual NTSTATUS SetPowerState(DEVICE_POWER_STATE DevicePowerState, DXGK_DISPLAY_INFORMATION* pDispInfo) = 0;
|
|
virtual NTSTATUS HWInit(PCM_RESOURCE_LIST pResList, DXGK_DISPLAY_INFORMATION* pDispInfo) = 0;
|
|
virtual NTSTATUS HWClose(void) = 0;
|
|
QXL_NON_PAGED virtual BOOLEAN InterruptRoutine(_In_ PDXGKRNL_INTERFACE pDxgkInterface, _In_ ULONG MessageNumber) = 0;
|
|
QXL_NON_PAGED virtual VOID DpcRoutine(PVOID) = 0;
|
|
QXL_NON_PAGED virtual VOID ResetDevice(void) = 0;
|
|
QXL_NON_PAGED virtual VOID VSyncInterruptPostProcess(_In_ PDXGKRNL_INTERFACE) = 0;
|
|
virtual NTSTATUS AcquireFrameBuffer(CURRENT_BDD_MODE* pCurrentBddMode) { return STATUS_SUCCESS; }
|
|
virtual NTSTATUS ReleaseFrameBuffer(CURRENT_BDD_MODE* pCurrentBddMode) { return STATUS_SUCCESS; }
|
|
|
|
ULONG GetModeCount(void) const {return m_ModeCount;}
|
|
PVIDEO_MODE_INFORMATION GetModeInfo(UINT idx) {return &m_ModeInfo[idx];}
|
|
USHORT GetModeNumber(USHORT idx) {return m_ModeNumbers[idx];}
|
|
USHORT GetCurrentModeIndex(void) {return m_CurrentMode;}
|
|
VOID SetCurrentModeIndex(USHORT idx) {m_CurrentMode = idx;}
|
|
virtual NTSTATUS ExecutePresentDisplayOnly(_In_ BYTE* DstAddr,
|
|
_In_ UINT DstBitPerPixel,
|
|
_In_ BYTE* SrcAddr,
|
|
_In_ UINT SrcBytesPerPixel,
|
|
_In_ LONG SrcPitch,
|
|
_In_ ULONG NumMoves,
|
|
_In_ D3DKMT_MOVE_RECT* pMoves,
|
|
_In_ ULONG NumDirtyRects,
|
|
_In_ RECT* pDirtyRect,
|
|
_In_ D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation,
|
|
_In_ const CURRENT_BDD_MODE* pModeCur) = 0;
|
|
|
|
virtual VOID BlackOutScreen(CURRENT_BDD_MODE* pCurrentBddMod) = 0;
|
|
virtual NTSTATUS SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape) = 0;
|
|
virtual NTSTATUS SetPointerPosition(_In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition) = 0;
|
|
virtual NTSTATUS Escape(_In_ CONST DXGKARG_ESCAPE* pEscap) = 0;
|
|
NTSTATUS AcquireDisplayInfo(DXGK_DISPLAY_INFORMATION& DispInfo);
|
|
ULONG GetId(void) { return m_Id; }
|
|
virtual BOOLEAN IsBIOSCompatible() { return TRUE; }
|
|
protected:
|
|
virtual NTSTATUS GetModeList(DXGK_DISPLAY_INFORMATION* pDispInfo) = 0;
|
|
protected:
|
|
QxlDod* m_pQxlDod;
|
|
PVIDEO_MODE_INFORMATION m_ModeInfo;
|
|
ULONG m_ModeCount;
|
|
PUSHORT m_ModeNumbers;
|
|
USHORT m_CurrentMode;
|
|
ULONG m_Id;
|
|
};
|
|
|
|
class VgaDevice :
|
|
public HwDeviceInterface
|
|
{
|
|
public:
|
|
VgaDevice(_In_ QxlDod* pQxlDod);
|
|
~VgaDevice(void);
|
|
NTSTATUS QueryCurrentMode(PVIDEO_MODE RequestedMode);
|
|
NTSTATUS SetCurrentMode(ULONG Mode);
|
|
NTSTATUS GetCurrentMode(ULONG* Mode);
|
|
NTSTATUS SetPowerState(DEVICE_POWER_STATE DevicePowerState, DXGK_DISPLAY_INFORMATION* pDispInfo);
|
|
NTSTATUS HWInit(PCM_RESOURCE_LIST pResList, DXGK_DISPLAY_INFORMATION* pDispInfo);
|
|
NTSTATUS HWClose(void);
|
|
NTSTATUS ExecutePresentDisplayOnly(_In_ BYTE* DstAddr,
|
|
_In_ UINT DstBitPerPixel,
|
|
_In_ BYTE* SrcAddr,
|
|
_In_ UINT SrcBytesPerPixel,
|
|
_In_ LONG SrcPitch,
|
|
_In_ ULONG NumMoves,
|
|
_In_ D3DKMT_MOVE_RECT* pMoves,
|
|
_In_ ULONG NumDirtyRects,
|
|
_In_ RECT* pDirtyRect,
|
|
_In_ D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation,
|
|
_In_ const CURRENT_BDD_MODE* pModeCur);
|
|
VOID BlackOutScreen(CURRENT_BDD_MODE* pCurrentBddMod);
|
|
QXL_NON_PAGED BOOLEAN InterruptRoutine(_In_ PDXGKRNL_INTERFACE pDxgkInterface, _In_ ULONG MessageNumber);
|
|
QXL_NON_PAGED VOID DpcRoutine(PVOID);
|
|
QXL_NON_PAGED VOID ResetDevice(VOID);
|
|
QXL_NON_PAGED VOID VSyncInterruptPostProcess(_In_ PDXGKRNL_INTERFACE);
|
|
NTSTATUS AcquireFrameBuffer(CURRENT_BDD_MODE* pCurrentBddMode);
|
|
NTSTATUS ReleaseFrameBuffer(CURRENT_BDD_MODE* pCurrentBddMode);
|
|
NTSTATUS SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape);
|
|
NTSTATUS SetPointerPosition(_In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition);
|
|
NTSTATUS Escape(_In_ CONST DXGKARG_ESCAPE* pEscap);
|
|
protected:
|
|
NTSTATUS GetModeList(DXGK_DISPLAY_INFORMATION* pDispInfo);
|
|
private:
|
|
BOOL SetVideoModeInfo(UINT Idx, PVBE_MODEINFO pModeInfo);
|
|
};
|
|
|
|
typedef struct _MemSlot {
|
|
UINT64 start_phys_addr;
|
|
UINT64 end_phys_addr;
|
|
UINT8 *start_virt_addr;
|
|
UINT8 *last_virt_addr;
|
|
QXLPHYSICAL high_bits;
|
|
} MemSlot;
|
|
|
|
typedef struct MspaceInfo {
|
|
mspace _mspace;
|
|
UINT8 *mspace_start;
|
|
UINT8 *mspace_end;
|
|
} MspaceInfo;
|
|
|
|
enum {
|
|
MSPACE_TYPE_DEVRAM,
|
|
MSPACE_TYPE_VRAM,
|
|
NUM_MSPACES,
|
|
};
|
|
|
|
#define RELEASE_RES(res) if (!--(res)->refs) (res)->free(res);
|
|
#define GET_RES(res) (++(res)->refs)
|
|
|
|
/* Debug helpers - tag each resource with this enum */
|
|
enum {
|
|
RESOURCE_TYPE_DRAWABLE = 1,
|
|
RESOURCE_TYPE_SURFACE,
|
|
RESOURCE_TYPE_PATH,
|
|
RESOURCE_TYPE_CLIP_RECTS,
|
|
RESOURCE_TYPE_QUIC_IMAGE,
|
|
RESOURCE_TYPE_BITMAP_IMAGE,
|
|
RESOURCE_TYPE_SURFACE_IMAGE,
|
|
RESOURCE_TYPE_SRING,
|
|
RESOURCE_TYPE_CURSOR,
|
|
RESOURCE_TYPE_BUF,
|
|
RESOURCE_TYPE_UPDATE,
|
|
};
|
|
|
|
#ifdef DBG
|
|
#define RESOURCE_TYPE(res, val) do { res->type = val; } while (0)
|
|
#define INCREMENT_VSYNC_COUNTER(counter) InterlockedIncrement(counter)
|
|
#else
|
|
#define RESOURCE_TYPE(res, val)
|
|
#define INCREMENT_VSYNC_COUNTER(counter)
|
|
#endif
|
|
|
|
typedef struct Resource Resource;
|
|
struct Resource {
|
|
UINT32 refs;
|
|
void* ptr;
|
|
#ifdef DBG
|
|
UINT32 type;
|
|
#endif
|
|
void (*free)(Resource *res);
|
|
UINT8 res[0];
|
|
};
|
|
|
|
#define TIMEOUT_TO_MS ((LONGLONG) 1 * 10 * 1000)
|
|
|
|
BOOLEAN
|
|
FORCEINLINE
|
|
DoWaitForObject(
|
|
PVOID Object,
|
|
PLARGE_INTEGER Timeout,
|
|
LPCSTR name)
|
|
{
|
|
NTSTATUS status;
|
|
TimeMeasurement tm;
|
|
status = KeWaitForSingleObject (
|
|
Object,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
Timeout);
|
|
ASSERT(NT_SUCCESS(status));
|
|
tm.Stop();
|
|
if (name && tm.Diff() > 1900)
|
|
{
|
|
// 2 seconds in PresentDisplayOnly triggers watchdog on Win10RS1
|
|
// when VSync control enabled. Print the exact event name.
|
|
DbgPrint(TRACE_LEVEL_ERROR, ("Waiting %d ms for %s\n", tm.Diff(), name));
|
|
}
|
|
return (status == STATUS_SUCCESS);
|
|
}
|
|
|
|
#define WaitForObject(o, timeout) DoWaitForObject((o), (timeout), #o)
|
|
|
|
VOID
|
|
FORCEINLINE
|
|
ReleaseMutex(
|
|
PKMUTEX Mutex,
|
|
BOOLEAN locked)
|
|
{
|
|
if (locked)
|
|
{
|
|
KeReleaseMutex(Mutex, FALSE);
|
|
}
|
|
}
|
|
|
|
#define QXL_SLEEP(msec) do { \
|
|
LARGE_INTEGER timeout; \
|
|
timeout.QuadPart = -msec * TIMEOUT_TO_MS; \
|
|
KeDelayExecutionThread (KernelMode, FALSE, &timeout);\
|
|
} while (0);
|
|
|
|
#define IMAGE_HASH_INIT_VAL(width, height, format) \
|
|
((UINT32)((width) & 0x1FFF) | ((UINT32)((height) & 0x1FFF) << 13) |\
|
|
((UINT32)(format) << 26))
|
|
|
|
#define MAX_OUTPUT_RES 6
|
|
|
|
typedef struct QXLOutput {
|
|
LIST_ENTRY list;
|
|
UINT32 num_res;
|
|
#ifdef DBG
|
|
UINT32 type;
|
|
#endif
|
|
Resource *resources[MAX_OUTPUT_RES];
|
|
UINT8 data[0];
|
|
} QXLOutput;
|
|
|
|
typedef struct Ring RingItem;
|
|
typedef struct Ring {
|
|
RingItem *prev;
|
|
RingItem *next;
|
|
} Ring;
|
|
|
|
typedef struct InternalImage {
|
|
QXLImage image;
|
|
} InternalImage;
|
|
|
|
typedef struct InternalCursor {
|
|
QXLCursor cursor;
|
|
} InternalCursor;
|
|
|
|
#define CURSOR_ALLOC_SIZE (PAGE_SIZE << 1)
|
|
|
|
typedef struct DpcCbContext {
|
|
void* ptr;
|
|
UINT32 data;
|
|
} DPC_CB_CONTEXT,* PDPC_CB_CONTEXT;
|
|
|
|
#define BITMAP_ALLOC_BASE (sizeof(Resource) + sizeof(InternalImage) + sizeof(QXLDataChunk))
|
|
#define BITS_BUF_MAX (64 * 1024)
|
|
#define MIN(x, y) (((x) <= (y)) ? (x) : (y))
|
|
#define MAX(x, y) (((x) >= (y)) ? (x) : (y))
|
|
#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
|
|
|
|
// operation to be run by the presentation thread
|
|
class QxlPresentOperation
|
|
{
|
|
public:
|
|
QxlPresentOperation() {}
|
|
QxlPresentOperation(const QxlPresentOperation&) = delete;
|
|
void operator=(const QxlPresentOperation&) = delete;
|
|
// execute the operation
|
|
virtual void Run()=0;
|
|
virtual ~QxlPresentOperation() {}
|
|
};
|
|
|
|
#include "start-packed.h"
|
|
SPICE_RING_DECLARE(QXLPresentOnlyRing, QxlPresentOperation*, 1024);
|
|
#include "end-packed.h"
|
|
|
|
class QxlDevice :
|
|
public HwDeviceInterface
|
|
{
|
|
public:
|
|
QxlDevice(_In_ QxlDod* pQxlDod);
|
|
~QxlDevice(void);
|
|
NTSTATUS QueryCurrentMode(PVIDEO_MODE RequestedMode);
|
|
NTSTATUS SetCurrentMode(ULONG Mode);
|
|
NTSTATUS GetCurrentMode(ULONG* Mode);
|
|
NTSTATUS SetPowerState(DEVICE_POWER_STATE DevicePowerState, DXGK_DISPLAY_INFORMATION* pDispInfo);
|
|
NTSTATUS HWInit(PCM_RESOURCE_LIST pResList, DXGK_DISPLAY_INFORMATION* pDispInfo);
|
|
NTSTATUS HWClose(void);
|
|
NTSTATUS ExecutePresentDisplayOnly(_In_ BYTE* DstAddr,
|
|
_In_ UINT DstBitPerPixel,
|
|
_In_ BYTE* SrcAddr,
|
|
_In_ UINT SrcBytesPerPixel,
|
|
_In_ LONG SrcPitch,
|
|
_In_ ULONG NumMoves,
|
|
_In_ D3DKMT_MOVE_RECT* pMoves,
|
|
_In_ ULONG NumDirtyRects,
|
|
_In_ RECT* pDirtyRect,
|
|
_In_ D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation,
|
|
_In_ const CURRENT_BDD_MODE* pModeCur);
|
|
VOID BlackOutScreen(CURRENT_BDD_MODE* pCurrentBddMod);
|
|
QXL_NON_PAGED BOOLEAN InterruptRoutine(_In_ PDXGKRNL_INTERFACE pDxgkInterface, _In_ ULONG MessageNumber);
|
|
QXL_NON_PAGED VOID DpcRoutine(PVOID);
|
|
QXL_NON_PAGED VOID ResetDevice(VOID);
|
|
QXL_NON_PAGED VOID VSyncInterruptPostProcess(_In_ PDXGKRNL_INTERFACE);
|
|
NTSTATUS SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape);
|
|
NTSTATUS SetPointerPosition(_In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition);
|
|
NTSTATUS Escape(_In_ CONST DXGKARG_ESCAPE* pEscap);
|
|
BOOLEAN IsBIOSCompatible() { return FALSE; }
|
|
protected:
|
|
NTSTATUS GetModeList(DXGK_DISPLAY_INFORMATION* pDispInfo);
|
|
QXLDrawable *PrepareBltBits (BLT_INFO* pDst,
|
|
CONST BLT_INFO* pSrc,
|
|
UINT NumRects,
|
|
_In_reads_(NumRects) CONST RECT *pRects,
|
|
POINT* pSourcePoint);
|
|
QXLDrawable *PrepareCopyBits(const RECT& rect, const POINT& sourcePoint);
|
|
QXLDrawable *Drawable(UINT8 type,
|
|
CONST RECT *area,
|
|
CONST RECT *clip,
|
|
UINT32 surface_id);
|
|
void PushDrawable(QXLDrawable *drawable);
|
|
void PushCursorCmd(QXLCursorCmd *cursor_cmd);
|
|
QXLDrawable *GetDrawable();
|
|
QXLCursorCmd *CursorCmd();
|
|
void *AllocMem(UINT32 mspace_type, size_t size, BOOL force);
|
|
VOID SetImageId(InternalImage *internal,
|
|
BOOL cache_me,
|
|
LONG width,
|
|
LONG height,
|
|
UINT8 format, UINT32 key);
|
|
private:
|
|
NTSTATUS QxlInit(DXGK_DISPLAY_INFORMATION* pDispInfo);
|
|
void QxlClose(void);
|
|
void UnmapMemory(void);
|
|
BOOL SetVideoModeInfo(UINT Idx, QXLMode* pModeInfo);
|
|
void UpdateVideoModeInfo(UINT Idx, UINT xres, UINT yres, UINT bpp);
|
|
BOOL InitMemSlots(void);
|
|
BOOL CreateMemSlots(void);
|
|
void DestroyMemSlots(void);
|
|
void CreatePrimarySurface(PVIDEO_MODE_INFORMATION pModeInfo);
|
|
void DestroyPrimarySurface(void);
|
|
void SetupHWSlot(UINT8 Idx, MemSlot *pSlot);
|
|
void SetupMemSlot(UINT8 Idx, UINT64 pastart, UINT64 paend, UINT8 *vastart, UINT8 *valast);
|
|
BOOL CreateEvents(void);
|
|
BOOL CreateRings(void);
|
|
inline UINT8 *VA(QXLPHYSICAL paddr);
|
|
inline QXLPHYSICAL PA(PVOID virt);
|
|
void InitDeviceMemoryResources(void);
|
|
NTSTATUS InitMonitorConfig();
|
|
void InitMspace(UINT32 mspace_type, UINT8 *start, size_t capacity);
|
|
void FlushReleaseRing();
|
|
void FreeMem(void *ptr);
|
|
UINT64 ReleaseOutput(UINT64 output_id);
|
|
void WaitForReleaseRing(void);
|
|
BOOL SetClip(const RECT *clip, QXLDrawable *drawable);
|
|
void AddRes(QXLOutput *output, Resource *res);
|
|
void DrawableAddRes(QXLDrawable *drawable, Resource *res);
|
|
void CursorCmdAddRes(QXLCursorCmd *cmd, Resource *res);
|
|
void FreeClipRects(Resource *res);
|
|
void static FreeClipRectsEx(Resource *res);
|
|
void FreeBitmapImage(Resource *res);
|
|
void static FreeBitmapImageEx(Resource *res);
|
|
void static FreeCursorEx(Resource *res);
|
|
void FreeCursor(Resource *res);
|
|
void WaitForCmdRing(void);
|
|
void PushCmd(void);
|
|
void WaitForCursorRing(void);
|
|
void PushCursor(void);
|
|
BOOLEAN AttachNewBitmap(QXLDrawable *drawable, UINT8 *src, UINT8 *src_end, INT pitch, BOOLEAN bForce);
|
|
void DiscardDrawable(QXLDrawable *drawable);
|
|
BOOLEAN PutBytesAlign(QXLDataChunk **chunk_ptr, UINT8 **now_ptr,
|
|
UINT8 **end_ptr, UINT8 *src, int size,
|
|
size_t alloc_size, PLIST_ENTRY pDelayed, BOOLEAN bDevRam = FALSE);
|
|
QXLDataChunk *MakeChunk(DelayedChunk *pdc);
|
|
ULONG PrepareDrawable(QXLDrawable*& drawable);
|
|
void AsyncIo(UCHAR Port, UCHAR Value);
|
|
void SyncIo(UCHAR Port, UCHAR Value);
|
|
NTSTATUS UpdateChildStatus(BOOLEAN connect);
|
|
NTSTATUS SetCustomDisplay(QXLEscapeSetCustomDisplay* custom_display);
|
|
void SetMonitorConfig(QXLHead* monitor_config);
|
|
NTSTATUS StartPresentThread();
|
|
void StopPresentThread();
|
|
void PresentThreadRoutine();
|
|
static void PresentThreadRoutineWrapper(HANDLE dev) {
|
|
((QxlDevice *)dev)->PresentThreadRoutine();
|
|
}
|
|
void PostToWorkerThread(QxlPresentOperation *operation);
|
|
|
|
static LONG GetMaxSourceMappingHeight(RECT* DirtyRects, ULONG NumDirtyRects);
|
|
|
|
private:
|
|
USHORT m_CustomMode;
|
|
|
|
PUCHAR m_IoBase;
|
|
BOOLEAN m_IoMapped;
|
|
ULONG m_IoSize;
|
|
|
|
PHYSICAL_ADDRESS m_RamPA;
|
|
UINT8 *m_RamStart;
|
|
QXLRam *m_RamHdr;
|
|
ULONG m_RamSize;
|
|
|
|
PHYSICAL_ADDRESS m_VRamPA;
|
|
UINT8 *m_VRamStart;
|
|
ULONG m_VRamSize;
|
|
|
|
QXLRom *m_RomHdr;
|
|
ULONG m_RomSize;
|
|
|
|
MemSlot m_MemSlots[2];
|
|
enum { m_MainMemSlot = 0, m_SurfaceMemSlot = 1 };
|
|
UINT8 m_SlotIdBits;
|
|
UINT8 m_SlotGenBits;
|
|
QXLPHYSICAL m_VaSlotMask;
|
|
|
|
QXLCommandRing *m_CommandRing;
|
|
QXLCursorRing *m_CursorRing;
|
|
QXLReleaseRing *m_ReleaseRing;
|
|
|
|
KEVENT m_DisplayEvent;
|
|
KEVENT m_CursorEvent;
|
|
KEVENT m_IoCmdEvent;
|
|
KEVENT m_PresentEvent;
|
|
KEVENT m_PresentThreadReadyEvent;
|
|
|
|
PUCHAR m_LogPort;
|
|
PUCHAR m_LogBuf;
|
|
|
|
KMUTEX m_MemLock;
|
|
KMUTEX m_CmdLock;
|
|
KMUTEX m_IoLock;
|
|
KMUTEX m_CrsLock;
|
|
MspaceInfo m_MSInfo[NUM_MSPACES];
|
|
|
|
UINT64 m_FreeOutputs;
|
|
LONG m_Pending;
|
|
|
|
QXLMonitorsConfig* m_monitor_config;
|
|
QXLPHYSICAL* m_monitor_config_pa;
|
|
|
|
QXLPresentOnlyRing m_PresentRing[1];
|
|
// generation, updated when resolution change
|
|
// this is used to detect if a draw command is obsoleted
|
|
// and should not be executed
|
|
uint16_t m_DrawGeneration;
|
|
HANDLE m_PresentThread;
|
|
BOOLEAN m_bActive;
|
|
};
|
|
|
|
class QxlDod {
|
|
private:
|
|
DEVICE_OBJECT* m_pPhysicalDevice;
|
|
DXGKRNL_INTERFACE m_DxgkInterface;
|
|
DXGK_DEVICE_INFO m_DeviceInfo;
|
|
|
|
DEVICE_POWER_STATE m_MonitorPowerState;
|
|
DEVICE_POWER_STATE m_AdapterPowerState;
|
|
QXL_FLAGS m_Flags;
|
|
|
|
CURRENT_BDD_MODE m_CurrentModes[MAX_VIEWS];
|
|
|
|
D3DDDI_VIDEO_PRESENT_SOURCE_ID m_SystemDisplaySourceId;
|
|
DXGKARG_SETPOINTERSHAPE m_PointerShape;
|
|
|
|
HwDeviceInterface* m_pHWDevice;
|
|
KTIMER m_VsyncTimer;
|
|
KDPC m_VsyncTimerDpc;
|
|
BOOLEAN m_bVsyncEnabled;
|
|
LONG m_VsyncFiredCounter;
|
|
public:
|
|
QxlDod(_In_ DEVICE_OBJECT* pPhysicalDeviceObject);
|
|
~QxlDod(void);
|
|
#pragma code_seg(push)
|
|
#pragma code_seg()
|
|
BOOLEAN IsDriverActive() const
|
|
{
|
|
return m_Flags.DriverStarted;
|
|
}
|
|
#pragma code_seg(pop)
|
|
|
|
NTSTATUS StartDevice(_In_ DXGK_START_INFO* pDxgkStartInfo,
|
|
_In_ DXGKRNL_INTERFACE* pDxgkInterface,
|
|
_Out_ ULONG* pNumberOfViews,
|
|
_Out_ ULONG* pNumberOfChildren);
|
|
NTSTATUS StopDevice(VOID);
|
|
// Must be Non-Paged
|
|
QXL_NON_PAGED VOID ResetDevice(VOID);
|
|
|
|
NTSTATUS DispatchIoRequest(_In_ ULONG VidPnSourceId,
|
|
_In_ VIDEO_REQUEST_PACKET* pVideoRequestPacket);
|
|
NTSTATUS SetPowerState(_In_ ULONG HardwareUid,
|
|
_In_ DEVICE_POWER_STATE DevicePowerState,
|
|
_In_ POWER_ACTION ActionType);
|
|
// Report back child capabilities
|
|
NTSTATUS QueryChildRelations(_Out_writes_bytes_(ChildRelationsSize) DXGK_CHILD_DESCRIPTOR* pChildRelations,
|
|
_In_ ULONG ChildRelationsSize);
|
|
|
|
NTSTATUS QueryChildStatus(_Inout_ DXGK_CHILD_STATUS* pChildStatus,
|
|
_In_ BOOLEAN NonDestructiveOnly);
|
|
|
|
// Return EDID if previously retrieved
|
|
NTSTATUS QueryDeviceDescriptor(_In_ ULONG ChildUid,
|
|
_Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor);
|
|
|
|
// Must be Non-Paged
|
|
// BDD doesn't have interrupts, so just returns false
|
|
QXL_NON_PAGED BOOLEAN InterruptRoutine(_In_ ULONG MessageNumber);
|
|
|
|
QXL_NON_PAGED VOID DpcRoutine(VOID);
|
|
|
|
// Return DriverCaps, doesn't support other queries though
|
|
NTSTATUS QueryAdapterInfo(_In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo);
|
|
|
|
NTSTATUS SetPointerPosition(_In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition);
|
|
|
|
NTSTATUS SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape);
|
|
|
|
NTSTATUS Escape(_In_ CONST DXGKARG_ESCAPE* pEscape);
|
|
|
|
NTSTATUS PresentDisplayOnly(_In_ CONST DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly);
|
|
|
|
NTSTATUS QueryInterface(_In_ CONST PQUERY_INTERFACE QueryInterface);
|
|
|
|
NTSTATUS IsSupportedVidPn(_Inout_ DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPn);
|
|
|
|
NTSTATUS RecommendFunctionalVidPn(_In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST pRecommendFunctionalVidPn);
|
|
|
|
NTSTATUS RecommendVidPnTopology(_In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST pRecommendVidPnTopology);
|
|
|
|
NTSTATUS RecommendMonitorModes(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes);
|
|
|
|
NTSTATUS EnumVidPnCofuncModality(_In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModality);
|
|
|
|
NTSTATUS SetVidPnSourceVisibility(_In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY* pSetVidPnSourceVisibility);
|
|
|
|
NTSTATUS CommitVidPn(_In_ CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPn);
|
|
|
|
NTSTATUS UpdateActiveVidPnPresentPath(_In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST pUpdateActiveVidPnPresentPath);
|
|
|
|
NTSTATUS QueryVidPnHWCapability(_Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY* pVidPnHWCaps);
|
|
|
|
// Part of PnPStop (PnP instance only), returns current mode information (which will be passed to fallback instance by dxgkrnl)
|
|
NTSTATUS StopDeviceAndReleasePostDisplayOwnership(_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
|
|
_Out_ DXGK_DISPLAY_INFORMATION* pDisplayInfo);
|
|
|
|
// Must be Non-Paged
|
|
// Call to initialize as part of bugcheck
|
|
QXL_NON_PAGED NTSTATUS SystemDisplayEnable(_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
|
|
_In_ PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags,
|
|
_Out_ UINT* pWidth,
|
|
_Out_ UINT* pHeight,
|
|
_Out_ D3DDDIFORMAT* pColorFormat);
|
|
|
|
// Must be Non-Paged
|
|
// Write out pixels as part of bugcheck
|
|
QXL_NON_PAGED VOID SystemDisplayWrite(_In_reads_bytes_(SourceHeight * SourceStride) VOID* pSource,
|
|
_In_ UINT SourceWidth,
|
|
_In_ UINT SourceHeight,
|
|
_In_ UINT SourceStride,
|
|
_In_ INT PositionX,
|
|
_In_ INT PositionY);
|
|
PDXGKRNL_INTERFACE GetDxgkInterface(void) { return &m_DxgkInterface;}
|
|
NTSTATUS AcquireDisplayInfo(DXGK_DISPLAY_INFORMATION& DispInfo)
|
|
{
|
|
return m_DxgkInterface.DxgkCbAcquirePostDisplayOwnership(m_DxgkInterface.DeviceHandle, &DispInfo);
|
|
}
|
|
VOID EnableVsync(BOOLEAN bEnable);
|
|
private:
|
|
VOID CleanUp(VOID);
|
|
NTSTATUS CheckHardware();
|
|
NTSTATUS WriteHWInfoStr(_In_ HANDLE DevInstRegKeyHandle, _In_ PCWSTR pszwValueName, _In_ PCSTR pszValue);
|
|
// Set the given source mode on the given path
|
|
NTSTATUS SetSourceModeAndPath(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode,
|
|
CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath);
|
|
|
|
// Add the current mode to the given monitor source mode set
|
|
NTSTATUS AddSingleMonitorMode(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes);
|
|
|
|
// Add the current mode to the given VidPn source mode set
|
|
NTSTATUS AddSingleSourceMode(_In_ CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface,
|
|
D3DKMDT_HVIDPNSOURCEMODESET hVidPnSourceModeSet,
|
|
D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId);
|
|
|
|
// Add the current mode (or the matching to pinned source mode) to the give VidPn target mode set
|
|
NTSTATUS AddSingleTargetMode(_In_ CONST DXGK_VIDPNTARGETMODESET_INTERFACE* pVidPnTargetModeSetInterface,
|
|
D3DKMDT_HVIDPNTARGETMODESET hVidPnTargetModeSet,
|
|
_In_opt_ CONST D3DKMDT_VIDPN_SOURCE_MODE* pVidPnPinnedSourceModeInfo,
|
|
D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId);
|
|
QXL_NON_PAGED D3DDDI_VIDEO_PRESENT_SOURCE_ID FindSourceForTarget(D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, BOOLEAN DefaultToZero);
|
|
NTSTATUS IsVidPnSourceModeFieldsValid(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode) const;
|
|
NTSTATUS IsVidPnPathFieldsValid(CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath) const;
|
|
NTSTATUS RegisterHWInfo(_In_ ULONG Id);
|
|
QXL_NON_PAGED VOID VsyncTimerProc();
|
|
static QXL_NON_PAGED VOID VsyncTimerProcGate(_In_ _KDPC *dpc, _In_ PVOID context, _In_ PVOID arg1, _In_ PVOID arg2);
|
|
QXL_NON_PAGED VOID IndicateVSyncInterrupt();
|
|
static QXL_NON_PAGED BOOLEAN VsyncTimerSynchRoutine(PVOID context);
|
|
};
|
|
|
|
NTSTATUS
|
|
MapFrameBuffer(
|
|
_In_ PHYSICAL_ADDRESS PhysicalAddress,
|
|
_In_ ULONG Length,
|
|
_Outptr_result_bytebuffer_(Length) VOID** VirtualAddress);
|
|
|
|
NTSTATUS
|
|
UnmapFrameBuffer(
|
|
_In_reads_bytes_(Length) VOID* VirtualAddress,
|
|
_In_ ULONG Length);
|
|
|
|
QXL_NON_PAGED UINT BPPFromPixelFormat(D3DDDIFORMAT Format);
|
|
QXL_NON_PAGED D3DDDIFORMAT PixelFormatFromBPP(UINT BPP);
|
|
UINT SpiceFromPixelFormat(D3DDDIFORMAT Format);
|
|
|
|
QXL_NON_PAGED VOID CopyBitsGeneric(
|
|
BLT_INFO* pDst,
|
|
CONST BLT_INFO* pSrc,
|
|
UINT NumRects,
|
|
_In_reads_(NumRects) CONST RECT *pRects);
|
|
|
|
QXL_NON_PAGED VOID CopyBits32_32(
|
|
BLT_INFO* pDst,
|
|
CONST BLT_INFO* pSrc,
|
|
UINT NumRects,
|
|
_In_reads_(NumRects) CONST RECT *pRects);
|
|
QXL_NON_PAGED VOID BltBits (
|
|
BLT_INFO* pDst,
|
|
CONST BLT_INFO* pSrc,
|
|
UINT NumRects,
|
|
_In_reads_(NumRects) CONST RECT *pRects);
|
|
|
|
QXL_NON_PAGED BYTE* GetRowStart(_In_ CONST BLT_INFO* pBltInfo, CONST RECT* pRect);
|
|
QXL_NON_PAGED VOID GetPitches(_In_ CONST BLT_INFO* pBltInfo, _Out_ LONG* pPixelPitch, _Out_ LONG* pRowPitch);
|