612 lines
14 KiB
C
612 lines
14 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1989-1993 Microsoft Corporation
|
||
|
Copyright (c) 1994 International Business Machines Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
sysevent.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module implements the system event related code.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Kernel mode
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
#include "exp.h"
|
||
|
|
||
|
//
|
||
|
// Define the type for entries placed on the system events queue.
|
||
|
//
|
||
|
|
||
|
typedef struct _SYSTEMEVENT_PACKET {
|
||
|
LIST_ENTRY ListEntry;
|
||
|
ULONG EventID;
|
||
|
ULONG EventDataLength;
|
||
|
UCHAR EventData[1];
|
||
|
} SYSTEMEVENT_PACKET , *PSYSTEMEVENT_PACKET;
|
||
|
|
||
|
//
|
||
|
// Define the type for entries placed on the system events Dpc.
|
||
|
//
|
||
|
|
||
|
typedef struct _EXP_SYSTEM_EVENT_CONTEXT {
|
||
|
KDPC SysEventDpc;
|
||
|
KTIMER SysEventTimer;
|
||
|
} EXP_SYSTEM_EVENT_CONTEXT , *PEXP_SYSTEM_EVENT_CONTEXT;
|
||
|
|
||
|
//
|
||
|
// Define a global varibles used by the error logging code.
|
||
|
//
|
||
|
|
||
|
#ifdef _PNP_POWER_
|
||
|
|
||
|
KSPIN_LOCK ExpSysEventDataLock;
|
||
|
LIST_ENTRY ExpSysEventQueueHead;
|
||
|
WORK_QUEUE_ITEM ExpSysEventWorkItem;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
VOID
|
||
|
ExpSysEventDpc(
|
||
|
IN struct _KDPC *Dpc,
|
||
|
IN PVOID DeferredContext,
|
||
|
IN PVOID SystemArgument1,
|
||
|
IN PVOID SystemArgument2
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
ExpQueueSysEventRequest(
|
||
|
VOID
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
ExpSysEventThread(
|
||
|
IN PVOID StartContext
|
||
|
);
|
||
|
|
||
|
NTSTATUS
|
||
|
ExpSysEventPipeConnect(
|
||
|
PHANDLE Handle
|
||
|
);
|
||
|
|
||
|
#ifdef _PNP_POWER_
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text(INIT, ExpSysEventInitialization)
|
||
|
#pragma alloc_text(PAGE, ExpQueueSysEventRequest)
|
||
|
#pragma alloc_text(PAGE, ExpSysEventThread)
|
||
|
#pragma alloc_text(PAGE, ExpSysEventPipeConnect)
|
||
|
#endif
|
||
|
|
||
|
BOOLEAN
|
||
|
ExpSysEventInitialization(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function initalize the SysEvent related code.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
NONE.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
A value of TRUE is returned if the system event object is
|
||
|
successfully initialized. Otherwise a value of FALSE is returned.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Initialize SysEvent spin lock.
|
||
|
//
|
||
|
KeInitializeSpinLock( &ExpSysEventDataLock );
|
||
|
|
||
|
// Initialize SysEvent queue.
|
||
|
InitializeListHead( &ExpSysEventQueueHead );
|
||
|
|
||
|
// Initialize work item entry
|
||
|
ExInitializeWorkItem( &ExpSysEventWorkItem, ExpSysEventThread, NULL );
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ExPostSystemEvent(
|
||
|
IN SYSTEM_EVENT_ID EventID,
|
||
|
IN PVOID EventData OPTIONAL,
|
||
|
IN ULONG EventDataLength
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This API queues a free form data structure to the session manager . It is
|
||
|
used to notify the session manager of various SysEvents.
|
||
|
Actually rePipe the SysEvent by signalling events, enqueing
|
||
|
APCs, and so forth.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
EventID - Supplies the ID of the event.
|
||
|
|
||
|
EventDataLength - Supplies the length of the record that is to
|
||
|
send the event information.
|
||
|
|
||
|
EventData - Supplies a pointer to a record that is to send the
|
||
|
event information.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
PSYSTEMEVENT_PACKET systemevent;
|
||
|
PLIST_ENTRY listEntry;
|
||
|
|
||
|
#ifndef _PNP_POWER
|
||
|
|
||
|
return ;
|
||
|
|
||
|
#else
|
||
|
|
||
|
//
|
||
|
// Begin by attempting to allocate storage for the SysEvent packet. If
|
||
|
// one cannot be allocated, ignore this and simply return .
|
||
|
//
|
||
|
|
||
|
|
||
|
// NOTE: Even if no SysEvent object created , any system event should
|
||
|
// have been remembered . When boot up , some device drivers or HAL would
|
||
|
// rePipe LID state and/or card insertion event.
|
||
|
|
||
|
systemevent = ExAllocatePoolWithTag( NonPagedPool,
|
||
|
FIELD_OFFSET(SYSTEMEVENT_PACKET, EventData ) +
|
||
|
EventDataLength ,
|
||
|
'EsyS' );
|
||
|
|
||
|
if( !systemevent ) {
|
||
|
// No system resource is found . Ignore this System Event
|
||
|
return ;
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the systemevent packet and insert it onto the tail of the list.
|
||
|
// Note that this is done because some drivers have dependencies on LIFO
|
||
|
// notification ordering.
|
||
|
//
|
||
|
|
||
|
systemevent->EventID = EventID;
|
||
|
systemevent->EventDataLength = EventDataLength;
|
||
|
|
||
|
if (ARGUMENT_PRESENT(EventData)) {
|
||
|
|
||
|
// copy the event data from EventData to buffer .
|
||
|
|
||
|
RtlCopyMemory( systemevent->EventData,
|
||
|
EventData,
|
||
|
EventDataLength );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Insert this event into the system event queue
|
||
|
//
|
||
|
|
||
|
listEntry = ExInterlockedInsertTailList (&ExpSysEventQueueHead,
|
||
|
&systemevent->ListEntry,
|
||
|
&ExpSysEventDataLock );
|
||
|
|
||
|
//
|
||
|
// If this was the first item added, then request work item
|
||
|
//
|
||
|
|
||
|
if (!listEntry) {
|
||
|
ExQueueWorkItem( &ExpSysEventWorkItem, DelayedWorkQueue );
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#ifdef _PNP_POWER_
|
||
|
|
||
|
VOID
|
||
|
ExpSysEventThread (
|
||
|
IN PVOID StartContext
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This is the main loop for the system event rePipe thread which executes in the
|
||
|
system process context. This routine is started when the system is
|
||
|
initialized.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
StartContext - Startup context; not used.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
NTSTATUS status;
|
||
|
IO_STATUS_BLOCK iosb;
|
||
|
PSYSTEMEVENT_PACKET systemevent;
|
||
|
PLIST_ENTRY listEntry, listEntry2;
|
||
|
ULONG writeLength;
|
||
|
HANDLE Handle;
|
||
|
|
||
|
UNREFERENCED_PARAMETER( StartContext );
|
||
|
|
||
|
//
|
||
|
// Check to see whether a connection has been made to the error log
|
||
|
// Pipe. If the Pipe is not connected return.
|
||
|
//
|
||
|
|
||
|
if (!NT_SUCCESS(ExpSysEventPipeConnect(&Handle))) {
|
||
|
|
||
|
KdPrint(( "EX: ExpSysEventPipeConnect is Failed\n" ));
|
||
|
//
|
||
|
// The Pipe could not be connected.
|
||
|
// Set a timer up for another attempt later.
|
||
|
//
|
||
|
|
||
|
ExpQueueSysEventRequest();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (;;) {
|
||
|
|
||
|
// Loop dequeueing packets from the queue head and attempt to
|
||
|
// write the named pipe file.
|
||
|
//
|
||
|
// If the write works, continue looping until there are no more packets.
|
||
|
// Otherwise, indicate that the connection has been broken, cleanup,
|
||
|
// place the packet back onto the head of the queue, and start from the
|
||
|
// top of the loop again.
|
||
|
//
|
||
|
|
||
|
listEntry = ExpSysEventQueueHead.Flink;
|
||
|
if (listEntry == &ExpSysEventQueueHead) {
|
||
|
|
||
|
//
|
||
|
// Indicate that no more work will be done.
|
||
|
//
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
systemevent = CONTAINING_RECORD( listEntry,
|
||
|
SYSTEMEVENT_PACKET,
|
||
|
ListEntry );
|
||
|
|
||
|
writeLength = FIELD_OFFSET(SYSTEMEVENT_PACKET, EventData ) -
|
||
|
FIELD_OFFSET(SYSTEMEVENT_PACKET, EventID ) +
|
||
|
systemevent->EventDataLength;
|
||
|
|
||
|
status = NtWriteFile( Handle,
|
||
|
NULL, // Event
|
||
|
NULL, // ApcRoutine
|
||
|
NULL, // ApcContext
|
||
|
&iosb,
|
||
|
&(systemevent->EventID),
|
||
|
writeLength,
|
||
|
NULL, // ByteOffset
|
||
|
NULL // Key
|
||
|
) ;
|
||
|
|
||
|
if (!NT_SUCCESS( status )) {
|
||
|
|
||
|
//
|
||
|
// The send failed. Set a timer up for another attempt later.
|
||
|
//
|
||
|
|
||
|
ExpQueueSysEventRequest();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Event sent. Remove it from the queue.
|
||
|
//
|
||
|
|
||
|
listEntry2 = ExInterlockedRemoveHeadList (&ExpSysEventQueueHead,
|
||
|
&ExpSysEventDataLock );
|
||
|
ASSERT (listEntry2 == listEntry);
|
||
|
ExFreePool( systemevent );
|
||
|
}
|
||
|
|
||
|
NtClose (Handle);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
ExpSysEventPipeConnect (
|
||
|
PHANDLE Handle
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine attempts to connect to the SysEvents pipe . If the connection
|
||
|
was made successfully and the pipe allows suficiently large messages, then
|
||
|
the SysEvents pipe to the namedpipe handle, SysEventPipeConnected is set to
|
||
|
TRUE and TRUE is retuned. Otherwise a timer is started to queue a
|
||
|
worker thread at a later time, unless there is a pending connection.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns TRUE if the Pipe was connected.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
UNICODE_STRING sysEventPipeName;
|
||
|
HANDLE npfsHandle;
|
||
|
UNICODE_STRING unicodeSysEventPipeName;
|
||
|
UNICODE_STRING unicodeNpfsName;
|
||
|
NTSTATUS status;
|
||
|
SECURITY_QUALITY_OF_SERVICE dynamicQos;
|
||
|
OBJECT_ATTRIBUTES objectAttributes;
|
||
|
IO_STATUS_BLOCK iosb;
|
||
|
PFILE_PIPE_WAIT_FOR_BUFFER waitPipe;
|
||
|
ULONG waitPipeLength;
|
||
|
FILE_PIPE_INFORMATION pipeInfoBuffer;
|
||
|
|
||
|
|
||
|
//
|
||
|
// If the ErrorLogPipe is connected then return true.
|
||
|
//
|
||
|
|
||
|
RtlInitUnicodeString( &unicodeNpfsName, L"\\Device\\NamedPipe\\" );
|
||
|
|
||
|
RtlInitUnicodeString( &sysEventPipeName, L"SysEvents" );
|
||
|
RtlInitUnicodeString( &unicodeSysEventPipeName,
|
||
|
L"\\Device\\NamedPipe\\SysEvents" );
|
||
|
|
||
|
//
|
||
|
// Wait for the server's pipe to reach a listen state...
|
||
|
//
|
||
|
|
||
|
InitializeObjectAttributes(
|
||
|
&objectAttributes,
|
||
|
&unicodeNpfsName,
|
||
|
OBJ_CASE_INSENSITIVE,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
|
||
|
status = NtOpenFile( &npfsHandle,
|
||
|
GENERIC_READ | SYNCHRONIZE,
|
||
|
&objectAttributes,
|
||
|
&iosb,
|
||
|
FILE_SHARE_READ,
|
||
|
0 );
|
||
|
|
||
|
if (NT_SUCCESS( status ) ) {
|
||
|
waitPipeLength = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) +
|
||
|
sysEventPipeName.Length;
|
||
|
|
||
|
waitPipe = ExAllocatePoolWithTag( NonPagedPool,
|
||
|
waitPipeLength ,
|
||
|
'SpxE' );
|
||
|
waitPipe->TimeoutSpecified = FALSE;
|
||
|
|
||
|
waitPipe->NameLength = sysEventPipeName.Length;
|
||
|
|
||
|
RtlMoveMemory( waitPipe->Name,
|
||
|
sysEventPipeName.Buffer,
|
||
|
sysEventPipeName.Length );
|
||
|
|
||
|
status = NtFsControlFile( npfsHandle,
|
||
|
NULL, // Event
|
||
|
NULL, // ApcRoutine
|
||
|
NULL, // ApcContext
|
||
|
&iosb,
|
||
|
FSCTL_PIPE_WAIT,
|
||
|
waitPipe,
|
||
|
waitPipeLength,
|
||
|
NULL,
|
||
|
0 );
|
||
|
|
||
|
if (status == STATUS_PENDING) {
|
||
|
status = NtWaitForSingleObject( npfsHandle, TRUE, NULL ) ;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Close NemdPipe Handle
|
||
|
//
|
||
|
|
||
|
NtClose( npfsHandle );
|
||
|
ExFreePool( waitPipe );
|
||
|
}
|
||
|
|
||
|
|
||
|
if (NT_SUCCESS( status ) ) {
|
||
|
|
||
|
//
|
||
|
// Initialize the attributes
|
||
|
//
|
||
|
|
||
|
InitializeObjectAttributes(
|
||
|
&objectAttributes,
|
||
|
&unicodeSysEventPipeName,
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Set up the security quality of service parameters to use over the
|
||
|
// Pipe. Use the most efficient (least overhead) - which is dynamic
|
||
|
// rather than static tracking.
|
||
|
//
|
||
|
|
||
|
dynamicQos.ImpersonationLevel = SecurityImpersonation;
|
||
|
dynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
||
|
dynamicQos.EffectiveOnly = TRUE;
|
||
|
|
||
|
objectAttributes.SecurityQualityOfService = (PVOID)(&dynamicQos);
|
||
|
|
||
|
//
|
||
|
// And now open it...
|
||
|
//
|
||
|
|
||
|
status = NtOpenFile( Handle,
|
||
|
GENERIC_WRITE | SYNCHRONIZE, // NEED CHK
|
||
|
&objectAttributes,
|
||
|
&iosb,
|
||
|
FILE_SHARE_WRITE,
|
||
|
0 ) ;
|
||
|
|
||
|
if (NT_SUCCESS( status ) ) {
|
||
|
|
||
|
//
|
||
|
// Set ReadMode and CompletionMode
|
||
|
//
|
||
|
|
||
|
pipeInfoBuffer.ReadMode = FILE_PIPE_MESSAGE_MODE;
|
||
|
pipeInfoBuffer.CompletionMode = FILE_PIPE_COMPLETE_OPERATION;
|
||
|
|
||
|
NtSetInformationFile( Handle,
|
||
|
&iosb,
|
||
|
&pipeInfoBuffer,
|
||
|
sizeof(FILE_PIPE_INFORMATION),
|
||
|
FilePipeInformation );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
ExpSysEventDpc(
|
||
|
IN struct _KDPC *Dpc,
|
||
|
IN PVOID DeferredContext,
|
||
|
IN PVOID SystemArgument1,
|
||
|
IN PVOID SystemArgument2
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine queues a work request to the worker thread to process system
|
||
|
events. It is called by a timer DPC when the system event pipe cannot be
|
||
|
connected. The DPC is freed after this routine completes.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Dpc - Supplies a pointer to the DPC structure. This structre is freed by
|
||
|
this routine.
|
||
|
|
||
|
DeferredContext - Unused.
|
||
|
|
||
|
SystemArgument1 - Unused.
|
||
|
|
||
|
SystemArgument2 - Unused.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Free memory for this context
|
||
|
//
|
||
|
|
||
|
ExFreePool(DeferredContext);
|
||
|
|
||
|
//
|
||
|
// Queue work item
|
||
|
//
|
||
|
|
||
|
ExQueueWorkItem( &ExpSysEventWorkItem, DelayedWorkQueue );
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ExpQueueSysEventRequest(
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine sets a timer to fire after 30 seconds. The timer queues a
|
||
|
DPC which then queues a worker thread request to run the Systen Event thread
|
||
|
routine.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
LARGE_INTEGER interval;
|
||
|
PEXP_SYSTEM_EVENT_CONTEXT context;
|
||
|
|
||
|
//
|
||
|
// Allocate a context block which will contain the timer and the DPC.
|
||
|
//
|
||
|
|
||
|
context = ExAllocatePoolWithTag( NonPagedPool,
|
||
|
sizeof(EXP_SYSTEM_EVENT_CONTEXT),
|
||
|
'QpxE');
|
||
|
|
||
|
if (context == NULL) {
|
||
|
// no memory - requeue workitem
|
||
|
ExQueueWorkItem( &ExpSysEventWorkItem, DelayedWorkQueue );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
KeInitializeTimer( &context->SysEventTimer );
|
||
|
KeInitializeDpc( &context->SysEventDpc, ExpSysEventDpc, context );
|
||
|
|
||
|
//
|
||
|
// Delay for 20 seconds and try to connect to the pipe again.
|
||
|
//
|
||
|
|
||
|
interval.QuadPart = -200000000;
|
||
|
KeSetTimer( &context->SysEventTimer, interval, &context->SysEventDpc );
|
||
|
}
|
||
|
|
||
|
#endif // _PNP_POWER_
|
||
|
|