summaryrefslogtreecommitdiff
path: root/qxldod/QxlDod.h
blob: d889b9dec445de0a4203e42b01417629a5d7b975 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
/*
 * 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             800
#define MIN_HEIGHT_SIZE            600
#define INITIAL_WIDTH              1024
#define INITIAL_HEIGHT             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; }
    virtual void GetFinalDisplayInfo(DXGK_DISPLAY_INFORMATION* pDisplayInfo) {}

    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 m_bUefiMode; }
    void GetFinalDisplayInfo(DXGK_DISPLAY_INFORMATION* pDisplayInfo);
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);
    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;
    BOOLEAN m_bUefiMode;
    DXGK_DISPLAY_INFORMATION m_InitialDisplayInfo;
};

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);