mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 05:52:56 +00:00
440 lines
13 KiB
C
440 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$
|
|
*
|
|
* 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 <ntddk.h>
|
|
#include <scsi.h>
|
|
#include <ntdddisk.h>
|
|
#include <ntddscsi.h>
|
|
#include <include/class2.h>
|
|
#include <diskdump/diskdump.h>
|
|
#include <ndk/rtlfuncs.h>
|
|
|
|
#include "../scsiport/scsiport_int.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* It's already defined in scsiport_int.h */
|
|
#undef VERSION
|
|
#define VERSION "0.0.1"
|
|
|
|
#undef KeGetCurrentIrql
|
|
/* PROTOTYPES ***************************************************************/
|
|
|
|
NTSTATUS STDCALL
|
|
DiskDumpPrepare(PDEVICE_OBJECT DeviceObject, 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 =
|
|
{
|
|
(PVOID)DiskDumpPrepare,
|
|
(PVOID)DiskDumpInit,
|
|
(PVOID)DiskDumpWrite,
|
|
(PVOID)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->SynchronizeIrql)
|
|
{
|
|
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 = 0, OldIrql2 = 0;
|
|
KIRQL CurrentIrql = KeGetCurrentIrql();
|
|
|
|
if (CurrentIrql < (CoreDumpPortDeviceExtension->Interrupt->SynchronizeIrql - 1))
|
|
{
|
|
KeRaiseIrql(CoreDumpPortDeviceExtension->Interrupt->SynchronizeIrql - 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->SynchronizeIrql, &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->SynchronizeIrql - 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 DeviceObject, PDUMP_POINTERS DumpPointers)
|
|
{
|
|
PIMAGE_NT_HEADERS NtHeader;
|
|
PVOID ImportDirectory;
|
|
ULONG ImportDirectorySize;
|
|
PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory;
|
|
PVOID DriverBase;
|
|
PCH Name;
|
|
ULONG i;
|
|
ULONG Hint;
|
|
PVOID* ImportAddressList;
|
|
PULONG FunctionNameList;
|
|
|
|
/* Save the information from the kernel. */
|
|
CoreDumpClassDevice = DeviceObject;
|
|
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_DESCRIPTOR)ImportDirectory;
|
|
DPRINT("Processeing import directory at %p\n", ImportModuleDirectory);
|
|
while (ImportModuleDirectory->Name)
|
|
{
|
|
/* Check to make sure that import lib is kernel */
|
|
Name = (PCHAR) DriverBase + ImportModuleDirectory->Name;
|
|
|
|
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 +
|
|
(ULONG_PTR)ImportModuleDirectory->FirstThunk);
|
|
|
|
/* Get the list of functions to import */
|
|
if (ImportModuleDirectory->OriginalFirstThunk != 0)
|
|
{
|
|
FunctionNameList = (PULONG) ((PUCHAR)DriverBase +
|
|
(ULONG_PTR)ImportModuleDirectory->OriginalFirstThunk);
|
|
}
|
|
else
|
|
{
|
|
FunctionNameList = (PULONG) ((PUCHAR)DriverBase +
|
|
(ULONG_PTR)ImportModuleDirectory->FirstThunk);
|
|
}
|
|
/* Walk through function list and fixup addresses */
|
|
while (*FunctionNameList != 0L)
|
|
{
|
|
if ((*FunctionNameList) & 0x80000000) // hint
|
|
{
|
|
Name = NULL;
|
|
|
|
Hint = (*FunctionNameList) & 0xffff;
|
|
}
|
|
else // hint-name
|
|
{
|
|
Name = (PCHAR)((ULONG)DriverBase +
|
|
*FunctionNameList + 2);
|
|
Hint = *(PUSHORT)((ULONG)DriverBase + *FunctionNameList);
|
|
}
|
|
#if 0
|
|
DPRINT(" Hint:%04x Name:%s\n", Hint, pName);
|
|
#endif
|
|
|
|
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 */
|