update for HEAD-2003091401
[reactos.git] / drivers / storage / diskdump / diskdump.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2001, 2002, 2003 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /* $Id$
20  *
21  * COPYRIGHT:       See COPYING in the top level directory
22  * PROJECT:         ReactOS kernel
23  * FILE:            services/storage/diskdump/diskdump.c
24  * PURPOSE:         Dumping crash data to the pagefile
25  * PROGRAMMER:      Eric Kohl (ekohl@rz-online.de)
26  */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <ddk/ntddk.h>
31 #include <ddk/scsi.h>
32 #include <ddk/class2.h>
33 #include <ddk/ntddscsi.h>
34 #include <napi/core.h>
35 #include "../scsiport/scsiport_int.h"
36
37 #define NDEBUG
38 #include <debug.h>
39
40 #define VERSION  "0.0.1"
41
42 /* PROTOTYPES ***************************************************************/
43
44 NTSTATUS STDCALL
45 DiskDumpPrepare(PDEVICE_OBJECT StorageDevice, PDUMP_POINTERS DumpPointers);
46 VOID
47 DiskDumpScsiInvalid(VOID);
48 VOID
49 _DiskDumpScsiPortNotification(IN SCSI_NOTIFICATION_TYPE NotificationType,
50                              IN PVOID HwDeviceExtension,
51                              ...);
52 NTSTATUS STDCALL
53 DiskDumpInit(VOID);
54 NTSTATUS STDCALL
55 DiskDumpFinish(VOID);
56 NTSTATUS STDCALL
57 DiskDumpWrite(LARGE_INTEGER StartAddress, PMDL Mdl);
58
59 typedef VOID (*SCSIPORTNOTIFICATION)(IN SCSI_NOTIFICATION_TYPE NotificationType,
60                                      IN PVOID HwDeviceExtension,
61                                      ...);
62
63 /* GLOBALS ******************************************************************/
64
65 MM_CORE_DUMP_FUNCTIONS DiskDumpFunctions =
66   {
67     DiskDumpPrepare,
68     DiskDumpInit,
69     DiskDumpWrite,
70     DiskDumpFinish,
71   };
72
73 typedef struct
74 {
75   PCH Name;
76   ULONG Ordinal;
77   PVOID OldFunction;
78   PVOID NewFunction;
79 } SUBSTITUTE_EXPORT;
80
81 static SCSI_REQUEST_BLOCK CoreDumpSrb;
82 static DUMP_POINTERS CoreDumpPointers;
83 static PDEVICE_OBJECT CoreDumpClassDevice;
84 static PDEVICE_EXTENSION CoreDumpClass2DeviceExtension;
85 static PDEVICE_OBJECT CoreDumpPortDevice;
86 static SCSI_PORT_DEVICE_EXTENSION* CoreDumpPortDeviceExtension;
87 BOOLEAN IsDumping = FALSE;
88 static PDRIVER_OBJECT DiskDumpDriver;
89 static UCHAR DiskDumpSenseData[SENSE_BUFFER_SIZE];
90 static BOOLEAN IrqComplete, IrqNextRequest;
91 PVOID OldScsiPortNotification;
92 static SUBSTITUTE_EXPORT DiskDumpExports[] =
93   {
94     {"ScsiPortConvertPhysicalAddressToUlong", 2, NULL, NULL},
95     {"ScsiPortConvertUlongToPhysicalAddress", 3, NULL, NULL},
96     {"ScsiPortFreeDeviceBase", 5, NULL, DiskDumpScsiInvalid},
97     {"ScsiPortGetBusData", 6, NULL, DiskDumpScsiInvalid},
98     {"ScsiPortGetDeviceBase", 7, DiskDumpScsiInvalid},
99     {"ScsiPortInitialize", 13, NULL, DiskDumpScsiInvalid},
100     {"ScsiPortNotification", 17, NULL, _DiskDumpScsiPortNotification},
101     {"ScsiPortReadPortBufferUlong", 19, NULL},
102     {"ScsiPortReadPortBufferUshort", 20, NULL},
103     {"ScsiPortReadPortUchar", 21, NULL, NULL},
104     {"ScsiPortReadPortUshort", 23, NULL, NULL},
105     {"ScsiPortStallExecution", 31, NULL, NULL},
106     {"ScsiPortWritePortBufferUlong", 34, NULL},
107     {"ScsiPortWritePortBufferUshort", 35, NULL},
108     {"ScsiPortWritePortUchar", 36, NULL, NULL},
109     {"ScsiDebugPrint", 0, NULL, NULL},
110   };
111
112 /* FUNCTIONS ****************************************************************/
113
114
115
116 VOID
117 DiskDumpScsiPortNotification(IN SCSI_NOTIFICATION_TYPE NotificationType,
118                              IN PVOID HwDeviceExtension,
119                              ...)
120 {
121   if (NotificationType == RequestComplete)
122     {
123       IrqComplete = TRUE;
124     }
125   if (NotificationType == NextRequest)
126     {
127       IrqNextRequest = TRUE;
128     }
129 }
130
131 VOID
132 DiskDumpScsiInvalid(VOID)
133 {
134   DbgPrint("DISKDUMP: Error: Miniport called a function not supported at dump time.\n");
135   KeBugCheck(0);
136 }
137
138 VOID STDCALL
139 DiskDumpBuildRequest(LARGE_INTEGER StartingOffset, PMDL Mdl)
140 {
141   LARGE_INTEGER StartingBlock;
142   PSCSI_REQUEST_BLOCK Srb;
143   PCDB Cdb;
144   ULONG LogicalBlockAddress;
145   USHORT TransferBlocks;
146
147   /* Calculate logical block address */
148   StartingBlock.QuadPart = StartingOffset.QuadPart >> CoreDumpClass2DeviceExtension->SectorShift;
149   LogicalBlockAddress = (ULONG)StartingBlock.u.LowPart;
150
151   DPRINT("Logical block address: %lu\n", LogicalBlockAddress);
152
153   /* Allocate and initialize an SRB */
154   Srb = &CoreDumpSrb;
155
156   Srb->SrbFlags = 0;
157   Srb->Length = sizeof(SCSI_REQUEST_BLOCK); //SCSI_REQUEST_BLOCK_SIZE;
158   Srb->OriginalRequest = NULL;
159   Srb->PathId = CoreDumpClass2DeviceExtension->PathId;
160   Srb->TargetId = CoreDumpClass2DeviceExtension->TargetId;
161   Srb->Lun = CoreDumpClass2DeviceExtension->Lun;
162   Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
163   Srb->DataBuffer = Mdl->MappedSystemVa;
164   Srb->DataTransferLength = PAGE_SIZE;
165   Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
166   Srb->QueueSortKey = LogicalBlockAddress;
167
168   Srb->SenseInfoBuffer = DiskDumpSenseData;
169   Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
170
171   Srb->TimeOutValue =
172     ((Srb->DataTransferLength + 0xFFFF) >> 16) * CoreDumpClass2DeviceExtension->TimeOutValue;
173
174   Srb->SrbStatus = SRB_STATUS_SUCCESS;
175   Srb->ScsiStatus = 0;
176   Srb->NextSrb = 0;
177
178   Srb->CdbLength = 10;
179   Cdb = (PCDB)Srb->Cdb;
180
181   /* Initialize ATAPI packet (12 bytes) */
182   RtlZeroMemory(Cdb, MAXIMUM_CDB_SIZE);
183
184   Cdb->CDB10.LogicalUnitNumber = CoreDumpClass2DeviceExtension->Lun;
185   TransferBlocks = (USHORT)(PAGE_SIZE >> 
186                             CoreDumpClass2DeviceExtension->SectorShift);
187
188   /* Copy little endian values into CDB in big endian format */
189   Cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte3;
190   Cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte2;
191   Cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte1;
192   Cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte0;
193
194   Cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&TransferBlocks)->Byte1;
195   Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&TransferBlocks)->Byte0;
196
197
198   /* Write Command. */
199   Srb->SrbFlags |= SRB_FLAGS_DATA_OUT;
200   Cdb->CDB10.OperationCode = SCSIOP_WRITE;
201
202   /* Leave caching disabled. */
203 }
204
205 BOOLEAN STDCALL
206 DiskDumpIsr(PKINTERRUPT Interrupt, PVOID ServiceContext)
207 {
208   if (!CoreDumpPortDeviceExtension->HwInterrupt(&CoreDumpPortDeviceExtension->MiniPortDeviceExtension))
209     {
210       return(FALSE);
211     }
212   return(TRUE);
213 }
214
215 NTSTATUS STDCALL
216 DiskDumpInit(VOID)
217 {
218   KIRQL CurrentIrql = KeGetCurrentIrql();
219   IsDumping = TRUE;
220   if (CurrentIrql >= CoreDumpPortDeviceExtension->Interrupt->SynchLevel)
221     {
222       DbgPrint("DISKDUMP: Error: Crash inside high priority interrupt routine.\n");
223       return(STATUS_UNSUCCESSFUL);
224     }  
225   CoreDumpPortDeviceExtension->Interrupt->ServiceRoutine = DiskDumpIsr;
226   
227   return(STATUS_SUCCESS);
228 }
229
230 NTSTATUS STDCALL
231 DiskDumpFinish(VOID)
232 {
233   return(STATUS_SUCCESS);
234 }
235
236 NTSTATUS STDCALL
237 DiskDumpWrite(LARGE_INTEGER Address, PMDL Mdl)
238 {
239   KIRQL OldIrql, OldIrql2;
240   KIRQL CurrentIrql = KeGetCurrentIrql();
241
242   if (CurrentIrql < (CoreDumpPortDeviceExtension->Interrupt->SynchLevel - 1))
243     {
244       KeRaiseIrql(CoreDumpPortDeviceExtension->Interrupt->SynchLevel - 1, &OldIrql);
245     }  
246
247   /* Adjust the address for the start of the partition. */
248   Address.QuadPart +=
249     (CoreDumpClass2DeviceExtension->StartingOffset.QuadPart + CoreDumpClass2DeviceExtension->DMByteSkew);
250
251   /* Assume the device is always able to transfer a page so no need to split up the transfer. */
252
253   /* Build an SRB to describe the write. */
254   DiskDumpBuildRequest(Address, Mdl);
255
256   /* Start i/o on the HBA. */
257   IrqComplete = IrqNextRequest = FALSE;
258   KeRaiseIrql(CoreDumpPortDeviceExtension->Interrupt->SynchLevel, &OldIrql2);
259   if (!CoreDumpPortDeviceExtension->HwStartIo(&CoreDumpPortDeviceExtension->MiniPortDeviceExtension,
260                                               &CoreDumpSrb))
261     {
262       KeLowerIrql(OldIrql);
263       DbgPrint("DISKDUMP: Error: Miniport HwStartIo failed.\n");
264       return(STATUS_UNSUCCESSFUL);
265     }
266   KeLowerIrql(OldIrql2);
267
268   /* Wait for the miniport to finish. */
269   __asm__ ("sti\n\t");
270   while (!IrqComplete || !IrqNextRequest)
271     {
272       __asm__ ("hlt\n\t");
273     }  
274   if (CurrentIrql < (CoreDumpPortDeviceExtension->Interrupt->SynchLevel - 1))
275     {
276       KeLowerIrql(OldIrql);
277     }
278   __asm__("cli\n\t");
279
280   /* Check the result. */
281   if (SRB_STATUS(CoreDumpSrb.SrbStatus) != SRB_STATUS_SUCCESS)
282     {
283       DbgPrint("DISKDUMP: Error: SRB failed.\n");
284       return(STATUS_UNSUCCESSFUL);
285     }
286   return(STATUS_SUCCESS);
287 }
288
289 NTSTATUS STDCALL
290 DiskDumpPrepare(PDEVICE_OBJECT StorageDevice, PDUMP_POINTERS DumpPointers)
291 {
292   PIMAGE_NT_HEADERS NtHeader;
293   PVOID ImportDirectory;
294   ULONG ImportDirectorySize;
295   PIMAGE_IMPORT_MODULE_DIRECTORY ImportModuleDirectory;
296   PVOID DriverBase;
297   PCH Name;
298   ULONG i;
299   ULONG Hint;
300   PVOID* ImportAddressList;
301   PULONG FunctionNameList;
302
303   /* Save the information from the kernel. */
304   CoreDumpClassDevice = StorageDevice;
305   CoreDumpPointers = *DumpPointers;
306   CoreDumpClass2DeviceExtension = (PDEVICE_EXTENSION)CoreDumpClassDevice->DeviceExtension;
307   CoreDumpPortDevice = DumpPointers->DeviceObject;
308   CoreDumpPortDeviceExtension = CoreDumpPortDevice->DeviceExtension;
309
310   /* Replace all the miniport driver's imports with our functions. */
311   DriverBase = CoreDumpPortDevice->DriverObject->DriverStart;
312   NtHeader = RtlImageNtHeader(DriverBase);
313   ImportDirectory = RtlImageDirectoryEntryToData(DriverBase,
314                                                  TRUE,
315                                                  IMAGE_DIRECTORY_ENTRY_IMPORT,
316                                                  &ImportDirectorySize);
317   if (ImportDirectory == NULL || ImportDirectorySize == 0)
318     {
319       DbgPrint("DISKDUMP: Error: Miniport has no imports?\n");
320       return(STATUS_UNSUCCESSFUL);
321     }
322   /*  Process each import module  */
323   ImportModuleDirectory = (PIMAGE_IMPORT_MODULE_DIRECTORY)ImportDirectory;
324   DPRINT("Processeing import directory at %p\n", ImportModuleDirectory);
325   while (ImportModuleDirectory->dwRVAModuleName)
326     {
327       /*  Check to make sure that import lib is kernel  */
328       Name = (PCHAR) DriverBase + ImportModuleDirectory->dwRVAModuleName;
329
330       if (strcmp(Name, "scsiport.sys") != 0)
331         {
332           DbgPrint("DISKDUMP: Warning: Miniport has illegal imports.\n");
333           ImportModuleDirectory++;
334           continue;
335         }
336
337       /*  Get the import address list  */
338       ImportAddressList = (PVOID *) ((PUCHAR)DriverBase + 
339                                      ImportModuleDirectory->dwRVAFunctionAddressList);
340       
341       /*  Get the list of functions to import  */
342       if (ImportModuleDirectory->dwRVAFunctionNameList != 0)
343         {
344           FunctionNameList = (PULONG) ((PUCHAR)DriverBase + 
345                                        ImportModuleDirectory->dwRVAFunctionNameList);
346         }
347       else
348         {
349           FunctionNameList = (PULONG) ((PUCHAR)DriverBase + 
350                                        ImportModuleDirectory->dwRVAFunctionAddressList);
351         }
352       /*  Walk through function list and fixup addresses  */
353       while (*FunctionNameList != 0L)
354         {
355           if ((*FunctionNameList) & 0x80000000) // hint
356             {
357               Name = NULL;
358                       
359               Hint = (*FunctionNameList) & 0xffff;
360             }
361           else // hint-name
362             {
363               Name = (PCHAR)((DWORD)DriverBase + 
364                               *FunctionNameList + 2);
365               Hint = *(PWORD)((DWORD)DriverBase + *FunctionNameList);
366             }
367           DPRINT("  Hint:%04x  Name:%s\n", Hint, pName);
368
369           for (i = 0; i < (sizeof(DiskDumpExports) / sizeof(DiskDumpExports[0])); i++)
370             {
371               if (DiskDumpExports[i].Ordinal == Hint ||
372                   (Name != NULL && strcmp(DiskDumpExports[i].Name, Name) == 0))
373                 {
374                   break;
375                 }
376             }
377           if (i == (sizeof(DiskDumpExports) / sizeof(DiskDumpExports[0])))
378             {
379               DbgPrint("DISKDUMP: Error: Miniport imports unknown symbol %s.\n", Name);
380               return(STATUS_UNSUCCESSFUL);
381             }
382           if (strcmp(Name, "ScsiPortNotification") == 0)
383             {
384               OldScsiPortNotification = *ImportAddressList;
385             }
386           DiskDumpExports[i].OldFunction = *ImportAddressList;
387           if (DiskDumpExports[i].NewFunction != NULL)
388             {         
389               *ImportAddressList = DiskDumpExports[i].NewFunction;
390             }
391           
392           ImportAddressList++;
393           FunctionNameList++;
394         }
395       ImportModuleDirectory++;
396     }
397   return(STATUS_SUCCESS);
398 }
399
400 /**********************************************************************
401  * NAME                                                 EXPORTED
402  *      DriverEntry
403  *
404  * DESCRIPTION
405  *      This function initializes the driver, locates and claims 
406  *      hardware resources, and creates various NT objects needed
407  *      to process I/O requests.
408  *
409  * RUN LEVEL
410  *      PASSIVE_LEVEL
411  *
412  * ARGUMENTS
413  *      DriverObject
414  *              System allocated Driver Object for this driver
415  *
416  *      RegistryPath
417  *              Name of registry driver service key
418  *
419  * RETURN VALUE
420  *      Status
421  */
422
423 NTSTATUS STDCALL
424 DriverEntry(IN PDRIVER_OBJECT DriverObject,
425             IN PUNICODE_STRING RegistryPath)
426 {
427   DiskDumpDriver = DriverObject;
428   return(STATUS_SUCCESS);
429 }
430
431
432 /* EOF */