[WIN32K][VIDEOPRT] Improve initialization and interfacing with INBV.
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Tue, 26 Nov 2019 01:49:35 +0000 (02:49 +0100)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 2 Dec 2019 01:33:20 +0000 (02:33 +0100)
CORE-12149

VIDEOPRT:
=========

Improve interfacing with INBV, so as to detect when an external module
acquired INBV display ownership, and whether ownership is being released
later on. (This does NOT rely on hooking!)

For this purpose we improve the IntVideoPortResetDisplayParameters(Ex)
callback that gets registered with an InbvNotifyDisplayOwnershipLost()
call during initialization, and we add a monitoring thread.

The callback is called whenever an external module calls
InbvAcquireDisplayOwnership(), for example the bugcheck code or the KDBG
debugger in SCREEN mode. When this happens, a flag that tells the
monitoring thread to start monitoring INBV is set (ReactOS-specific),
and the display adapters get reset with HwResetHw() (as done on Windows).

Due to the fact that this INBV callback can be called at *ANY* IRQL, we
cannot use dispatcher synchronization mechanisms such as events to tell
the INBV monitoring thread to start its operations, so we need to rely
instead on a flag to be set. And, since INBV doesn't provide with any
proper callback/notification system either, we need to actively monitor
its state by pooling. To reduce the load on the system the monitoring
thread performs 1-second waits between each check for the flag set by
the INBV callback, and during checking the INBV ownership status.

When the INBV ownership is detected to be released by an external module,
the INBV callback is re-registered (this is *MANDATORY* since the
external module has called InbvNotifyDisplayOwnershipLost() with a
different callback parameter!), and then we callout to Win32k for
re-enabling the display.

This has the virtue of correctly resetting the display once the KDBG
debugger in SCREEN mode is being exited, and fixes CORE-12149 .

The following additional fixes were needed:

VIDEOPRT & WIN32K:
==================

Remove the registration with INBV that was previously done in a ReactOS-
specific hacked IRP_MJ_WRITE call; it is now done correctly during the
video device opening done by EngpRegisterGraphicsDevice() in the VIDEOPRT's
IRP_MJ_CREATE handler, as done on Windows.

WIN32K:
=======

- Stub the VideoPortCallout() support, for VIDEOPRT -> WIN32 callbacks.
  This function gets registered with VIDEOPRT through an
  IOCTL_VIDEO_INIT_WIN32K_CALLBACKS call in EngpRegisterGraphicsDevice().

- Only partially implement the 'VideoFindAdapterCallout' case, that just
  re-enables the primary display by refreshing it (using the new function
  UserRefreshDisplay()).

VIDEOPRT:
=========

- PVIDEO_WIN32K_CALLOUT is an NTAPI (stdcall) callback.

- In the IntVideoPortResetDisplayParameters(Ex) callback, reset all the
  "resettable" adapters registered in the HwResetAdaptersList list.
  We thus get rid of the global ResetDisplayParametersDeviceExtension.

- Make the IntVideoPortResetDisplayParameters(Ex) callback slightly more
  robust (using SEH) against potential HwResetListEntry list corruption
  or invalid DriverExtension->InitializationData.HwResetHw() that would
  otherwise trigger a BSOD, and this would be disastrous since that
  callback is precisely called when INBV is acquired, typically when the
  BSOD code initializes the display for displaying its information...

Extras:
- Validate the IrpStack->MajorFunction in IntVideoPortDispatchDeviceControl()
  and implement IRP_MJ_SHUTDOWN handling. Stub out the other IOCTLs that
  are handled by VIDEOPRT only (and not by the miniports).

- VIDEOPRT doesn't require IRP_MJ_INTERNAL_DEVICE_CONTROL (unused).

- Implement IOCTL_VIDEO_PREPARE_FOR_EARECOVERY that just resets the
  display to standard VGA 80x25 text mode.

sdk/include/psdk/ntddvdeo.h
win32ss/drivers/videoprt/CMakeLists.txt
win32ss/drivers/videoprt/dispatch.c
win32ss/drivers/videoprt/int10.c
win32ss/drivers/videoprt/videoprt.c
win32ss/drivers/videoprt/videoprt.h
win32ss/gdi/eng/device.c
win32ss/user/ntuser/display.c

index 8fcd361..5e2b63b 100644 (file)
@@ -75,7 +75,7 @@ extern "C" {
   CTL_CODE(FILE_DEVICE_VIDEO, 0x0b, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
 #define IOCTL_VIDEO_DISABLE_CURSOR \
-  CTL_CODE (FILE_DEVICE_VIDEO, 0x109, METHOD_BUFFERED, FILE_ANY_ACCESS)
+  CTL_CODE(FILE_DEVICE_VIDEO, 0x109, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
 #define IOCTL_VIDEO_DISABLE_POINTER \
   CTL_CODE(FILE_DEVICE_VIDEO, 0x10f, METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -254,7 +254,7 @@ typedef struct _VIDEO_WIN32K_CALLBACKS_PARAMS {
 
 typedef
 VOID
-(*PVIDEO_WIN32K_CALLOUT)(
+(NTAPI *PVIDEO_WIN32K_CALLOUT)(
   _In_ PVOID Params);
 
 typedef struct _VIDEO_WIN32K_CALLBACKS {
index 664f8cb..be11654 100644 (file)
@@ -29,6 +29,7 @@ add_library(videoprt MODULE
     ${CMAKE_CURRENT_BINARY_DIR}/videoprt.def)
 
 set_module_type(videoprt kernelmodedriver)
+target_link_libraries(videoprt ${PSEH_LIB})
 add_importlibs(videoprt ntoskrnl hal)
 add_pch(videoprt videoprt.h SOURCE)
 add_cd_file(TARGET videoprt DESTINATION reactos/system32/drivers FOR all)
index d168254..42f7023 100644 (file)
@@ -22,6 +22,7 @@
 #include "videoprt.h"
 
 #include <ndk/inbvfuncs.h>
+#include <ndk/obfuncs.h>
 #include <ndk/psfuncs.h>
 
 #define NDEBUG
 
 /* GLOBAL VARIABLES ***********************************************************/
 
-PVIDEO_PORT_DEVICE_EXTENSION ResetDisplayParametersDeviceExtension = NULL;
-PVIDEO_WIN32K_CALLOUT Win32kCallout;
+static PVIDEO_WIN32K_CALLOUT Win32kCallout = NULL;
+static HANDLE InbvThreadHandle = NULL;
+static BOOLEAN InbvMonitoring = FALSE;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
+static VOID
+VideoPortWin32kCallout(
+    _In_ PVIDEO_WIN32K_CALLBACKS_PARAMS CallbackParams)
+{
+    if (!Win32kCallout)
+        return;
+
+    /* Perform the call in the context of CSRSS */
+    if (!CsrProcess)
+        return;
+
+    KeAttachProcess(CsrProcess);
+    Win32kCallout(CallbackParams);
+    KeDetachProcess();
+}
+
 /*
- * Reset display to blue screen
+ * Reinitialize the display to base VGA mode.
+ *
+ * Returns TRUE if it completely resets the adapter to the given character mode.
+ * Returns FALSE otherwise, indicating that the HAL should perform the VGA mode
+ * reset itself after HwVidResetHw() returns control.
+ *
+ * This callback has been registered with InbvNotifyDisplayOwnershipLost()
+ * and is called by InbvAcquireDisplayOwnership(), typically when the bugcheck
+ * code regains display access. Therefore this routine can be called at any
+ * IRQL, and in particular at IRQL = HIGH_LEVEL. This routine must also reside
+ * completely in non-paged pool, and cannot perform the following actions:
+ * Allocate memory, access pageable memory, use any synchronization mechanisms
+ * or call any routine that must execute at IRQL = DISPATCH_LEVEL or below.
  */
-BOOLEAN
+static BOOLEAN
 NTAPI
-IntVideoPortResetDisplayParameters(ULONG Columns, ULONG Rows)
+IntVideoPortResetDisplayParametersEx(
+    _In_ ULONG Columns,
+    _In_ ULONG Rows,
+    _In_ BOOLEAN CalledByInbv)
 {
+    BOOLEAN Success = TRUE;
+    PLIST_ENTRY PrevEntry, Entry;
+    PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
     PVIDEO_PORT_DRIVER_EXTENSION DriverExtension;
 
-    if (ResetDisplayParametersDeviceExtension == NULL)
+    if (IsListEmpty(&HwResetAdaptersList))
         return FALSE;
 
-    DriverExtension = ResetDisplayParametersDeviceExtension->DriverExtension;
+    if (CalledByInbv)
+    {
+        /*
+         * We have been unexpectedly called via a callback from
+         * InbvAcquireDisplayOwnership(): start monitoring INBV.
+         */
+        InbvMonitoring = TRUE;
+    }
 
-    if (DriverExtension->InitializationData.HwResetHw != NULL)
+    for (PrevEntry = &HwResetAdaptersList, Entry = PrevEntry->Flink;
+         Entry != &HwResetAdaptersList;
+         PrevEntry = Entry, Entry = Entry->Flink)
     {
-        if (DriverExtension->InitializationData.HwResetHw(
-                    &ResetDisplayParametersDeviceExtension->MiniPortDeviceExtension,
-                    Columns, Rows))
+        /*
+         * Check whether the entry address is properly aligned,
+         * the device and driver extensions must be readable and
+         * the device extension properly back-linked to the last entry.
+         */
+// #define IS_ALIGNED(addr, align) (((ULONG64)(addr) & (align - 1)) == 0)
+        if (((ULONG_PTR)Entry & (sizeof(ULONG_PTR) - 1)) != 0)
+            return FALSE;
+
+        DeviceExtension = CONTAINING_RECORD(Entry,
+                                            VIDEO_PORT_DEVICE_EXTENSION,
+                                            HwResetListEntry);
+        /*
+         * As this function can be called as part of the INBV initialization
+         * by the bugcheck code, avoid any problems and protect all accesses
+         * within SEH.
+         */
+        _SEH2_TRY
+        {
+            DriverExtension = DeviceExtension->DriverExtension;
+            ASSERT(DriverExtension);
+
+            if (DeviceExtension->HwResetListEntry.Blink != PrevEntry)
+                _SEH2_YIELD(return FALSE);
+
+            if ((DeviceExtension->DeviceOpened >= 1) &&
+                (DriverExtension->InitializationData.HwResetHw != NULL))
+            {
+                Success &= DriverExtension->InitializationData.HwResetHw(
+                                &DeviceExtension->MiniPortDeviceExtension,
+                                Columns, Rows);
+            }
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            ResetDisplayParametersDeviceExtension = NULL;
-            return TRUE;
         }
+        _SEH2_END;
     }
 
-    ResetDisplayParametersDeviceExtension = NULL;
-    return FALSE;
+    return Success;
 }
 
+/* This callback is registered with InbvNotifyDisplayOwnershipLost() */
+static BOOLEAN
+NTAPI
+IntVideoPortResetDisplayParameters(ULONG Columns, ULONG Rows)
+{
+    /* Call the extended function, specifying we were called by INBV */
+    return IntVideoPortResetDisplayParametersEx(Columns, Rows, TRUE);
+}
+
+/*
+ * (Adapted for ReactOS/Win2k3 from an original comment
+ *  by Gé van Geldorp, June 2003, r4937)
+ *
+ * DISPLAY OWNERSHIP
+ *
+ * So, who owns the physical display and is allowed to write to it?
+ *
+ * In NT 5.x (Win2k/Win2k3), upon boot INBV/BootVid owns the display, unless
+ * /NOGUIBOOT has been specified in the boot command line. Later in the boot
+ * sequence, WIN32K.SYS opens the DISPLAY device. This open call ends up in
+ * VIDEOPRT.SYS. This component takes ownership of the display by calling
+ * InbvNotifyDisplayOwnershipLost() -- effectively telling INBV to release
+ * ownership of the display it previously had. From that moment on, the display
+ * is owned by that component and can be switched to graphics mode. The display
+ * is not supposed to return to text mode, except in case of a bugcheck.
+ * The bugcheck code calls InbvAcquireDisplayOwnership() so as to make INBV
+ * re-take display ownership, and calls back the function previously registered
+ * by VIDEOPRT.SYS with InbvNotifyDisplayOwnershipLost(). After the bugcheck,
+ * execution is halted. So, under NT, the only possible sequence of display
+ * modes is text mode -> graphics mode -> text mode (the latter hopefully
+ * happening very infrequently).
+ *
+ * In ReactOS things are a little bit different. We want to have a functional
+ * interactive text mode. We should be able to switch back and forth from
+ * text mode to graphics mode when a GUI app is started and then finished.
+ * Also, when the system bugchecks in graphics mode we want to switch back to
+ * text mode and show the bugcheck information. Last but not least, when using
+ * KDBG in /DEBUGPORT=SCREEN mode, breaking into the debugger would trigger a
+ * switch to text mode, and the user would expect that by continuing execution
+ * a switch back to graphics mode is done.
+ */
+static VOID
+NTAPI
+InbvMonitorThread(
+    _In_ PVOID Context)
+{
+    VIDEO_WIN32K_CALLBACKS_PARAMS CallbackParams;
+    LARGE_INTEGER Delay;
+    USHORT i;
+
+    KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
+
+    while (TRUE)
+    {
+        /*
+         * During one second, check the INBV status each 100 milliseconds,
+         * then revert to 1 second delay.
+         */
+        i = 10;
+        Delay.QuadPart = (LONGLONG)-100*1000*10; // 100 millisecond delay
+        while (!InbvMonitoring)
+        {
+            KeDelayExecutionThread(KernelMode, FALSE, &Delay);
+
+            if ((i > 0) && (--i == 0))
+                Delay.QuadPart = (LONGLONG)-1*1000*1000*10; // 1 second delay
+        }
+
+        /*
+         * Loop while the display is owned by INBV. We cannot do anything else
+         * than polling since INBV does not offer a proper notification system.
+         *
+         * During one second, check the INBV status each 100 milliseconds,
+         * then revert to 1 second delay.
+         */
+        i = 10;
+        Delay.QuadPart = (LONGLONG)-100*1000*10; // 100 millisecond delay
+        while (InbvCheckDisplayOwnership())
+        {
+            KeDelayExecutionThread(KernelMode, FALSE, &Delay);
+
+            if ((i > 0) && (--i == 0))
+                Delay.QuadPart = (LONGLONG)-1*1000*1000*10; // 1 second delay
+        }
+
+        /* Reset the monitoring */
+        InbvMonitoring = FALSE;
+
+        /*
+         * Somebody released INBV display ownership, usually by invoking
+         * InbvNotifyDisplayOwnershipLost(). However the caller of this
+         * function certainly specified a different callback than ours.
+         * As we are going to be the only owner of the active display,
+         * we need to re-register our own display reset callback.
+         */
+        InbvNotifyDisplayOwnershipLost(IntVideoPortResetDisplayParameters);
+
+        /* Tell Win32k to reset the display */
+        CallbackParams.CalloutType = VideoFindAdapterCallout;
+        // CallbackParams.PhysDisp = NULL;
+        CallbackParams.Param = (ULONG_PTR)TRUE; // TRUE: Re-enable display; FALSE: Disable display.
+        VideoPortWin32kCallout(&CallbackParams);
+    }
+
+    // FIXME: See IntVideoPortInbvCleanup().
+    // PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+static NTSTATUS
+IntVideoPortInbvInitialize(VOID)
+{
+    /* Create the INBV monitoring thread if needed */
+    if (!InbvThreadHandle)
+    {
+        NTSTATUS Status;
+        OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(NULL, OBJ_KERNEL_HANDLE);
+
+        Status = PsCreateSystemThread(&InbvThreadHandle,
+                                      0,
+                                      &ObjectAttributes,
+                                      NULL,
+                                      NULL,
+                                      InbvMonitorThread,
+                                      NULL);
+        if (!NT_SUCCESS(Status))
+            InbvThreadHandle = NULL;
+    }
+
+    /* Re-register the display reset callback with INBV */
+    InbvNotifyDisplayOwnershipLost(IntVideoPortResetDisplayParameters);
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+IntVideoPortInbvCleanup(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
+    // HANDLE ThreadHandle;
+
+    DeviceExtension = (PVIDEO_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    if ((DeviceExtension->DeviceOpened >= 1) &&
+        (InterlockedDecrement((PLONG)&DeviceExtension->DeviceOpened) == 0))
+    {
+        // RemoveEntryList(&DeviceExtension->HwResetListEntry);
+        InbvNotifyDisplayOwnershipLost(NULL);
+        IntVideoPortResetDisplayParametersEx(80, 50, FALSE);
+        // or InbvAcquireDisplayOwnership(); ?
+    }
+
+#if 0
+    // TODO: Find the best way to communicate the request.
+    /* Signal the INBV monitoring thread and wait for it to terminate */
+    ThreadHandle = InterlockedExchangePointer((PVOID*)&InbvThreadHandle, NULL);
+    if (ThreadHandle)
+    {
+        KeWaitForSingleObject(&ThreadHandle, Executive, KernelMode, FALSE, NULL);
+        /* Close its handle */
+        ObCloseHandle(ThreadHandle, KernelMode);
+    }
+#endif
+
+    return STATUS_SUCCESS;
+}
+
+
 NTSTATUS
 NTAPI
 IntVideoPortAddDevice(
@@ -101,36 +342,39 @@ IntVideoPortDispatchOpen(
     IN PDEVICE_OBJECT DeviceObject,
     IN PIRP Irp)
 {
+    NTSTATUS Status;
     PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
     PVIDEO_PORT_DRIVER_EXTENSION DriverExtension;
-    NTSTATUS Status;
 
     TRACE_(VIDEOPRT, "IntVideoPortDispatchOpen\n");
 
-    if (CsrssInitialized == FALSE)
+    if (!CsrProcess)
     {
         /*
          * We know the first open call will be from the CSRSS process
          * to let us know its handle.
          */
-
         INFO_(VIDEOPRT, "Referencing CSRSS\n");
-        Csrss = (PKPROCESS)PsGetCurrentProcess();
-        INFO_(VIDEOPRT, "Csrss %p\n", Csrss);
+        CsrProcess = (PKPROCESS)PsGetCurrentProcess();
+        ObReferenceObject(CsrProcess);
+        INFO_(VIDEOPRT, "CsrProcess 0x%p\n", CsrProcess);
 
         Status = IntInitializeVideoAddressSpace();
         if (!NT_SUCCESS(Status))
         {
             ERR_(VIDEOPRT, "IntInitializeVideoAddressSpace() failed: 0x%lx\n", Status);
+            ObDereferenceObject(CsrProcess);
+            CsrProcess = NULL;
             return Status;
         }
-
-        CsrssInitialized = TRUE;
     }
 
     DeviceExtension = (PVIDEO_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
     DriverExtension = DeviceExtension->DriverExtension;
 
+    // FIXME: (Re-)initialize INBV only if DeviceObject doesn't belong to a mirror driver.
+    IntVideoPortInbvInitialize();
+
     if (DriverExtension->InitializationData.HwInitialize(&DeviceExtension->MiniPortDeviceExtension))
     {
         Irp->IoStatus.Status = STATUS_SUCCESS;
@@ -161,30 +405,19 @@ IntVideoPortDispatchClose(
     IN PDEVICE_OBJECT DeviceObject,
     IN PIRP Irp)
 {
-    PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
-
     TRACE_(VIDEOPRT, "IntVideoPortDispatchClose\n");
 
-    DeviceExtension = (PVIDEO_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
-    if ((DeviceExtension->DeviceOpened >= 1) &&
-        (InterlockedDecrement((PLONG)&DeviceExtension->DeviceOpened) == 0))
-    {
-        ResetDisplayParametersDeviceExtension = NULL;
-        InbvNotifyDisplayOwnershipLost(NULL);
-        ResetDisplayParametersDeviceExtension = DeviceExtension;
-        IntVideoPortResetDisplayParameters(80, 50);
-    }
+    IntVideoPortInbvCleanup(DeviceObject);
 
     Irp->IoStatus.Status = STATUS_SUCCESS;
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
     return STATUS_SUCCESS;
 }
 
 PSTR
 IoctlName(ULONG Ioctl)
 {
-    switch(Ioctl)
+    switch (Ioctl)
     {
         case IOCTL_VIDEO_ENABLE_VDM:
             return "IOCTL_VIDEO_ENABLE_VDM"; // CTL_CODE(FILE_DEVICE_VIDEO, 0x00, METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -296,7 +529,7 @@ IoctlName(ULONG Ioctl)
             return "IOCTL_VIDEO_SET_DISPLAY_BRIGHTNESS"; // CTL_CODE(FILE_DEVICE_VIDEO, 0x127, METHOD_BUFFERED, FILE_ANY_ACCESS)
     }
 
-    return "<unknown ioctl code";
+    return "<unknown ioctl code>";
 }
 
 static
@@ -376,7 +609,7 @@ VideoPortInitWin32kCallbacks(
     /* Save the callout function globally */
     Win32kCallout = Win32kCallbacks->Callout;
 
-    /* Return reasonable values to win32k */
+    /* Return reasonable values to Win32k */
     Win32kCallbacks->bACPI = FALSE;
     Win32kCallbacks->pPhysDeviceObject = DeviceObject;
     Win32kCallbacks->DualviewFlags = 0;
@@ -465,12 +698,64 @@ IntVideoPortDispatchDeviceControl(
 
     IrpStack = IoGetCurrentIrpStackLocation(Irp);
 
+    switch (IrpStack->MajorFunction)
+    {
+        case IRP_MJ_DEVICE_CONTROL:
+            /* This is the main part of this function and is handled below */
+            break;
+
+        case IRP_MJ_SHUTDOWN:
+        {
+            /* Dereference CSRSS */
+            PKPROCESS OldCsrProcess;
+            OldCsrProcess = InterlockedExchangePointer((PVOID*)&CsrProcess, NULL);
+            if (OldCsrProcess)
+                ObDereferenceObject(OldCsrProcess);
+
+            Irp->IoStatus.Status = STATUS_SUCCESS;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_SUCCESS;
+        }
+
+        default:
+            ERR_(VIDEOPRT, "- Unknown MajorFunction 0x%x\n", IrpStack->MajorFunction);
+            Irp->IoStatus.Status = STATUS_SUCCESS;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_SUCCESS;
+    }
+
     IoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
 
-    INFO_(VIDEOPRT, "- IoControlCode: %x: %s\n", IoControlCode, IoctlName(IoControlCode));
+    INFO_(VIDEOPRT, "- IoControlCode: 0x%x: %s\n", IoControlCode, IoctlName(IoControlCode));
 
-    switch(IoControlCode)
+    switch (IoControlCode)
     {
+        case IOCTL_VIDEO_ENABLE_VDM:
+        case IOCTL_VIDEO_DISABLE_VDM:
+        case IOCTL_VIDEO_REGISTER_VDM:
+            WARN_(VIDEOPRT, "- IOCTL_VIDEO_*_VDM are UNIMPLEMENTED!\n");
+            Status = STATUS_NOT_IMPLEMENTED;
+            break;
+
+        case IOCTL_VIDEO_SET_OUTPUT_DEVICE_POWER_STATE:
+        case IOCTL_VIDEO_GET_OUTPUT_DEVICE_POWER_STATE:
+            WARN_(VIDEOPRT, "- IOCTL_VIDEO_GET/SET_OUTPUT_DEVICE_POWER_STATE are UNIMPLEMENTED!\n");
+            Status = STATUS_NOT_IMPLEMENTED;
+            break;
+
+        case IOCTL_VIDEO_SET_POWER_MANAGEMENT:
+        case IOCTL_VIDEO_GET_POWER_MANAGEMENT:
+            WARN_(VIDEOPRT, "- IOCTL_VIDEO_GET/SET_POWER_MANAGEMENT are UNIMPLEMENTED!\n");
+            Status = STATUS_NOT_IMPLEMENTED;
+            break;
+
+        case IOCTL_VIDEO_QUERY_SUPPORTED_BRIGHTNESS:
+        case IOCTL_VIDEO_QUERY_DISPLAY_BRIGHTNESS:
+        case IOCTL_VIDEO_SET_DISPLAY_BRIGHTNESS:
+            WARN_(VIDEOPRT, "- IOCTL_VIDEO_*_BRIGHTNESS are UNIMPLEMENTED!\n");
+            Status = STATUS_NOT_IMPLEMENTED;
+            break;
+
         case IOCTL_VIDEO_INIT_WIN32K_CALLBACKS:
             INFO_(VIDEOPRT, "- IOCTL_VIDEO_INIT_WIN32K_CALLBACKS\n");
             Status = VideoPortInitWin32kCallbacks(DeviceObject,
@@ -479,6 +764,11 @@ IntVideoPortDispatchDeviceControl(
                                                   &Irp->IoStatus.Information);
             break;
 
+        case IOCTL_VIDEO_IS_VGA_DEVICE:
+            WARN_(VIDEOPRT, "- IOCTL_VIDEO_IS_VGA_DEVICE is UNIMPLEMENTED!\n");
+            Status = STATUS_NOT_IMPLEMENTED;
+            break;
+
         case IOCTL_VIDEO_USE_DEVICE_IN_SESSION:
             INFO_(VIDEOPRT, "- IOCTL_VIDEO_USE_DEVICE_IN_SESSION\n");
             Status = VideoPortUseDeviceInSession(DeviceObject,
@@ -487,71 +777,35 @@ IntVideoPortDispatchDeviceControl(
                                                  &Irp->IoStatus.Information);
             break;
 
+        case IOCTL_VIDEO_PREPARE_FOR_EARECOVERY:
+            INFO_(VIDEOPRT, "- IOCTL_VIDEO_PREPARE_FOR_EARECOVERY\n");
+            /*
+             * The Win32k Watchdog Timer detected that a thread spent more time
+             * in a display driver than the allotted time its threshold specified,
+             * and thus is going to attempt to recover by switching to VGA mode.
+             * If this attempt fails, the watchdog generates bugcheck 0xEA
+             * "THREAD_STUCK_IN_DEVICE_DRIVER".
+             *
+             * Prepare the recovery by resetting the display adapters to
+             * standard VGA 80x25 text mode.
+             */
+            IntVideoPortResetDisplayParametersEx(80, 25, FALSE);
+            Status = STATUS_SUCCESS;
+            break;
+
         default:
             /* Forward to the Miniport Driver */
             Status = VideoPortForwardDeviceControl(DeviceObject, Irp);
             break;
     }
 
-    INFO_(VIDEOPRT, "- Returned status: %x\n", Irp->IoStatus.Status);
+    INFO_(VIDEOPRT, "- Returned status: 0x%x\n", Status);
 
     Irp->IoStatus.Status = Status;
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
     return Status;
 }
 
-/*
- * IntVideoPortWrite
- *
- * This is a bit of a hack. We want to take ownership of the display as late
- * as possible, just before the switch to graphics mode. Win32k knows when
- * this happens, we don't. So we need Win32k to inform us. This could be done
- * using an IOCTL, but there's no way of knowing which IOCTL codes are unused
- * in the communication between GDI driver and miniport driver. So we use
- * IRP_MJ_WRITE as the signal that win32k is ready to switch to graphics mode,
- * since we know for certain that there is no read/write activity going on
- * between GDI and miniport drivers.
- * We don't actually need the data that is passed, we just trigger on the fact
- * that an IRP_MJ_WRITE was sent.
- *
- * Run Level
- *    PASSIVE_LEVEL
- */
-NTSTATUS
-NTAPI
-IntVideoPortDispatchWrite(
-    IN PDEVICE_OBJECT DeviceObject,
-    IN PIRP Irp)
-{
-    PIO_STACK_LOCATION piosStack = IoGetCurrentIrpStackLocation(Irp);
-    PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension;
-    NTSTATUS nErrCode;
-
-    DeviceExtension = (PVIDEO_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
-
-    /*
-     * Storing the device extension pointer in a static variable is an
-     * ugly hack. Unfortunately, we need it in IntVideoPortResetDisplayParameters
-     * and InbvNotifyDisplayOwnershipLost doesn't allow us to pass a userdata
-     * parameter. On the bright side, the DISPLAY device is opened
-     * exclusively, so there can be only one device extension active at
-     * any point in time.
-     *
-     * FIXME: We should process all opened display devices in
-     * IntVideoPortResetDisplayParameters.
-     */
-    ResetDisplayParametersDeviceExtension = DeviceExtension;
-    InbvNotifyDisplayOwnershipLost(IntVideoPortResetDisplayParameters);
-
-    nErrCode = STATUS_SUCCESS;
-    Irp->IoStatus.Information = piosStack->Parameters.Write.Length;
-    Irp->IoStatus.Status = nErrCode;
-    IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
-    return nErrCode;
-}
-
 NTSTATUS
 NTAPI
 IntVideoPortPnPStartDevice(
index f83ed0f..caa50dd 100644 (file)
@@ -393,7 +393,7 @@ VideoPortInt10(
     INT10_BIOS_ARGUMENTS Int10BiosArguments;
     VP_STATUS Status;
 
-    if (!CsrssInitialized)
+    if (!CsrProcess)
     {
         return ERROR_INVALID_PARAMETER;
     }
index 812f55a..6e75d56 100644 (file)
 
 /* GLOBAL VARIABLES ***********************************************************/
 
-ULONG CsrssInitialized = FALSE;
-PKPROCESS Csrss = NULL;
+PKPROCESS CsrProcess = NULL;
 ULONG VideoPortDeviceNumber = 0;
 KMUTEX VideoPortInt10Mutex;
+RTL_STATIC_LIST_HEAD(HwResetAdaptersList);
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
@@ -406,6 +406,14 @@ IntVideoPortFindAdapter(
         goto Failure;
     }
 
+    /* If the device can be reset, insert it in the list of resettable adapters */
+    InitializeListHead(&DeviceExtension->HwResetListEntry);
+    if (DriverExtension->InitializationData.HwResetHw != NULL)
+    {
+        InsertTailList(&HwResetAdaptersList,
+                       &DeviceExtension->HwResetListEntry);
+    }
+
     /* Query children of the device. */
     VideoPortEnumerateChildren(&DeviceExtension->MiniPortDeviceExtension, NULL);
 
@@ -427,9 +435,9 @@ IntAttachToCSRSS(
     PKAPC_STATE ApcState)
 {
     *CallingProcess = (PKPROCESS)PsGetCurrentProcess();
-    if (*CallingProcess != Csrss)
+    if (*CallingProcess != CsrProcess)
     {
-        KeStackAttachProcess(Csrss, ApcState);
+        KeStackAttachProcess(CsrProcess, ApcState);
     }
 }
 
@@ -439,7 +447,7 @@ IntDetachFromCSRSS(
     PKPROCESS *CallingProcess,
     PKAPC_STATE ApcState)
 {
-    if (*CallingProcess != Csrss)
+    if (*CallingProcess != CsrProcess)
     {
         KeUnstackDetachProcess(ApcState);
     }
@@ -516,10 +524,8 @@ VideoPortInitialize(
     DriverObject->MajorFunction[IRP_MJ_CLOSE] = IntVideoPortDispatchClose;
     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
         IntVideoPortDispatchDeviceControl;
-    DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
+    DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] =
         IntVideoPortDispatchDeviceControl;
-    DriverObject->MajorFunction[IRP_MJ_WRITE] =
-        IntVideoPortDispatchWrite; // ReactOS-specific hack
     DriverObject->DriverUnload = IntVideoPortUnload;
 
     /* Determine type of the miniport driver */
@@ -608,8 +614,8 @@ VideoPortInitialize(
     DriverExtension->HwContext = HwContext;
 
     /*
-     * Plug & Play drivers registers the device in AddDevice routine. For
-     * legacy drivers we must do it now.
+     * Plug & Play drivers registers the device in AddDevice routine.
+     * For legacy drivers we must do it now.
      */
     if (LegacyDetection)
     {
@@ -617,7 +623,7 @@ VideoPortInitialize(
 
         if (HwInitializationData->HwInitDataSize != SIZE_OF_NT4_VIDEO_HW_INITIALIZATION_DATA)
         {
-            /* power management */
+            /* Power management */
             DriverObject->MajorFunction[IRP_MJ_POWER] = IntVideoPortDispatchPower;
         }
 
index 3490e56..94289dc 100644 (file)
 #include <windef.h>
 #include <wdmguid.h>
 
-#define TAG_VIDEO_PORT  'PDIV'
-#define TAG_VIDEO_PORT_BUFFER  '\0mpV'
-#define TAG_REQUEST_PACKET 'qRpV'
+/* PSEH for SEH Support */
+#include <pseh/pseh2.h>
 
-#define GUID_STRING_LENGTH 38 * sizeof(WCHAR)
+#define TAG_VIDEO_PORT          'PDIV'
+#define TAG_VIDEO_PORT_BUFFER   '\0mpV'
+#define TAG_REQUEST_PACKET      'qRpV'
+
+#define GUID_STRING_LENGTH (38 * sizeof(WCHAR))
 
 typedef struct _VIDEO_PORT_ADDRESS_MAPPING
 {
@@ -102,6 +105,7 @@ typedef struct _VIDEO_PORT_DEVICE_EXTENSTION
    AGP_BUS_INTERFACE_STANDARD AgpInterface;
    KMUTEX DeviceLock;
    LIST_ENTRY DmaAdapterList, ChildDeviceList;
+   LIST_ENTRY HwResetListEntry;
    ULONG SessionId;
    CHAR MiniPortDeviceExtension[1];
 } VIDEO_PORT_DEVICE_EXTENSION, *PVIDEO_PORT_DEVICE_EXTENSION;
@@ -204,11 +208,6 @@ IntVideoPortDispatchSystemControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp);
 
-NTSTATUS NTAPI
-IntVideoPortDispatchWrite(
-   IN PDEVICE_OBJECT DeviceObject,
-   IN PIRP Irp);
-
 VOID NTAPI
 IntVideoPortUnload(PDRIVER_OBJECT DriverObject);
 
@@ -244,10 +243,10 @@ IntVideoPortMapPhysicalMemory(
 
 /* videoprt.c */
 
-extern ULONG CsrssInitialized;
-extern PKPROCESS Csrss;
+extern PKPROCESS CsrProcess;
 extern ULONG VideoPortDeviceNumber;
 extern KMUTEX VideoPortInt10Mutex;
+extern LIST_ENTRY HwResetAdaptersList;
 
 VOID FASTCALL
 IntAttachToCSRSS(PKPROCESS *CallingProcess, PKAPC_STATE ApcState);
index 7691aad..21f63f8 100644 (file)
@@ -8,7 +8,9 @@
  */
 
 #include <win32k.h>
-DBG_DEFAULT_CHANNEL(EngDev)
+#include <ntddvdeo.h>
+
+DBG_DEFAULT_CHANNEL(EngDev);
 
 PGRAPHICS_DEVICE gpPrimaryGraphicsDevice;
 PGRAPHICS_DEVICE gpVgaGraphicsDevice;
@@ -156,6 +158,70 @@ EngpPopulateDeviceModeList(
     return TRUE;
 }
 
+extern VOID
+UserRefreshDisplay(IN PPDEVOBJ ppdev);
+
+// PVIDEO_WIN32K_CALLOUT
+VOID
+NTAPI
+VideoPortCallout(
+    _In_ PVOID Params)
+{
+/*
+ * IMPORTANT NOTICE!! On Windows XP/2003 this function triggers the creation of
+ * a specific VideoPortCalloutThread() system thread using the same mechanism
+ * as the RIT/desktop/Ghost system threads.
+ */
+
+    PVIDEO_WIN32K_CALLBACKS_PARAMS CallbackParams = (PVIDEO_WIN32K_CALLBACKS_PARAMS)Params;
+
+    TRACE("VideoPortCallout(0x%p, 0x%x)\n",
+          CallbackParams, CallbackParams ? CallbackParams->CalloutType : -1);
+
+    if (!CallbackParams)
+        return;
+
+    switch (CallbackParams->CalloutType)
+    {
+        case VideoFindAdapterCallout:
+        {
+            TRACE("VideoPortCallout: VideoFindAdapterCallout called - Param = %s\n",
+                  CallbackParams->Param ? "TRUE" : "FALSE");
+            if (CallbackParams->Param == TRUE)
+            {
+                /* Re-enable the display */
+                UserRefreshDisplay(gppdevPrimary);
+            }
+            else
+            {
+                /* Disable the display */
+                NOTHING; // Nothing to do for the moment...
+            }
+
+            CallbackParams->Status = STATUS_SUCCESS;
+            break;
+        }
+
+        case VideoPowerNotifyCallout:
+        case VideoDisplaySwitchCallout:
+        case VideoEnumChildPdoNotifyCallout:
+        case VideoWakeupCallout:
+        case VideoChangeDisplaySettingsCallout:
+        case VideoPnpNotifyCallout:
+        case VideoDxgkDisplaySwitchCallout:
+        case VideoDxgkMonitorEventCallout:
+        case VideoDxgkFindAdapterTdrCallout:
+            ERR("VideoPortCallout: CalloutType 0x%x is UNIMPLEMENTED!\n", CallbackParams->CalloutType);
+            CallbackParams->Status = STATUS_NOT_IMPLEMENTED;
+            break;
+
+        default:
+            ERR("VideoPortCallout: Unknown CalloutType 0x%x\n", CallbackParams->CalloutType);
+            CallbackParams->Status = STATUS_UNSUCCESSFUL;
+            break;
+    }
+}
+
 PGRAPHICS_DEVICE
 NTAPI
 EngpRegisterGraphicsDevice(
@@ -168,10 +234,10 @@ EngpRegisterGraphicsDevice(
     PDEVICE_OBJECT pDeviceObject;
     PFILE_OBJECT pFileObject;
     NTSTATUS Status;
+    VIDEO_WIN32K_CALLBACKS Win32kCallbacks;
+    ULONG ulReturn;
     PWSTR pwsz;
     ULONG cj;
-    SIZE_T cjWritten;
-    BOOL bEnable = TRUE;
 
     TRACE("EngpRegisterGraphicsDevice(%wZ)\n", pustrDeviceName);
 
@@ -197,13 +263,34 @@ EngpRegisterGraphicsDevice(
         return NULL;
     }
 
-    /* Enable the device */
-    EngFileWrite(pFileObject, &bEnable, sizeof(BOOL), &cjWritten);
-
     /* Copy the device and file object pointers */
     pGraphicsDevice->DeviceObject = pDeviceObject;
     pGraphicsDevice->FileObject = pFileObject;
 
+    /* Initialize and register the device with videoprt for Win32k callbacks */
+    Win32kCallbacks.PhysDisp = pGraphicsDevice;
+    Win32kCallbacks.Callout = VideoPortCallout;
+    // Reset the data being returned prior to the call.
+    Win32kCallbacks.bACPI = FALSE;
+    Win32kCallbacks.pPhysDeviceObject = NULL;
+    Win32kCallbacks.DualviewFlags = 0;
+    Status = (NTSTATUS)EngDeviceIoControl((HANDLE)pDeviceObject,
+                                          IOCTL_VIDEO_INIT_WIN32K_CALLBACKS,
+                                          &Win32kCallbacks,
+                                          sizeof(Win32kCallbacks),
+                                          &Win32kCallbacks,
+                                          sizeof(Win32kCallbacks),
+                                          &ulReturn);
+    if (Status != ERROR_SUCCESS)
+    {
+        ERR("EngDeviceIoControl(0x%p, IOCTL_VIDEO_INIT_WIN32K_CALLBACKS) failed, Status 0x%lx\n",
+            pDeviceObject, Status);
+    }
+    // TODO: Set flags according to the results.
+    // if (Win32kCallbacks.bACPI)
+    // if (Win32kCallbacks.DualviewFlags & ???)
+    // Win32kCallbacks.pPhysDeviceObject;
+
     /* Copy the device name */
     RtlStringCbCopyNW(pGraphicsDevice->szNtDeviceName,
                       sizeof(pGraphicsDevice->szNtDeviceName),
index cebaf8c..be0335c 100644 (file)
@@ -285,6 +285,45 @@ InitVideo(VOID)
     return STATUS_SUCCESS;
 }
 
+VOID
+UserRefreshDisplay(IN PPDEVOBJ ppdev)
+{
+    ULONG_PTR ulResult;
+    // PVOID pvOldCursor;
+
+    // TODO: Re-enable the cursor reset code once this function becomes called
+    // from within a Win32 thread... Indeed UserSetCursor() requires this, but
+    // at the moment this function is directly called from a separate thread
+    // from within videoprt, instead of by a separate win32k system thread.
+
+    if (!ppdev)
+        return;
+
+    PDEVOBJ_vReference(ppdev);
+
+    /* Remove mouse pointer */
+    // pvOldCursor = UserSetCursor(NULL, TRUE);
+
+    /* Do the mode switch -- Use the actual same current mode */
+    ulResult = PDEVOBJ_bSwitchMode(ppdev, ppdev->pdmwDev);
+    ASSERT(ulResult);
+
+    /* Restore mouse pointer, no hooks called */
+    // pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
+    // ASSERT(pvOldCursor == NULL);
+
+    /* Update the system metrics */
+    InitMetrics();
+
+    /* Set new size of the monitor */
+    // UserUpdateMonitorSize((HDEV)ppdev);
+
+    //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
+    UserRedrawDesktop();
+
+    PDEVOBJ_vRelease(ppdev);
+}
+
 NTSTATUS
 NTAPI
 UserEnumDisplayDevices(