786 lines
18 KiB
C
786 lines
18 KiB
C
#include "ki.h"
|
|
|
|
#define STATIC
|
|
|
|
#define IDBG 0
|
|
|
|
#if DBG
|
|
#define DBGMSG(a) DbgPrint(a)
|
|
#else
|
|
#define DBGMSG(a)
|
|
#endif
|
|
|
|
//
|
|
// Externals.
|
|
//
|
|
|
|
NTSTATUS
|
|
KiLoadMTRR (
|
|
PVOID Context
|
|
);
|
|
|
|
// --- AMD Structure definitions ---
|
|
|
|
// K6 MTRR hardware register layout.
|
|
|
|
// Single MTRR control register.
|
|
|
|
typedef struct _AMDK6_MTRR {
|
|
ULONG type:2;
|
|
ULONG mask:15;
|
|
ULONG base:15;
|
|
} AMDK6_MTRR, *PAMDK6_MTRR;
|
|
|
|
// MSR image, contains two control regs.
|
|
|
|
typedef struct _AMDK6_MTRR_MSR_IMAGE {
|
|
union {
|
|
struct {
|
|
AMDK6_MTRR mtrr0;
|
|
AMDK6_MTRR mtrr1;
|
|
} hw;
|
|
ULONGLONG QuadPart;
|
|
} u;
|
|
} AMDK6_MTRR_MSR_IMAGE, *PAMDK6_MTRR_MSR_IMAGE;
|
|
|
|
// MTRR reg type field values.
|
|
|
|
#define AMDK6_MTRR_TYPE_DISABLED 0
|
|
#define AMDK6_MTRR_TYPE_UC 1
|
|
#define AMDK6_MTRR_TYPE_WC 2
|
|
#define AMDK6_MTRR_TYPE_MASK 3
|
|
|
|
// AMD K6 MTRR MSR Index number
|
|
|
|
#define AMDK6_MTRR_MSR 0xC0000085
|
|
|
|
//
|
|
// Region table entry - used to track all write combined regions.
|
|
//
|
|
// Set BaseAddress to AMDK6_REGION_UNUSED for unused entries.
|
|
//
|
|
|
|
typedef struct _AMDK6_MTRR_REGION {
|
|
ULONG BaseAddress;
|
|
ULONG Size;
|
|
MEMORY_CACHING_TYPE RegionType;
|
|
ULONG RegionFlags;
|
|
} AMDK6_MTRR_REGION, *PAMDK6_MTRR_REGION;
|
|
|
|
#define MAX_K6_REGIONS 2 // Limit the write combined regions to 2 since that's how many MTRRs we have available.
|
|
|
|
//
|
|
// Value to set base address to for unused indication.
|
|
//
|
|
|
|
#define AMDK6_REGION_UNUSED 0xFFFFFFFF
|
|
|
|
//
|
|
// Flag to indicate that this region was set up by the BIOS.
|
|
//
|
|
|
|
#define AMDK6_REGION_FLAGS_BIOS 0x00000001
|
|
|
|
//
|
|
// Usage count for hardware MTRR registers.
|
|
//
|
|
|
|
#define AMDK6_MAX_MTRR 2
|
|
|
|
//
|
|
// AMD Function Prototypes.
|
|
//
|
|
|
|
VOID
|
|
KiAmdK6InitializeMTRR (
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
KiAmdK6RestoreMTRR (
|
|
);
|
|
|
|
NTSTATUS
|
|
KiAmdK6MtrrSetMemoryType (
|
|
ULONG BaseAddress,
|
|
ULONG Size,
|
|
MEMORY_CACHING_TYPE Type
|
|
);
|
|
|
|
BOOLEAN
|
|
KiAmdK6AddRegion (
|
|
ULONG BaseAddress,
|
|
ULONG Size,
|
|
MEMORY_CACHING_TYPE Type,
|
|
ULONG Flags
|
|
);
|
|
|
|
NTSTATUS
|
|
KiAmdK6MtrrCommitChanges (
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
KiAmdK6HandleWcRegionRequest (
|
|
ULONG BaseAddress,
|
|
ULONG Size
|
|
);
|
|
|
|
VOID
|
|
KiAmdK6MTRRAddRegionFromHW (
|
|
AMDK6_MTRR RegImage
|
|
);
|
|
|
|
PAMDK6_MTRR_REGION
|
|
KiAmdK6FindFreeRegion (
|
|
MEMORY_CACHING_TYPE Type
|
|
);
|
|
|
|
#pragma alloc_text(INIT,KiAmdK6InitializeMTRR)
|
|
#pragma alloc_text(PAGELK,KiAmdK6RestoreMTRR)
|
|
#pragma alloc_text(PAGELK,KiAmdK6MtrrSetMemoryType)
|
|
#pragma alloc_text(PAGELK,KiAmdK6AddRegion)
|
|
#pragma alloc_text(PAGELK,KiAmdK6MtrrCommitChanges)
|
|
#pragma alloc_text(PAGELK,KiAmdK6HandleWcRegionRequest)
|
|
#pragma alloc_text(PAGELK,KiAmdK6MTRRAddRegionFromHW)
|
|
#pragma alloc_text(PAGELK,KiAmdK6FindFreeRegion)
|
|
|
|
// --- AMD Global Variables ---
|
|
|
|
extern KSPIN_LOCK KiRangeLock;
|
|
|
|
// AmdK6Regions - Table to track wc regions.
|
|
|
|
AMDK6_MTRR_REGION AmdK6Regions[MAX_K6_REGIONS];
|
|
ULONG AmdK6RegionCount;
|
|
|
|
// Usage counter for hardware MTRRs.
|
|
|
|
ULONG AmdMtrrHwUsageCount;
|
|
|
|
// Global variable image of MTRR MSR.
|
|
|
|
AMDK6_MTRR_MSR_IMAGE KiAmdK6Mtrr;
|
|
|
|
// --- AMD Start of code ---
|
|
|
|
VOID
|
|
KiAmdK6InitializeMTRR (
|
|
VOID
|
|
)
|
|
{
|
|
ULONG i;
|
|
KIRQL OldIrql;
|
|
|
|
DBGMSG("KiAmdK6InitializeMTRR: Initializing K6 MTRR support\n");
|
|
|
|
KiAmdK6Mtrr.u.hw.mtrr0.type = AMDK6_MTRR_TYPE_DISABLED;
|
|
KiAmdK6Mtrr.u.hw.mtrr1.type = AMDK6_MTRR_TYPE_DISABLED;
|
|
AmdK6RegionCount = MAX_K6_REGIONS;
|
|
AmdMtrrHwUsageCount = 0;
|
|
|
|
//
|
|
// Set all regions to free.
|
|
//
|
|
|
|
for (i = 0; i < AmdK6RegionCount; i++) {
|
|
AmdK6Regions[i].BaseAddress = AMDK6_REGION_UNUSED;
|
|
AmdK6Regions[i].RegionFlags = 0;
|
|
}
|
|
|
|
//
|
|
// Initialize the spin lock.
|
|
//
|
|
// N.B. Normally this is done by KiInitializeMTRR but that
|
|
// routine is not called in the AMD K6 case.
|
|
//
|
|
|
|
KeInitializeSpinLock (&KiRangeLock);
|
|
|
|
//
|
|
// Read the MTRR registers to see if the BIOS has set them up.
|
|
// If so, add entries to the region table and adjust the usage
|
|
// count. Serialize the region table.
|
|
//
|
|
|
|
KeAcquireSpinLock (&KiRangeLock, &OldIrql);
|
|
|
|
KiAmdK6Mtrr.u.QuadPart = RDMSR (AMDK6_MTRR_MSR);
|
|
|
|
//
|
|
// Check MTRR0 first.
|
|
//
|
|
|
|
KiAmdK6MTRRAddRegionFromHW(KiAmdK6Mtrr.u.hw.mtrr0);
|
|
|
|
//
|
|
// Now check MTRR1.
|
|
//
|
|
|
|
KiAmdK6MTRRAddRegionFromHW(KiAmdK6Mtrr.u.hw.mtrr1);
|
|
|
|
//
|
|
// Release the locks.
|
|
//
|
|
|
|
KeReleaseSpinLock (&KiRangeLock, OldIrql);
|
|
}
|
|
|
|
VOID
|
|
KiAmdK6MTRRAddRegionFromHW (
|
|
AMDK6_MTRR RegImage
|
|
)
|
|
{
|
|
ULONG BaseAddress, Size, TempMask;
|
|
|
|
//
|
|
// Check to see if this MTRR is enabled.
|
|
//
|
|
|
|
if (RegImage.type != AMDK6_MTRR_TYPE_DISABLED) {
|
|
|
|
//
|
|
// If this is a write combined region then add an entry to
|
|
// the region table.
|
|
//
|
|
|
|
if ((RegImage.type & AMDK6_MTRR_TYPE_UC) == 0) {
|
|
|
|
//
|
|
// Create a new resion table entry.
|
|
//
|
|
|
|
BaseAddress = RegImage.base << 17;
|
|
|
|
//
|
|
// Calculate the size base on the mask value.
|
|
//
|
|
|
|
TempMask = RegImage.mask;
|
|
|
|
//
|
|
// There should never be 4GB WC region!
|
|
//
|
|
|
|
ASSERT (TempMask != 0);
|
|
|
|
//
|
|
// Start with 128 size and search upward.
|
|
//
|
|
|
|
Size = 0x00020000;
|
|
|
|
while ((TempMask & 0x00000001) == 0) {
|
|
TempMask >>= 1;
|
|
Size <<= 1;
|
|
}
|
|
|
|
//
|
|
// Add the region to the table.
|
|
//
|
|
|
|
KiAmdK6AddRegion(BaseAddress,
|
|
Size,
|
|
MmWriteCombined,
|
|
AMDK6_REGION_FLAGS_BIOS);
|
|
|
|
AmdMtrrHwUsageCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
KiAmdK6MtrrSetMemoryType (
|
|
ULONG BaseAddress,
|
|
ULONG Size,
|
|
MEMORY_CACHING_TYPE Type
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
KIRQL OldIrql;
|
|
|
|
switch(Type) {
|
|
case MmWriteCombined:
|
|
|
|
//
|
|
// H/W needs updating, lock down the code required to effect
|
|
// the change.
|
|
//
|
|
|
|
if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
|
|
|
|
//
|
|
// Code can not be locked down. Supplying a new range type
|
|
// requires that the caller calls at irql < dispatch_level.
|
|
//
|
|
|
|
DBGMSG ("KeAmdK6SetPhysicalCacheTypeRange failed due to calling IRQL == DISPATCH_LEVEL\n");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Lock the code.
|
|
//
|
|
|
|
MmLockPagableSectionByHandle(ExPageLockHandle);
|
|
|
|
//
|
|
// Serialize the region table.
|
|
//
|
|
|
|
KeAcquireSpinLock (&KiRangeLock, &OldIrql);
|
|
|
|
Status = KiAmdK6HandleWcRegionRequest(BaseAddress, Size);
|
|
|
|
//
|
|
// Release the locks.
|
|
//
|
|
|
|
KeReleaseSpinLock (&KiRangeLock, OldIrql);
|
|
MmUnlockPagableImageSection(ExPageLockHandle);
|
|
|
|
break; // End of WriteCombined case.
|
|
|
|
case MmNonCached:
|
|
|
|
//
|
|
// Add an entry to the region table.
|
|
//
|
|
|
|
// Don't need to add these to the region table. Non-cached regions are
|
|
// accessed using a non-caching virtual pointer set up in the page tables.
|
|
|
|
break;
|
|
|
|
case MmCached:
|
|
|
|
//
|
|
// Redundant. These should be filtered out in
|
|
// KeAmdK6SetPhysicalCacheTypeRange();
|
|
//
|
|
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
break;
|
|
|
|
default:
|
|
DBGMSG ("KeAmdK6SetPhysicalCacheTypeRange: no such cache type\n");
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
KiAmdK6HandleWcRegionRequest (
|
|
ULONG BaseAddress,
|
|
ULONG Size
|
|
)
|
|
{
|
|
ULONG i;
|
|
ULONG AdjustedSize, AdjustedEndAddress, AlignmentMask;
|
|
ULONG CombinedBase, CombinedSize, CombinedAdjustedSize;
|
|
PAMDK6_MTRR_REGION pRegion;
|
|
BOOLEAN bCanCombine, bValidRange;
|
|
|
|
//
|
|
// Try and find a region that overlaps or is adjacent to the new one and
|
|
// check to see if the combined region would be a legal mapping.
|
|
//
|
|
|
|
for (i = 0; i < AmdK6RegionCount; i++) {
|
|
pRegion = &AmdK6Regions[i];
|
|
if ((pRegion->BaseAddress != AMDK6_REGION_UNUSED) &&
|
|
(pRegion->RegionType == MmWriteCombined)) {
|
|
|
|
//
|
|
// Does the new start address overlap or adjoin an
|
|
// existing WC region?
|
|
//
|
|
|
|
if (((pRegion->BaseAddress >= BaseAddress) &&
|
|
(pRegion->BaseAddress <= (BaseAddress + Size))) ||
|
|
((BaseAddress <= (pRegion->BaseAddress + pRegion->Size)) &&
|
|
(BaseAddress >= pRegion->BaseAddress))) {
|
|
|
|
//
|
|
// Combine the two regions into one.
|
|
//
|
|
|
|
AdjustedEndAddress = BaseAddress + Size;
|
|
|
|
if (pRegion->BaseAddress < BaseAddress) {
|
|
CombinedBase = pRegion->BaseAddress;
|
|
} else {
|
|
CombinedBase = BaseAddress;
|
|
}
|
|
|
|
if ((pRegion->BaseAddress + pRegion->Size) >
|
|
AdjustedEndAddress) {
|
|
CombinedSize = (pRegion->BaseAddress + pRegion->Size) -
|
|
CombinedBase;
|
|
} else {
|
|
CombinedSize = AdjustedEndAddress - CombinedBase;
|
|
}
|
|
|
|
//
|
|
// See if the new region would be a legal mapping.
|
|
//
|
|
//
|
|
// Find the smallest legal size that is equal to the requested range. Scan
|
|
// all ranges from 128k - 2G. (Start at 2G and work down).
|
|
//
|
|
|
|
CombinedAdjustedSize = 0x80000000;
|
|
AlignmentMask = 0x7fffffff;
|
|
bCanCombine = FALSE;
|
|
|
|
while (CombinedAdjustedSize > 0x00010000) {
|
|
|
|
//
|
|
// Check the size to see if it matches the requested limit.
|
|
//
|
|
|
|
if (CombinedAdjustedSize == CombinedSize) {
|
|
|
|
//
|
|
// This one works.
|
|
// Check to see if the base address conforms to the MTRR restrictions.
|
|
//
|
|
|
|
if ((CombinedBase & AlignmentMask) == 0) {
|
|
bCanCombine = TRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Bump it down to the next range size and try again.
|
|
//
|
|
|
|
CombinedAdjustedSize >>= 1;
|
|
AlignmentMask >>= 1;
|
|
}
|
|
}
|
|
|
|
if (bCanCombine) {
|
|
//
|
|
// If the resized range is OK, record the change in the region
|
|
// table and commit the changes to hardware.
|
|
//
|
|
|
|
pRegion->BaseAddress = CombinedBase;
|
|
pRegion->Size = CombinedAdjustedSize;
|
|
|
|
//
|
|
// Reset the BIOS flag since we now "own" this region (if we didn't already).
|
|
//
|
|
|
|
pRegion->RegionFlags &= ~AMDK6_REGION_FLAGS_BIOS;
|
|
|
|
return KiAmdK6MtrrCommitChanges();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// A valid combination could not be found, so try to create a new range for this request.
|
|
//
|
|
// Find the smallest legal size that is less than or equal to the requested range. Scan
|
|
// all ranges from 128k - 2G. (Start at 2G and work down).
|
|
//
|
|
|
|
AdjustedSize = 0x80000000;
|
|
AlignmentMask = 0x7fffffff;
|
|
bValidRange = FALSE;
|
|
|
|
while (AdjustedSize > 0x00010000) {
|
|
|
|
//
|
|
// Check the size to see if it matches the requested limit.
|
|
//
|
|
|
|
if (AdjustedSize == Size) {
|
|
|
|
//
|
|
// This one works.
|
|
//
|
|
// Check to see if the base address conforms to the MTRR restrictions.
|
|
//
|
|
|
|
if ((BaseAddress & AlignmentMask) == 0) {
|
|
bValidRange = TRUE;
|
|
}
|
|
|
|
//
|
|
// Stop looking.
|
|
//
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Bump it down to the next range size and try again.
|
|
//
|
|
|
|
AdjustedSize >>= 1;
|
|
AlignmentMask >>= 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Couldn't find a legal region that fit.
|
|
//
|
|
|
|
if (!bValidRange) {
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
|
|
//
|
|
// If we got this far then this is a new WC region.
|
|
// Create a new region entry for this request.
|
|
//
|
|
|
|
if (!KiAmdK6AddRegion(BaseAddress, AdjustedSize, MmWriteCombined, 0)) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Commit the changes to hardware.
|
|
//
|
|
|
|
return KiAmdK6MtrrCommitChanges();
|
|
}
|
|
|
|
BOOLEAN
|
|
KiAmdK6AddRegion (
|
|
ULONG BaseAddress,
|
|
ULONG Size,
|
|
MEMORY_CACHING_TYPE Type,
|
|
ULONG Flags
|
|
)
|
|
{
|
|
PAMDK6_MTRR_REGION pRegion;
|
|
|
|
if ((pRegion = KiAmdK6FindFreeRegion(Type)) == NULL) {
|
|
return FALSE;
|
|
}
|
|
pRegion->BaseAddress = BaseAddress;
|
|
pRegion->Size = Size;
|
|
pRegion->RegionType = Type;
|
|
pRegion->RegionFlags = Flags;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PAMDK6_MTRR_REGION
|
|
KiAmdK6FindFreeRegion (
|
|
MEMORY_CACHING_TYPE Type
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
//
|
|
// If this is a MmWriteCombined request, limit the number of
|
|
// regions to match the actual hardware support.
|
|
//
|
|
|
|
if (Type == MmWriteCombined) {
|
|
if (AmdMtrrHwUsageCount >= AMDK6_MAX_MTRR) {
|
|
|
|
//
|
|
// Search the table to see if there are any BIOS entries
|
|
// we can replace.
|
|
//
|
|
|
|
for (i = 0; i < AmdK6RegionCount; i++) {
|
|
if (AmdK6Regions[i].RegionFlags & AMDK6_REGION_FLAGS_BIOS) {
|
|
return &AmdK6Regions[i];
|
|
}
|
|
}
|
|
|
|
//
|
|
// No free HW MTRRs and no reusable entries.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find the next free region in the table.
|
|
//
|
|
|
|
for (i = 0; i < AmdK6RegionCount; i++) {
|
|
if (AmdK6Regions[i].BaseAddress == AMDK6_REGION_UNUSED) {
|
|
|
|
if (Type == MmWriteCombined) {
|
|
AmdMtrrHwUsageCount++;
|
|
}
|
|
return &AmdK6Regions[i];
|
|
}
|
|
}
|
|
|
|
|
|
DBGMSG("AmdK6FindFreeRegion: Region Table is Full!\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
KiAmdK6MtrrCommitChanges (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Commits the values in the table to hardware.
|
|
|
|
This procedure builds the MTRR images into the KiAmdK6Mtrr variable and
|
|
calls KiLoadMTRR to actually load the register.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i, dwWcRangeCount = 0;
|
|
ULONG RangeTemp, RangeMask;
|
|
|
|
//
|
|
// Reset the MTRR image for both MTRRs disabled.
|
|
//
|
|
|
|
KiAmdK6Mtrr.u.hw.mtrr0.type = AMDK6_MTRR_TYPE_DISABLED;
|
|
KiAmdK6Mtrr.u.hw.mtrr1.type = AMDK6_MTRR_TYPE_DISABLED;
|
|
|
|
//
|
|
// Find the Write Combining Regions, if any and set up the MTRR register.
|
|
//
|
|
|
|
for (i = 0; i < AmdK6RegionCount; i++) {
|
|
|
|
//
|
|
// Is this a valid region, and is it a write combined type?
|
|
//
|
|
|
|
if ((AmdK6Regions[i].BaseAddress != AMDK6_REGION_UNUSED) &&
|
|
(AmdK6Regions[i].RegionType == MmWriteCombined)) {
|
|
|
|
//
|
|
// Calculate the correct mask for this range size. The
|
|
// BaseAddress and size were validated and adjusted in
|
|
// AmdK6MtrrSetMemoryType().
|
|
//
|
|
// Start with 128K and scan for all legal range values and
|
|
// build the appropriate range mask at the same time.
|
|
//
|
|
|
|
RangeTemp = 0x00020000;
|
|
RangeMask = 0xfffe0000;
|
|
|
|
while (RangeTemp != 0) {
|
|
if (RangeTemp == AmdK6Regions[i].Size) {
|
|
break;
|
|
}
|
|
RangeTemp <<= 1;
|
|
RangeMask <<= 1;
|
|
}
|
|
if (RangeTemp == 0) {
|
|
|
|
//
|
|
// Not a valid range size. This can never happen!!
|
|
//
|
|
|
|
DBGMSG ("AmdK6MtrrCommitChanges: Bad WC range in region table!\n");
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Add the region to the next available register.
|
|
//
|
|
|
|
if (dwWcRangeCount == 0) {
|
|
|
|
KiAmdK6Mtrr.u.hw.mtrr0.base = AmdK6Regions[i].BaseAddress >> 17;
|
|
KiAmdK6Mtrr.u.hw.mtrr0.mask = RangeMask >> 17;
|
|
KiAmdK6Mtrr.u.hw.mtrr0.type = AMDK6_MTRR_TYPE_WC;
|
|
dwWcRangeCount++;
|
|
|
|
} else if (dwWcRangeCount == 1) {
|
|
|
|
KiAmdK6Mtrr.u.hw.mtrr1.base = AmdK6Regions[i].BaseAddress >> 17;
|
|
KiAmdK6Mtrr.u.hw.mtrr1.mask = RangeMask >> 17;
|
|
KiAmdK6Mtrr.u.hw.mtrr1.type = AMDK6_MTRR_TYPE_WC;
|
|
dwWcRangeCount++;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Should never happen! This should have been caught in
|
|
// the calling routine.
|
|
//
|
|
|
|
DBGMSG ("AmdK6MtrrCommitChanges: Not enough MTRR registers to satisfy region table!\n");
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Commit the changes to hardware.
|
|
//
|
|
|
|
KiLoadMTRR(NULL);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
KiAmdK6MtrrWRMSR (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write the AMD K6 MTRRs.
|
|
|
|
Note: Access to KiAmdK6Mtrr has been synchronized around this
|
|
call.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Write the MTRRs
|
|
//
|
|
|
|
WRMSR (AMDK6_MTRR_MSR, KiAmdK6Mtrr.u.QuadPart);
|
|
}
|
|
|
|
|