crashdmp.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. /*++
  2. Copyright (c) 2014 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. crashdmp.c
  9. Abstract:
  10. This module implements support for collecting and writing out crash dump
  11. data in the unfortunate event of a fatal system error.
  12. Author:
  13. Chris Stevens 26-Aug-2014
  14. Environment:
  15. Kernel
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include <minoca/kernel/kernel.h>
  21. #include <minoca/kernel/crashdmp.h>
  22. #include <minoca/intrface/disk.h>
  23. #include "kep.h"
  24. //
  25. // ---------------------------------------------------------------- Definitions
  26. //
  27. //
  28. // ------------------------------------------------------ Data Type Definitions
  29. //
  30. /*++
  31. Structure Description:
  32. This structure defines the saved context for a crash dump file.
  33. Members:
  34. ListEntry - Stores pointers to the next and previous crash dump files.
  35. FileHandle - Stores a handle to the open file.
  36. FileSize - Stores the size of the file, in bytes.
  37. BlockIoContext - Stores the I/O context necessary to perform block-level
  38. writes to the crash dump file.
  39. Device - Stores a pointer to the device the crash dump file ultimately
  40. writes to.
  41. DiskInterface - Stores a pointer to the disk interface.
  42. --*/
  43. typedef struct _CRASH_DUMP_FILE {
  44. LIST_ENTRY ListEntry;
  45. PIO_HANDLE FileHandle;
  46. ULONGLONG FileSize;
  47. FILE_BLOCK_IO_CONTEXT BlockIoContext;
  48. PDEVICE Device;
  49. PDISK_INTERFACE DiskInterface;
  50. } CRASH_DUMP_FILE, *PCRASH_DUMP_FILE;
  51. //
  52. // ----------------------------------------------- Internal Function Prototypes
  53. //
  54. VOID
  55. KepDestroyCrashDumpFile (
  56. PCRASH_DUMP_FILE CrashDumpFile
  57. );
  58. USHORT
  59. KepCalculateChecksum (
  60. PVOID Data,
  61. ULONG DataSize
  62. );
  63. VOID
  64. KepCrashDumpDiskInterfaceNotificationCallback (
  65. PVOID Context,
  66. PDEVICE Device,
  67. PVOID InterfaceBuffer,
  68. ULONG InterfaceBufferSize,
  69. BOOL Arrival
  70. );
  71. //
  72. // -------------------------------------------------------------------- Globals
  73. //
  74. //
  75. // Store a list of available crash dump files and a lock to protect them.
  76. //
  77. KSPIN_LOCK KeCrashDumpListLock;
  78. LIST_ENTRY KeCrashDumpListHead;
  79. //
  80. // Store the UUID of the disk interface.
  81. //
  82. UUID KeDiskInterfaceUuid = UUID_DISK_INTERFACE;
  83. //
  84. // Store a scratch I/O buffer used to write portions of the crash dump file.
  85. //
  86. PIO_BUFFER KeCrashDumpScratchBuffer;
  87. //
  88. // Store a boolean indicating whether to write all crash dump files or just
  89. // stop at the first successfully written one.
  90. //
  91. BOOL KeWriteAllCrashDumpFiles;
  92. //
  93. // ------------------------------------------------------------------ Functions
  94. //
  95. VOID
  96. KeRegisterCrashDumpFile (
  97. HANDLE Handle,
  98. BOOL Register
  99. )
  100. /*++
  101. Routine Description:
  102. This routine registers a file for use as a crash dump file.
  103. Arguments:
  104. Handle - Supplies a handle to the page file to register.
  105. Register - Supplies a boolean indicating if the page file is registering
  106. (TRUE) or de-registering (FALSE).
  107. Return Value:
  108. None.
  109. --*/
  110. {
  111. PFILE_BLOCK_INFORMATION BlockInformation;
  112. PFILE_BLOCK_IO_CONTEXT BlockIoContext;
  113. PCRASH_DUMP_FILE CrashDumpFile;
  114. PLIST_ENTRY CurrentEntry;
  115. PDEVICE DiskDevice;
  116. PDISK_INTERFACE DiskInterface;
  117. PVOID DiskToken;
  118. ULONGLONG FileSize;
  119. PIO_HANDLE IoHandle;
  120. PCRASH_DUMP_FILE NewCrashDumpFile;
  121. PCRASH_DUMP_FILE OldCrashDumpFile;
  122. KSTATUS Status;
  123. PDEVICE Volume;
  124. BlockInformation = NULL;
  125. IoHandle = (PIO_HANDLE)Handle;
  126. NewCrashDumpFile = NULL;
  127. OldCrashDumpFile = NULL;
  128. //
  129. // If registering the page file, then look up necessary information and
  130. // create the crash dump file.
  131. //
  132. if (Register != FALSE) {
  133. Status = IoGetDevice(IoHandle, &Volume);
  134. if (!KSUCCESS(Status)) {
  135. goto RegisterCrashDumpFileEnd;
  136. }
  137. DiskDevice = IoGetDiskDevice(Volume);
  138. ASSERT(DiskDevice != NULL);
  139. //
  140. // Query the file system to get a list of device offsets and sizes for
  141. // the location of the page file.
  142. //
  143. Status = IoGetFileBlockInformation(IoHandle, &BlockInformation);
  144. if (!KSUCCESS(Status)) {
  145. goto RegisterCrashDumpFileEnd;
  146. }
  147. //
  148. // Create the crash dump file structure.
  149. //
  150. NewCrashDumpFile = MmAllocateNonPagedPool(sizeof(CRASH_DUMP_FILE),
  151. KE_ALLOCATION_TAG);
  152. if (NewCrashDumpFile == NULL) {
  153. Status = STATUS_INSUFFICIENT_RESOURCES;
  154. goto RegisterCrashDumpFileEnd;
  155. }
  156. RtlZeroMemory(NewCrashDumpFile, sizeof(CRASH_DUMP_FILE));
  157. NewCrashDumpFile->FileHandle = IoHandle;
  158. Status = IoGetFileSize(IoHandle, &FileSize);
  159. if (!KSUCCESS(Status)) {
  160. goto RegisterCrashDumpFileEnd;
  161. }
  162. NewCrashDumpFile->FileSize = FileSize;
  163. NewCrashDumpFile->Device = DiskDevice;
  164. BlockIoContext = &(NewCrashDumpFile->BlockIoContext);
  165. BlockIoContext->FileBlockInformation = BlockInformation;
  166. BlockInformation = NULL;
  167. Status = IoRegisterForInterfaceNotifications(
  168. &KeDiskInterfaceUuid,
  169. KepCrashDumpDiskInterfaceNotificationCallback,
  170. NewCrashDumpFile->Device,
  171. NewCrashDumpFile,
  172. TRUE);
  173. if (!KSUCCESS(Status)) {
  174. goto RegisterCrashDumpFileEnd;
  175. }
  176. //
  177. // If the interface wasn't immediately filled in, then fail.
  178. //
  179. DiskInterface = NewCrashDumpFile->DiskInterface;
  180. if (DiskInterface == NULL) {
  181. Status = STATUS_NOT_SUPPORTED;
  182. goto RegisterCrashDumpFileEnd;
  183. }
  184. //
  185. // The crash dump file is ready to go. Give the disk a heads up that
  186. // it's block I/O routines may be called into action. This gives it a
  187. // chance to allocate any memory it may need later.
  188. //
  189. if (DiskInterface->BlockIoInitialize != NULL) {
  190. DiskToken = DiskInterface->DiskToken;
  191. Status = DiskInterface->BlockIoInitialize(DiskToken);
  192. if (!KSUCCESS(Status)) {
  193. goto RegisterCrashDumpFileEnd;
  194. }
  195. }
  196. }
  197. //
  198. // Search for a crash file with the same handle.
  199. //
  200. KeAcquireSpinLock(&KeCrashDumpListLock);
  201. CurrentEntry = KeCrashDumpListHead.Next;
  202. while (CurrentEntry != &KeCrashDumpListHead) {
  203. CrashDumpFile = LIST_VALUE(CurrentEntry, CRASH_DUMP_FILE, ListEntry);
  204. if (CrashDumpFile->FileHandle == IoHandle) {
  205. OldCrashDumpFile = CrashDumpFile;
  206. LIST_REMOVE(CurrentEntry);
  207. break;
  208. }
  209. CurrentEntry = CurrentEntry->Next;
  210. }
  211. if (NewCrashDumpFile != NULL) {
  212. INSERT_BEFORE(&(NewCrashDumpFile->ListEntry), &KeCrashDumpListHead);
  213. }
  214. KeReleaseSpinLock(&KeCrashDumpListLock);
  215. if (OldCrashDumpFile != NULL) {
  216. KepDestroyCrashDumpFile(CrashDumpFile);
  217. }
  218. Status = STATUS_SUCCESS;
  219. RegisterCrashDumpFileEnd:
  220. if (!KSUCCESS(Status)) {
  221. if (NewCrashDumpFile != NULL) {
  222. KepDestroyCrashDumpFile(NewCrashDumpFile);
  223. }
  224. if (BlockInformation != NULL) {
  225. IoDestroyFileBlockInformation(BlockInformation);
  226. }
  227. }
  228. return;
  229. }
  230. KSTATUS
  231. KepInitializeCrashDumpSupport (
  232. VOID
  233. )
  234. /*++
  235. Routine Description:
  236. This routine initializes system crash dump support.
  237. Arguments:
  238. None.
  239. Return Value:
  240. Status code.
  241. --*/
  242. {
  243. ULONG IoBufferFlags;
  244. ULONG PageSize;
  245. INITIALIZE_LIST_HEAD(&KeCrashDumpListHead);
  246. KeInitializeSpinLock(&KeCrashDumpListLock);
  247. PageSize = MmPageSize();
  248. ASSERT(PageSize >= sizeof(CRASH_DUMP_HEADER));
  249. IoBufferFlags = IO_BUFFER_FLAG_PHYSICALLY_CONTIGUOUS;
  250. KeCrashDumpScratchBuffer = MmAllocateNonPagedIoBuffer(0,
  251. MAX_ULONGLONG,
  252. 0,
  253. PageSize,
  254. IoBufferFlags);
  255. if (KeCrashDumpScratchBuffer == NULL) {
  256. return STATUS_INSUFFICIENT_RESOURCES;
  257. }
  258. return STATUS_SUCCESS;
  259. }
  260. KSTATUS
  261. KepWriteCrashDump (
  262. ULONG CrashCode,
  263. ULONGLONG Parameter1,
  264. ULONGLONG Parameter2,
  265. ULONGLONG Parameter3,
  266. ULONGLONG Parameter4
  267. )
  268. /*++
  269. Routine Description:
  270. This routine writes crash dump data to disk.
  271. Arguments:
  272. CrashCode - Supplies the reason for the system crash.
  273. Parameter1 - Supplies an optional parameter regarding the crash.
  274. Parameter2 - Supplies an optional parameter regarding the crash.
  275. Parameter3 - Supplies an optional parameter regarding the crash.
  276. Parameter4 - Supplies an optional parameter regarding the crash.
  277. Return Value:
  278. Status code.
  279. --*/
  280. {
  281. PFILE_BLOCK_IO_CONTEXT BlockIoContext;
  282. PDISK_BLOCK_IO_RESET BlockIoReset;
  283. PVOID Buffer;
  284. ULONG BufferSize;
  285. UINTN BytesCompleted;
  286. USHORT Checksum;
  287. PCRASH_DUMP_FILE CrashFile;
  288. PLIST_ENTRY CurrentEntry;
  289. PCRASH_DUMP_HEADER Header;
  290. KSTATUS Status;
  291. SYSTEM_VERSION_INFORMATION VersionInformation;
  292. if (LIST_EMPTY(&KeCrashDumpListHead)) {
  293. RtlDebugPrint("No registered crash dump files.\n");
  294. }
  295. Status = STATUS_SUCCESS;
  296. CurrentEntry = KeCrashDumpListHead.Next;
  297. while (CurrentEntry != &KeCrashDumpListHead) {
  298. CrashFile = LIST_VALUE(CurrentEntry, CRASH_DUMP_FILE, ListEntry);
  299. CurrentEntry = CurrentEntry->Next;
  300. //
  301. // If there's no interface for this one, skip it.
  302. //
  303. if (CrashFile->DiskInterface == NULL) {
  304. RtlDebugPrint("Skipping dump file 0x%x without interface.\n",
  305. CrashFile);
  306. continue;
  307. }
  308. //
  309. // The file should be big enough for the minimal dump.
  310. //
  311. ASSERT(CrashFile->FileSize >= sizeof(CRASH_DUMP_HEADER));
  312. //
  313. // Reset the disk for the crash dump, giving the device a heads up that
  314. // writes are about to come in.
  315. //
  316. BlockIoContext = &(CrashFile->BlockIoContext);
  317. BlockIoReset = BlockIoContext->BlockIoReset;
  318. if (BlockIoReset != NULL) {
  319. Status = BlockIoReset(CrashFile->BlockIoContext.DiskToken);
  320. if (!KSUCCESS(Status)) {
  321. goto WriteCrashDumpEnd;
  322. }
  323. }
  324. //
  325. // Copy the crash dump header to the scratch buffer.
  326. //
  327. ASSERT(KeCrashDumpScratchBuffer->FragmentCount == 1);
  328. ASSERT(KeCrashDumpScratchBuffer->Fragment[0].VirtualAddress != NULL);
  329. Header = KeCrashDumpScratchBuffer->Fragment[0].VirtualAddress;
  330. RtlZeroMemory(Header, sizeof(CRASH_DUMP_HEADER));
  331. Header->Signature = CRASH_DUMP_SIGNATURE;
  332. Header->Type = CrashDumpMinimal;
  333. Header->DumpSize = sizeof(CRASH_DUMP_HEADER);
  334. Header->CrashCode = CrashCode;
  335. Header->Parameter1 = Parameter1;
  336. Header->Parameter2 = Parameter2;
  337. Header->Parameter3 = Parameter3;
  338. Header->Parameter4 = Parameter4;
  339. //
  340. // Copy the system version information to the header if available.
  341. //
  342. Buffer = (PVOID)(Header + 1);
  343. BufferSize = KeCrashDumpScratchBuffer->Fragment[0].Size -
  344. sizeof(CRASH_DUMP_HEADER);
  345. Status = KeGetSystemVersion(&VersionInformation, Buffer, &BufferSize);
  346. if (KSUCCESS(Status)) {
  347. Header->MajorVersion = VersionInformation.MajorVersion;
  348. Header->MinorVersion = VersionInformation.MinorVersion;
  349. Header->Revision = VersionInformation.Revision;
  350. Header->SerialVersion = VersionInformation.SerialVersion;
  351. Header->ReleaseLevel = VersionInformation.ReleaseLevel;
  352. Header->DebugLevel = VersionInformation.DebugLevel;
  353. if (VersionInformation.ProductName != NULL) {
  354. Header->ProductNameOffset =
  355. (PVOID)VersionInformation.ProductName - (PVOID)Header;
  356. }
  357. if (VersionInformation.BuildString != NULL) {
  358. Header->BuildStringOffset =
  359. (PVOID)VersionInformation.BuildString - (PVOID)Header;
  360. }
  361. RtlCopyMemory(&(Header->BuildTime),
  362. &(VersionInformation.BuildTime),
  363. sizeof(SYSTEM_TIME));
  364. Header->DumpSize += BufferSize;
  365. }
  366. //
  367. // Calculate the header's checksum. Do not include the product and
  368. // build strings as they are outside the header.
  369. //
  370. Checksum = KepCalculateChecksum(Header, sizeof(CRASH_DUMP_HEADER));
  371. Header->HeaderChecksum = Checksum;
  372. //
  373. // Write the header out to the file.
  374. //
  375. Status = IoWriteFileBlocks(&(CrashFile->BlockIoContext),
  376. KeCrashDumpScratchBuffer,
  377. 0,
  378. (UINTN)Header->DumpSize,
  379. &BytesCompleted);
  380. if (!KSUCCESS(Status)) {
  381. RtlDebugPrint("Failed to write crash dump to file 0x%x: %d\n",
  382. CrashFile,
  383. Status);
  384. continue;
  385. }
  386. //
  387. // One crash dump file was successfully written. If that's all that's
  388. // requested, stop now.
  389. //
  390. if (KeWriteAllCrashDumpFiles == FALSE) {
  391. break;
  392. }
  393. }
  394. WriteCrashDumpEnd:
  395. return Status;
  396. }
  397. //
  398. // --------------------------------------------------------- Internal Functions
  399. //
  400. VOID
  401. KepCrashDumpDiskInterfaceNotificationCallback (
  402. PVOID Context,
  403. PDEVICE Device,
  404. PVOID InterfaceBuffer,
  405. ULONG InterfaceBufferSize,
  406. BOOL Arrival
  407. )
  408. /*++
  409. Routine Description:
  410. This routine is called when the disk interface associated with a crash
  411. dump file appears or disappears.
  412. Arguments:
  413. Context - Supplies the caller's context pointer, supplied when the caller
  414. requested interface notifications.
  415. Device - Supplies a pointer to the device exposing or deleting the
  416. interface.
  417. InterfaceBuffer - Supplies a pointer to the interface buffer of the
  418. interface.
  419. InterfaceBufferSize - Supplies the buffer size.
  420. Arrival - Supplies TRUE if a new interface is arriving, or FALSE if an
  421. interface is departing.
  422. Return Value:
  423. None.
  424. --*/
  425. {
  426. PFILE_BLOCK_IO_CONTEXT BlockIoContext;
  427. PDISK_INTERFACE DiskInterface;
  428. PCRASH_DUMP_FILE DumpFile;
  429. ASSERT(KeGetRunLevel() == RunLevelLow);
  430. DumpFile = Context;
  431. BlockIoContext = &(DumpFile->BlockIoContext);
  432. if (Arrival != FALSE) {
  433. DiskInterface = InterfaceBuffer;
  434. if ((InterfaceBufferSize < sizeof(DISK_INTERFACE)) ||
  435. (DiskInterface->Version < DISK_INTERFACE_VERSION)) {
  436. return;
  437. }
  438. //
  439. // It's not expected that the device would expose multiple disk
  440. // interfaces.
  441. //
  442. ASSERT(DumpFile->DiskInterface == NULL);
  443. DumpFile->DiskInterface = InterfaceBuffer;
  444. BlockIoContext->DiskToken = DiskInterface->DiskToken;
  445. BlockIoContext->BlockSize = DiskInterface->BlockSize;
  446. BlockIoContext->BlockCount = DiskInterface->BlockCount;
  447. BlockIoContext->BlockIoReset = DiskInterface->BlockIoReset;
  448. BlockIoContext->BlockIoRead = DiskInterface->BlockIoRead;
  449. BlockIoContext->BlockIoWrite = DiskInterface->BlockIoWrite;
  450. //
  451. // The interface is disappearing.
  452. //
  453. } else {
  454. DumpFile->DiskInterface = NULL;
  455. BlockIoContext->BlockIoRead = NULL;
  456. BlockIoContext->BlockIoWrite = NULL;
  457. BlockIoContext->BlockIoReset = NULL;
  458. }
  459. return;
  460. }
  461. VOID
  462. KepDestroyCrashDumpFile (
  463. PCRASH_DUMP_FILE CrashDumpFile
  464. )
  465. /*++
  466. Routine Description:
  467. This routine destroys a crash dump file.
  468. Arguments:
  469. CrashDumpFile - Supplies a pointer to the crash dump file to destroy.
  470. Return Value:
  471. None.
  472. --*/
  473. {
  474. PFILE_BLOCK_IO_CONTEXT BlockIoContext;
  475. KSTATUS Status;
  476. BlockIoContext = &(CrashDumpFile->BlockIoContext);
  477. if (BlockIoContext->FileBlockInformation != NULL) {
  478. IoDestroyFileBlockInformation(BlockIoContext->FileBlockInformation);
  479. }
  480. Status = IoUnregisterForInterfaceNotifications(
  481. &KeDiskInterfaceUuid,
  482. KepCrashDumpDiskInterfaceNotificationCallback,
  483. CrashDumpFile->Device,
  484. CrashDumpFile);
  485. if (!KSUCCESS(Status)) {
  486. ASSERT(FALSE);
  487. return;
  488. }
  489. MmFreeNonPagedPool(CrashDumpFile);
  490. return;
  491. }
  492. USHORT
  493. KepCalculateChecksum (
  494. PVOID Data,
  495. ULONG DataSize
  496. )
  497. /*++
  498. Routine Description:
  499. This routine calculates the one's compliment checksum of a data buffer.
  500. Arguments:
  501. Data - Supplies a pointer to the data on which to calculate the checksum.
  502. DataSize - Supplies the size of the data, in bytes.
  503. Return Value:
  504. Returns thee one's compliment checksum of the data.
  505. --*/
  506. {
  507. ULONG ShortIndex;
  508. PUSHORT ShortPointer;
  509. ULONG Sum;
  510. Sum = 0;
  511. //
  512. // Now checksum the actual header and data.
  513. //
  514. ShortPointer = (PUSHORT)Data;
  515. for (ShortIndex = 0;
  516. ShortIndex < DataSize / sizeof(USHORT);
  517. ShortIndex += 1) {
  518. Sum += ShortPointer[ShortIndex];
  519. }
  520. //
  521. // If the data size is odd, then grab the last byte.
  522. //
  523. if ((DataSize & 0x1) != 0) {
  524. Sum += *((PUCHAR)&(ShortPointer[ShortIndex]));
  525. }
  526. //
  527. // With one's complement arithmetic, every time a wraparound occurs the
  528. // carry must be added back in on the right (to skip over "negative zero").
  529. // Perform all these carries at once by adding in the high word. That
  530. // addition itself can also cause a wraparound, which is why the while loop
  531. // is there.
  532. //
  533. while ((Sum >> (sizeof(USHORT) * BITS_PER_BYTE)) != 0) {
  534. Sum = (Sum & 0xFFFF) + (Sum >> (sizeof(USHORT) * BITS_PER_BYTE));
  535. }
  536. //
  537. // The checksum is the one's complement of the sum.
  538. //
  539. return (USHORT)~Sum;
  540. }