reactos/reactos/drivers/storage/diskdump/diskdump.c
David Welch 9231f7b9a2 - Added support for crash dumps:-
* Recognize the option /CRASHDUMP on the kernel command line to specify
the type of dump desired.
* Added a new driver (diskdump) which wraps the miniport for the disk
containing the pagefile and does the crash dump i/o.
* Added a FSCTL to get the mapping between LCNs and disk offsets so
crash dumps don't need to go through the filesystem.

svn path=/trunk/; revision=5887
2003-08-27 21:28:08 +00:00

432 lines
13 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2001, 2002, 2003 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: diskdump.c,v 1.1 2003/08/27 21:28:08 dwelch Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: services/storage/diskdump/diskdump.c
* PURPOSE: Dumping crash data to the pagefile
* PROGRAMMER: Eric Kohl (ekohl@rz-online.de)
*/
/* INCLUDES *****************************************************************/
#include <ddk/ntddk.h>
#include <ddk/scsi.h>
#include <ddk/class2.h>
#include <ddk/ntddscsi.h>
#include <napi/core.h>
#include "../scsiport/scsiport_int.h"
#define NDEBUG
#include <debug.h>
#define VERSION "0.0.1"
/* PROTOTYPES ***************************************************************/
NTSTATUS STDCALL
DiskDumpPrepare(PDEVICE_OBJECT StorageDevice, PDUMP_POINTERS DumpPointers);
VOID
DiskDumpScsiInvalid(VOID);
VOID
_DiskDumpScsiPortNotification(IN SCSI_NOTIFICATION_TYPE NotificationType,
IN PVOID HwDeviceExtension,
...);
NTSTATUS STDCALL
DiskDumpInit(VOID);
NTSTATUS STDCALL
DiskDumpFinish(VOID);
NTSTATUS STDCALL
DiskDumpWrite(LARGE_INTEGER StartAddress, PMDL Mdl);
typedef VOID (*SCSIPORTNOTIFICATION)(IN SCSI_NOTIFICATION_TYPE NotificationType,
IN PVOID HwDeviceExtension,
...);
/* GLOBALS ******************************************************************/
MM_CORE_DUMP_FUNCTIONS DiskDumpFunctions =
{
DiskDumpPrepare,
DiskDumpInit,
DiskDumpWrite,
DiskDumpFinish,
};
typedef struct
{
PCH Name;
ULONG Ordinal;
PVOID OldFunction;
PVOID NewFunction;
} SUBSTITUTE_EXPORT;
static SCSI_REQUEST_BLOCK CoreDumpSrb;
static DUMP_POINTERS CoreDumpPointers;
static PDEVICE_OBJECT CoreDumpClassDevice;
static PDEVICE_EXTENSION CoreDumpClass2DeviceExtension;
static PDEVICE_OBJECT CoreDumpPortDevice;
static SCSI_PORT_DEVICE_EXTENSION* CoreDumpPortDeviceExtension;
BOOLEAN IsDumping = FALSE;
static PDRIVER_OBJECT DiskDumpDriver;
static UCHAR DiskDumpSenseData[SENSE_BUFFER_SIZE];
static BOOLEAN IrqComplete, IrqNextRequest;
PVOID OldScsiPortNotification;
static SUBSTITUTE_EXPORT DiskDumpExports[] =
{
{"ScsiPortConvertPhysicalAddressToUlong", 2, NULL, NULL},
{"ScsiPortConvertUlongToPhysicalAddress", 3, NULL, NULL},
{"ScsiPortFreeDeviceBase", 5, NULL, DiskDumpScsiInvalid},
{"ScsiPortGetBusData", 6, NULL, DiskDumpScsiInvalid},
{"ScsiPortGetDeviceBase", 7, DiskDumpScsiInvalid},
{"ScsiPortInitialize", 13, NULL, DiskDumpScsiInvalid},
{"ScsiPortNotification", 17, NULL, _DiskDumpScsiPortNotification},
{"ScsiPortReadPortBufferUlong", 19, NULL},
{"ScsiPortReadPortBufferUshort", 20, NULL},
{"ScsiPortReadPortUchar", 21, NULL, NULL},
{"ScsiPortReadPortUshort", 23, NULL, NULL},
{"ScsiPortStallExecution", 31, NULL, NULL},
{"ScsiPortWritePortBufferUlong", 34, NULL},
{"ScsiPortWritePortBufferUshort", 35, NULL},
{"ScsiPortWritePortUchar", 36, NULL, NULL},
{"ScsiDebugPrint", 0, NULL, NULL},
};
/* FUNCTIONS ****************************************************************/
VOID
DiskDumpScsiPortNotification(IN SCSI_NOTIFICATION_TYPE NotificationType,
IN PVOID HwDeviceExtension,
...)
{
if (NotificationType == RequestComplete)
{
IrqComplete = TRUE;
}
if (NotificationType == NextRequest)
{
IrqNextRequest = TRUE;
}
}
VOID
DiskDumpScsiInvalid(VOID)
{
DbgPrint("DISKDUMP: Error: Miniport called a function not supported at dump time.\n");
KeBugCheck(0);
}
VOID STDCALL
DiskDumpBuildRequest(LARGE_INTEGER StartingOffset, PMDL Mdl)
{
LARGE_INTEGER StartingBlock;
PSCSI_REQUEST_BLOCK Srb;
PCDB Cdb;
ULONG LogicalBlockAddress;
USHORT TransferBlocks;
/* Calculate logical block address */
StartingBlock.QuadPart = StartingOffset.QuadPart >> CoreDumpClass2DeviceExtension->SectorShift;
LogicalBlockAddress = (ULONG)StartingBlock.u.LowPart;
DPRINT("Logical block address: %lu\n", LogicalBlockAddress);
/* Allocate and initialize an SRB */
Srb = &CoreDumpSrb;
Srb->SrbFlags = 0;
Srb->Length = sizeof(SCSI_REQUEST_BLOCK); //SCSI_REQUEST_BLOCK_SIZE;
Srb->OriginalRequest = NULL;
Srb->PathId = CoreDumpClass2DeviceExtension->PathId;
Srb->TargetId = CoreDumpClass2DeviceExtension->TargetId;
Srb->Lun = CoreDumpClass2DeviceExtension->Lun;
Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
Srb->DataBuffer = Mdl->MappedSystemVa;
Srb->DataTransferLength = PAGE_SIZE;
Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
Srb->QueueSortKey = LogicalBlockAddress;
Srb->SenseInfoBuffer = DiskDumpSenseData;
Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
Srb->TimeOutValue =
((Srb->DataTransferLength + 0xFFFF) >> 16) * CoreDumpClass2DeviceExtension->TimeOutValue;
Srb->SrbStatus = SRB_STATUS_SUCCESS;
Srb->ScsiStatus = 0;
Srb->NextSrb = 0;
Srb->CdbLength = 10;
Cdb = (PCDB)Srb->Cdb;
/* Initialize ATAPI packet (12 bytes) */
RtlZeroMemory(Cdb, MAXIMUM_CDB_SIZE);
Cdb->CDB10.LogicalUnitNumber = CoreDumpClass2DeviceExtension->Lun;
TransferBlocks = (USHORT)(PAGE_SIZE >>
CoreDumpClass2DeviceExtension->SectorShift);
/* Copy little endian values into CDB in big endian format */
Cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte3;
Cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte2;
Cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte1;
Cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte0;
Cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&TransferBlocks)->Byte1;
Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&TransferBlocks)->Byte0;
/* Write Command. */
Srb->SrbFlags |= SRB_FLAGS_DATA_OUT;
Cdb->CDB10.OperationCode = SCSIOP_WRITE;
/* Leave caching disabled. */
}
BOOLEAN STDCALL
DiskDumpIsr(PKINTERRUPT Interrupt, PVOID ServiceContext)
{
if (!CoreDumpPortDeviceExtension->HwInterrupt(&CoreDumpPortDeviceExtension->MiniPortDeviceExtension))
{
return(FALSE);
}
return(TRUE);
}
NTSTATUS STDCALL
DiskDumpInit(VOID)
{
KIRQL CurrentIrql = KeGetCurrentIrql();
IsDumping = TRUE;
if (CurrentIrql >= CoreDumpPortDeviceExtension->Interrupt->SynchLevel)
{
DbgPrint("DISKDUMP: Error: Crash inside high priority interrupt routine.\n");
return(STATUS_UNSUCCESSFUL);
}
CoreDumpPortDeviceExtension->Interrupt->ServiceRoutine = DiskDumpIsr;
return(STATUS_SUCCESS);
}
NTSTATUS STDCALL
DiskDumpFinish(VOID)
{
return(STATUS_SUCCESS);
}
NTSTATUS STDCALL
DiskDumpWrite(LARGE_INTEGER Address, PMDL Mdl)
{
KIRQL OldIrql, OldIrql2;
KIRQL CurrentIrql = KeGetCurrentIrql();
if (CurrentIrql < (CoreDumpPortDeviceExtension->Interrupt->SynchLevel - 1))
{
KeRaiseIrql(CoreDumpPortDeviceExtension->Interrupt->SynchLevel - 1, &OldIrql);
}
/* Adjust the address for the start of the partition. */
Address.QuadPart +=
(CoreDumpClass2DeviceExtension->StartingOffset.QuadPart + CoreDumpClass2DeviceExtension->DMByteSkew);
/* Assume the device is always able to transfer a page so no need to split up the transfer. */
/* Build an SRB to describe the write. */
DiskDumpBuildRequest(Address, Mdl);
/* Start i/o on the HBA. */
IrqComplete = IrqNextRequest = FALSE;
KeRaiseIrql(CoreDumpPortDeviceExtension->Interrupt->SynchLevel, &OldIrql2);
if (!CoreDumpPortDeviceExtension->HwStartIo(&CoreDumpPortDeviceExtension->MiniPortDeviceExtension,
&CoreDumpSrb))
{
KeLowerIrql(OldIrql);
DbgPrint("DISKDUMP: Error: Miniport HwStartIo failed.\n");
return(STATUS_UNSUCCESSFUL);
}
KeLowerIrql(OldIrql2);
/* Wait for the miniport to finish. */
__asm__ ("sti\n\t");
while (!IrqComplete || !IrqNextRequest)
{
__asm__ ("hlt\n\t");
}
if (CurrentIrql < (CoreDumpPortDeviceExtension->Interrupt->SynchLevel - 1))
{
KeLowerIrql(OldIrql);
}
__asm__("cli\n\t");
/* Check the result. */
if (SRB_STATUS(CoreDumpSrb.SrbStatus) != SRB_STATUS_SUCCESS)
{
DbgPrint("DISKDUMP: Error: SRB failed.\n");
return(STATUS_UNSUCCESSFUL);
}
return(STATUS_SUCCESS);
}
NTSTATUS STDCALL
DiskDumpPrepare(PDEVICE_OBJECT StorageDevice, PDUMP_POINTERS DumpPointers)
{
PIMAGE_NT_HEADERS NtHeader;
PVOID ImportDirectory;
ULONG ImportDirectorySize;
PIMAGE_IMPORT_MODULE_DIRECTORY ImportModuleDirectory;
PVOID DriverBase;
PCH Name;
ULONG i;
ULONG Hint;
PVOID* ImportAddressList;
PULONG FunctionNameList;
/* Save the information from the kernel. */
CoreDumpClassDevice = StorageDevice;
CoreDumpPointers = *DumpPointers;
CoreDumpClass2DeviceExtension = (PDEVICE_EXTENSION)CoreDumpClassDevice->DeviceExtension;
CoreDumpPortDevice = DumpPointers->DeviceObject;
CoreDumpPortDeviceExtension = CoreDumpPortDevice->DeviceExtension;
/* Replace all the miniport driver's imports with our functions. */
DriverBase = CoreDumpPortDevice->DriverObject->DriverStart;
NtHeader = RtlImageNtHeader(DriverBase);
ImportDirectory = RtlImageDirectoryEntryToData(DriverBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&ImportDirectorySize);
if (ImportDirectory == NULL || ImportDirectorySize == 0)
{
DbgPrint("DISKDUMP: Error: Miniport has no imports?\n");
return(STATUS_UNSUCCESSFUL);
}
/* Process each import module */
ImportModuleDirectory = (PIMAGE_IMPORT_MODULE_DIRECTORY)ImportDirectory;
DPRINT("Processeing import directory at %p\n", ImportModuleDirectory);
while (ImportModuleDirectory->dwRVAModuleName)
{
/* Check to make sure that import lib is kernel */
Name = (PCHAR) DriverBase + ImportModuleDirectory->dwRVAModuleName;
if (strcmp(Name, "scsiport.sys") != 0)
{
DbgPrint("DISKDUMP: Warning: Miniport has illegal imports.\n");
ImportModuleDirectory++;
continue;
}
/* Get the import address list */
ImportAddressList = (PVOID *) ((PUCHAR)DriverBase +
ImportModuleDirectory->dwRVAFunctionAddressList);
/* Get the list of functions to import */
if (ImportModuleDirectory->dwRVAFunctionNameList != 0)
{
FunctionNameList = (PULONG) ((PUCHAR)DriverBase +
ImportModuleDirectory->dwRVAFunctionNameList);
}
else
{
FunctionNameList = (PULONG) ((PUCHAR)DriverBase +
ImportModuleDirectory->dwRVAFunctionAddressList);
}
/* Walk through function list and fixup addresses */
while (*FunctionNameList != 0L)
{
if ((*FunctionNameList) & 0x80000000) // hint
{
Name = NULL;
Hint = (*FunctionNameList) & 0xffff;
}
else // hint-name
{
Name = (PCHAR)((DWORD)DriverBase +
*FunctionNameList + 2);
Hint = *(PWORD)((DWORD)DriverBase + *FunctionNameList);
}
DPRINT(" Hint:%04x Name:%s\n", Hint, pName);
for (i = 0; i < (sizeof(DiskDumpExports) / sizeof(DiskDumpExports[0])); i++)
{
if (DiskDumpExports[i].Ordinal == Hint ||
(Name != NULL && strcmp(DiskDumpExports[i].Name, Name) == 0))
{
break;
}
}
if (i == (sizeof(DiskDumpExports) / sizeof(DiskDumpExports[0])))
{
DbgPrint("DISKDUMP: Error: Miniport imports unknown symbol %s.\n", Name);
return(STATUS_UNSUCCESSFUL);
}
if (strcmp(Name, "ScsiPortNotification") == 0)
{
OldScsiPortNotification = *ImportAddressList;
}
DiskDumpExports[i].OldFunction = *ImportAddressList;
if (DiskDumpExports[i].NewFunction != NULL)
{
*ImportAddressList = DiskDumpExports[i].NewFunction;
}
ImportAddressList++;
FunctionNameList++;
}
ImportModuleDirectory++;
}
return(STATUS_SUCCESS);
}
/**********************************************************************
* NAME EXPORTED
* DriverEntry
*
* DESCRIPTION
* This function initializes the driver, locates and claims
* hardware resources, and creates various NT objects needed
* to process I/O requests.
*
* RUN LEVEL
* PASSIVE_LEVEL
*
* ARGUMENTS
* DriverObject
* System allocated Driver Object for this driver
*
* RegistryPath
* Name of registry driver service key
*
* RETURN VALUE
* Status
*/
NTSTATUS STDCALL
DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
DiskDumpDriver = DriverObject;
return(STATUS_SUCCESS);
}
/* EOF */