Example #1
0
/**
  Get the attribute of variable MTRRs.

  This function shadows the content of variable MTRRs into an
  internal array: VariableMtrr.

  @param  MtrrValidBitsMask     The mask for the valid bit of the MTRR
  @param  MtrrValidAddressMask  The valid address mask for MTRR
  @param  VariableMtrr          The array to shadow variable MTRRs content

  @return                       The return value of this paramter indicates the
                                number of MTRRs which has been used.

**/
UINT32
EFIAPI
MtrrGetMemoryAttributeInVariableMtrr (
  IN  UINT64                    MtrrValidBitsMask,
  IN  UINT64                    MtrrValidAddressMask,
  OUT VARIABLE_MTRR             *VariableMtrr
  )
{
  UINTN   Index;
  UINT32  MsrNum;
  UINT32  UsedMtrr;
  UINT32  FirmwareVariableMtrrCount;
  UINT32  VariableMtrrEnd;

  if (!IsMtrrSupported ()) {
    return 0;
  }

  FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount ();
  VariableMtrrEnd = MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (2 * GetVariableMtrrCount ()) - 1;

  ZeroMem (VariableMtrr, sizeof (VARIABLE_MTRR) * MTRR_NUMBER_OF_VARIABLE_MTRR);
  UsedMtrr = 0;

  for (MsrNum = MTRR_LIB_IA32_VARIABLE_MTRR_BASE, Index = 0;
       (
         (MsrNum < VariableMtrrEnd) &&
         (Index < FirmwareVariableMtrrCount)
       );
       MsrNum += 2
      ) {
    if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) != 0) {
      VariableMtrr[Index].Msr          = MsrNum;
      VariableMtrr[Index].BaseAddress  = (AsmReadMsr64 (MsrNum) &
                                          MtrrValidAddressMask);
      VariableMtrr[Index].Length       = ((~(AsmReadMsr64 (MsrNum + 1) &
                                             MtrrValidAddressMask)
                                          ) &
                                          MtrrValidBitsMask
                                         ) + 1;
      VariableMtrr[Index].Type         = (AsmReadMsr64 (MsrNum) & 0x0ff);
      VariableMtrr[Index].Valid        = TRUE;
      VariableMtrr[Index].Used         = TRUE;
      UsedMtrr = UsedMtrr  + 1;
      Index++;
    }
  }
  return UsedMtrr;
}
Example #2
0
/**
  This function attempts to set the attributes for a memory range.

  @param  BaseAddress            The physical address that is the start
                                 address of a memory region.
  @param  Length                 The size in bytes of the memory region.
  @param  Attributes             The bit mask of attributes to set for the
                                 memory region.

  @retval RETURN_SUCCESS            The attributes were set for the memory
                                    region.
  @retval RETURN_INVALID_PARAMETER  Length is zero.
  @retval RETURN_UNSUPPORTED        The processor does not support one or
                                    more bytes of the memory resource range
                                    specified by BaseAddress and Length.
  @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support
                                    for the memory resource range specified
                                    by BaseAddress and Length.
  @retval RETURN_ACCESS_DENIED      The attributes for the memory resource
                                    range specified by BaseAddress and Length
                                    cannot be modified.
  @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to
                                    modify the attributes of the memory
                                    resource range.

**/
RETURN_STATUS
EFIAPI
MtrrSetMemoryAttribute (
  IN PHYSICAL_ADDRESS        BaseAddress,
  IN UINT64                  Length,
  IN MTRR_MEMORY_CACHE_TYPE  Attribute
  )
{
  UINT64                    TempQword;
  RETURN_STATUS             Status;
  UINT64                    MemoryType;
  UINT64                    Alignment;
  BOOLEAN                   OverLap;
  BOOLEAN                   Positive;
  UINT32                    MsrNum;
  UINTN                     MtrrNumber;
  VARIABLE_MTRR             VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR];
  UINT32                    UsedMtrr;
  UINT64                    MtrrValidBitsMask;
  UINT64                    MtrrValidAddressMask;
  BOOLEAN                   OverwriteExistingMtrr;
  UINT32                    FirmwareVariableMtrrCount;
  UINT32                    VariableMtrrEnd;
  MTRR_CONTEXT              MtrrContext;

  DEBUG((DEBUG_CACHE, "MtrrSetMemoryAttribute() %a:%016lx-%016lx\n", mMtrrMemoryCacheTypeShortName[Attribute], BaseAddress, Length));

  if (!IsMtrrSupported ()) {
    Status = RETURN_UNSUPPORTED;
    goto Done;
  }

  FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount ();
  VariableMtrrEnd = MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (2 * GetVariableMtrrCount ()) - 1;

  MtrrLibInitializeMtrrMask(&MtrrValidBitsMask, &MtrrValidAddressMask);

  TempQword = 0;
  MemoryType = (UINT64)Attribute;
  OverwriteExistingMtrr = FALSE;

  //
  // Check for an invalid parameter
  //
  if (Length == 0) {
    Status = RETURN_INVALID_PARAMETER;
    goto Done;
  }

  if (
       (BaseAddress & ~MtrrValidAddressMask) != 0 ||
       (Length & ~MtrrValidAddressMask) != 0
     ) {
    Status = RETURN_UNSUPPORTED;
    goto Done;
  }

  //
  // Check if Fixed MTRR
  //
  Status = RETURN_SUCCESS;
  while ((BaseAddress < BASE_1MB) && (Length > 0) && Status == RETURN_SUCCESS) {
    PreMtrrChange (&MtrrContext);
    Status = ProgramFixedMtrr (MemoryType, &BaseAddress, &Length);
    PostMtrrChange (&MtrrContext);
    if (RETURN_ERROR (Status)) {
      goto Done;
    }
  }

  if (Length == 0) {
    //
    // A Length of 0 can only make sense for fixed MTTR ranges.
    // Since we just handled the fixed MTRRs, we can skip the
    // variable MTRR section.
    //
    goto Done;
  }

  //
  // Since memory ranges below 1MB will be overridden by the fixed MTRRs,
  // we can set the base to 0 to save variable MTRRs.
  //
  if (BaseAddress == BASE_1MB) {
    BaseAddress = 0;
    Length += SIZE_1MB;
  }

  //
  // Check for overlap
  //
  UsedMtrr = MtrrGetMemoryAttributeInVariableMtrr (MtrrValidBitsMask, MtrrValidAddressMask, VariableMtrr);
  OverLap = CheckMemoryAttributeOverlap (BaseAddress, BaseAddress + Length - 1, VariableMtrr);
  if (OverLap) {
    Status = CombineMemoryAttribute (MemoryType, &BaseAddress, &Length, VariableMtrr, &UsedMtrr, &OverwriteExistingMtrr);
    if (RETURN_ERROR (Status)) {
      goto Done;
    }

    if (Length == 0) {
      //
      // Combined successfully, invalidate the now-unused MTRRs
      //
      InvalidateMtrr(VariableMtrr);
      Status = RETURN_SUCCESS;
      goto Done;
    }
  }

  //
  // The memory type is the same with the type specified by
  // MTRR_LIB_IA32_MTRR_DEF_TYPE.
  //
  if ((!OverwriteExistingMtrr) && (Attribute == MtrrGetDefaultMemoryType ())) {
    //
    // Invalidate the now-unused MTRRs
    //
    InvalidateMtrr(VariableMtrr);
    goto Done;
  }

  Positive = GetMtrrNumberAndDirection (BaseAddress, Length, &MtrrNumber);

  if ((UsedMtrr + MtrrNumber) > FirmwareVariableMtrrCount) {
    Status = RETURN_OUT_OF_RESOURCES;
    goto Done;
  }

  //
  // Invalidate the now-unused MTRRs
  //
  InvalidateMtrr(VariableMtrr);

  //
  // Find first unused MTRR
  //
  for (MsrNum = MTRR_LIB_IA32_VARIABLE_MTRR_BASE;
       MsrNum < VariableMtrrEnd;
       MsrNum += 2
      ) {
    if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
      break;
    }
  }

  if (BaseAddress != 0) {
    do {
      //
      // Calculate the alignment of the base address.
      //
      Alignment = LShiftU64 (1, (UINTN)LowBitSet64 (BaseAddress));

      if (Alignment > Length) {
        break;
      }

      //
      // Find unused MTRR
      //
      for (; MsrNum < VariableMtrrEnd; MsrNum += 2) {
        if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
          break;
        }
      }

      ProgramVariableMtrr (
        MsrNum,
        BaseAddress,
        Alignment,
        MemoryType,
        MtrrValidAddressMask
        );
      BaseAddress += Alignment;
      Length -= Alignment;
    } while (TRUE);

    if (Length == 0) {
      goto Done;
    }
  }

  TempQword = Length;

  if (!Positive) {
    Length = Power2MaxMemory (LShiftU64 (TempQword, 1));

    //
    // Find unused MTRR
    //
    for (; MsrNum < VariableMtrrEnd; MsrNum += 2) {
      if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
        break;
      }
    }

    ProgramVariableMtrr (
      MsrNum,
      BaseAddress,
      Length,
      MemoryType,
      MtrrValidAddressMask
      );
    BaseAddress += Length;
    TempQword   = Length - TempQword;
    MemoryType  = MTRR_CACHE_UNCACHEABLE;
  }

  do {
    //
    // Find unused MTRR
    //
    for (; MsrNum < VariableMtrrEnd; MsrNum += 2) {
      if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
        break;
      }
    }

    Length = Power2MaxMemory (TempQword);
    if (!Positive) {
      BaseAddress -= Length;
    }

    ProgramVariableMtrr (
      MsrNum,
      BaseAddress,
      Length,
      MemoryType,
      MtrrValidAddressMask
      );

    if (Positive) {
      BaseAddress += Length;
    }
    TempQword -= Length;

  } while (TempQword > 0);

Done:
  DEBUG((DEBUG_CACHE, "  Status = %r\n", Status));
  if (!RETURN_ERROR (Status)) {
    MtrrDebugPrintAllMtrrs ();
  }

  return Status;
}
Example #3
0
/**
  Combine memory attributes.

  If overlap exists between given memory range and MTRRs, try to combine them.

  @param  Attributes             The memory type to set.
  @param  Base                   The base address of memory range.
  @param  Length                 The length of memory range.
  @param  VariableMtrr           The array to shadow variable MTRRs content
  @param  UsedMtrr               The number of MTRRs which has already been used
  @param  OverwriteExistingMtrr  Returns whether an existing MTRR was used

  @retval EFI_SUCCESS            Memory region successfully combined.
  @retval EFI_ACCESS_DENIED      Memory region cannot be combined.

**/
RETURN_STATUS
CombineMemoryAttribute (
  IN     UINT64             Attributes,
  IN OUT UINT64             *Base,
  IN OUT UINT64             *Length,
  IN     VARIABLE_MTRR      *VariableMtrr,
  IN OUT UINT32             *UsedMtrr,
  OUT    BOOLEAN            *OverwriteExistingMtrr
  )
{
  UINT32  Index;
  UINT64  CombineStart;
  UINT64  CombineEnd;
  UINT64  MtrrEnd;
  UINT64  EndAddress;
  UINT32  FirmwareVariableMtrrCount;
  BOOLEAN CoveredByExistingMtrr;

  FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount ();

  *OverwriteExistingMtrr = FALSE;
  CoveredByExistingMtrr = FALSE;
  EndAddress = *Base +*Length - 1;

  for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {

    MtrrEnd = VariableMtrr[Index].BaseAddress + VariableMtrr[Index].Length - 1;
    if (
         !VariableMtrr[Index].Valid ||
         (
           *Base > (MtrrEnd) ||
           (EndAddress < VariableMtrr[Index].BaseAddress)
         )
       ) {
      continue;
    }

    //
    // Combine same attribute MTRR range
    //
    if (Attributes == VariableMtrr[Index].Type) {
      //
      // if the Mtrr range contain the request range, set a flag, then continue to 
      // invalidate any MTRR of the same request range with higher priority cache type.
      //
      if (VariableMtrr[Index].BaseAddress <= *Base && MtrrEnd >= EndAddress) {
        CoveredByExistingMtrr = TRUE;
        continue;
      }
      //
      // invalid this MTRR, and program the combine range
      //
      CombineStart  =
        (*Base) < VariableMtrr[Index].BaseAddress ?
          (*Base) :
          VariableMtrr[Index].BaseAddress;
      CombineEnd    = EndAddress > MtrrEnd ? EndAddress : MtrrEnd;

      //
      // Record the MTRR usage status in VariableMtrr array.
      //
      InvalidateShadowMtrr (Index, VariableMtrr, UsedMtrr);
      *Base       = CombineStart;
      *Length     = CombineEnd - CombineStart + 1;
      EndAddress  = CombineEnd;
      *OverwriteExistingMtrr = TRUE;
      continue;
    } else {
      //
      // The cache type is different, but the range is convered by one MTRR
      //
      if (VariableMtrr[Index].BaseAddress == *Base && MtrrEnd == EndAddress) {
        InvalidateShadowMtrr (Index, VariableMtrr, UsedMtrr);
        continue;
      }

    }

    if ((Attributes== MTRR_CACHE_WRITE_THROUGH &&
         VariableMtrr[Index].Type == MTRR_CACHE_WRITE_BACK) ||
        (Attributes == MTRR_CACHE_WRITE_BACK &&
         VariableMtrr[Index].Type == MTRR_CACHE_WRITE_THROUGH) ||
        (Attributes == MTRR_CACHE_UNCACHEABLE) ||
        (VariableMtrr[Index].Type == MTRR_CACHE_UNCACHEABLE)
     ) {
      *OverwriteExistingMtrr = TRUE;
      continue;
    }
    //
    // Other type memory overlap is invalid
    //
    return RETURN_ACCESS_DENIED;
  }

  if (CoveredByExistingMtrr) {
    *Length = 0;
  }

  return RETURN_SUCCESS;
}
Example #4
0
/**
  This function attempts to set the attributes for a memory range.

  @param  BaseAddress            The physical address that is the start
                                 address of a memory region.
  @param  Length                 The size in bytes of the memory region.
  @param  Attributes             The bit mask of attributes to set for the
                                 memory region.

  @retval RETURN_SUCCESS            The attributes were set for the memory
                                    region.
  @retval RETURN_INVALID_PARAMETER  Length is zero.
  @retval RETURN_UNSUPPORTED        The processor does not support one or
                                    more bytes of the memory resource range
                                    specified by BaseAddress and Length.
  @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support
                                    for the memory resource range specified
                                    by BaseAddress and Length.
  @retval RETURN_ACCESS_DENIED      The attributes for the memory resource
                                    range specified by BaseAddress and Length
                                    cannot be modified.
  @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to
                                    modify the attributes of the memory
                                    resource range.

**/
RETURN_STATUS
EFIAPI
MtrrSetMemoryAttribute (
  IN PHYSICAL_ADDRESS        BaseAddress,
  IN UINT64                  Length,
  IN MTRR_MEMORY_CACHE_TYPE  Attribute
  )
{
  UINT64                    TempQword;
  RETURN_STATUS             Status;
  UINT64                    MemoryType;
  UINT64                    Remainder;
  BOOLEAN                   OverLap;
  BOOLEAN                   Positive;
  UINT32                    MsrNum;
  UINTN                     MtrrNumber;
  VARIABLE_MTRR             VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR];
  UINT32                    UsedMtrr;
  UINT64                    MtrrValidBitsMask;
  UINT64                    MtrrValidAddressMask;
  UINTN                     Cr4;
  BOOLEAN                   OverwriteExistingMtrr;
  UINT32                    FirmwareVariableMtrrCount;
  UINT32                    VariableMtrrEnd;

  if (!IsMtrrSupported ()) {
    return RETURN_UNSUPPORTED;
  }

  FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount ();
  VariableMtrrEnd = MTRR_LIB_IA32_VARIABLE_MTRR_BASE + (2 * GetVariableMtrrCount ()) - 1;

  MtrrLibInitializeMtrrMask(&MtrrValidBitsMask, &MtrrValidAddressMask);

  TempQword = 0;
  MemoryType = (UINT64)Attribute;
  OverwriteExistingMtrr = FALSE;

  //
  // Check for an invalid parameter
  //
  if (Length == 0) {
    return RETURN_INVALID_PARAMETER;
  }

  if (
       (BaseAddress &~MtrrValidAddressMask) != 0 ||
       (Length &~MtrrValidAddressMask) != 0
     ) {
    return RETURN_UNSUPPORTED;
  }

  //
  // Check if Fixed MTRR
  //
  Status = RETURN_SUCCESS;
  while ((BaseAddress < BASE_1MB) && (Length > 0) && Status == RETURN_SUCCESS) {
    Cr4 = PreMtrrChange ();
    Status = ProgramFixedMtrr (MemoryType, &BaseAddress, &Length);
    PostMtrrChange (Cr4);
    if (RETURN_ERROR (Status)) {
      return Status;
    }
  }

  if (Length == 0) {
    //
    // A Length of 0 can only make sense for fixed MTTR ranges.
    // Since we just handled the fixed MTRRs, we can skip the
    // variable MTRR section.
    //
    goto Done;
  }

  //
  // Since memory ranges below 1MB will be overridden by the fixed MTRRs,
  // we can set the bade to 0 to save variable MTRRs.
  //
  if (BaseAddress == BASE_1MB) {
    BaseAddress = 0;
    Length += SIZE_1MB;
  }

  //
  // Check memory base address alignment
  //
  DivU64x64Remainder (BaseAddress, Power2MaxMemory (LShiftU64 (Length, 1)), &Remainder);
  if (Remainder != 0) {
    DivU64x64Remainder (BaseAddress, Power2MaxMemory (Length), &Remainder);
    if (Remainder != 0) {
      Status = RETURN_UNSUPPORTED;
      goto Done;
    }
  }

  //
  // Check for overlap
  //
  UsedMtrr = MtrrGetMemoryAttributeInVariableMtrr (MtrrValidBitsMask, MtrrValidAddressMask, VariableMtrr);
  OverLap = CheckMemoryAttributeOverlap (BaseAddress, BaseAddress + Length - 1, VariableMtrr);
  if (OverLap) {
    Status = CombineMemoryAttribute (MemoryType, &BaseAddress, &Length, VariableMtrr, &UsedMtrr, &OverwriteExistingMtrr);
    if (RETURN_ERROR (Status)) {
      goto Done;
    }

    if (Length == 0) {
      //
      // Combined successfully
      //
      Status = RETURN_SUCCESS;
      goto Done;
    }
  }

  //
  // Program Variable MTRRs
  //
  // Avoid hardcode here and read data dynamically
  //
  if (UsedMtrr >= FirmwareVariableMtrrCount) {
    Status = RETURN_OUT_OF_RESOURCES;
    goto Done;
  }

  //
  // The memory type is the same with the type specified by
  // MTRR_LIB_IA32_MTRR_DEF_TYPE.
  //
  if ((!OverwriteExistingMtrr) && (Attribute == GetMtrrDefaultMemoryType ())) {
    //
    // Invalidate the now-unused MTRRs
    //
    InvalidateMtrr(VariableMtrr);
    goto Done;
  }

  TempQword = Length;


  if (TempQword == Power2MaxMemory (TempQword)) {
    //
    // Invalidate the now-unused MTRRs
    //
    InvalidateMtrr(VariableMtrr);

    //
    // Find first unused MTRR
    //
    for (MsrNum = MTRR_LIB_IA32_VARIABLE_MTRR_BASE;
         MsrNum < VariableMtrrEnd;
         MsrNum += 2
        ) {
      if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
        break;
      }
    }

    ProgramVariableMtrr (
      MsrNum,
      BaseAddress,
      Length,
      MemoryType,
      MtrrValidAddressMask
      );
  } else {

    Positive = GetDirection (TempQword, &MtrrNumber);

    if ((UsedMtrr + MtrrNumber) > FirmwareVariableMtrrCount) {
      Status = RETURN_OUT_OF_RESOURCES;
      goto Done;
    }

    //
    // Invalidate the now-unused MTRRs
    //
    InvalidateMtrr(VariableMtrr);

    //
    // Find first unused MTRR
    //
    for (MsrNum = MTRR_LIB_IA32_VARIABLE_MTRR_BASE;
         MsrNum < VariableMtrrEnd;
         MsrNum += 2
        ) {
      if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
        break;
      }
    }

    if (!Positive) {
      Length = Power2MaxMemory (LShiftU64 (TempQword, 1));
      ProgramVariableMtrr (
        MsrNum,
        BaseAddress,
        Length,
        MemoryType,
        MtrrValidAddressMask
        );
      BaseAddress += Length;
      TempQword   = Length - TempQword;
      MemoryType  = MTRR_CACHE_UNCACHEABLE;
    }

    do {
      //
      // Find unused MTRR
      //
      for (; MsrNum < VariableMtrrEnd; MsrNum += 2) {
        if ((AsmReadMsr64 (MsrNum + 1) & MTRR_LIB_CACHE_MTRR_ENABLED) == 0) {
          break;
        }
      }

      Length = Power2MaxMemory (TempQword);
      if (!Positive) {
        BaseAddress -= Length;
      }

      ProgramVariableMtrr (
        MsrNum,
        BaseAddress,
        Length,
        MemoryType,
        MtrrValidAddressMask
        );

      if (Positive) {
        BaseAddress += Length;
      }
      TempQword -= Length;

    } while (TempQword > 0);
  }

Done:
  return Status;

}