/*++ Copyright (c) 1991 - 2001 Microsoft Corporation Module Name: ## # ## ### ###### #### ## ## ##### ##### #### #### ##### ##### ## ### ## ### ## ## # ## ## ## ## ## ## ## # ## # ## ## ## ## ## ### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## # # ## ## ## ## ## ####### ## ## ## ## ## ### ## ## ## ## ## ### ### ####### ## ## ## ## ## ## ## ## ## ## ## ##### ##### ### ### ## ## ## ## # ## ## ## ## ## ## ## ## ## ## # ## ## ## ## ## ## ## #### ## ## ##### ##### ##### ## #### ## ## Abstract: This module contains functions specfic to the watchdog device. The logic in this module is not hardware specific, but is logic that is common to all hardware implementations. Author: Wesley Witt (wesw) 1-Oct-2001 Environment: Kernel mode only. Notes: --*/ #include "internal.h" NTSTATUS SaWatchdogDeviceInitialization( IN PSAPORT_DRIVER_EXTENSION DriverExtension ) /*++ Routine Description: This is the NVRAM specific code for driver initialization. This function is called by SaPortInitialize, which is called by the NVRAM driver's DriverEntry function. Arguments: DriverExtension - Driver extension structure Return Value: NT status code. --*/ { UNREFERENCED_PARAMETER(DriverExtension); return STATUS_SUCCESS; } NTSTATUS SaWatchdogIoValidation( IN PWATCHDOG_DEVICE_EXTENSION DeviceExtension, IN PIRP Irp, PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This is the NVRAM specific code for processing all I/O validation for reads and writes. Arguments: DeviceExtension - NVRAM device extension Irp - Pointer to an IRP structure that describes the requested I/O operation. IrpSp - Irp stack pointer Return Value: NT status code. --*/ { UNREFERENCED_PARAMETER(DeviceExtension); UNREFERENCED_PARAMETER(Irp); UNREFERENCED_PARAMETER(IrpSp); return STATUS_NOT_SUPPORTED; } NTSTATUS SaWatchdogShutdownNotification( IN PWATCHDOG_DEVICE_EXTENSION DeviceExtension, IN PIRP Irp, PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This is the NVRAM specific code for processing the system shutdown notification. Arguments: DeviceExtension - NVRAM device extension Irp - Pointer to an IRP structure that describes the requested I/O operation. IrpSp - Irp stack pointer Return Value: NT status code. --*/ { UNREFERENCED_PARAMETER(DeviceExtension); UNREFERENCED_PARAMETER(Irp); UNREFERENCED_PARAMETER(IrpSp); return STATUS_SUCCESS; } VOID WatchdogProcessPingThread( IN PVOID StartContext ) /*++ Routine Description: This function runs as a system thread and serves to ping the watchdog hardware while the system boots. Arguments: StartContext - Context pointer; device extension Return Value: None. --*/ { PWATCHDOG_DEVICE_EXTENSION DeviceExtension = (PWATCHDOG_DEVICE_EXTENSION) StartContext; NTSTATUS Status; LARGE_INTEGER DueTime; UNICODE_STRING UnicodeString; PFILE_OBJECT DisplayFileObject = NULL; PDEVICE_OBJECT DisplayDeviceObject = NULL; BOOLEAN BusyMessageDisplayed = FALSE; ULONG TimerValue = WATCHDOG_TIMER_VALUE; // // Set the timer resolution // Status = CallMiniPortDriverDeviceControl( DeviceExtension, DeviceExtension->DeviceObject, IOCTL_SAWD_SET_TIMER, &TimerValue, sizeof(ULONG), NULL, 0 ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( DeviceExtension->DeviceType, "Failed to ping the watchdog\n", Status ); } // // Ping loop // while (1) { // // Get a pointer to the display device // if (DisplayDeviceObject == NULL) { RtlInitUnicodeString( &UnicodeString, SA_DEVICE_DISPLAY_NAME_STRING ); Status = IoGetDeviceObjectPointer( &UnicodeString, FILE_ALL_ACCESS, &DisplayFileObject, &DisplayDeviceObject ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( DeviceExtension->DeviceType, "IoGetDeviceObjectPointer failed", Status ); } } // // Display the busy message if necessary // if (DisplayDeviceObject && BusyMessageDisplayed == FALSE) { Status = CallMiniPortDriverDeviceControl( DeviceExtension, DisplayDeviceObject, IOCTL_SADISPLAY_BUSY_MESSAGE, NULL, 0, NULL, 0 ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( DeviceExtension->DeviceType, "Failed to display the busy message", Status ); } else { BusyMessageDisplayed = TRUE; ObDereferenceObject( DisplayFileObject ); } } // // Call the watchdog driver so that the hardware is pinged // and prevent the system from rebooting // Status = CallMiniPortDriverDeviceControl( DeviceExtension, DeviceExtension->DeviceObject, IOCTL_SAWD_PING, NULL, 0, NULL, 0 ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( DeviceExtension->DeviceType, "Failed to ping the watchdog\n", Status ); } // // Wait... // DueTime.QuadPart = -SecToNano(WATCHDOG_PING_SECONDS); Status = KeWaitForSingleObject( &DeviceExtension->PingEvent, Executive, KernelMode, FALSE, &DueTime ); if (Status != STATUS_TIMEOUT) { // // The ping event was triggered // // // Call the watchdog driver so that the hardware is pinged // and prevent the system from rebooting // Status = CallMiniPortDriverDeviceControl( DeviceExtension, DeviceExtension->DeviceObject, IOCTL_SAWD_PING, NULL, 0, NULL, 0 ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( DeviceExtension->DeviceType, "Failed to ping the watchdog\n", Status ); } return; } else { // // We timed out // ExAcquireFastMutex( &DeviceExtension->DeviceLock ); if (DeviceExtension->ActiveProcessCount == 0) { KeQuerySystemTime( &DueTime ); DueTime.QuadPart = NanoToSec( DueTime.QuadPart - DeviceExtension->LastProcessTime.QuadPart ); if (DueTime.QuadPart > WATCHDOG_INIT_SECONDS) { ExReleaseFastMutex( &DeviceExtension->DeviceLock ); return; } } ExReleaseFastMutex( &DeviceExtension->DeviceLock ); } } } VOID WatchdogProcessWatchThread( IN PVOID StartContext ) /*++ Routine Description: This function runs as a system thread and the sole purpose is to wait for a list of processes to terminate. Arguments: StartContext - Context pointer; device extension Return Value: None. --*/ { PWATCHDOG_PROCESS_WATCH ProcessWatch = (PWATCHDOG_PROCESS_WATCH) StartContext; PWATCHDOG_DEVICE_EXTENSION DeviceExtension = ProcessWatch->DeviceExtension; NTSTATUS Status; OBJECT_ATTRIBUTES Obja; CLIENT_ID ClientId; HANDLE ProcessHandle = NULL; LARGE_INTEGER CurrentTime; __try { InitializeObjectAttributes( &Obja, NULL, 0, NULL, NULL ); ClientId.UniqueThread = NULL; ClientId.UniqueProcess = ProcessWatch->ProcessId; Status = ZwOpenProcess( &ProcessHandle, PROCESS_ALL_ACCESS, &Obja, &ClientId ); if (!NT_SUCCESS(Status)) { ERROR_RETURN( DeviceExtension->DeviceType, "ZwOpenProcess failed", Status ); } // // Wait for the process to complete // Status = ZwWaitForSingleObject( ProcessHandle, FALSE, NULL ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( DeviceExtension->DeviceType, "KeWaitForSingleObject failed", Status ); } // // The process terminated // ExAcquireFastMutex( &DeviceExtension->DeviceLock ); DeviceExtension->ActiveProcessCount -= 1; if (DeviceExtension->ActiveProcessCount == 0) { KeQuerySystemTime( &CurrentTime ); CurrentTime.QuadPart = CurrentTime.QuadPart - DeviceExtension->LastProcessTime.QuadPart; if (NanoToSec(CurrentTime.QuadPart) > WATCHDOG_INIT_SECONDS) { KeSetEvent( &DeviceExtension->PingEvent, 0, FALSE ); } } ExReleaseFastMutex( &DeviceExtension->DeviceLock ); } __finally { if (ProcessHandle != NULL) { ZwClose( ProcessHandle ); } ExFreePool( ProcessWatch ); } } VOID WatchdogInitializeThread( IN PVOID StartContext ) /*++ Routine Description: This function runs as a system thread and serves to look for a list of processes running on the system. Arguments: StartContext - Context pointer; device extension Return Value: None. --*/ { PWATCHDOG_DEVICE_EXTENSION DeviceExtension = (PWATCHDOG_DEVICE_EXTENSION) StartContext; NTSTATUS Status; ULONG BufferSize; PUCHAR Buffer = NULL; PSYSTEM_PROCESS_INFORMATION ProcessInfo; ULONG TotalOffset; ULONG TaskBufferSize = 0; PKEY_VALUE_FULL_INFORMATION KeyInformation = NULL; PUCHAR p; ULONG TaskCount = 0; OBJECT_ATTRIBUTES Obja; HANDLE ThreadHandle; LARGE_INTEGER DueTime; UNICODE_STRING ProcessName; PWATCHDOG_PROCESS_WATCH ProcessWatch; PHANDLE Tasks = NULL; __try { // // Read the task names from the registry // Status = ReadRegistryValue( DeviceExtension->DriverExtension, &DeviceExtension->DriverExtension->RegistryPath, L"ExceptionTasks", &KeyInformation ); if (!NT_SUCCESS(Status)) { ERROR_RETURN( DeviceExtension->DeviceType, "ReadRegistryValue failed", Status ); } if (KeyInformation->Type != REG_MULTI_SZ) { Status = STATUS_OBJECT_TYPE_MISMATCH; ERROR_RETURN( DeviceExtension->DeviceType, "ExceptionTasks value is corrupt", Status ); } // // Count the number of tasks // p = (PUCHAR)((PUCHAR)KeyInformation + KeyInformation->DataOffset); while (*p) { p += (STRING_SZ(p) + sizeof(WCHAR)); TaskCount += 1; } if (TaskCount == 0) { Status = STATUS_NO_MORE_ENTRIES; ERROR_RETURN( DeviceExtension->DeviceType, "No tasks specified in the ExceptionTasks registry value", Status ); } // // Allocate an array to hold the process handles // Tasks = (PHANDLE) ExAllocatePool( NonPagedPool, (TaskCount + 1) * sizeof(HANDLE) ); if (Tasks == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; ERROR_RETURN( DeviceExtension->DeviceType, "Failed to allocate pool for task array buffer", Status ); } while (1) { // // Query the system for the number of tasks that are running // Status = ZwQuerySystemInformation( SystemProcessInformation, NULL, 0, &BufferSize ); if (Status != STATUS_INFO_LENGTH_MISMATCH) { ERROR_RETURN( DeviceExtension->DeviceType, "ZwQuerySystemInformation failed", Status ); } // // Allocate the pool to hold that process information // Buffer = (PUCHAR) ExAllocatePool( NonPagedPool, BufferSize + 2048 ); if (Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; ERROR_RETURN( DeviceExtension->DeviceType, "Failed to allocate pool for system information buffer", Status ); } // // Get the task list from the system // Status = ZwQuerySystemInformation( SystemProcessInformation, Buffer, BufferSize, NULL ); if (!NT_SUCCESS(Status)) { ERROR_RETURN( DeviceExtension->DeviceType, "ZwQuerySystemInformation failed", Status ); } // // Loop over each running process and check it // against the exception process list. If the process // is specified as an exception process then open a handle // to the process. // TaskCount = 0; p = (PUCHAR)((PUCHAR)KeyInformation + KeyInformation->DataOffset); // // Walk the list of processes in the registry value // while (*p) { // // Loop initialization // ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) Buffer; TotalOffset = 0; RtlInitUnicodeString( &ProcessName, (PWSTR)p ); // // Walk the processes in the system process list // and try to match each with the selected process // from the registry. // while (1) { // // Only valid process names // if (ProcessInfo->ImageName.Buffer) { // // Compare the process names // if (RtlCompareUnicodeString( &ProcessInfo->ImageName, &ProcessName, TRUE ) == 0) { // // Check to see if we've already seen the process in // a previous loop thru thr process list // if (Tasks[TaskCount] != ProcessInfo->UniqueProcessId) { // // The process matches and is new so set things up so // that we start watching the process to end // Tasks[TaskCount] = ProcessInfo->UniqueProcessId; ProcessWatch = (PWATCHDOG_PROCESS_WATCH) ExAllocatePool( NonPagedPool, sizeof(WATCHDOG_PROCESS_WATCH) ); if (ProcessWatch == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; ERROR_RETURN( DeviceExtension->DeviceType, "Failed to allocate pool for process watch structure", Status ); } // // Start the ping thread iff this is the first being watched // ExAcquireFastMutex( &DeviceExtension->DeviceLock ); if (DeviceExtension->ActiveProcessCount == 0) { InitializeObjectAttributes( &Obja, NULL, OBJ_KERNEL_HANDLE, NULL, NULL ); Status = PsCreateSystemThread( &ThreadHandle, 0, &Obja, 0, NULL, WatchdogProcessPingThread, DeviceExtension ); if (!NT_SUCCESS(Status)) { ExReleaseFastMutex( &DeviceExtension->DeviceLock ); ERROR_RETURN( DeviceExtension->DeviceType, "PsCreateSystemThread failed", Status ); } ZwClose( ThreadHandle ); } KeQuerySystemTime( &DeviceExtension->LastProcessTime ); DeviceExtension->ActiveProcessCount += 1; ExReleaseFastMutex( &DeviceExtension->DeviceLock ); // // Start a thread to watch this process // ProcessWatch->DeviceExtension = DeviceExtension; ProcessWatch->ProcessId = ProcessInfo->UniqueProcessId; InitializeObjectAttributes( &Obja, NULL, OBJ_KERNEL_HANDLE, NULL, NULL ); Status = PsCreateSystemThread( &ThreadHandle, 0, &Obja, 0, NULL, WatchdogProcessWatchThread, ProcessWatch ); if (!NT_SUCCESS(Status)) { ERROR_RETURN( DeviceExtension->DeviceType, "PsCreateSystemThread failed", Status ); } ZwClose( ThreadHandle ); } } } // // Loop to the next process in the system process list // if (ProcessInfo->NextEntryOffset == 0) { break; } TotalOffset += ProcessInfo->NextEntryOffset; ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &Buffer[TotalOffset]; } // // Loop to the next process in the registry list // p += (STRING_SZ(p) + sizeof(WCHAR)); TaskCount += 1; } // // Clean up all resources allocated in this loop // ExFreePool( Buffer ); Buffer = NULL; // // Delay execution before looping again // DueTime.QuadPart = -SecToNano(WATCHDOG_INIT_SECONDS); Status = KeWaitForSingleObject( &DeviceExtension->StopEvent, Executive, KernelMode, FALSE, &DueTime ); if (Status != STATUS_TIMEOUT) { __leave; } } } __finally { if (KeyInformation) { ExFreePool( KeyInformation ); } if (Buffer) { ExFreePool( Buffer ); } if (Tasks) { ExFreePool( Tasks ); } } } ULONG IsTextModeSetupRunning( IN PWATCHDOG_DEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: This function checks to see if we are running in text mode setup. Arguments: DeviceExtension - NVRAM device extension Return Value: NT status code. --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; UNICODE_STRING UnicodeString; HANDLE SetupKey = NULL; ULONG TextModeSetupInProgress = 0; // // Check to see if we're running in GUI mode setup // __try { RtlInitUnicodeString( &UnicodeString, L"\\Registry\\Machine\\System\\ControlSet001\\Services\\setupdd" ); InitializeObjectAttributes( &Obja, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = ZwOpenKey( &SetupKey, KEY_READ, &Obja ); if (NT_SUCCESS(Status)) { TextModeSetupInProgress = 1; } else { ERROR_RETURN( DeviceExtension->DeviceType, "ZwOpenKey failed", Status ); } } __finally { if (SetupKey) { ZwClose( SetupKey ); } } return TextModeSetupInProgress; } ULONG IsGuiModeSetupRunning( IN PWATCHDOG_DEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: This function checks to see if we are running in GUI mode setup. Arguments: DeviceExtension - NVRAM device extension Return Value: NT status code. --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; UNICODE_STRING UnicodeString; HANDLE SetupKey = NULL; UCHAR KeyInformationBuffer[sizeof(KEY_VALUE_FULL_INFORMATION)+64]; PKEY_VALUE_FULL_INFORMATION KeyInformation = (PKEY_VALUE_FULL_INFORMATION) KeyInformationBuffer; ULONG KeyValueLength; ULONG SystemSetupInProgress = 0; // // Check to see if we're running in GUI mode setup // __try { RtlInitUnicodeString( &UnicodeString, L"\\Registry\\Machine\\System\\Setup" ); InitializeObjectAttributes( &Obja, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = ZwOpenKey( &SetupKey, KEY_READ, &Obja ); if (!NT_SUCCESS(Status)) { ERROR_RETURN( DeviceExtension->DeviceType, "ZwOpenKey failed", Status ); } RtlInitUnicodeString( &UnicodeString, L"SystemSetupInProgress" ); Status = ZwQueryValueKey( SetupKey, &UnicodeString, KeyValueFullInformation, KeyInformation, sizeof(KeyInformationBuffer), &KeyValueLength ); if (!NT_SUCCESS(Status)) { ERROR_RETURN( DeviceExtension->DeviceType, "ZwQueryValueKey failed", Status ); } if (KeyInformation->Type != REG_DWORD) { Status = STATUS_OBJECT_TYPE_MISMATCH; ERROR_RETURN( DeviceExtension->DeviceType, "SystemSetupInProgress value is corrupt", Status ); } SystemSetupInProgress = *(PULONG)((PUCHAR)KeyInformation + KeyInformation->DataOffset); } __finally { if (SetupKey) { ZwClose( SetupKey ); } } return SystemSetupInProgress; } NTSTATUS SaWatchdogStartDevice( IN PWATCHDOG_DEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: This is the NVRAM specific code for processing the PNP start device request. Arguments: DeviceExtension - NVRAM device extension Return Value: NT status code. --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; HANDLE ThreadHandle; ULONG SetupInProgress = 0; // // Setup the device extension fields // ExInitializeFastMutex( &DeviceExtension->DeviceLock ); KeInitializeEvent( &DeviceExtension->PingEvent, SynchronizationEvent, FALSE ); KeInitializeEvent( &DeviceExtension->StopEvent, SynchronizationEvent, FALSE ); // // Check to see if we're running in setup // if (IsTextModeSetupRunning( DeviceExtension ) || IsGuiModeSetupRunning( DeviceExtension )) { SetupInProgress = 1; } if (SetupInProgress != 0) { // // Start the ping thread so that setup is not terminated // DeviceExtension->ActiveProcessCount += 1; InitializeObjectAttributes( &Obja, NULL, OBJ_KERNEL_HANDLE, NULL, NULL ); Status = PsCreateSystemThread( &ThreadHandle, 0, &Obja, 0, NULL, WatchdogProcessPingThread, DeviceExtension ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( DeviceExtension->DeviceType, "PsCreateSystemThread failed", Status ); } else { ZwClose( ThreadHandle ); } return STATUS_SUCCESS; } // // Start the delay boot initialization thread // InitializeObjectAttributes( &Obja, NULL, OBJ_KERNEL_HANDLE, NULL, NULL ); Status = PsCreateSystemThread( &ThreadHandle, 0, &Obja, 0, NULL, WatchdogInitializeThread, DeviceExtension ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( DeviceExtension->DeviceType, "PsCreateSystemThread failed", Status ); } else { ZwClose( ThreadHandle ); } return STATUS_SUCCESS; } DECLARE_IOCTL_HANDLER( HandleWdDisable ) /*++ Routine Description: This routine allows the watchdog timer to be started or stopped. Arguments: DeviceObject - The device object for the target device. Irp - Pointer to an IRP structure that describes the requested I/O operation. DeviceExtension - Pointer to the main port driver device extension. InputBuffer - Pointer to the user's input buffer InputBufferLength - Length in bytes of the input buffer OutputBuffer - Pointer to the user's output buffer OutputBufferLength - Length in bytes of the output buffer Return Value: NT status code. --*/ { if (InputBufferLength != sizeof(ULONG)) { REPORT_ERROR( DeviceExtension->DeviceType, "Input buffer != sizeof(ULONG)", STATUS_INVALID_BUFFER_SIZE ); return CompleteRequest( Irp, STATUS_INVALID_BUFFER_SIZE, 0 ); } return DO_DEFAULT(); } DECLARE_IOCTL_HANDLER( HandleWdQueryExpireBehavior ) /*++ Routine Description: This routine queries the watchdog expiry behavior Arguments: DeviceObject - The device object for the target device. Irp - Pointer to an IRP structure that describes the requested I/O operation. DeviceExtension - Pointer to the main port driver device extension. InputBuffer - Pointer to the user's input buffer InputBufferLength - Length in bytes of the input buffer OutputBuffer - Pointer to the user's output buffer OutputBufferLength - Length in bytes of the output buffer Return Value: NT status code. --*/ { if (OutputBufferLength != sizeof(ULONG)) { REPORT_ERROR( DeviceExtension->DeviceType, "output buffer != sizeof(ULONG)", STATUS_INVALID_BUFFER_SIZE ); return CompleteRequest( Irp, STATUS_INVALID_BUFFER_SIZE, 0 ); } return DO_DEFAULT(); } DECLARE_IOCTL_HANDLER( HandleWdSetExpireBehavior ) /*++ Routine Description: This routine set/changes the watchdog expiry behavior Arguments: DeviceObject - The device object for the target device. Irp - Pointer to an IRP structure that describes the requested I/O operation. DeviceExtension - Pointer to the main port driver device extension. InputBuffer - Pointer to the user's input buffer InputBufferLength - Length in bytes of the input buffer OutputBuffer - Pointer to the user's output buffer OutputBufferLength - Length in bytes of the output buffer Return Value: NT status code. --*/ { if (InputBufferLength != sizeof(ULONG)) { REPORT_ERROR( DeviceExtension->DeviceType, "Input buffer != sizeof(ULONG)", STATUS_INVALID_BUFFER_SIZE ); return CompleteRequest( Irp, STATUS_INVALID_BUFFER_SIZE, 0 ); } return DO_DEFAULT(); } DECLARE_IOCTL_HANDLER( HandleWdPing ) /*++ Routine Description: This routine pings the watchdog timer to prevent the timer from expiring and restarting the system. Arguments: DeviceObject - The device object for the target device. Irp - Pointer to an IRP structure that describes the requested I/O operation. DeviceExtension - Pointer to the main port driver device extension. InputBuffer - Pointer to the user's input buffer InputBufferLength - Length in bytes of the input buffer OutputBuffer - Pointer to the user's output buffer OutputBufferLength - Length in bytes of the output buffer Return Value: NT status code. --*/ { if (!IS_IRP_INTERNAL( Irp )) { KeSetEvent( &((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->StopEvent, 0, FALSE ); KeSetEvent( &((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->PingEvent, 0, FALSE ); } return DO_DEFAULT(); } DECLARE_IOCTL_HANDLER( HandleWdDelayBoot ) /*++ Routine Description: This routine pings the watchdog timer to prevent the timer from expiring and restarting the system. Arguments: DeviceObject - The device object for the target device. Irp - Pointer to an IRP structure that describes the requested I/O operation. DeviceExtension - Pointer to the main port driver device extension. InputBuffer - Pointer to the user's input buffer InputBufferLength - Length in bytes of the input buffer OutputBuffer - Pointer to the user's output buffer OutputBufferLength - Length in bytes of the output buffer Return Value: NT status code. --*/ { NTSTATUS Status; LARGE_INTEGER CurrentTime; OBJECT_ATTRIBUTES Obja; HANDLE ThreadHandle; if (InputBufferLength != sizeof(ULONG)) { REPORT_ERROR( ((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->DeviceType, "Input buffer length != sizeof(ULONG)", STATUS_INVALID_BUFFER_SIZE ); return CompleteRequest( Irp, STATUS_INVALID_BUFFER_SIZE, 0 ); } ExAcquireFastMutex( &((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->DeviceLock ); switch (*((PULONG)InputBuffer)) { case 0: // // Disable the delay boot, meaning that the system should continue booting // ((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->ActiveProcessCount -= 1; if (((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->ActiveProcessCount == 0) { KeQuerySystemTime( &CurrentTime ); CurrentTime.QuadPart = CurrentTime.QuadPart - ((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->LastProcessTime.QuadPart; if (NanoToSec(CurrentTime.QuadPart) > WATCHDOG_INIT_SECONDS) { KeSetEvent( &((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->PingEvent, 0, FALSE ); } } break; case 1: // // Enable the delay boot, meaning that the system will delay until this driver is finished // if (((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->ActiveProcessCount == 0) { InitializeObjectAttributes( &Obja, NULL, OBJ_KERNEL_HANDLE, NULL, NULL ); Status = PsCreateSystemThread( &ThreadHandle, 0, &Obja, 0, NULL, WatchdogProcessPingThread, DeviceExtension ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( ((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->DeviceType, "PsCreateSystemThread failed", Status ); } else { ZwClose( ThreadHandle ); } } ((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->ActiveProcessCount += 1; break; default: break; } ExReleaseFastMutex( &((PWATCHDOG_DEVICE_EXTENSION)DeviceExtension)->DeviceLock ); return DO_DEFAULT(); } DECLARE_IOCTL_HANDLER( HandleWdQueryTimer ) /*++ Routine Description: This routine queries the watchdog timer value. The timer counts down from a BIOS set value to zero. When the timer reaches zero the BIOS assumes that the system is non-responsive and the system is either restarted or shutdown. Arguments: DeviceObject - The device object for the target device. Irp - Pointer to an IRP structure that describes the requested I/O operation. DeviceExtension - Pointer to the main port driver device extension. InputBuffer - Pointer to the user's input buffer InputBufferLength - Length in bytes of the input buffer OutputBuffer - Pointer to the user's output buffer OutputBufferLength - Length in bytes of the output buffer Return Value: NT status code. --*/ { if (OutputBufferLength != sizeof(ULONG)) { REPORT_ERROR( DeviceExtension->DeviceType, "Output buffer != sizeof(ULONG)", STATUS_INVALID_BUFFER_SIZE ); return CompleteRequest( Irp, STATUS_INVALID_BUFFER_SIZE, 0 ); } return DO_DEFAULT(); } DECLARE_IOCTL_HANDLER( HandleWdSetTimer ) /*++ Routine Description: This routine sets/changes the watchdog timer value. The timer counts down from a BIOS set value to zero. When the timer reaches zero the BIOS assumes that the system is non-responsive and the system is either restarted or shutdown. Arguments: DeviceObject - The device object for the target device. Irp - Pointer to an IRP structure that describes the requested I/O operation. DeviceExtension - Pointer to the main port driver device extension. InputBuffer - Pointer to the user's input buffer InputBufferLength - Length in bytes of the input buffer OutputBuffer - Pointer to the user's output buffer OutputBufferLength - Length in bytes of the output buffer Return Value: NT status code. --*/ { if (InputBufferLength != sizeof(ULONG)) { REPORT_ERROR( DeviceExtension->DeviceType, "Input buffer != sizeof(ULONG)", STATUS_INVALID_BUFFER_SIZE ); return CompleteRequest( Irp, STATUS_INVALID_BUFFER_SIZE, 0 ); } return DO_DEFAULT(); }