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.

785 lines
18 KiB

  1. #include "ki.h"
  2. #define STATIC
  3. #define IDBG 0
  4. #if DBG
  5. #define DBGMSG(a) DbgPrint(a)
  6. #else
  7. #define DBGMSG(a)
  8. #endif
  9. //
  10. // Externals.
  11. //
  12. NTSTATUS
  13. KiLoadMTRR (
  14. PVOID Context
  15. );
  16. // --- AMD Structure definitions ---
  17. // K6 MTRR hardware register layout.
  18. // Single MTRR control register.
  19. typedef struct _AMDK6_MTRR {
  20. ULONG type:2;
  21. ULONG mask:15;
  22. ULONG base:15;
  23. } AMDK6_MTRR, *PAMDK6_MTRR;
  24. // MSR image, contains two control regs.
  25. typedef struct _AMDK6_MTRR_MSR_IMAGE {
  26. union {
  27. struct {
  28. AMDK6_MTRR mtrr0;
  29. AMDK6_MTRR mtrr1;
  30. } hw;
  31. ULONGLONG QuadPart;
  32. } u;
  33. } AMDK6_MTRR_MSR_IMAGE, *PAMDK6_MTRR_MSR_IMAGE;
  34. // MTRR reg type field values.
  35. #define AMDK6_MTRR_TYPE_DISABLED 0
  36. #define AMDK6_MTRR_TYPE_UC 1
  37. #define AMDK6_MTRR_TYPE_WC 2
  38. #define AMDK6_MTRR_TYPE_MASK 3
  39. // AMD K6 MTRR MSR Index number
  40. #define AMDK6_MTRR_MSR 0xC0000085
  41. //
  42. // Region table entry - used to track all write combined regions.
  43. //
  44. // Set BaseAddress to AMDK6_REGION_UNUSED for unused entries.
  45. //
  46. typedef struct _AMDK6_MTRR_REGION {
  47. ULONG BaseAddress;
  48. ULONG Size;
  49. MEMORY_CACHING_TYPE RegionType;
  50. ULONG RegionFlags;
  51. } AMDK6_MTRR_REGION, *PAMDK6_MTRR_REGION;
  52. #define MAX_K6_REGIONS 2 // Limit the write combined regions to 2 since that's how many MTRRs we have available.
  53. //
  54. // Value to set base address to for unused indication.
  55. //
  56. #define AMDK6_REGION_UNUSED 0xFFFFFFFF
  57. //
  58. // Flag to indicate that this region was set up by the BIOS.
  59. //
  60. #define AMDK6_REGION_FLAGS_BIOS 0x00000001
  61. //
  62. // Usage count for hardware MTRR registers.
  63. //
  64. #define AMDK6_MAX_MTRR 2
  65. //
  66. // AMD Function Prototypes.
  67. //
  68. VOID
  69. KiAmdK6InitializeMTRR (
  70. VOID
  71. );
  72. NTSTATUS
  73. KiAmdK6RestoreMTRR (
  74. );
  75. NTSTATUS
  76. KiAmdK6MtrrSetMemoryType (
  77. ULONG BaseAddress,
  78. ULONG Size,
  79. MEMORY_CACHING_TYPE Type
  80. );
  81. BOOLEAN
  82. KiAmdK6AddRegion (
  83. ULONG BaseAddress,
  84. ULONG Size,
  85. MEMORY_CACHING_TYPE Type,
  86. ULONG Flags
  87. );
  88. NTSTATUS
  89. KiAmdK6MtrrCommitChanges (
  90. VOID
  91. );
  92. NTSTATUS
  93. KiAmdK6HandleWcRegionRequest (
  94. ULONG BaseAddress,
  95. ULONG Size
  96. );
  97. VOID
  98. KiAmdK6MTRRAddRegionFromHW (
  99. AMDK6_MTRR RegImage
  100. );
  101. PAMDK6_MTRR_REGION
  102. KiAmdK6FindFreeRegion (
  103. MEMORY_CACHING_TYPE Type
  104. );
  105. #pragma alloc_text(INIT,KiAmdK6InitializeMTRR)
  106. #pragma alloc_text(PAGELK,KiAmdK6RestoreMTRR)
  107. #pragma alloc_text(PAGELK,KiAmdK6MtrrSetMemoryType)
  108. #pragma alloc_text(PAGELK,KiAmdK6AddRegion)
  109. #pragma alloc_text(PAGELK,KiAmdK6MtrrCommitChanges)
  110. #pragma alloc_text(PAGELK,KiAmdK6HandleWcRegionRequest)
  111. #pragma alloc_text(PAGELK,KiAmdK6MTRRAddRegionFromHW)
  112. #pragma alloc_text(PAGELK,KiAmdK6FindFreeRegion)
  113. // --- AMD Global Variables ---
  114. extern KSPIN_LOCK KiRangeLock;
  115. // AmdK6Regions - Table to track wc regions.
  116. AMDK6_MTRR_REGION AmdK6Regions[MAX_K6_REGIONS];
  117. ULONG AmdK6RegionCount;
  118. // Usage counter for hardware MTRRs.
  119. ULONG AmdMtrrHwUsageCount;
  120. // Global variable image of MTRR MSR.
  121. AMDK6_MTRR_MSR_IMAGE KiAmdK6Mtrr;
  122. // --- AMD Start of code ---
  123. VOID
  124. KiAmdK6InitializeMTRR (
  125. VOID
  126. )
  127. {
  128. ULONG i;
  129. KIRQL OldIrql;
  130. DBGMSG("KiAmdK6InitializeMTRR: Initializing K6 MTRR support\n");
  131. KiAmdK6Mtrr.u.hw.mtrr0.type = AMDK6_MTRR_TYPE_DISABLED;
  132. KiAmdK6Mtrr.u.hw.mtrr1.type = AMDK6_MTRR_TYPE_DISABLED;
  133. AmdK6RegionCount = MAX_K6_REGIONS;
  134. AmdMtrrHwUsageCount = 0;
  135. //
  136. // Set all regions to free.
  137. //
  138. for (i = 0; i < AmdK6RegionCount; i++) {
  139. AmdK6Regions[i].BaseAddress = AMDK6_REGION_UNUSED;
  140. AmdK6Regions[i].RegionFlags = 0;
  141. }
  142. //
  143. // Initialize the spin lock.
  144. //
  145. // N.B. Normally this is done by KiInitializeMTRR but that
  146. // routine is not called in the AMD K6 case.
  147. //
  148. KeInitializeSpinLock (&KiRangeLock);
  149. //
  150. // Read the MTRR registers to see if the BIOS has set them up.
  151. // If so, add entries to the region table and adjust the usage
  152. // count. Serialize the region table.
  153. //
  154. KeAcquireSpinLock (&KiRangeLock, &OldIrql);
  155. KiAmdK6Mtrr.u.QuadPart = RDMSR (AMDK6_MTRR_MSR);
  156. //
  157. // Check MTRR0 first.
  158. //
  159. KiAmdK6MTRRAddRegionFromHW(KiAmdK6Mtrr.u.hw.mtrr0);
  160. //
  161. // Now check MTRR1.
  162. //
  163. KiAmdK6MTRRAddRegionFromHW(KiAmdK6Mtrr.u.hw.mtrr1);
  164. //
  165. // Release the locks.
  166. //
  167. KeReleaseSpinLock (&KiRangeLock, OldIrql);
  168. }
  169. VOID
  170. KiAmdK6MTRRAddRegionFromHW (
  171. AMDK6_MTRR RegImage
  172. )
  173. {
  174. ULONG BaseAddress, Size, TempMask;
  175. //
  176. // Check to see if this MTRR is enabled.
  177. //
  178. if (RegImage.type != AMDK6_MTRR_TYPE_DISABLED) {
  179. //
  180. // If this is a write combined region then add an entry to
  181. // the region table.
  182. //
  183. if ((RegImage.type & AMDK6_MTRR_TYPE_UC) == 0) {
  184. //
  185. // Create a new resion table entry.
  186. //
  187. BaseAddress = RegImage.base << 17;
  188. //
  189. // Calculate the size base on the mask value.
  190. //
  191. TempMask = RegImage.mask;
  192. //
  193. // There should never be 4GB WC region!
  194. //
  195. ASSERT (TempMask != 0);
  196. //
  197. // Start with 128 size and search upward.
  198. //
  199. Size = 0x00020000;
  200. while ((TempMask & 0x00000001) == 0) {
  201. TempMask >>= 1;
  202. Size <<= 1;
  203. }
  204. //
  205. // Add the region to the table.
  206. //
  207. KiAmdK6AddRegion(BaseAddress,
  208. Size,
  209. MmWriteCombined,
  210. AMDK6_REGION_FLAGS_BIOS);
  211. AmdMtrrHwUsageCount++;
  212. }
  213. }
  214. }
  215. NTSTATUS
  216. KiAmdK6MtrrSetMemoryType (
  217. ULONG BaseAddress,
  218. ULONG Size,
  219. MEMORY_CACHING_TYPE Type
  220. )
  221. {
  222. NTSTATUS Status = STATUS_SUCCESS;
  223. KIRQL OldIrql;
  224. switch(Type) {
  225. case MmWriteCombined:
  226. //
  227. // H/W needs updating, lock down the code required to effect
  228. // the change.
  229. //
  230. if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
  231. //
  232. // Code can not be locked down. Supplying a new range type
  233. // requires that the caller calls at irql < dispatch_level.
  234. //
  235. DBGMSG ("KeAmdK6SetPhysicalCacheTypeRange failed due to calling IRQL == DISPATCH_LEVEL\n");
  236. return STATUS_UNSUCCESSFUL;
  237. }
  238. //
  239. // Lock the code.
  240. //
  241. MmLockPagableSectionByHandle(ExPageLockHandle);
  242. //
  243. // Serialize the region table.
  244. //
  245. KeAcquireSpinLock (&KiRangeLock, &OldIrql);
  246. Status = KiAmdK6HandleWcRegionRequest(BaseAddress, Size);
  247. //
  248. // Release the locks.
  249. //
  250. KeReleaseSpinLock (&KiRangeLock, OldIrql);
  251. MmUnlockPagableImageSection(ExPageLockHandle);
  252. break; // End of WriteCombined case.
  253. case MmNonCached:
  254. //
  255. // Add an entry to the region table.
  256. //
  257. // Don't need to add these to the region table. Non-cached regions are
  258. // accessed using a non-caching virtual pointer set up in the page tables.
  259. break;
  260. case MmCached:
  261. //
  262. // Redundant. These should be filtered out in
  263. // KeAmdK6SetPhysicalCacheTypeRange();
  264. //
  265. Status = STATUS_NOT_SUPPORTED;
  266. break;
  267. default:
  268. DBGMSG ("KeAmdK6SetPhysicalCacheTypeRange: no such cache type\n");
  269. Status = STATUS_INVALID_PARAMETER;
  270. break;
  271. }
  272. return Status;
  273. }
  274. NTSTATUS
  275. KiAmdK6HandleWcRegionRequest (
  276. ULONG BaseAddress,
  277. ULONG Size
  278. )
  279. {
  280. ULONG i;
  281. ULONG AdjustedSize, AdjustedEndAddress, AlignmentMask;
  282. ULONG CombinedBase, CombinedSize, CombinedAdjustedSize;
  283. PAMDK6_MTRR_REGION pRegion;
  284. BOOLEAN bCanCombine, bValidRange;
  285. //
  286. // Try and find a region that overlaps or is adjacent to the new one and
  287. // check to see if the combined region would be a legal mapping.
  288. //
  289. for (i = 0; i < AmdK6RegionCount; i++) {
  290. pRegion = &AmdK6Regions[i];
  291. if ((pRegion->BaseAddress != AMDK6_REGION_UNUSED) &&
  292. (pRegion->RegionType == MmWriteCombined)) {
  293. //
  294. // Does the new start address overlap or adjoin an
  295. // existing WC region?
  296. //
  297. if (((pRegion->BaseAddress >= BaseAddress) &&
  298. (pRegion->BaseAddress <= (BaseAddress + Size))) ||
  299. ((BaseAddress <= (pRegion->BaseAddress + pRegion->Size)) &&
  300. (BaseAddress >= pRegion->BaseAddress))) {
  301. //
  302. // Combine the two regions into one.
  303. //
  304. AdjustedEndAddress = BaseAddress + Size;
  305. if (pRegion->BaseAddress < BaseAddress) {
  306. CombinedBase = pRegion->BaseAddress;
  307. } else {
  308. CombinedBase = BaseAddress;
  309. }
  310. if ((pRegion->BaseAddress + pRegion->Size) >
  311. AdjustedEndAddress) {
  312. CombinedSize = (pRegion->BaseAddress + pRegion->Size) -
  313. CombinedBase;
  314. } else {
  315. CombinedSize = AdjustedEndAddress - CombinedBase;
  316. }
  317. //
  318. // See if the new region would be a legal mapping.
  319. //
  320. //
  321. // Find the smallest legal size that is equal to the requested range. Scan
  322. // all ranges from 128k - 2G. (Start at 2G and work down).
  323. //
  324. CombinedAdjustedSize = 0x80000000;
  325. AlignmentMask = 0x7fffffff;
  326. bCanCombine = FALSE;
  327. while (CombinedAdjustedSize > 0x00010000) {
  328. //
  329. // Check the size to see if it matches the requested limit.
  330. //
  331. if (CombinedAdjustedSize == CombinedSize) {
  332. //
  333. // This one works.
  334. // Check to see if the base address conforms to the MTRR restrictions.
  335. //
  336. if ((CombinedBase & AlignmentMask) == 0) {
  337. bCanCombine = TRUE;
  338. }
  339. break;
  340. } else {
  341. //
  342. // Bump it down to the next range size and try again.
  343. //
  344. CombinedAdjustedSize >>= 1;
  345. AlignmentMask >>= 1;
  346. }
  347. }
  348. if (bCanCombine) {
  349. //
  350. // If the resized range is OK, record the change in the region
  351. // table and commit the changes to hardware.
  352. //
  353. pRegion->BaseAddress = CombinedBase;
  354. pRegion->Size = CombinedAdjustedSize;
  355. //
  356. // Reset the BIOS flag since we now "own" this region (if we didn't already).
  357. //
  358. pRegion->RegionFlags &= ~AMDK6_REGION_FLAGS_BIOS;
  359. return KiAmdK6MtrrCommitChanges();
  360. }
  361. }
  362. }
  363. }
  364. // A valid combination could not be found, so try to create a new range for this request.
  365. //
  366. // Find the smallest legal size that is less than or equal to the requested range. Scan
  367. // all ranges from 128k - 2G. (Start at 2G and work down).
  368. //
  369. AdjustedSize = 0x80000000;
  370. AlignmentMask = 0x7fffffff;
  371. bValidRange = FALSE;
  372. while (AdjustedSize > 0x00010000) {
  373. //
  374. // Check the size to see if it matches the requested limit.
  375. //
  376. if (AdjustedSize == Size) {
  377. //
  378. // This one works.
  379. //
  380. // Check to see if the base address conforms to the MTRR restrictions.
  381. //
  382. if ((BaseAddress & AlignmentMask) == 0) {
  383. bValidRange = TRUE;
  384. }
  385. //
  386. // Stop looking.
  387. //
  388. break;
  389. } else {
  390. //
  391. // Bump it down to the next range size and try again.
  392. //
  393. AdjustedSize >>= 1;
  394. AlignmentMask >>= 1;
  395. }
  396. }
  397. //
  398. // Couldn't find a legal region that fit.
  399. //
  400. if (!bValidRange) {
  401. return STATUS_NOT_SUPPORTED;
  402. }
  403. //
  404. // If we got this far then this is a new WC region.
  405. // Create a new region entry for this request.
  406. //
  407. if (!KiAmdK6AddRegion(BaseAddress, AdjustedSize, MmWriteCombined, 0)) {
  408. return STATUS_UNSUCCESSFUL;
  409. }
  410. //
  411. // Commit the changes to hardware.
  412. //
  413. return KiAmdK6MtrrCommitChanges();
  414. }
  415. BOOLEAN
  416. KiAmdK6AddRegion (
  417. ULONG BaseAddress,
  418. ULONG Size,
  419. MEMORY_CACHING_TYPE Type,
  420. ULONG Flags
  421. )
  422. {
  423. PAMDK6_MTRR_REGION pRegion;
  424. if ((pRegion = KiAmdK6FindFreeRegion(Type)) == NULL) {
  425. return FALSE;
  426. }
  427. pRegion->BaseAddress = BaseAddress;
  428. pRegion->Size = Size;
  429. pRegion->RegionType = Type;
  430. pRegion->RegionFlags = Flags;
  431. return TRUE;
  432. }
  433. PAMDK6_MTRR_REGION
  434. KiAmdK6FindFreeRegion (
  435. MEMORY_CACHING_TYPE Type
  436. )
  437. {
  438. ULONG i;
  439. //
  440. // If this is a MmWriteCombined request, limit the number of
  441. // regions to match the actual hardware support.
  442. //
  443. if (Type == MmWriteCombined) {
  444. if (AmdMtrrHwUsageCount >= AMDK6_MAX_MTRR) {
  445. //
  446. // Search the table to see if there are any BIOS entries
  447. // we can replace.
  448. //
  449. for (i = 0; i < AmdK6RegionCount; i++) {
  450. if (AmdK6Regions[i].RegionFlags & AMDK6_REGION_FLAGS_BIOS) {
  451. return &AmdK6Regions[i];
  452. }
  453. }
  454. //
  455. // No free HW MTRRs and no reusable entries.
  456. //
  457. return FALSE;
  458. }
  459. }
  460. //
  461. // Find the next free region in the table.
  462. //
  463. for (i = 0; i < AmdK6RegionCount; i++) {
  464. if (AmdK6Regions[i].BaseAddress == AMDK6_REGION_UNUSED) {
  465. if (Type == MmWriteCombined) {
  466. AmdMtrrHwUsageCount++;
  467. }
  468. return &AmdK6Regions[i];
  469. }
  470. }
  471. DBGMSG("AmdK6FindFreeRegion: Region Table is Full!\n");
  472. return NULL;
  473. }
  474. NTSTATUS
  475. KiAmdK6MtrrCommitChanges (
  476. VOID
  477. )
  478. /*++
  479. Routine Description:
  480. Commits the values in the table to hardware.
  481. This procedure builds the MTRR images into the KiAmdK6Mtrr variable and
  482. calls KiLoadMTRR to actually load the register.
  483. Arguments:
  484. None.
  485. Return Value:
  486. None.
  487. --*/
  488. {
  489. ULONG i, dwWcRangeCount = 0;
  490. ULONG RangeTemp, RangeMask;
  491. //
  492. // Reset the MTRR image for both MTRRs disabled.
  493. //
  494. KiAmdK6Mtrr.u.hw.mtrr0.type = AMDK6_MTRR_TYPE_DISABLED;
  495. KiAmdK6Mtrr.u.hw.mtrr1.type = AMDK6_MTRR_TYPE_DISABLED;
  496. //
  497. // Find the Write Combining Regions, if any and set up the MTRR register.
  498. //
  499. for (i = 0; i < AmdK6RegionCount; i++) {
  500. //
  501. // Is this a valid region, and is it a write combined type?
  502. //
  503. if ((AmdK6Regions[i].BaseAddress != AMDK6_REGION_UNUSED) &&
  504. (AmdK6Regions[i].RegionType == MmWriteCombined)) {
  505. //
  506. // Calculate the correct mask for this range size. The
  507. // BaseAddress and size were validated and adjusted in
  508. // AmdK6MtrrSetMemoryType().
  509. //
  510. // Start with 128K and scan for all legal range values and
  511. // build the appropriate range mask at the same time.
  512. //
  513. RangeTemp = 0x00020000;
  514. RangeMask = 0xfffe0000;
  515. while (RangeTemp != 0) {
  516. if (RangeTemp == AmdK6Regions[i].Size) {
  517. break;
  518. }
  519. RangeTemp <<= 1;
  520. RangeMask <<= 1;
  521. }
  522. if (RangeTemp == 0) {
  523. //
  524. // Not a valid range size. This can never happen!!
  525. //
  526. DBGMSG ("AmdK6MtrrCommitChanges: Bad WC range in region table!\n");
  527. return STATUS_NOT_SUPPORTED;
  528. }
  529. //
  530. // Add the region to the next available register.
  531. //
  532. if (dwWcRangeCount == 0) {
  533. KiAmdK6Mtrr.u.hw.mtrr0.base = AmdK6Regions[i].BaseAddress >> 17;
  534. KiAmdK6Mtrr.u.hw.mtrr0.mask = RangeMask >> 17;
  535. KiAmdK6Mtrr.u.hw.mtrr0.type = AMDK6_MTRR_TYPE_WC;
  536. dwWcRangeCount++;
  537. } else if (dwWcRangeCount == 1) {
  538. KiAmdK6Mtrr.u.hw.mtrr1.base = AmdK6Regions[i].BaseAddress >> 17;
  539. KiAmdK6Mtrr.u.hw.mtrr1.mask = RangeMask >> 17;
  540. KiAmdK6Mtrr.u.hw.mtrr1.type = AMDK6_MTRR_TYPE_WC;
  541. dwWcRangeCount++;
  542. } else {
  543. //
  544. // Should never happen! This should have been caught in
  545. // the calling routine.
  546. //
  547. DBGMSG ("AmdK6MtrrCommitChanges: Not enough MTRR registers to satisfy region table!\n");
  548. return STATUS_NOT_SUPPORTED;
  549. }
  550. }
  551. }
  552. //
  553. // Commit the changes to hardware.
  554. //
  555. KiLoadMTRR(NULL);
  556. return STATUS_SUCCESS;
  557. }
  558. VOID
  559. KiAmdK6MtrrWRMSR (
  560. VOID
  561. )
  562. /*++
  563. Routine Description:
  564. Write the AMD K6 MTRRs.
  565. Note: Access to KiAmdK6Mtrr has been synchronized around this
  566. call.
  567. Arguments:
  568. None.
  569. Return Value:
  570. None.
  571. --*/
  572. {
  573. //
  574. // Write the MTRRs
  575. //
  576. WRMSR (AMDK6_MTRR_MSR, KiAmdK6Mtrr.u.QuadPart);
  577. }