windows-nt/Source/XPSP1/NT/base/hals/mcasample/imca/sys/imca.c
2020-09-26 16:20:57 +08:00

958 lines
22 KiB
C

/*++
Module Name:
mca.c
Abstract:
Sample device driver to register itself with the HAL and log Machine Check
Errors on a Intel Architecture Platform
Author:
Environment:
Kernel mode
Notes:
Revision History:
--*/
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <ntddk.h>
#include "imca.h"
//
// Device names for the MCA driver
//
#define MCA_DEVICE_NAME "\\Device\\imca" // ANSI Name
#define MCA_DEVICE_NAME_U L"\\Device\\imca" // Unicode Name
#define MCA_DEVICE_NAME_DOS "\\DosDevices\\imca" // Device Name for Win32 App
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
MCAOpen(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
MCAClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
MCAStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
ERROR_SEVERITY
MCADriverExceptionCallback(
IN PDEVICE_OBJECT DeviceObject,
IN PMCA_EXCEPTION InException
);
VOID
MCADriverDpcCallback(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
);
NTSTATUS
MCACleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
McaCancelIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
MCADeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
MCAUnload(
IN PDRIVER_OBJECT DriverObject
);
NTSTATUS
MCACreateSymbolicLinkObject(
VOID
);
VOID
MCAProcessWorkItem(
PVOID Context
);
//
// This temporary buffer holds the data between the Machine Check error
// notification from HAL and the asynchronous IOCTL completion to the
// application
//
typedef struct _MCA_DEVICE_EXTENSION {
PDEVICE_OBJECT DeviceObject;
PIRP SavedIrp;
BOOLEAN WorkItemQueued;
WORK_QUEUE_ITEM WorkItem;
// Place to log the exceptions. Whenever the exception callback
// routine is called by the HAL MCA component, record the exception here.
// This can potentially be a link list.
MCA_EXCEPTION McaException;
} MCA_DEVICE_EXTENSION, *PMCA_DEVICE_EXTENSION;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(INIT, MCACreateSymbolicLinkObject)
#endif // ALLOC_PRAGMA
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine does the driver specific initialization at entry time
Arguments:
DriverObject: Pointer to the driver object
RegistryPath: Path to driver's registry key
Return Value:
Success or failure
--*/
{
UNICODE_STRING UnicodeString;
NTSTATUS Status = STATUS_SUCCESS;
PMCA_DEVICE_EXTENSION Extension;
PDEVICE_OBJECT McaDeviceObject;
MCA_DRIVER_INFO McaDriverInfo;
//
// Create device object for MCA device.
//
RtlInitUnicodeString(&UnicodeString, MCA_DEVICE_NAME_U);
//
// Device is created as exclusive since only a single thread can send
// I/O requests to this device
//
Status = IoCreateDevice(
DriverObject,
sizeof(MCA_DEVICE_EXTENSION),
&UnicodeString,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&McaDeviceObject
);
if (!NT_SUCCESS( Status )) {
DbgPrint("Mca DriverEntry: IoCreateDevice failed\n");
return Status;
}
McaDeviceObject->Flags |= DO_BUFFERED_IO;
Extension = McaDeviceObject->DeviceExtension;
RtlZeroMemory(Extension, sizeof(MCA_DEVICE_EXTENSION));
Extension->DeviceObject = McaDeviceObject;
//
// Make the device visible to Win32 subsystem
//
Status = MCACreateSymbolicLinkObject ();
if (!NT_SUCCESS( Status )) {
DbgPrint("Mca DriverEntry: McaCreateSymbolicLinkObject failed\n");
return Status;
}
//
// Set up the device driver entry points.
//
DriverObject->MajorFunction[IRP_MJ_CREATE] = MCAOpen;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MCAClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MCADeviceControl;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MCACleanup;
DriverObject->DriverUnload = MCAUnload;
DriverObject->DriverStartIo = MCAStartIo;
//
// Register the driver with the HAL
//
McaDriverInfo.ExceptionCallback = MCADriverExceptionCallback;
McaDriverInfo.DpcCallback = MCADriverDpcCallback;
McaDriverInfo.DeviceContext = McaDeviceObject;
Status = HalSetSystemInformation(
HalMcaRegisterDriver,
sizeof(MCA_DRIVER_INFO),
(PVOID)&McaDriverInfo
);
if (!NT_SUCCESS( Status )) {
DbgPrint("Mca DriverEntry: HalMcaRegisterDriver failed\n");
//
// Clean up whatever we have done so far
//
MCAUnload(DriverObject);
return(STATUS_UNSUCCESSFUL);
}
//
// This is the place where you would check non-volatile area (if any) for
// any Machine Check errros logged and process them.
// ...
//
return STATUS_SUCCESS;
}
NTSTATUS
MCACreateSymbolicLinkObject(
VOID
)
/*++
Routine Description:
Makes MCA device visible to Win32 subsystem
Arguments:
None
Return Value:
Success or failure
--*/
{
NTSTATUS Status;
STRING DosString;
STRING NtString;
UNICODE_STRING DosUnicodeString;
UNICODE_STRING NtUnicodeString;
//
// Create a symbolic link for sharing.
//
RtlInitAnsiString( &DosString, MCA_DEVICE_NAME_DOS );
Status = RtlAnsiStringToUnicodeString(
&DosUnicodeString,
&DosString,
TRUE
);
if ( !NT_SUCCESS( Status )) {
return Status;
}
RtlInitAnsiString( &NtString, MCA_DEVICE_NAME );
Status = RtlAnsiStringToUnicodeString(
&NtUnicodeString,
&NtString,
TRUE
);
if ( !NT_SUCCESS( Status )) {
return Status;
}
Status = IoCreateSymbolicLink(
&DosUnicodeString,
&NtUnicodeString
);
RtlFreeUnicodeString( &DosUnicodeString );
RtlFreeUnicodeString( &NtUnicodeString );
return (Status);
}
//
// Dispatch routine for close requests
//
NTSTATUS
MCAClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Close dispatch routine
Arguments:
DeviceObject: Pointer to the device object
Irp: Incoming Irp
Return Value:
Success or failure
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
//
// Complete the request and return status.
//
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return (Status);
}
NTSTATUS
MCAOpen(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch routine for create/open requests.
Arguments:
DeviceObject: Pointer to the device object
Irp: Incoming Irp
Return Value:
Success or failure
--*/
{
//
// Complete the request and return status.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = FILE_OPENED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return (STATUS_SUCCESS);
}
ERROR_SEVERITY
MCADriverExceptionCallback(
IN PDEVICE_OBJECT DeviceObject,
IN PMCA_EXCEPTION InException
)
/*++
Routine Description:
This is the callback routine for MCA exception. It was registered
by this driver at INIT time with the HAL as a callback when a
non-restartable error occurs. This routine simply copies the
information to a platform specific area
NOTE: If the information needs to be saved in NVRAM, this is the place
to do it.
Once you return from this callback, the system is going to bugcheck.
Arguments:
DeviceObject: Pointer to the device object
InException: Exception information record
Return Value:
None
--*/
{
PMCA_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
PCHAR Destination, Source;
UCHAR Bytes;
//
// An exception has occured on a processor.
// Perform any vendor specific action here like saving stuff in NVRAM
// NOTE : No system services of any kind can be used here.
//
//
// Save the exception from HAL. May want to use link list for these
// exceptions.
//
Destination = (PCHAR)&(Extension->McaException); // Put your platform
// specific destination
Source = (PCHAR)InException;
//
// Copy from source to destination here
//
#if defined(_IA64_)
//
// Return information to the generic HAL MCA handler.
//
// Update it accordingly here, the default value being the ERROR_SEVERITY value
// in the MCA exception.
//
return( InException->ErrorSeverity );
#endif // _IA64_
}
//
// DPC routine for IRP completion
//
VOID
MCADriverDpcCallback(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This is the DPC - callback routine for MCA exception. It was registered
by this driver at INIT time with the HAL as a DPC callback when a
restartable error occurs (which causes Machine Check exception)
Arguments:
Dpc: The DPC Object itself
DefferedContext: Pointer to the device object
SystemContext1: Not used
SystemContext2: Not used
Return Value:
None
--*/
{
PMCA_DEVICE_EXTENSION Extension;
Extension = ((PDEVICE_OBJECT)DeferredContext)->DeviceExtension;
if (Extension->SavedIrp == NULL) {
//
// We got an MCA exception but no app was asking for anything.
//
return;
}
//
// If we have reached this point, it means that the exception was
// restartable. Since we cannot read the log at Dispatch level,
// queue a work item to read the Machine Check log at Passive level
//
if (Extension->WorkItemQueued == FALSE) {
//
// Set a boolean to indicate that we have already queued a work item
//
Extension->WorkItemQueued = TRUE;
//
// Initialize the work item
//
ExInitializeWorkItem(&Extension->WorkItem,
(PWORKER_THREAD_ROUTINE)MCAProcessWorkItem,
(PVOID)DeferredContext
);
//
// Queue the work item for processing at PASSIVE level
//
ExQueueWorkItem(&Extension->WorkItem, CriticalWorkQueue);
}
}
VOID
MCAProcessWorkItem(
PVOID Context
)
/*++
Routine Description:
This routine gets invoked when a work item is queued from the DPC
callback routine for a restartable machine check error.
Its job is to read the machine check registers and copy the log
to complete the asynchronous IRP
Arguments:
Context : Pointer to the device object
Return Value:
None
--*/
{
PMCA_DEVICE_EXTENSION Extension;
KIRQL CancelIrql;
ULONG ReturnedLength;
NTSTATUS Status;
Extension = ((PDEVICE_OBJECT)Context)->DeviceExtension;
//
// Mark this IRP as non-cancellable
//
IoAcquireCancelSpinLock(&CancelIrql);
if (Extension->SavedIrp->Cancel == TRUE) {
IoReleaseCancelSpinLock(CancelIrql);
} else {
IoSetCancelRoutine(Extension->SavedIrp, NULL);
IoReleaseCancelSpinLock(CancelIrql);
//
// Call HalQuerySystemInformation() to obtain MCA log.
//
Status = HalQuerySystemInformation(
HalMcaLogInformation,
sizeof(MCA_EXCEPTION),
Extension->SavedIrp->AssociatedIrp.SystemBuffer,
&ReturnedLength
);
ASSERT(Status != STATUS_NO_SUCH_DEVICE);
ASSERT(Status != STATUS_NOT_FOUND);
IoStartPacket(((PDEVICE_OBJECT)Context), Extension->SavedIrp, 0, NULL);
Extension->SavedIrp = NULL;
Extension->WorkItemQueued = FALSE;
}
}
VOID
MCAStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine completes the async call from the app
Arguments:
DeviceObject: Pointer to the device object
Irp: Incoming Irp
Return Value:
None
--*/
{
//
// The system Buffer has already been setup
//
Irp->IoStatus.Information = sizeof(MCA_EXCEPTION);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
IoStartNextPacket(DeviceObject, TRUE);
}
VOID
McaCancelIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function gets called when the IRP is cancelled. When this routine
is called, we hold the cancel spin lock and we are at DISPATCH level
Arguments:
DeviceObject and the Irp to be cancelled.
Return Value:
None.
--*/
{
((PMCA_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->SavedIrp = NULL;
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoReleaseCancelSpinLock(Irp->CancelIrql);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
NTSTATUS
MCADeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch routine for the IOCTL requests to driver.
It accepts an I/O Request Packet, performs the request, and then
returns with the appropriate status.
Arguments:
DeviceObject: Pointer to the device object
Irp: Incoming Irp
Return Value:
Success or failure
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
PMCA_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
KIRQL CancelIrql;
ULONG ReturnedLength;
ULONG PhysicalAddress;
KIRQL OldIrql;
//
// Get a pointer to the current stack location in the IRP. This is
// where the function codes and parameters are stored.
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// The individual IOCTLs will return errors if HAL MCA is not installed
//
//
// Switch on the IOCTL code that is being requested by the user. If the
// operation is a valid one for this device do the needful.
//
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_READ_BANKS:
//
// we need a user buffer for this call to complete
// Our user buffer is in SystemBuffer
//
if (Irp->AssociatedIrp.SystemBuffer == NULL) {
Status = STATUS_UNSUCCESSFUL;
break;
}
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength !=
sizeof(MCA_EXCEPTION)) {
Status = STATUS_UNSUCCESSFUL;
break;
}
//
// Call HalQuerySystemInformation() to obtain MCA log.
// This call can also fail if the processor does not support
// Intel Machine Check Architecture
//
Status = HalQuerySystemInformation(
HalMcaLogInformation,
sizeof(MCA_EXCEPTION),
Irp->AssociatedIrp.SystemBuffer,
&ReturnedLength
);
if (NT_SUCCESS(Status)) {
Irp->IoStatus.Information = ReturnedLength;
} else {
if (Status == STATUS_NO_SUCH_DEVICE) {
//
// MCA support not available\n");
//
NOTHING;
}
if (Status == STATUS_NOT_FOUND) {
//
// No machine check errors present\n");
//
NOTHING;
}
Irp->IoStatus.Information = 0;
}
break;
case IOCTL_READ_BANKS_ASYNC:
if (Irp->AssociatedIrp.SystemBuffer == NULL) {
Status = STATUS_UNSUCCESSFUL;
break;
}
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength !=
sizeof(MCA_EXCEPTION)) {
Status = STATUS_UNSUCCESSFUL;
break;
}
//
// Implementation Note:
//
// Our Async model is such that the next DeviceIoControl
// does not start from the app until the previous one
// completes (asynchronously). Since there is no inherent
// parallelism that is needed here, we do not have to worry
// about protecting data integrity in the face of more than
// one app level ioctls active at the same time.
//
//
// asynchronous reads provide a mechanism for the
// app to asynchronously get input from the HAL on an
// exception. This request is marked as pending at this time
// but it will be completed when an MCA exception occurs.
//
IoMarkIrpPending(Irp);
//
// Complete the processing in StartIo Dispatch routine
// ASSERT: at any given time there is only 1 async call pending
// So just save the pointer
//
if (Extension->SavedIrp == NULL) {
Extension->SavedIrp = Irp;
} else {
//
// We can have ONLY one outstanding ASYNC request
//
Status = STATUS_DEVICE_BUSY;
break;
}
//
// Set the IRP to cancellable state
//
IoAcquireCancelSpinLock(&CancelIrql);
IoSetCancelRoutine(Irp, McaCancelIrp);
IoReleaseCancelSpinLock(CancelIrql);
return(STATUS_PENDING);
break;
default:
//
// This should not happen
//
DbgPrint("MCA driver: Bad ioctl\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
}
//
// Copy the final status into the return status, complete the request and
// get out of here.
//
if (Status != STATUS_PENDING) {
//
// Complete the Io Request
//
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
}
return (Status);
}
NTSTATUS
MCACleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is the dispatch routine for cleanup requests.
All queued IRPs are completed with STATUS_CANCELLED.
Arguments:
DeviceObject: Pointer to the device object
Irp: Incoming Irp
Return Value:
Success or failure
--*/
{
PIRP CurrentIrp;
PMCA_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
//
// Complete all queued requests with STATUS_CANCELLED.
//
if (Extension->SavedIrp != NULL) {
CurrentIrp = Extension->SavedIrp;
//
// Acquire the Cancel Spinlock
//
IoAcquireCancelSpinLock(&CurrentIrp->CancelIrql);
Extension->SavedIrp = NULL;
if (CurrentIrp->Cancel == TRUE) {
//
// Cancel routine got called for this one.
// No need to do anything else
//
IoReleaseCancelSpinLock(CurrentIrp->CancelIrql);
} else {
if (CurrentIrp->CancelRoutine == NULL) {
//
// Release the Cancel Spinlock
//
IoReleaseCancelSpinLock(CurrentIrp->CancelIrql);
} else {
(CurrentIrp->CancelRoutine)(DeviceObject, CurrentIrp );
}
}
}
//
// Complete the Cleanup Dispatch with STATUS_SUCCESS
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(STATUS_SUCCESS);
}
VOID
MCAUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
Dispatch routine for unloads
Arguments:
DeviceObject: Pointer to the device object
Return Value:
None
--*/
{
NTSTATUS Status;
STRING DosString;
UNICODE_STRING DosUnicodeString;
//
// Delete the user visible device name.
//
RtlInitAnsiString( &DosString, MCA_DEVICE_NAME_DOS );
Status = RtlAnsiStringToUnicodeString(
&DosUnicodeString,
&DosString,
TRUE
);
if ( !NT_SUCCESS( Status )) {
DbgPrint("MCAUnload: Error in RtlAnsiStringToUnicodeString\n");
return;
}
Status = IoDeleteSymbolicLink(
&DosUnicodeString
);
RtlFreeUnicodeString( &DosUnicodeString );
//
// Delete the device object
//
IoDeleteDevice(DriverObject->DeviceObject);
return;
}