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