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.

685 lines
16 KiB

  1. #include "ki.h"
  2. PVOID
  3. Ki386AllocateContiguousMemory(
  4. IN OUT PIDENTITY_MAP IdentityMap,
  5. IN ULONG Pages,
  6. IN BOOLEAN Low4Meg
  7. );
  8. BOOLEAN
  9. Ki386IdentityMapMakeValid(
  10. IN OUT PIDENTITY_MAP IdentityMap,
  11. IN PHARDWARE_PTE PageTableEntry,
  12. OUT PVOID *Page OPTIONAL
  13. );
  14. BOOLEAN
  15. Ki386MapAddress(
  16. IN OUT PIDENTITY_MAP IdentityMap,
  17. IN ULONG Va,
  18. IN PHYSICAL_ADDRESS PhysicalAddress
  19. );
  20. PVOID
  21. Ki386ConvertPte(
  22. IN OUT PHARDWARE_PTE Pte
  23. );
  24. PHYSICAL_ADDRESS
  25. Ki386BuildIdentityBuffer(
  26. IN OUT PIDENTITY_MAP IdentityMap,
  27. IN PVOID StartVa,
  28. IN ULONG Length,
  29. OUT PULONG PagesToMap
  30. );
  31. #ifdef ALLOC_PRAGMA
  32. #pragma alloc_text(INIT,Ki386AllocateContiguousMemory)
  33. #pragma alloc_text(INIT,Ki386BuildIdentityBuffer)
  34. #pragma alloc_text(INIT,Ki386ClearIdentityMap)
  35. #pragma alloc_text(INIT,Ki386ConvertPte)
  36. #pragma alloc_text(INIT,Ki386CreateIdentityMap)
  37. #pragma alloc_text(INIT,Ki386EnableTargetLargePage)
  38. #pragma alloc_text(INIT,Ki386IdentityMapMakeValid)
  39. #pragma alloc_text(INIT,Ki386MapAddress)
  40. #endif
  41. #define PTES_PER_PAGE (PAGE_SIZE / sizeof(HARDWARE_PTE))
  42. BOOLEAN
  43. Ki386CreateIdentityMap(
  44. IN OUT PIDENTITY_MAP IdentityMap,
  45. IN PVOID StartVa,
  46. IN PVOID EndVa
  47. )
  48. {
  49. /*++
  50. This function creates an identity mapping for a region of memory.
  51. If the region of memory passed in includes memory that lies above
  52. 4G, then a new buffer is allocated below 4G.
  53. Arguments:
  54. IdentityMap - Pointer to the structure which will be filled with the newly
  55. created top-level directory address. It also provides
  56. storage for the pointers used in alloating and freeing the
  57. memory.
  58. StartVa - Pointer to the first byte of the region of memory that is to be
  59. memory mapped.
  60. EndVa - Pointer to the byte immediately after the last byte of the region
  61. that is to be memory mapped.
  62. Return Value:
  63. TRUE if the function succeeds, FALSE otherwise.
  64. Note - Ki386ClearIdentityMap() should be called even on FALSE return to
  65. free any memory allocated.
  66. --*/
  67. ULONG pageDirectoryIndex;
  68. ULONG pagesToMap;
  69. PCHAR currentVa;
  70. ULONG length;
  71. BOOLEAN result;
  72. PHARDWARE_PTE pageDirectory;
  73. PHARDWARE_PTE pageDirectoryEntry;
  74. PHYSICAL_ADDRESS identityAddress;
  75. #if defined(_X86PAE_)
  76. ULONG pageDirectoryPointerTableIndex;
  77. PHARDWARE_PTE pageDirectoryPointerTable;
  78. PHARDWARE_PTE pageDirectoryPointerTableEntry;
  79. #endif
  80. //
  81. // Initialize the IdentityMap structure to a known state.
  82. //
  83. RtlZeroMemory( IdentityMap, sizeof(IDENTITY_MAP) );
  84. length = (PCHAR)EndVa - (PCHAR)StartVa;
  85. //
  86. // Get the physical address of the input buffer (or suitable copy).
  87. //
  88. identityAddress = Ki386BuildIdentityBuffer( IdentityMap,
  89. StartVa,
  90. length,
  91. &pagesToMap );
  92. if( identityAddress.QuadPart == 0) {
  93. //
  94. // The input buffer was not contiguous or not below 4G, and a
  95. // suitable buffer could not be allocated.
  96. //
  97. return FALSE;
  98. }
  99. IdentityMap->IdentityAddr = identityAddress.LowPart;
  100. //
  101. // Set up the mappings.
  102. //
  103. currentVa = StartVa;
  104. do {
  105. //
  106. // Map in the virtual address
  107. //
  108. result = Ki386MapAddress( IdentityMap,
  109. (ULONG)currentVa,
  110. identityAddress );
  111. if (result == FALSE) {
  112. return FALSE;
  113. }
  114. //
  115. // Map in the identity (physical) address
  116. //
  117. result = Ki386MapAddress( IdentityMap,
  118. identityAddress.LowPart,
  119. identityAddress );
  120. if (result == FALSE) {
  121. return FALSE;
  122. }
  123. //
  124. // Advance both the Va and identityAddress pointers in anticipation
  125. // of mapping in another page.
  126. //
  127. currentVa += PAGE_SIZE;
  128. identityAddress.QuadPart += PAGE_SIZE;
  129. pagesToMap -= 1;
  130. } while (pagesToMap > 0);
  131. //
  132. // Now go through the page directory pointer table and page directories,
  133. // converting virtual page frames to physical ones.
  134. //
  135. #if defined(_X86PAE_)
  136. //
  137. // This PAE-only outer loop walks the page directory pointer table entries
  138. // and processes each valid page directory referenced.
  139. //
  140. pageDirectoryPointerTable = IdentityMap->TopLevelDirectory;
  141. for (pageDirectoryPointerTableIndex = 0;
  142. pageDirectoryPointerTableIndex < (1 << PPI_BITS);
  143. pageDirectoryPointerTableIndex++) {
  144. pageDirectoryPointerTableEntry =
  145. &pageDirectoryPointerTable[ pageDirectoryPointerTableIndex ];
  146. if (pageDirectoryPointerTableEntry->Valid == 0) {
  147. continue;
  148. }
  149. pageDirectory =
  150. (PHARDWARE_PTE)Ki386ConvertPte( pageDirectoryPointerTableEntry );
  151. #else
  152. pageDirectory = IdentityMap->TopLevelDirectory;
  153. #endif
  154. for (pageDirectoryIndex = 0;
  155. pageDirectoryIndex < PTES_PER_PAGE;
  156. pageDirectoryIndex++) {
  157. pageDirectoryEntry = &pageDirectory[ pageDirectoryIndex ];
  158. if (pageDirectoryEntry->Valid == 0) {
  159. continue;
  160. }
  161. Ki386ConvertPte( pageDirectoryEntry );
  162. }
  163. #if defined(_X86PAE_)
  164. }
  165. #endif
  166. identityAddress = MmGetPhysicalAddress( IdentityMap->TopLevelDirectory );
  167. IdentityMap->IdentityCR3 = identityAddress.LowPart;
  168. return TRUE;
  169. }
  170. PVOID
  171. Ki386AllocateContiguousMemory(
  172. IN OUT PIDENTITY_MAP IdentityMap,
  173. IN ULONG Pages,
  174. IN BOOLEAN Low4Meg
  175. )
  176. /*++
  177. This function allocates page-aligned, physically contiguous memory.
  178. The allocation is recorded in the IdentityMap structure, so that it
  179. can be freed on cleanup.
  180. Arguments:
  181. IdentityMap - Context pointer for this identity mapping.
  182. Pages - Number of pages to allocate
  183. Low4Meg - Indicates whether the allocation must be below 4M.
  184. Return Value:
  185. Pointer to the new page on success, NULL otherwise.
  186. --*/
  187. {
  188. ULONG pageListIndex;
  189. PVOID page;
  190. ULONG allocationSize;
  191. PHYSICAL_ADDRESS highestAddress;
  192. if (Low4Meg != FALSE) {
  193. //
  194. // The caller has specified that a page must reside physically
  195. // below 4 MB.
  196. //
  197. highestAddress.LowPart = 0xFFFFFFFF;
  198. highestAddress.HighPart = 0;
  199. } else {
  200. //
  201. // Memory can reside anywhere
  202. //
  203. highestAddress.LowPart = 0xFFFFFFFF;
  204. highestAddress.HighPart = 0xFFFFFFFF;
  205. }
  206. allocationSize = Pages * PAGE_SIZE;
  207. page = MmAllocateContiguousMemory( allocationSize, highestAddress );
  208. if (page != NULL) {
  209. //
  210. // Record that this page was allocated so that it can be freed when
  211. // the IdentityMap structure is cleared.
  212. //
  213. pageListIndex = IdentityMap->PagesAllocated;
  214. IdentityMap->PageList[ pageListIndex ] = page;
  215. IdentityMap->PagesAllocated++;
  216. //
  217. // Initialize it.
  218. //
  219. RtlZeroMemory( page, allocationSize );
  220. }
  221. return page;
  222. }
  223. BOOLEAN
  224. Ki386IdentityMapMakeValid(
  225. IN OUT PIDENTITY_MAP IdentityMap,
  226. IN PHARDWARE_PTE PageTableEntry,
  227. OUT PVOID *Page OPTIONAL
  228. )
  229. /*++
  230. If the page table has the valid bit set, this function merely returns
  231. the address referenced by the page table entry.
  232. If the page table does not have the valid bit set, then another page
  233. is allocated and inserted into the page table entry and the entry is
  234. marked valid.
  235. NOTE: At this point, PTE frames are virtual. After the entire mapping
  236. is built, we go through and convert all virtual frames to physical
  237. ones.
  238. Arguments:
  239. IdentityMap - Context pointer for this identity mapping.
  240. PageTableEntry - Pointer to the page table entry.
  241. Page - Virtual address now referenced by the PTE, whether it was
  242. valid before or not.
  243. Return Value:
  244. TRUE on success, FALSE otherwise.
  245. --*/
  246. {
  247. PVOID page;
  248. if (PageTableEntry->Valid != 0) {
  249. //
  250. // If it already is present, there is nothing to do except record
  251. // the virtual page number that is already there.
  252. //
  253. page = (PVOID)((ULONG)(PageTableEntry->PageFrameNumber << PAGE_SHIFT));
  254. } else {
  255. //
  256. // The page table entry is not valid. Allocate a new page table.
  257. //
  258. page = Ki386AllocateContiguousMemory( IdentityMap, 1, FALSE );
  259. if (page == NULL) {
  260. return FALSE;
  261. }
  262. //
  263. // Insert it into the page table entry and mark it valid.
  264. //
  265. // NOTE: Virtual page numbers are inserted into the page table
  266. // structure as it is being built. When it is finished, we walk
  267. // the tables and convert all of the virtual page numbers to
  268. // physical page numbers.
  269. //
  270. PageTableEntry->PageFrameNumber = ((ULONG)page) >> PAGE_SHIFT;
  271. PageTableEntry->Valid = 1;
  272. }
  273. if (ARGUMENT_PRESENT( Page )) {
  274. *Page = page;
  275. }
  276. return TRUE;
  277. }
  278. BOOLEAN
  279. Ki386MapAddress(
  280. IN OUT PIDENTITY_MAP IdentityMap,
  281. IN ULONG Va,
  282. IN PHYSICAL_ADDRESS PhysicalAddress
  283. )
  284. /*++
  285. Creates a new virtual->physical mapping in the identity map.
  286. Arguments:
  287. IdentityMap - Context pointer for this identity mapping.
  288. Va - Virtual address to map.
  289. PhysicalAddress - Physical address to map.
  290. Return Value:
  291. TRUE on success, FALSE otherwise.
  292. --*/
  293. {
  294. PHARDWARE_PTE pageTable;
  295. PHARDWARE_PTE pageTableEntry;
  296. PHARDWARE_PTE pageDirectory;
  297. PHARDWARE_PTE pageDirectoryEntry;
  298. PVOID table;
  299. ULONG index;
  300. BOOLEAN result;
  301. #if defined(_X86PAE_)
  302. PHARDWARE_PTE pageDirectoryPointerTable;
  303. PHARDWARE_PTE pageDirectoryPointerTableEntry;
  304. #endif
  305. if (IdentityMap->TopLevelDirectory == NULL) {
  306. //
  307. // Allocate a top-level directory structure, either a page directory
  308. // or a page directory pointer table.
  309. //
  310. table = Ki386AllocateContiguousMemory( IdentityMap, 1, TRUE );
  311. if (table == FALSE) {
  312. return FALSE;
  313. }
  314. IdentityMap->TopLevelDirectory = table;
  315. }
  316. #if defined(_X86PAE_)
  317. index = KiGetPpeIndex( Va );
  318. pageDirectoryPointerTable = IdentityMap->TopLevelDirectory;
  319. pageDirectoryPointerTableEntry = &pageDirectoryPointerTable[ index ];
  320. result = Ki386IdentityMapMakeValid( IdentityMap,
  321. pageDirectoryPointerTableEntry,
  322. &pageDirectory );
  323. if (result == FALSE) {
  324. return FALSE;
  325. }
  326. #else
  327. pageDirectory = IdentityMap->TopLevelDirectory;
  328. #endif
  329. //
  330. // Get a pointer to the appropriate page directory entry. If it is
  331. // not valid, allocate a new page table and mark the page directory
  332. // entry valid and writeable.
  333. //
  334. index = KiGetPdeIndex( Va );
  335. pageDirectoryEntry = &pageDirectory[ index ];
  336. result = Ki386IdentityMapMakeValid( IdentityMap,
  337. pageDirectoryEntry,
  338. &pageTable );
  339. if (result == FALSE) {
  340. return FALSE;
  341. }
  342. pageDirectoryEntry->Write = 1;
  343. //
  344. // Get a pointer to the appropriate page table entry and fill it in.
  345. //
  346. index = KiGetPteIndex( Va );
  347. pageTableEntry = &pageTable[ index ];
  348. #if defined(_X86PAE_)
  349. pageTableEntry->PageFrameNumber = PhysicalAddress.QuadPart >> PAGE_SHIFT;
  350. #else
  351. pageTableEntry->PageFrameNumber = PhysicalAddress.LowPart >> PAGE_SHIFT;
  352. #endif
  353. pageTableEntry->Valid = 1;
  354. return TRUE;
  355. }
  356. PVOID
  357. Ki386ConvertPte(
  358. IN OUT PHARDWARE_PTE Pte
  359. )
  360. /*++
  361. Converts the virtual frame number in a PTE to a physical frame number.
  362. Arguments:
  363. Pte - Pointer to the page table entry to convert.
  364. Return Value:
  365. None.
  366. --*/
  367. {
  368. PVOID va;
  369. PHYSICAL_ADDRESS physicalAddress;
  370. va = (PVOID)(Pte->PageFrameNumber << PAGE_SHIFT);
  371. physicalAddress = MmGetPhysicalAddress( va );
  372. #if defined(_X86PAE_)
  373. Pte->PageFrameNumber = physicalAddress.QuadPart >> PAGE_SHIFT;
  374. #else
  375. Pte->PageFrameNumber = physicalAddress.LowPart >> PAGE_SHIFT;
  376. #endif
  377. return va;
  378. }
  379. PHYSICAL_ADDRESS
  380. Ki386BuildIdentityBuffer(
  381. IN OUT PIDENTITY_MAP IdentityMap,
  382. IN PVOID StartVa,
  383. IN ULONG Length,
  384. OUT PULONG PagesToMap
  385. )
  386. {
  387. /*++
  388. This function checks to see if the physical memory backing a virtual
  389. buffer is physically contiguous and lies completely below 4G.
  390. If these requirements are met, then the physical address of StartVa is
  391. returned.
  392. If not, then a physically contiguous buffer is allocated, the contents
  393. of the region is copied in, and its address is returned.
  394. Arguments:
  395. IdentityMap - Pointer to the identity map building structure.
  396. StartVa - Virtual address of the start of the region for which a
  397. physically contiguous copy is desired.
  398. Length - Length of the region for which a physically contiguous copy
  399. is desired.
  400. --*/
  401. ULONG pagesToMap;
  402. ULONG pagesRemaining;
  403. PCHAR nextVirtualAddress;
  404. PHYSICAL_ADDRESS nextPhysicalAddress;
  405. PHYSICAL_ADDRESS physicalAddress;
  406. PHYSICAL_ADDRESS firstPhysicalAddress;
  407. ULONG pageOffset;
  408. PCHAR identityBuffer;
  409. //
  410. // Count the number of pages in the buffer, and record the physical
  411. // address of the start of the buffer.
  412. //
  413. pagesToMap = ADDRESS_AND_SIZE_TO_SPAN_PAGES( StartVa, Length );
  414. nextVirtualAddress = StartVa;
  415. firstPhysicalAddress = MmGetPhysicalAddress( StartVa );
  416. nextPhysicalAddress = firstPhysicalAddress;
  417. //
  418. // Examine each page in the region.
  419. //
  420. pagesRemaining = pagesToMap;
  421. while (TRUE) {
  422. physicalAddress = MmGetPhysicalAddress( nextVirtualAddress );
  423. if (physicalAddress.QuadPart != nextPhysicalAddress.QuadPart) {
  424. //
  425. // The buffer is not physically contiguous.
  426. //
  427. break;
  428. }
  429. if (physicalAddress.HighPart != 0) {
  430. //
  431. // The buffer does not lie entirely below 4G
  432. //
  433. break;
  434. }
  435. pagesRemaining -= 1;
  436. if (pagesRemaining == 0) {
  437. //
  438. // All of the pages in the buffer have been examined, and have
  439. // been found to meet the critera. Return the physical address
  440. // of the start of the buffer.
  441. //
  442. *PagesToMap = pagesToMap;
  443. return firstPhysicalAddress;
  444. }
  445. nextVirtualAddress += PAGE_SIZE;
  446. nextPhysicalAddress.QuadPart += PAGE_SIZE;
  447. }
  448. //
  449. // The buffer does not meet the criteria and so its contents must be
  450. // copied to a buffer that does.
  451. //
  452. identityBuffer = Ki386AllocateContiguousMemory( IdentityMap,
  453. pagesToMap,
  454. TRUE );
  455. if (identityBuffer == 0) {
  456. //
  457. // A contiguous region of the appropriate size could not be located
  458. // below 4G physical.
  459. //
  460. physicalAddress.QuadPart = 0;
  461. } else {
  462. //
  463. // Got an appropriate physical buffer, now copy in the data
  464. //
  465. pageOffset = (ULONG)StartVa & (PAGE_SIZE-1);
  466. identityBuffer += pageOffset;
  467. RtlCopyMemory( identityBuffer, StartVa, Length );
  468. physicalAddress = MmGetPhysicalAddress( identityBuffer );
  469. *PagesToMap = pagesToMap;
  470. }
  471. return physicalAddress;
  472. }
  473. VOID
  474. Ki386ClearIdentityMap(
  475. IN PIDENTITY_MAP IdentityMap
  476. )
  477. {
  478. /*++
  479. This function just frees the page directory and page tables created in
  480. Ki386CreateIdentityMap().
  481. --*/
  482. ULONG index;
  483. PVOID page;
  484. //
  485. // IdentityMap->PageList is an array of addresses of pages allocated with
  486. // MmAllocateContiguousMemory(). Walk the array, freeing each page.
  487. //
  488. for (index = 0; index < IdentityMap->PagesAllocated; index++) {
  489. page = IdentityMap->PageList[ index ];
  490. MmFreeContiguousMemory( page );
  491. }
  492. }
  493. VOID
  494. Ki386EnableTargetLargePage(
  495. IN PIDENTITY_MAP IdentityMap
  496. )
  497. {
  498. /*++
  499. This function just passes info on to the assembly routine
  500. Ki386EnableLargePage().
  501. --*/
  502. Ki386EnableCurrentLargePage(IdentityMap->IdentityAddr,
  503. IdentityMap->IdentityCR3);
  504. }