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.

827 lines
20 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. memory.c
  5. Abstract:
  6. This module implements the routines which add and remove physical memory
  7. from the system.
  8. Author:
  9. Dave Richards (daveri) 16-Aug-1999
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "pnpmem.h"
  15. //
  16. // MM uses STATUS_NOT_SUPPORTED to indicate that the memory manager is
  17. // not configured for dynamic memory insertion/removal.
  18. // Unfortunately, this same error code has special meaning to PNP, so
  19. // propagating it blindly from MM is unwise.
  20. //
  21. #define MAP_MMERROR(x) (x == STATUS_NOT_SUPPORTED ? STATUS_UNSUCCESSFUL : x)
  22. #define rgzReservedResources L"\\Registry\\Machine\\Hardware\\ResourceMap\\System Resources\\Loader Reserved"
  23. #define rgzReservedResourcesValue L".Raw"
  24. NTSTATUS
  25. PmAddPhysicalMemoryRange(
  26. IN PDEVICE_OBJECT DeviceObject,
  27. IN ULONGLONG Start,
  28. IN ULONGLONG End
  29. );
  30. VOID
  31. PmLogAddError(
  32. IN PDEVICE_OBJECT DeviceObject,
  33. IN ULONGLONG Start,
  34. IN ULONGLONG Size,
  35. IN NTSTATUS Status
  36. );
  37. NTSTATUS
  38. PmRemovePhysicalMemoryRange(
  39. IN ULONGLONG Start,
  40. IN ULONGLONG End
  41. );
  42. PPM_RANGE_LIST
  43. PmRetrieveReservedMemoryResources(
  44. VOID
  45. );
  46. #ifdef ALLOC_PRAGMA
  47. #pragma alloc_text(PAGE, PmAddPhysicalMemoryRange)
  48. #pragma alloc_text(PAGE, PmAddPhysicalMemory)
  49. #pragma alloc_text(PAGE, PmGetRegistryValue)
  50. #pragma alloc_text(PAGE, PmLogAddError)
  51. #pragma alloc_text(PAGE, PmRetrieveReservedMemoryResources)
  52. #pragma alloc_text(PAGE, PmTrimReservedMemory)
  53. #pragma alloc_text(PAGE, PmRemovePhysicalMemoryRange)
  54. #pragma alloc_text(PAGE, PmRemovePhysicalMemory)
  55. #endif
  56. VOID
  57. PmDumpOsMemoryRanges(PWSTR Prefix)
  58. {
  59. PPHYSICAL_MEMORY_RANGE memoryRanges;
  60. ULONG i;
  61. ULONGLONG start, end;
  62. memoryRanges = MmGetPhysicalMemoryRanges();
  63. if (memoryRanges == NULL) {
  64. return;
  65. }
  66. for (i = 0; memoryRanges[i].NumberOfBytes.QuadPart != 0; i++) {
  67. start = memoryRanges[i].BaseAddress.QuadPart;
  68. end = start + (memoryRanges[i].NumberOfBytes.QuadPart - 1);
  69. PmPrint((PNPMEM_MEMORY, "%ws: OS Range range 0x%16I64X to 0x%16I64X\n",
  70. Prefix, start, end));
  71. }
  72. ExFreePool(memoryRanges);
  73. }
  74. NTSTATUS
  75. PmGetRegistryValue(
  76. IN HANDLE KeyHandle,
  77. IN PWSTR ValueName,
  78. OUT PKEY_VALUE_PARTIAL_INFORMATION *Information
  79. )
  80. /*++
  81. Routine Description:
  82. This routine is invoked to retrieve the data for a registry key's value.
  83. This is done by querying the value of the key with a zero-length buffer
  84. to determine the size of the value, and then allocating a buffer and
  85. actually querying the value into the buffer.
  86. It is the responsibility of the caller to free the buffer.
  87. Arguments:
  88. KeyHandle - Supplies the key handle whose value is to be queried
  89. ValueName - Supplies the null-terminated Unicode name of the value.
  90. Information - Returns a pointer to the allocated data buffer.
  91. Return Value:
  92. The function value is the final status of the query operation.
  93. --*/
  94. {
  95. UNICODE_STRING unicodeString;
  96. NTSTATUS status;
  97. PKEY_VALUE_PARTIAL_INFORMATION infoBuffer;
  98. ULONG keyValueLength;
  99. PAGED_CODE();
  100. RtlInitUnicodeString( &unicodeString, ValueName );
  101. //
  102. // Figure out how big the data value is so that a buffer of the
  103. // appropriate size can be allocated.
  104. //
  105. status = ZwQueryValueKey( KeyHandle,
  106. &unicodeString,
  107. KeyValuePartialInformation,
  108. (PVOID) NULL,
  109. 0,
  110. &keyValueLength );
  111. //
  112. // handle highly unlikely case of a value that is zero sized.
  113. //
  114. if (NT_SUCCESS(status)) {
  115. return STATUS_UNSUCCESSFUL;
  116. }
  117. if (status != STATUS_BUFFER_OVERFLOW &&
  118. status != STATUS_BUFFER_TOO_SMALL) {
  119. return status;
  120. }
  121. //
  122. // Allocate a buffer large enough to contain the entire key data value.
  123. //
  124. infoBuffer = ExAllocatePool(PagedPool,
  125. keyValueLength);
  126. if (!infoBuffer) {
  127. return STATUS_INSUFFICIENT_RESOURCES;
  128. }
  129. //
  130. // Query the data for the key value.
  131. //
  132. status = ZwQueryValueKey( KeyHandle,
  133. &unicodeString,
  134. KeyValuePartialInformation,
  135. infoBuffer,
  136. keyValueLength,
  137. &keyValueLength );
  138. if (!NT_SUCCESS( status )) {
  139. ExFreePool( infoBuffer );
  140. return status;
  141. }
  142. //
  143. // Everything worked, so simply return the address of the allocated
  144. // buffer to the caller, who is now responsible for freeing it.
  145. //
  146. *Information = infoBuffer;
  147. return STATUS_SUCCESS;
  148. }
  149. PPM_RANGE_LIST
  150. PmRetrieveReservedMemoryResources(
  151. VOID
  152. )
  153. {
  154. OBJECT_ATTRIBUTES objectAttributes;
  155. UNICODE_STRING unicodeString;
  156. PKEY_VALUE_PARTIAL_INFORMATION valueInfo = NULL;
  157. PPM_RANGE_LIST reservedResourceRanges = NULL;
  158. HANDLE hReserved = NULL;
  159. NTSTATUS status;
  160. PAGED_CODE();
  161. RtlInitUnicodeString (&unicodeString, rgzReservedResources);
  162. InitializeObjectAttributes (&objectAttributes,
  163. &unicodeString,
  164. OBJ_CASE_INSENSITIVE,
  165. NULL, // handle
  166. NULL);
  167. status = ZwOpenKey(&hReserved, KEY_READ, &objectAttributes);
  168. if (!NT_SUCCESS(status)) {
  169. goto Error;
  170. }
  171. status = PmGetRegistryValue(hReserved,
  172. rgzReservedResourcesValue,
  173. &valueInfo);
  174. if (!NT_SUCCESS(status)) {
  175. goto Error;
  176. }
  177. if (valueInfo->Type != REG_RESOURCE_LIST) {
  178. goto Error;
  179. }
  180. reservedResourceRanges =
  181. PmCreateRangeListFromCmResourceList((PCM_RESOURCE_LIST) valueInfo->Data);
  182. // fall through
  183. Error:
  184. if (hReserved != NULL) {
  185. ZwClose(hReserved);
  186. }
  187. if (valueInfo != NULL) {
  188. ExFreePool(valueInfo);
  189. }
  190. return reservedResourceRanges;
  191. }
  192. VOID
  193. PmTrimReservedMemory(
  194. IN PPM_DEVICE_EXTENSION DeviceExtension,
  195. IN PPM_RANGE_LIST *PossiblyNewMemory
  196. )
  197. {
  198. PPM_RANGE_LIST reservedMemoryList = NULL, newList = NULL;
  199. ULONG i;
  200. NTSTATUS status;
  201. BOOLEAN bResult = FALSE;
  202. PAGED_CODE();
  203. if (*PossiblyNewMemory == NULL) {
  204. return;
  205. }
  206. reservedMemoryList = PmRetrieveReservedMemoryResources();
  207. if (reservedMemoryList == NULL) {
  208. goto Error;
  209. }
  210. newList = PmIntersectRangeList(reservedMemoryList, *PossiblyNewMemory);
  211. if (newList == NULL) {
  212. goto Error;
  213. }
  214. if (PmIsRangeListEmpty(newList)) {
  215. goto Cleanup;
  216. }
  217. DeviceExtension->FailQueryRemoves = TRUE;
  218. PmFreeRangeList(newList);
  219. newList = PmSubtractRangeList(*PossiblyNewMemory,
  220. reservedMemoryList);
  221. if (newList) {
  222. PmFreeRangeList(*PossiblyNewMemory);
  223. *PossiblyNewMemory = newList;
  224. newList = NULL;
  225. goto Cleanup;
  226. }
  227. //
  228. // Fall through to error case where we ensure that we don't
  229. // innocently tell the OS to use memory that is reserved.
  230. //
  231. Error:
  232. PmFreeRangeList(*PossiblyNewMemory);
  233. *PossiblyNewMemory = NULL;
  234. DeviceExtension->FailQueryRemoves = TRUE;
  235. Cleanup:
  236. if (reservedMemoryList != NULL) {
  237. PmFreeRangeList(reservedMemoryList);
  238. }
  239. if (newList != NULL) {
  240. PmFreeRangeList(newList);
  241. }
  242. return;
  243. }
  244. VOID
  245. PmLogAddError(
  246. IN PDEVICE_OBJECT DeviceObject,
  247. IN ULONGLONG Start,
  248. IN ULONGLONG Size,
  249. IN NTSTATUS Status
  250. )
  251. /*++
  252. Routine Description:
  253. This function logs a failure to add memory
  254. Arguments:
  255. DeviceObject - Device object object for which the memory add
  256. failed.
  257. Start - The start of the physical memory range.
  258. Size - The size of the physical memory range.
  259. Status - Status code returned by MM.
  260. Return Value:
  261. None.
  262. --*/
  263. {
  264. PIO_ERROR_LOG_PACKET packet;
  265. PWCHAR stringBuffer;
  266. UCHAR packetSize;
  267. int offset;
  268. //
  269. // Allocate a packet with space for 2 16 character hex value
  270. // strings including null terminators for each.
  271. //
  272. packetSize = sizeof(IO_ERROR_LOG_PACKET) + (sizeof(WCHAR)*(16+1))*2;
  273. packet = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry(DeviceObject,
  274. packetSize);
  275. if (packet == NULL) {
  276. return;
  277. }
  278. packet->DumpDataSize = 0;
  279. packet->NumberOfStrings = 2;
  280. packet->StringOffset = sizeof(IO_ERROR_LOG_PACKET);
  281. packet->ErrorCode = PNPMEM_ERR_FAILED_TO_ADD_MEMORY;
  282. packet->FinalStatus = Status;
  283. stringBuffer = (PWCHAR) (packet + 1);
  284. offset = swprintf(stringBuffer, L"%I64X", Start);
  285. swprintf(stringBuffer + offset + 1, L"%I64X", Size);
  286. IoWriteErrorLogEntry(packet);
  287. }
  288. NTSTATUS
  289. PmAddPhysicalMemoryRange(
  290. IN PDEVICE_OBJECT DeviceObject,
  291. IN ULONGLONG Start,
  292. IN ULONGLONG End
  293. )
  294. /*++
  295. Routine Description:
  296. This function uses MmAddPhysicalMemory to notify the memory manager that
  297. physical memory is available.
  298. Arguments:
  299. DeviceObject - device object of the memory device this range is part of.
  300. Start - The start of the physical memory range.
  301. End - The end of the physical memory range.
  302. Return Value:
  303. None.
  304. --*/
  305. {
  306. NTSTATUS PrevStatus;
  307. NTSTATUS Status;
  308. ULONGLONG Size;
  309. ULONGLONG CurrentSize;
  310. PHYSICAL_ADDRESS StartAddress;
  311. LARGE_INTEGER NumberOfBytes;
  312. PAGED_CODE();
  313. ASSERT((Start & (PAGE_SIZE - 1)) == 0);
  314. ASSERT((End & (PAGE_SIZE - 1)) == (PAGE_SIZE - 1));
  315. //
  316. // This loop attempts to add the memory specified in the
  317. // arguments:
  318. //
  319. // If an attempt to add memory fails, a chunk half as large will
  320. // be tried next iteration until a chunk succeeds or the whole add
  321. // operation fails.
  322. //
  323. // If an attempt to add memory succeeds, a chunk twice as large
  324. // (bounded by the original range) will be tried next iteration.
  325. //
  326. // Loop ends when the original range is exhausted or the
  327. // addition of a range fails completely.
  328. //
  329. PrevStatus = Status = STATUS_SUCCESS;
  330. CurrentSize = Size = End - Start + 1;
  331. while (Size > 0) {
  332. StartAddress.QuadPart = Start;
  333. NumberOfBytes.QuadPart = CurrentSize;
  334. //
  335. // MmAddPhysicalMemory() adds the specified physical address
  336. // range to the system. If any bytes are added,
  337. // STATUS_SUCCESS is returned and the NumberOfBytes field is
  338. // updated to reflect the number of bytes actually added.
  339. //
  340. Status = MmAddPhysicalMemory(
  341. &StartAddress,
  342. &NumberOfBytes
  343. );
  344. if (NT_SUCCESS(Status)) {
  345. ASSERT((ULONGLONG)StartAddress.QuadPart == Start);
  346. ASSERT((NumberOfBytes.QuadPart & (PAGE_SIZE - 1)) == 0);
  347. ASSERT((ULONGLONG)NumberOfBytes.QuadPart <= CurrentSize);
  348. Start += NumberOfBytes.QuadPart;
  349. Size -= NumberOfBytes.QuadPart;
  350. //
  351. // If successful this iteration and the previous, then add
  352. // twice as much next time.
  353. //
  354. // Trim next attempt to reflect the remaining memory.
  355. //
  356. if (NT_SUCCESS(PrevStatus)) {
  357. CurrentSize <<= 1;
  358. }
  359. if (CurrentSize > Size) {
  360. CurrentSize = Size;
  361. }
  362. } else {
  363. //
  364. // Failed to add a range. Halve the amount we're going to
  365. // try to add next time. Breaks out if we're trying to
  366. // add less than a page.
  367. //
  368. CurrentSize = (CurrentSize >> 1) & ~(PAGE_SIZE - 1);
  369. if (CurrentSize < PAGE_SIZE) {
  370. break;
  371. }
  372. }
  373. PrevStatus = Status;
  374. }
  375. //
  376. // If the last add operation we attempted failed completely, then
  377. // log the error for posterity.
  378. //
  379. if (!NT_SUCCESS(Status)) {
  380. PmLogAddError(DeviceObject, Start, Size, Status);
  381. PmPrint((DPFLTR_WARNING_LEVEL | PNPMEM_MEMORY,
  382. "Failed to add physical range 0x%I64X for 0x%I64X bytes\n",
  383. Start, Size));
  384. }
  385. //
  386. // We don't know what portion of the range we succeeded in adding,
  387. // and which we failed. Attempt in this case is all that matters.
  388. //
  389. return STATUS_SUCCESS;
  390. }
  391. NTSTATUS
  392. PmAddPhysicalMemory(
  393. IN PDEVICE_OBJECT DeviceObject,
  394. IN PPM_RANGE_LIST PossiblyNewMemory
  395. )
  396. /*++
  397. Routine Description:
  398. This function adds the physical memory in the PM_RANGE_LIST which
  399. the memory manager does not yet know about to the system. This
  400. requires getting a snapshot of the physical page map, then computing
  401. the set difference between the range list and the snapshot. The
  402. difference represents the memory the memory manager does not yet know
  403. about.
  404. Arguments:
  405. PossiblyNewMemory - The range list of physical addresses to be
  406. added. This memory may already be known to the system depending
  407. on whether the machine POSTed with this memory installed.
  408. Return Value:
  409. NTSTATUS
  410. --*/
  411. {
  412. PPM_RANGE_LIST knownPhysicalMemory, newMemory;
  413. NTSTATUS Status;
  414. NTSTATUS AddStatus;
  415. PLIST_ENTRY ListEntry;
  416. PPM_RANGE_LIST_ENTRY RangeListEntry;
  417. PAGED_CODE();
  418. //
  419. // Find out what physical memory regions MM already knows about
  420. //
  421. knownPhysicalMemory = PmCreateRangeListFromPhysicalMemoryRanges();
  422. if (knownPhysicalMemory == NULL) {
  423. return STATUS_INSUFFICIENT_RESOURCES;
  424. }
  425. //
  426. // Any memory in the ranges provided by this device that is
  427. // already known to MM is assumed to come from this device.
  428. // Presumeably the OS was handed this memory by firmware/POST.
  429. //
  430. // Find out what memory is contained by this device that MM
  431. // doesn't already know about by subtracting the MM physical
  432. // ranges from our device's memory ranges.
  433. //
  434. newMemory = PmSubtractRangeList(
  435. PossiblyNewMemory,
  436. knownPhysicalMemory
  437. );
  438. PmFreeRangeList(knownPhysicalMemory);
  439. //
  440. // Either we succeeded in getting a list of memory ranges to add
  441. // (including a possible null list) or we failed due to
  442. // insufficient resources. The latter represents a problem since
  443. // a memory shortage may be keeping us from adding memory to
  444. // relieve the memory shortage.
  445. //
  446. if (newMemory != NULL) {
  447. for (ListEntry = newMemory->List.Flink;
  448. ListEntry != &newMemory->List;
  449. ListEntry = ListEntry->Flink) {
  450. RangeListEntry = CONTAINING_RECORD(
  451. ListEntry,
  452. PM_RANGE_LIST_ENTRY,
  453. ListEntry
  454. );
  455. AddStatus = PmAddPhysicalMemoryRange(
  456. DeviceObject,
  457. RangeListEntry->Start,
  458. RangeListEntry->End
  459. );
  460. if (!NT_SUCCESS(AddStatus)) {
  461. PmPrint((DPFLTR_WARNING_LEVEL | PNPMEM_MEMORY,
  462. "Failed to add physical range 0x%I64X to 0x%I64X\n",
  463. RangeListEntry->Start, RangeListEntry->End));
  464. }
  465. }
  466. PmFreeRangeList(newMemory);
  467. }
  468. return STATUS_SUCCESS;
  469. }
  470. NTSTATUS
  471. PmRemovePhysicalMemoryRange(
  472. IN ULONGLONG Start,
  473. IN ULONGLONG End
  474. )
  475. /*++
  476. Routine Description:
  477. This function uses MmRemovePhysicalMemory to notify the memory manager that
  478. physical memory is no longer available.
  479. Arguments:
  480. Start - The start of the physical memory range.
  481. End - The end of the physical memory range.
  482. Return Value:
  483. None.
  484. --*/
  485. {
  486. #if 0
  487. NTSTATUS PrevStatus;
  488. NTSTATUS Status;
  489. ULONGLONG Size;
  490. ULONGLONG CurrentSize;
  491. PHYSICAL_ADDRESS StartAddress;
  492. LARGE_INTEGER NumberOfBytes;
  493. ASSERT((Start & (PAGE_SIZE - 1)) == 0);
  494. ASSERT((End & (PAGE_SIZE - 1)) == (PAGE_SIZE - 1));
  495. PrevStatus = Status = STATUS_SUCCESS;
  496. CurrentSize = Size = End - Start + 1;
  497. while (Size > 0) {
  498. StartAddress.QuadPart = Start;
  499. NumberOfBytes.QuadPart = CurrentSize;
  500. Status = MmRemovePhysicalMemory(
  501. &StartAddress,
  502. &NumberOfBytes
  503. );
  504. Status = MAP_MMERROR(Status);
  505. if (NT_SUCCESS(Status)) {
  506. ASSERT((ULONGLONG)StartAddress.QuadPart == Start);
  507. ASSERT((NumberOfBytes.QuadPart & (PAGE_SIZE - 1)) == 0);
  508. ASSERT((ULONGLONG)NumberOfBytes.QuadPart <= CurrentSize);
  509. Start += NumberOfBytes.QuadPart;
  510. Size -= NumberOfBytes.QuadPart;
  511. if (NT_SUCCESS(PrevStatus)) {
  512. CurrentSize <<= 1;
  513. }
  514. if (CurrentSize > Size) {
  515. CurrentSize = Size;
  516. }
  517. } else {
  518. CurrentSize = (CurrentSize >> 1) & ~(PAGE_SIZE - 1);
  519. if (CurrentSize < PAGE_SIZE) {
  520. break;
  521. }
  522. }
  523. PrevStatus = Status;
  524. }
  525. #else
  526. ULONGLONG Size;
  527. PHYSICAL_ADDRESS StartAddress;
  528. LARGE_INTEGER NumberOfBytes;
  529. NTSTATUS Status;
  530. PAGED_CODE();
  531. Size = (End - Start) + 1;
  532. StartAddress.QuadPart = Start;
  533. NumberOfBytes.QuadPart = Size;
  534. Status = MmRemovePhysicalMemory(
  535. &StartAddress,
  536. &NumberOfBytes
  537. );
  538. Status = MAP_MMERROR(Status);
  539. #endif
  540. //
  541. // If it failed, routine automatically readds the memory in
  542. // question.
  543. //
  544. return Status;
  545. }
  546. NTSTATUS
  547. PmRemovePhysicalMemory(
  548. IN PPM_RANGE_LIST RemoveMemoryList
  549. )
  550. /*++
  551. Routine Description:
  552. This function removes the physical memory in the PM_RANGE_LIST which
  553. the memory manager is currently using from the system. This
  554. requires getting a snapshot of the physical page map, then computing
  555. the set intersection between the source range list and the snapshot.
  556. The intersection represents the memory the memory manager needs to
  557. stop using.
  558. Arguments:
  559. RemoveMemoryList - The range list of physical addresses to be removed.
  560. Return Value:
  561. None.
  562. --*/
  563. {
  564. PPM_RANGE_LIST physicalMemoryList, inuseMemoryList;
  565. NTSTATUS Status;
  566. PLIST_ENTRY ListEntry;
  567. PPM_RANGE_LIST_ENTRY RangeListEntry;
  568. PAGED_CODE();
  569. //
  570. // Remove the intersection between what the OS knows about and
  571. // what we are providing.
  572. //
  573. physicalMemoryList = PmCreateRangeListFromPhysicalMemoryRanges();
  574. if (physicalMemoryList == NULL) {
  575. return STATUS_INSUFFICIENT_RESOURCES;
  576. }
  577. inuseMemoryList = PmIntersectRangeList(
  578. RemoveMemoryList,
  579. physicalMemoryList
  580. );
  581. if (inuseMemoryList != NULL) {
  582. Status = STATUS_SUCCESS;
  583. for (ListEntry = inuseMemoryList->List.Flink;
  584. ListEntry != &inuseMemoryList->List;
  585. ListEntry = ListEntry->Flink) {
  586. RangeListEntry = CONTAINING_RECORD(
  587. ListEntry,
  588. PM_RANGE_LIST_ENTRY,
  589. ListEntry
  590. );
  591. Status = PmRemovePhysicalMemoryRange(
  592. RangeListEntry->Start,
  593. RangeListEntry->End
  594. );
  595. if (!NT_SUCCESS(Status)) {
  596. //
  597. // If we failed to remove a particular range, bail
  598. // now. Code above should re-add the memory list if
  599. // appropriate i.e assume that some ranges may have
  600. // been removed successfully.
  601. //
  602. break;
  603. }
  604. }
  605. PmFreeRangeList(inuseMemoryList);
  606. } else {
  607. Status = STATUS_INSUFFICIENT_RESOURCES;
  608. }
  609. PmFreeRangeList(physicalMemoryList);
  610. return Status;
  611. }