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