/* * 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 #include #include #include #include #include "../scsiport/scsiport_int.h" #define NDEBUG #include #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 */