Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

mtrramd.c

Go to the documentation of this file.
00001 #include "ki.h" 00002 00003 #define STATIC 00004 00005 #define IDBG 0 00006 00007 #if DBG 00008 #define DBGMSG(a) DbgPrint(a) 00009 #else 00010 #define DBGMSG(a) 00011 #endif 00012 00013 // 00014 // Externals. 00015 // 00016 00017 NTSTATUS 00018 KiLoadMTRR ( 00019 PVOID Context 00020 ); 00021 00022 // --- AMD Structure definitions --- 00023 00024 // K6 MTRR hardware register layout. 00025 00026 // Single MTRR control register. 00027 00028 typedef struct _AMDK6_MTRR { 00029 ULONG type:2; 00030 ULONG mask:15; 00031 ULONG base:15; 00032 } AMDK6_MTRR, *PAMDK6_MTRR; 00033 00034 // MSR image, contains two control regs. 00035 00036 typedef struct _AMDK6_MTRR_MSR_IMAGE { 00037 union { 00038 struct { 00039 AMDK6_MTRR mtrr0; 00040 AMDK6_MTRR mtrr1; 00041 } hw; 00042 ULONGLONG QuadPart; 00043 } u; 00044 } AMDK6_MTRR_MSR_IMAGE, *PAMDK6_MTRR_MSR_IMAGE; 00045 00046 // MTRR reg type field values. 00047 00048 #define AMDK6_MTRR_TYPE_DISABLED 0 00049 #define AMDK6_MTRR_TYPE_UC 1 00050 #define AMDK6_MTRR_TYPE_WC 2 00051 #define AMDK6_MTRR_TYPE_MASK 3 00052 00053 // AMD K6 MTRR MSR Index number 00054 00055 #define AMDK6_MTRR_MSR 0xC0000085 00056 00057 // 00058 // Region table entry - used to track all write combined regions. 00059 // 00060 // Set BaseAddress to AMDK6_REGION_UNUSED for unused entries. 00061 // 00062 00063 typedef struct _AMDK6_MTRR_REGION { 00064 ULONG BaseAddress; 00065 ULONG Size; 00066 MEMORY_CACHING_TYPE RegionType; 00067 ULONG RegionFlags; 00068 } AMDK6_MTRR_REGION, *PAMDK6_MTRR_REGION; 00069 00070 #define MAX_K6_REGIONS 2 // Limit the write combined regions to 2 since that's how many MTRRs we have available. 00071 00072 // 00073 // Value to set base address to for unused indication. 00074 // 00075 00076 #define AMDK6_REGION_UNUSED 0xFFFFFFFF 00077 00078 // 00079 // Flag to indicate that this region was set up by the BIOS. 00080 // 00081 00082 #define AMDK6_REGION_FLAGS_BIOS 0x00000001 00083 00084 // 00085 // Usage count for hardware MTRR registers. 00086 // 00087 00088 #define AMDK6_MAX_MTRR 2 00089 00090 // 00091 // AMD Function Prototypes. 00092 // 00093 00094 VOID 00095 KiAmdK6InitializeMTRR ( 00096 VOID 00097 ); 00098 00099 NTSTATUS 00100 KiAmdK6RestoreMTRR ( 00101 ); 00102 00103 NTSTATUS 00104 KiAmdK6MtrrSetMemoryType ( 00105 ULONG BaseAddress, 00106 ULONG Size, 00107 MEMORY_CACHING_TYPE Type 00108 ); 00109 00110 BOOLEAN 00111 KiAmdK6AddRegion ( 00112 ULONG BaseAddress, 00113 ULONG Size, 00114 MEMORY_CACHING_TYPE Type, 00115 ULONG Flags 00116 ); 00117 00118 NTSTATUS 00119 KiAmdK6MtrrCommitChanges ( 00120 VOID 00121 ); 00122 00123 NTSTATUS 00124 KiAmdK6HandleWcRegionRequest ( 00125 ULONG BaseAddress, 00126 ULONG Size 00127 ); 00128 00129 VOID 00130 KiAmdK6MTRRAddRegionFromHW ( 00131 AMDK6_MTRR RegImage 00132 ); 00133 00134 PAMDK6_MTRR_REGION 00135 KiAmdK6FindFreeRegion ( 00136 MEMORY_CACHING_TYPE Type 00137 ); 00138 00139 #pragma alloc_text(INIT,KiAmdK6InitializeMTRR) 00140 #pragma alloc_text(PAGELK,KiAmdK6RestoreMTRR) 00141 #pragma alloc_text(PAGELK,KiAmdK6MtrrSetMemoryType) 00142 #pragma alloc_text(PAGELK,KiAmdK6AddRegion) 00143 #pragma alloc_text(PAGELK,KiAmdK6MtrrCommitChanges) 00144 #pragma alloc_text(PAGELK,KiAmdK6HandleWcRegionRequest) 00145 #pragma alloc_text(PAGELK,KiAmdK6MTRRAddRegionFromHW) 00146 #pragma alloc_text(PAGELK,KiAmdK6FindFreeRegion) 00147 00148 // --- AMD Global Variables --- 00149 00150 extern KSPIN_LOCK KiRangeLock; 00151 00152 // AmdK6Regions - Table to track wc regions. 00153 00154 AMDK6_MTRR_REGION AmdK6Regions[MAX_K6_REGIONS]; 00155 ULONG AmdK6RegionCount; 00156 00157 // Usage counter for hardware MTRRs. 00158 00159 ULONG AmdMtrrHwUsageCount; 00160 00161 // Global variable image of MTRR MSR. 00162 00163 AMDK6_MTRR_MSR_IMAGE KiAmdK6Mtrr; 00164 00165 // --- AMD Start of code --- 00166 00167 VOID 00168 KiAmdK6InitializeMTRR ( 00169 VOID 00170 ) 00171 { 00172 ULONG i; 00173 KIRQL OldIrql; 00174 00175 DBGMSG("KiAmdK6InitializeMTRR: Initializing K6 MTRR support\n"); 00176 00177 KiAmdK6Mtrr.u.hw.mtrr0.type = AMDK6_MTRR_TYPE_DISABLED; 00178 KiAmdK6Mtrr.u.hw.mtrr1.type = AMDK6_MTRR_TYPE_DISABLED; 00179 AmdK6RegionCount = MAX_K6_REGIONS; 00180 AmdMtrrHwUsageCount = 0; 00181 00182 // 00183 // Set all regions to free. 00184 // 00185 00186 for (i = 0; i < AmdK6RegionCount; i++) { 00187 AmdK6Regions[i].BaseAddress = AMDK6_REGION_UNUSED; 00188 AmdK6Regions[i].RegionFlags = 0; 00189 } 00190 00191 // 00192 // Initialize the spin lock. 00193 // 00194 // N.B. Normally this is done by KiInitializeMTRR but that 00195 // routine is not called in the AMD K6 case. 00196 // 00197 00198 KeInitializeSpinLock (&KiRangeLock); 00199 00200 // 00201 // Read the MTRR registers to see if the BIOS has set them up. 00202 // If so, add entries to the region table and adjust the usage 00203 // count. Serialize the region table. 00204 // 00205 00206 KeAcquireSpinLock (&KiRangeLock, &OldIrql); 00207 00208 KiAmdK6Mtrr.u.QuadPart = RDMSR (AMDK6_MTRR_MSR); 00209 00210 // 00211 // Check MTRR0 first. 00212 // 00213 00214 KiAmdK6MTRRAddRegionFromHW(KiAmdK6Mtrr.u.hw.mtrr0); 00215 00216 // 00217 // Now check MTRR1. 00218 // 00219 00220 KiAmdK6MTRRAddRegionFromHW(KiAmdK6Mtrr.u.hw.mtrr1); 00221 00222 // 00223 // Release the locks. 00224 // 00225 00226 KeReleaseSpinLock (&KiRangeLock, OldIrql); 00227 } 00228 00229 VOID 00230 KiAmdK6MTRRAddRegionFromHW ( 00231 AMDK6_MTRR RegImage 00232 ) 00233 { 00234 ULONG BaseAddress, Size, TempMask; 00235 00236 // 00237 // Check to see if this MTRR is enabled. 00238 // 00239 00240 if (RegImage.type != AMDK6_MTRR_TYPE_DISABLED) { 00241 00242 // 00243 // If this is a write combined region then add an entry to 00244 // the region table. 00245 // 00246 00247 if ((RegImage.type & AMDK6_MTRR_TYPE_UC) == 0) { 00248 00249 // 00250 // Create a new resion table entry. 00251 // 00252 00253 BaseAddress = RegImage.base << 17; 00254 00255 // 00256 // Calculate the size base on the mask value. 00257 // 00258 00259 TempMask = RegImage.mask; 00260 00261 // 00262 // There should never be 4GB WC region! 00263 // 00264 00265 ASSERT (TempMask != 0); 00266 00267 // 00268 // Start with 128 size and search upward. 00269 // 00270 00271 Size = 0x00020000; 00272 00273 while ((TempMask & 0x00000001) == 0) { 00274 TempMask >>= 1; 00275 Size <<= 1; 00276 } 00277 00278 // 00279 // Add the region to the table. 00280 // 00281 00282 KiAmdK6AddRegion(BaseAddress, 00283 Size, 00284 MmWriteCombined, 00285 AMDK6_REGION_FLAGS_BIOS); 00286 00287 AmdMtrrHwUsageCount++; 00288 } 00289 } 00290 } 00291 00292 00293 NTSTATUS 00294 KiAmdK6MtrrSetMemoryType ( 00295 ULONG BaseAddress, 00296 ULONG Size, 00297 MEMORY_CACHING_TYPE Type 00298 ) 00299 { 00300 NTSTATUS Status = STATUS_SUCCESS; 00301 KIRQL OldIrql; 00302 00303 switch(Type) { 00304 case MmWriteCombined: 00305 00306 // 00307 // H/W needs updating, lock down the code required to effect 00308 // the change. 00309 // 00310 00311 if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { 00312 00313 // 00314 // Code can not be locked down. Supplying a new range type 00315 // requires that the caller calls at irql < dispatch_level. 00316 // 00317 00318 DBGMSG ("KeAmdK6SetPhysicalCacheTypeRange failed due to calling IRQL == DISPATCH_LEVEL\n"); 00319 return STATUS_UNSUCCESSFUL; 00320 } 00321 00322 // 00323 // Lock the code. 00324 // 00325 00326 MmLockPagableSectionByHandle(ExPageLockHandle); 00327 00328 // 00329 // Serialize the region table. 00330 // 00331 00332 KeAcquireSpinLock (&KiRangeLock, &OldIrql); 00333 00334 Status = KiAmdK6HandleWcRegionRequest(BaseAddress, Size); 00335 00336 // 00337 // Release the locks. 00338 // 00339 00340 KeReleaseSpinLock (&KiRangeLock, OldIrql); 00341 MmUnlockPagableImageSection(ExPageLockHandle); 00342 00343 break; // End of WriteCombined case. 00344 00345 case MmNonCached: 00346 00347 // 00348 // Add an entry to the region table. 00349 // 00350 00351 // Don't need to add these to the region table. Non-cached regions are 00352 // accessed using a non-caching virtual pointer set up in the page tables. 00353 00354 break; 00355 00356 case MmCached: 00357 00358 // 00359 // Redundant. These should be filtered out in 00360 // KeAmdK6SetPhysicalCacheTypeRange(); 00361 // 00362 00363 Status = STATUS_NOT_SUPPORTED; 00364 break; 00365 00366 default: 00367 DBGMSG ("KeAmdK6SetPhysicalCacheTypeRange: no such cache type\n"); 00368 Status = STATUS_INVALID_PARAMETER; 00369 break; 00370 } 00371 return Status; 00372 } 00373 00374 NTSTATUS 00375 KiAmdK6HandleWcRegionRequest ( 00376 ULONG BaseAddress, 00377 ULONG Size 00378 ) 00379 { 00380 ULONG i; 00381 ULONG AdjustedSize, AdjustedEndAddress, AlignmentMask; 00382 ULONG CombinedBase, CombinedSize, CombinedAdjustedSize; 00383 PAMDK6_MTRR_REGION pRegion; 00384 BOOLEAN bCanCombine, bValidRange; 00385 00386 // 00387 // Try and find a region that overlaps or is adjacent to the new one and 00388 // check to see if the combined region would be a legal mapping. 00389 // 00390 00391 for (i = 0; i < AmdK6RegionCount; i++) { 00392 pRegion = &AmdK6Regions[i]; 00393 if ((pRegion->BaseAddress != AMDK6_REGION_UNUSED) && 00394 (pRegion->RegionType == MmWriteCombined)) { 00395 00396 // 00397 // Does the new start address overlap or adjoin an 00398 // existing WC region? 00399 // 00400 00401 if (((pRegion->BaseAddress >= BaseAddress) && 00402 (pRegion->BaseAddress <= (BaseAddress + Size))) || 00403 ((BaseAddress <= (pRegion->BaseAddress + pRegion->Size)) && 00404 (BaseAddress >= pRegion->BaseAddress))) { 00405 00406 // 00407 // Combine the two regions into one. 00408 // 00409 00410 AdjustedEndAddress = BaseAddress + Size; 00411 00412 if (pRegion->BaseAddress < BaseAddress) { 00413 CombinedBase = pRegion->BaseAddress; 00414 } else { 00415 CombinedBase = BaseAddress; 00416 } 00417 00418 if ((pRegion->BaseAddress + pRegion->Size) > 00419 AdjustedEndAddress) { 00420 CombinedSize = (pRegion->BaseAddress + pRegion->Size) - 00421 CombinedBase; 00422 } else { 00423 CombinedSize = AdjustedEndAddress - CombinedBase; 00424 } 00425 00426 // 00427 // See if the new region would be a legal mapping. 00428 // 00429 // 00430 // Find the smallest legal size that is equal to the requested range. Scan 00431 // all ranges from 128k - 2G. (Start at 2G and work down). 00432 // 00433 00434 CombinedAdjustedSize = 0x80000000; 00435 AlignmentMask = 0x7fffffff; 00436 bCanCombine = FALSE; 00437 00438 while (CombinedAdjustedSize > 0x00010000) { 00439 00440 // 00441 // Check the size to see if it matches the requested limit. 00442 // 00443 00444 if (CombinedAdjustedSize == CombinedSize) { 00445 00446 // 00447 // This one works. 00448 // Check to see if the base address conforms to the MTRR restrictions. 00449 // 00450 00451 if ((CombinedBase & AlignmentMask) == 0) { 00452 bCanCombine = TRUE; 00453 } 00454 00455 break; 00456 00457 } else { 00458 00459 // 00460 // Bump it down to the next range size and try again. 00461 // 00462 00463 CombinedAdjustedSize >>= 1; 00464 AlignmentMask >>= 1; 00465 } 00466 } 00467 00468 if (bCanCombine) { 00469 // 00470 // If the resized range is OK, record the change in the region 00471 // table and commit the changes to hardware. 00472 // 00473 00474 pRegion->BaseAddress = CombinedBase; 00475 pRegion->Size = CombinedAdjustedSize; 00476 00477 // 00478 // Reset the BIOS flag since we now "own" this region (if we didn't already). 00479 // 00480 00481 pRegion->RegionFlags &= ~AMDK6_REGION_FLAGS_BIOS; 00482 00483 return KiAmdK6MtrrCommitChanges(); 00484 } 00485 } 00486 } 00487 } 00488 00489 // A valid combination could not be found, so try to create a new range for this request. 00490 // 00491 // Find the smallest legal size that is less than or equal to the requested range. Scan 00492 // all ranges from 128k - 2G. (Start at 2G and work down). 00493 // 00494 00495 AdjustedSize = 0x80000000; 00496 AlignmentMask = 0x7fffffff; 00497 bValidRange = FALSE; 00498 00499 while (AdjustedSize > 0x00010000) { 00500 00501 // 00502 // Check the size to see if it matches the requested limit. 00503 // 00504 00505 if (AdjustedSize == Size) { 00506 00507 // 00508 // This one works. 00509 // 00510 // Check to see if the base address conforms to the MTRR restrictions. 00511 // 00512 00513 if ((BaseAddress & AlignmentMask) == 0) { 00514 bValidRange = TRUE; 00515 } 00516 00517 // 00518 // Stop looking. 00519 // 00520 00521 break; 00522 00523 } else { 00524 00525 // 00526 // Bump it down to the next range size and try again. 00527 // 00528 00529 AdjustedSize >>= 1; 00530 AlignmentMask >>= 1; 00531 } 00532 } 00533 00534 // 00535 // Couldn't find a legal region that fit. 00536 // 00537 00538 if (!bValidRange) { 00539 return STATUS_NOT_SUPPORTED; 00540 } 00541 00542 00543 // 00544 // If we got this far then this is a new WC region. 00545 // Create a new region entry for this request. 00546 // 00547 00548 if (!KiAmdK6AddRegion(BaseAddress, AdjustedSize, MmWriteCombined, 0)) { 00549 return STATUS_UNSUCCESSFUL; 00550 } 00551 00552 // 00553 // Commit the changes to hardware. 00554 // 00555 00556 return KiAmdK6MtrrCommitChanges(); 00557 } 00558 00559 BOOLEAN 00560 KiAmdK6AddRegion ( 00561 ULONG BaseAddress, 00562 ULONG Size, 00563 MEMORY_CACHING_TYPE Type, 00564 ULONG Flags 00565 ) 00566 { 00567 PAMDK6_MTRR_REGION pRegion; 00568 00569 if ((pRegion = KiAmdK6FindFreeRegion(Type)) == NULL) { 00570 return FALSE; 00571 } 00572 pRegion->BaseAddress = BaseAddress; 00573 pRegion->Size = Size; 00574 pRegion->RegionType = Type; 00575 pRegion->RegionFlags = Flags; 00576 00577 return TRUE; 00578 } 00579 00580 PAMDK6_MTRR_REGION 00581 KiAmdK6FindFreeRegion ( 00582 MEMORY_CACHING_TYPE Type 00583 ) 00584 { 00585 ULONG i; 00586 00587 // 00588 // If this is a MmWriteCombined request, limit the number of 00589 // regions to match the actual hardware support. 00590 // 00591 00592 if (Type == MmWriteCombined) { 00593 if (AmdMtrrHwUsageCount >= AMDK6_MAX_MTRR) { 00594 00595 // 00596 // Search the table to see if there are any BIOS entries 00597 // we can replace. 00598 // 00599 00600 for (i = 0; i < AmdK6RegionCount; i++) { 00601 if (AmdK6Regions[i].RegionFlags & AMDK6_REGION_FLAGS_BIOS) { 00602 return &AmdK6Regions[i]; 00603 } 00604 } 00605 00606 // 00607 // No free HW MTRRs and no reusable entries. 00608 // 00609 00610 return FALSE; 00611 } 00612 } 00613 00614 // 00615 // Find the next free region in the table. 00616 // 00617 00618 for (i = 0; i < AmdK6RegionCount; i++) { 00619 if (AmdK6Regions[i].BaseAddress == AMDK6_REGION_UNUSED) { 00620 00621 if (Type == MmWriteCombined) { 00622 AmdMtrrHwUsageCount++; 00623 } 00624 return &AmdK6Regions[i]; 00625 } 00626 } 00627 00628 00629 DBGMSG("AmdK6FindFreeRegion: Region Table is Full!\n"); 00630 00631 return NULL; 00632 } 00633 00634 NTSTATUS 00635 KiAmdK6MtrrCommitChanges ( 00636 VOID 00637 ) 00638 00639 /*++ 00640 00641 Routine Description: 00642 00643 Commits the values in the table to hardware. 00644 00645 This procedure builds the MTRR images into the KiAmdK6Mtrr variable and 00646 calls KiLoadMTRR to actually load the register. 00647 00648 Arguments: 00649 00650 None. 00651 00652 Return Value: 00653 00654 None. 00655 00656 --*/ 00657 00658 { 00659 ULONG i, dwWcRangeCount = 0; 00660 ULONG RangeTemp, RangeMask; 00661 00662 // 00663 // Reset the MTRR image for both MTRRs disabled. 00664 // 00665 00666 KiAmdK6Mtrr.u.hw.mtrr0.type = AMDK6_MTRR_TYPE_DISABLED; 00667 KiAmdK6Mtrr.u.hw.mtrr1.type = AMDK6_MTRR_TYPE_DISABLED; 00668 00669 // 00670 // Find the Write Combining Regions, if any and set up the MTRR register. 00671 // 00672 00673 for (i = 0; i < AmdK6RegionCount; i++) { 00674 00675 // 00676 // Is this a valid region, and is it a write combined type? 00677 // 00678 00679 if ((AmdK6Regions[i].BaseAddress != AMDK6_REGION_UNUSED) && 00680 (AmdK6Regions[i].RegionType == MmWriteCombined)) { 00681 00682 // 00683 // Calculate the correct mask for this range size. The 00684 // BaseAddress and size were validated and adjusted in 00685 // AmdK6MtrrSetMemoryType(). 00686 // 00687 // Start with 128K and scan for all legal range values and 00688 // build the appropriate range mask at the same time. 00689 // 00690 00691 RangeTemp = 0x00020000; 00692 RangeMask = 0xfffe0000; 00693 00694 while (RangeTemp != 0) { 00695 if (RangeTemp == AmdK6Regions[i].Size) { 00696 break; 00697 } 00698 RangeTemp <<= 1; 00699 RangeMask <<= 1; 00700 } 00701 if (RangeTemp == 0) { 00702 00703 // 00704 // Not a valid range size. This can never happen!! 00705 // 00706 00707 DBGMSG ("AmdK6MtrrCommitChanges: Bad WC range in region table!\n"); 00708 00709 return STATUS_NOT_SUPPORTED; 00710 } 00711 00712 // 00713 // Add the region to the next available register. 00714 // 00715 00716 if (dwWcRangeCount == 0) { 00717 00718 KiAmdK6Mtrr.u.hw.mtrr0.base = AmdK6Regions[i].BaseAddress >> 17; 00719 KiAmdK6Mtrr.u.hw.mtrr0.mask = RangeMask >> 17; 00720 KiAmdK6Mtrr.u.hw.mtrr0.type = AMDK6_MTRR_TYPE_WC; 00721 dwWcRangeCount++; 00722 00723 } else if (dwWcRangeCount == 1) { 00724 00725 KiAmdK6Mtrr.u.hw.mtrr1.base = AmdK6Regions[i].BaseAddress >> 17; 00726 KiAmdK6Mtrr.u.hw.mtrr1.mask = RangeMask >> 17; 00727 KiAmdK6Mtrr.u.hw.mtrr1.type = AMDK6_MTRR_TYPE_WC; 00728 dwWcRangeCount++; 00729 00730 } else { 00731 00732 // 00733 // Should never happen! This should have been caught in 00734 // the calling routine. 00735 // 00736 00737 DBGMSG ("AmdK6MtrrCommitChanges: Not enough MTRR registers to satisfy region table!\n"); 00738 00739 return STATUS_NOT_SUPPORTED; 00740 } 00741 } 00742 } 00743 00744 // 00745 // Commit the changes to hardware. 00746 // 00747 00748 KiLoadMTRR(NULL); 00749 00750 return STATUS_SUCCESS; 00751 } 00752 00753 VOID 00754 KiAmdK6MtrrWRMSR ( 00755 VOID 00756 ) 00757 00758 /*++ 00759 00760 Routine Description: 00761 00762 Write the AMD K6 MTRRs. 00763 00764 Note: Access to KiAmdK6Mtrr has been synchronized around this 00765 call. 00766 00767 Arguments: 00768 00769 None. 00770 00771 Return Value: 00772 00773 None. 00774 00775 --*/ 00776 00777 { 00778 // 00779 // Write the MTRRs 00780 // 00781 00782 WRMSR (AMDK6_MTRR_MSR, KiAmdK6Mtrr.u.QuadPart); 00783 } 00784 00785

Generated on Sat May 15 19:40:53 2004 for test by doxygen 1.3.7