Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

662 lines
14 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. mmap.cpp
  5. Abstract:
  6. Implementation of memory map class.
  7. Author:
  8. Matthew D Hendel (math) 16-Sep-1999
  9. Revision History:
  10. --*/
  11. #include "ntsdp.hpp"
  12. #define MEMORY_MAP_CHECK (0xDEADF00D)
  13. typedef struct _MEMORY_MAP_ENTRY
  14. {
  15. ULONG64 BaseOfRegion;
  16. ULONG SizeOfRegion;
  17. PVOID Region;
  18. PVOID UserData;
  19. BOOL AllowOverlap;
  20. struct _MEMORY_MAP_ENTRY * Next;
  21. } MEMORY_MAP_ENTRY, * PMEMORY_MAP_ENTRY;
  22. typedef struct _MEMORY_MAP
  23. {
  24. PMEMORY_MAP_ENTRY List;
  25. ULONG Check;
  26. ULONG RegionCount;
  27. } MEMORY_MAP, * PMEMORY_MAP;
  28. PMEMORY_MAP MemoryMap;
  29. PMEMORY_MAP_ENTRY
  30. MemoryMap_FindPreceedingRegion(
  31. ULONG64 Addr
  32. );
  33. PMEMORY_MAP_ENTRY
  34. MemoryMap_FindContainingRegion(
  35. ULONG64 Addr
  36. );
  37. BOOL
  38. MemoryMap_Create(
  39. VOID
  40. )
  41. /*++
  42. --*/
  43. {
  44. MemoryMap = (PMEMORY_MAP) malloc ( sizeof (MEMORY_MAP) );
  45. if (!MemoryMap)
  46. {
  47. return FALSE;
  48. }
  49. MemoryMap->Check = MEMORY_MAP_CHECK;
  50. MemoryMap->RegionCount = 0;
  51. MemoryMap->List = NULL;
  52. return TRUE;
  53. }
  54. BOOL
  55. MemoryMap_Destroy(
  56. VOID
  57. )
  58. /*++
  59. --*/
  60. {
  61. if (MemoryMap == NULL)
  62. {
  63. return TRUE;
  64. }
  65. PMEMORY_MAP_ENTRY Entry;
  66. PMEMORY_MAP_ENTRY Next;
  67. Entry = MemoryMap->List;
  68. while ( Entry != NULL )
  69. {
  70. Next = Entry->Next;
  71. free ( Entry );
  72. Entry = Next;
  73. }
  74. MemoryMap->Check = 0;
  75. free ( MemoryMap );
  76. MemoryMap = NULL;
  77. return TRUE;
  78. }
  79. PMEMORY_MAP_ENTRY
  80. AddMapEntry(ULONG64 BaseOfRegion, ULONG SizeOfRegion,
  81. PVOID Buffer, PVOID UserData,
  82. BOOL AllowOverlap)
  83. {
  84. PMEMORY_MAP_ENTRY PrevEntry;
  85. PMEMORY_MAP_ENTRY MapEntry;
  86. MapEntry = (MEMORY_MAP_ENTRY *)malloc ( sizeof ( MEMORY_MAP_ENTRY ) );
  87. if (!MapEntry)
  88. {
  89. return NULL;
  90. }
  91. MapEntry->BaseOfRegion = BaseOfRegion;
  92. MapEntry->SizeOfRegion = SizeOfRegion;
  93. MapEntry->Region = Buffer;
  94. MapEntry->UserData = UserData;
  95. MapEntry->AllowOverlap = AllowOverlap;
  96. MapEntry->Next = NULL;
  97. //
  98. // Insert the element.
  99. //
  100. PrevEntry = MemoryMap_FindPreceedingRegion(BaseOfRegion);
  101. if ( PrevEntry == NULL )
  102. {
  103. //
  104. // Insert at head.
  105. //
  106. MapEntry->Next = MemoryMap->List;
  107. MemoryMap->List = MapEntry;
  108. }
  109. else
  110. {
  111. //
  112. // Insert in order.
  113. //
  114. MapEntry->Next = PrevEntry->Next;
  115. PrevEntry->Next = MapEntry;
  116. }
  117. MemoryMap->RegionCount++;
  118. return MapEntry;
  119. }
  120. HRESULT
  121. MemoryMap_AddRegion(
  122. IN ULONG64 BaseOfRegion,
  123. IN ULONG SizeOfRegion,
  124. IN PVOID Buffer,
  125. IN PVOID UserData,
  126. IN BOOL AllowOverlap
  127. )
  128. /*++
  129. Routine Description:
  130. Add a region to a memory map. The region must not overlap with any
  131. other region that is already contained in the map.
  132. Return Values:
  133. TRUE - On success.
  134. FALSE - On failure.
  135. --*/
  136. {
  137. PMEMORY_MAP_ENTRY PrevEntry;
  138. PMEMORY_MAP_ENTRY MapEntry;
  139. //
  140. // The region size cannot be zero.
  141. //
  142. if (SizeOfRegion == 0)
  143. {
  144. ErrOut("**** MemoryMap_AddRegion: Empty region being added.\n");
  145. return S_OK;
  146. }
  147. if (IsBadReadPtr(Buffer, SizeOfRegion))
  148. {
  149. ErrOut("**** MemoryMap_AddRegion: Mapping too small to map "
  150. "%s:%X from %p\n", FormatAddr64(BaseOfRegion),
  151. SizeOfRegion, Buffer);
  152. return E_INVALIDARG;
  153. }
  154. if (MemoryMap == NULL)
  155. {
  156. ErrOut("Memory map not initialized\n");
  157. return E_NOINTERFACE;
  158. }
  159. ULONG64 EndOfRegion;
  160. ULONG64 EndOfEntryRegion;
  161. PMEMORY_MAP_ENTRY Entry;
  162. ULONG Size;
  163. while (SizeOfRegion > 0)
  164. {
  165. //
  166. // Find the first overlapping region.
  167. // We need to rescan the whole list as it
  168. // may have changed due to insertion of fragments
  169. // of the new region.
  170. //
  171. EndOfRegion = BaseOfRegion + SizeOfRegion;
  172. for (Entry = MemoryMap->List; Entry != NULL; Entry = Entry->Next)
  173. {
  174. EndOfEntryRegion = Entry->BaseOfRegion + Entry->SizeOfRegion;
  175. if (EndOfRegion > Entry->BaseOfRegion &&
  176. Entry->BaseOfRegion >= BaseOfRegion)
  177. {
  178. if (AllowOverlap || Entry->AllowOverlap)
  179. {
  180. // Overlap can occur when a stack, ethread or
  181. // eprocess is taken from static data in an image.
  182. // For example, the x86 idle process is static
  183. // data in ntoskrnl. Triage dumps contain the
  184. // eprocess data and it gets mapped and can overlap
  185. // with the ntoskrnl image.
  186. #if 0
  187. WarnOut("WARNING: Allowing overlapped region %s - %s\n",
  188. FormatAddr64(BaseOfRegion),
  189. FormatAddr64(BaseOfRegion + SizeOfRegion - 1));
  190. #endif
  191. Entry = NULL;
  192. }
  193. break;
  194. }
  195. }
  196. if (Entry == NULL ||
  197. BaseOfRegion < Entry->BaseOfRegion)
  198. {
  199. // There's a portion of the beginning of the new
  200. // region which does not overlap so add it and
  201. // trim the description.
  202. Size = Entry == NULL ? SizeOfRegion :
  203. (ULONG)(Entry->BaseOfRegion - BaseOfRegion);
  204. if (!AddMapEntry(BaseOfRegion, Size, Buffer, UserData,
  205. AllowOverlap))
  206. {
  207. return E_OUTOFMEMORY;
  208. }
  209. if (Size == SizeOfRegion)
  210. {
  211. // None of the region overlapped so we're done.
  212. return S_OK;
  213. }
  214. BaseOfRegion += Size;
  215. SizeOfRegion -= Size;
  216. Buffer = (PUCHAR)Buffer + Size;
  217. }
  218. //
  219. // Now handle the overlap.
  220. //
  221. if (SizeOfRegion > Entry->SizeOfRegion)
  222. {
  223. Size = Entry->SizeOfRegion;
  224. }
  225. else
  226. {
  227. Size = SizeOfRegion;
  228. }
  229. if (memcmp(Buffer, Entry->Region, Size))
  230. {
  231. ErrOut("**** MemoryMap_AddRegion: "
  232. "Conflicting region %s - %s\n",
  233. FormatAddr64(BaseOfRegion),
  234. FormatAddr64(BaseOfRegion + SizeOfRegion - 1));
  235. return HR_REGION_CONFLICT;
  236. }
  237. // Overlap region data matched so it's OK to just
  238. // trim the overlap out of the new region and move
  239. // on to the next possible overlap.
  240. BaseOfRegion += Size;
  241. SizeOfRegion -= Size;
  242. Buffer = (PUCHAR)Buffer + Size;
  243. }
  244. return S_OK;
  245. }
  246. BOOL
  247. MemoryMap_ReadMemory(
  248. IN ULONG64 BaseOfRange,
  249. IN OUT PVOID Buffer,
  250. IN ULONG SizeOfRange,
  251. OUT ULONG * BytesRead
  252. )
  253. /*++
  254. Routine Description:
  255. Read memory from the memory map. ReadMemory can read across regions, as
  256. long as there is no unallocated space between regions.
  257. This routine will do a partial read if the region ends before
  258. SizeOfRange bytes have been read. In that case BytesRead will return the
  259. number of bytes actually read.
  260. Arguments:
  261. BaseOfRange - The base address where we want to read memory from.
  262. SizeOfRange - The length of the region to read memory from.
  263. Buffer - A pointer to a buffer to read memory into.
  264. BytesRead - On success, the number of bytes successfully read.
  265. Return Values:
  266. TRUE - If any number of bytes were successfully read from the memory map.
  267. FALSE - If no bytes were read.
  268. --*/
  269. {
  270. ULONG BytesToReadFromRegion;
  271. PMEMORY_MAP_ENTRY Entry;
  272. ULONG64 BaseToRead;
  273. ULONG SizeToRead;
  274. PBYTE BufferForRead;
  275. ULONG_PTR OffsetToRead;
  276. ULONG Read;
  277. ULONG AvailSize;
  278. //
  279. // We return TRUE if we read any bytes successfully and FALSE if not.
  280. //
  281. *BytesRead = 0;
  282. BaseToRead = BaseOfRange;
  283. SizeToRead = SizeOfRange;
  284. BufferForRead = (PBYTE) Buffer;
  285. do
  286. {
  287. Entry = MemoryMap_FindContainingRegion(BaseToRead);
  288. if ( !Entry )
  289. {
  290. if (*BytesRead)
  291. {
  292. return TRUE;
  293. }
  294. else
  295. {
  296. return FALSE;
  297. }
  298. }
  299. PMEMORY_MAP_ENTRY NextEntry = Entry->Next;
  300. // Due to overlap there may be other entries
  301. // that need to be processed even before the
  302. // end of the containing region.
  303. AvailSize = Entry->SizeOfRegion;
  304. while (NextEntry != NULL)
  305. {
  306. if (NextEntry->BaseOfRegion > BaseToRead)
  307. {
  308. ULONG64 EntryDiff =
  309. NextEntry->BaseOfRegion - Entry->BaseOfRegion;
  310. if (EntryDiff < AvailSize)
  311. {
  312. AvailSize = (ULONG)EntryDiff;
  313. }
  314. break;
  315. }
  316. NextEntry = NextEntry->Next;
  317. }
  318. if (BaseToRead + SizeToRead > Entry->BaseOfRegion + AvailSize)
  319. {
  320. BytesToReadFromRegion = (ULONG)
  321. (Entry->BaseOfRegion - BaseToRead) + AvailSize;
  322. }
  323. else
  324. {
  325. BytesToReadFromRegion = SizeToRead;
  326. }
  327. OffsetToRead = (ULONG_PTR) (BaseToRead - Entry->BaseOfRegion);
  328. __try
  329. {
  330. RtlCopyMemory (BufferForRead,
  331. (PBYTE)Entry->Region + OffsetToRead,
  332. BytesToReadFromRegion
  333. );
  334. }
  335. __except(MappingExceptionFilter(GetExceptionInformation()))
  336. {
  337. return FALSE;
  338. }
  339. *BytesRead += BytesToReadFromRegion;
  340. BaseToRead += BytesToReadFromRegion;
  341. SizeToRead -= BytesToReadFromRegion;
  342. BufferForRead += BytesToReadFromRegion;
  343. } while ( SizeToRead );
  344. return TRUE;
  345. }
  346. BOOL
  347. MemoryMap_GetRegionInfo(
  348. IN ULONG64 Addr,
  349. OUT ULONG64* BaseOfRegion, OPTIONAL
  350. OUT ULONG* SizeOfRegion, OPTIONAL
  351. OUT PVOID* Buffer, OPTIONAL
  352. OUT PVOID* UserData OPTIONAL
  353. )
  354. /*++
  355. Routine Description:
  356. Get information about the region containing the address Addr.
  357. Arguments:
  358. Addr - An address that is contained within some region in the map.
  359. BaseOfRegion - Pointer to a buffer to return the region base.
  360. SizeOfRegion - Pointer to a buffer to retutn the region size.
  361. Buffer - Pointer to a buffer to return the region buffer pointer.
  362. UserData - Pointer to a buffer to return the region client param.
  363. Return Values:
  364. TRUE - On success.
  365. FALSE - On failure.
  366. --*/
  367. {
  368. PMEMORY_MAP_ENTRY Entry;
  369. Entry = MemoryMap_FindContainingRegion(Addr);
  370. if ( !Entry )
  371. {
  372. return FALSE;
  373. }
  374. if ( BaseOfRegion )
  375. {
  376. *BaseOfRegion = Entry->BaseOfRegion;
  377. }
  378. if ( SizeOfRegion )
  379. {
  380. *SizeOfRegion = Entry->SizeOfRegion;
  381. }
  382. if ( Buffer )
  383. {
  384. *Buffer = Entry->Region;
  385. }
  386. if ( UserData )
  387. {
  388. *UserData = Entry->UserData;
  389. }
  390. return TRUE;
  391. }
  392. BOOL
  393. MemoryMap_RemoveRegion(
  394. IN ULONG64 BaseOfRegion,
  395. IN ULONG SizeOfRegion
  396. )
  397. {
  398. if (MemoryMap == NULL)
  399. {
  400. return FALSE;
  401. }
  402. // XXX drewb - This should carve the given region out of
  403. // any existing regions. Right now we don't need general
  404. // removal functionality though so only handle the case
  405. // where the requested removal is an exact single region
  406. // match.
  407. PMEMORY_MAP_ENTRY PrevEntry;
  408. PMEMORY_MAP_ENTRY Entry;
  409. PrevEntry = MemoryMap_FindPreceedingRegion(BaseOfRegion);
  410. if (PrevEntry != NULL)
  411. {
  412. Entry = PrevEntry->Next;
  413. }
  414. else
  415. {
  416. Entry = MemoryMap->List;
  417. }
  418. if (Entry == NULL)
  419. {
  420. ErrOut("MemoryMap_RemoveRegion NULL region for %s:%x\n",
  421. FormatAddr64(BaseOfRegion), SizeOfRegion);
  422. return FALSE;
  423. }
  424. else if (Entry->BaseOfRegion != BaseOfRegion ||
  425. Entry->SizeOfRegion != SizeOfRegion)
  426. {
  427. ErrOut("MemoryMap_RemoveRegion region mismatch: "
  428. "%s:%x vs. entry %s:%x\n",
  429. FormatAddr64(BaseOfRegion), SizeOfRegion,
  430. FormatAddr64(Entry->BaseOfRegion), Entry->SizeOfRegion);
  431. return FALSE;
  432. }
  433. if (PrevEntry == NULL)
  434. {
  435. MemoryMap->List = Entry->Next;
  436. }
  437. else
  438. {
  439. PrevEntry->Next = Entry->Next;
  440. }
  441. free(Entry);
  442. MemoryMap->RegionCount--;
  443. return TRUE;
  444. }
  445. //
  446. // Private functions
  447. //
  448. PMEMORY_MAP_ENTRY
  449. MemoryMap_FindPreceedingRegion(
  450. IN ULONG64 BaseOfRegion
  451. )
  452. {
  453. if (MemoryMap == NULL)
  454. {
  455. return NULL;
  456. }
  457. PMEMORY_MAP_ENTRY PrevEntry;
  458. PMEMORY_MAP_ENTRY Entry;
  459. PrevEntry = NULL;
  460. Entry = MemoryMap->List;
  461. while (Entry != NULL)
  462. {
  463. //
  464. // Assuming they're in order.
  465. //
  466. if ( Entry->BaseOfRegion >= BaseOfRegion )
  467. {
  468. return PrevEntry;
  469. }
  470. PrevEntry = Entry;
  471. Entry = Entry->Next;
  472. }
  473. return PrevEntry;
  474. }
  475. PMEMORY_MAP_ENTRY
  476. MemoryMap_FindContainingRegion(
  477. IN ULONG64 Addr
  478. )
  479. {
  480. if (MemoryMap == NULL)
  481. {
  482. return NULL;
  483. }
  484. PMEMORY_MAP_ENTRY Entry;
  485. PMEMORY_MAP_ENTRY ReturnEntry = NULL;
  486. Entry = MemoryMap->List;
  487. //
  488. // We may have overlapping regions, so keep going until we find
  489. // the most precise one (assumed to be the one we care about)
  490. //
  491. while ( Entry != NULL )
  492. {
  493. if ( Entry->BaseOfRegion <= Addr &&
  494. Addr < Entry->BaseOfRegion + Entry->SizeOfRegion)
  495. {
  496. ReturnEntry = Entry;
  497. }
  498. else if (ReturnEntry != NULL &&
  499. Addr >= ReturnEntry->BaseOfRegion + ReturnEntry->SizeOfRegion)
  500. {
  501. // Optimization - we can stop searching if we've already
  502. // found a block and have now left its region. We can't
  503. // stop as long as we're in its region as there may
  504. // be more exact overlaps anywhere within the entire region.
  505. break;
  506. }
  507. Entry = Entry->Next;
  508. }
  509. return ReturnEntry;
  510. }