This commit will scan all the EFI raw section instances within the module's FV to make sure the NVDIMM root device SSDT can be properly located. Cc: Feng Tian <feng.tian@intel.com> Cc: Zhang Chao B <chao.b.zhang@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Chao Zhang <chao.b.zhang@intel.com>
		
			
				
	
	
		
			862 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			862 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  The realization of EFI_RAM_DISK_PROTOCOL.
 | 
						|
 | 
						|
  Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
 | 
						|
  (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
 | 
						|
  This program and the accompanying materials
 | 
						|
  are licensed and made available under the terms and conditions of the BSD License
 | 
						|
  which accompanies this distribution.  The full text of the license may be found at
 | 
						|
  http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "RamDiskImpl.h"
 | 
						|
 | 
						|
RAM_DISK_PRIVATE_DATA mRamDiskPrivateDataTemplate = {
 | 
						|
  RAM_DISK_PRIVATE_DATA_SIGNATURE,
 | 
						|
  NULL
 | 
						|
};
 | 
						|
 | 
						|
MEDIA_RAM_DISK_DEVICE_PATH  mRamDiskDeviceNodeTemplate = {
 | 
						|
  {
 | 
						|
    MEDIA_DEVICE_PATH,
 | 
						|
    MEDIA_RAM_DISK_DP,
 | 
						|
    {
 | 
						|
      (UINT8) (sizeof (MEDIA_RAM_DISK_DEVICE_PATH)),
 | 
						|
      (UINT8) ((sizeof (MEDIA_RAM_DISK_DEVICE_PATH)) >> 8)
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
BOOLEAN  mRamDiskSsdtTableKeyValid = FALSE;
 | 
						|
UINTN    mRamDiskSsdtTableKey;
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Initialize the RAM disk device node.
 | 
						|
 | 
						|
  @param[in]      PrivateData     Points to RAM disk private data.
 | 
						|
  @param[in, out] RamDiskDevNode  Points to the RAM disk device node.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
RamDiskInitDeviceNode (
 | 
						|
  IN     RAM_DISK_PRIVATE_DATA         *PrivateData,
 | 
						|
  IN OUT MEDIA_RAM_DISK_DEVICE_PATH    *RamDiskDevNode
 | 
						|
  )
 | 
						|
{
 | 
						|
  WriteUnaligned64 (
 | 
						|
    (UINT64 *) &(RamDiskDevNode->StartingAddr[0]),
 | 
						|
    (UINT64) PrivateData->StartingAddr
 | 
						|
    );
 | 
						|
  WriteUnaligned64 (
 | 
						|
    (UINT64 *) &(RamDiskDevNode->EndingAddr[0]),
 | 
						|
    (UINT64) PrivateData->StartingAddr + PrivateData->Size - 1
 | 
						|
    );
 | 
						|
  CopyGuid (&RamDiskDevNode->TypeGuid, &PrivateData->TypeGuid);
 | 
						|
  RamDiskDevNode->Instance = PrivateData->InstanceNumber;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Initialize and publish NVDIMM root device SSDT in ACPI table.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS        The NVDIMM root device SSDT is published.
 | 
						|
  @retval Others             The NVDIMM root device SSDT is not published.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RamDiskPublishSsdt (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                     Status;
 | 
						|
  EFI_ACPI_DESCRIPTION_HEADER    *Table;
 | 
						|
  UINTN                          SectionInstance;
 | 
						|
  UINTN                          TableSize;
 | 
						|
 | 
						|
  Status          = EFI_SUCCESS;
 | 
						|
  SectionInstance = 0;
 | 
						|
 | 
						|
  //
 | 
						|
  // Scan all the EFI raw section instances in FV to find the NVDIMM root
 | 
						|
  // device SSDT.
 | 
						|
  //
 | 
						|
  while (TRUE) {
 | 
						|
    Status = GetSectionFromFv (
 | 
						|
               &gEfiCallerIdGuid,
 | 
						|
               EFI_SECTION_RAW,
 | 
						|
               SectionInstance,
 | 
						|
               (VOID **) &Table,
 | 
						|
               &TableSize
 | 
						|
               );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Table->OemTableId == SIGNATURE_64 ('R', 'a', 'm', 'D', 'i', 's', 'k', ' ')) {
 | 
						|
      Status = mAcpiTableProtocol->InstallAcpiTable (
 | 
						|
                                     mAcpiTableProtocol,
 | 
						|
                                     Table,
 | 
						|
                                     TableSize,
 | 
						|
                                     &mRamDiskSsdtTableKey
 | 
						|
                                     );
 | 
						|
      ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
      if (!EFI_ERROR (Status)) {
 | 
						|
        mRamDiskSsdtTableKeyValid = TRUE;
 | 
						|
      }
 | 
						|
 | 
						|
      FreePool (Table);
 | 
						|
      return Status;
 | 
						|
    } else {
 | 
						|
      FreePool (Table);
 | 
						|
      SectionInstance++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Publish the RAM disk NVDIMM Firmware Interface Table (NFIT) to the ACPI
 | 
						|
  table.
 | 
						|
 | 
						|
  @param[in] PrivateData          Points to RAM disk private data.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS             The RAM disk NFIT has been published.
 | 
						|
  @retval others                  The RAM disk NFIT has not been published.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RamDiskPublishNfit (
 | 
						|
  IN RAM_DISK_PRIVATE_DATA        *PrivateData
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                                    Status;
 | 
						|
  EFI_MEMORY_DESCRIPTOR                         *MemoryMap;
 | 
						|
  EFI_MEMORY_DESCRIPTOR                         *MemoryMapEntry;
 | 
						|
  EFI_MEMORY_DESCRIPTOR                         *MemoryMapEnd;
 | 
						|
  UINTN                                         TableIndex;
 | 
						|
  VOID                                          *TableHeader;
 | 
						|
  EFI_ACPI_TABLE_VERSION                        TableVersion;
 | 
						|
  UINTN                                         TableKey;
 | 
						|
  EFI_ACPI_DESCRIPTION_HEADER                   *NfitHeader;
 | 
						|
  EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE
 | 
						|
                                                *SpaRange;
 | 
						|
  VOID                                          *Nfit;
 | 
						|
  UINT32                                        NfitLen;
 | 
						|
  UINTN                                         MemoryMapSize;
 | 
						|
  UINTN                                         MapKey;
 | 
						|
  UINTN                                         DescriptorSize;
 | 
						|
  UINT32                                        DescriptorVersion;
 | 
						|
  UINT64                                        CurrentData;
 | 
						|
  UINT8                                         Checksum;
 | 
						|
  BOOLEAN                                       MemoryFound;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the EFI memory map.
 | 
						|
  //
 | 
						|
  MemoryMapSize = 0;
 | 
						|
  MemoryMap     = NULL;
 | 
						|
  MemoryFound   = FALSE;
 | 
						|
 | 
						|
  Status = gBS->GetMemoryMap (
 | 
						|
                  &MemoryMapSize,
 | 
						|
                  MemoryMap,
 | 
						|
                  &MapKey,
 | 
						|
                  &DescriptorSize,
 | 
						|
                  &DescriptorVersion
 | 
						|
                  );
 | 
						|
  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
 | 
						|
  do {
 | 
						|
    MemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (MemoryMapSize);
 | 
						|
    ASSERT (MemoryMap != NULL);
 | 
						|
    Status = gBS->GetMemoryMap (
 | 
						|
                    &MemoryMapSize,
 | 
						|
                    MemoryMap,
 | 
						|
                    &MapKey,
 | 
						|
                    &DescriptorSize,
 | 
						|
                    &DescriptorVersion
 | 
						|
                    );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      FreePool (MemoryMap);
 | 
						|
    }
 | 
						|
  } while (Status == EFI_BUFFER_TOO_SMALL);
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  MemoryMapEntry = MemoryMap;
 | 
						|
  MemoryMapEnd   = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
 | 
						|
  while ((UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd) {
 | 
						|
    if ((MemoryMapEntry->Type == EfiReservedMemoryType) &&
 | 
						|
        (MemoryMapEntry->PhysicalStart <= PrivateData->StartingAddr) &&
 | 
						|
        (MemoryMapEntry->PhysicalStart +
 | 
						|
         MultU64x32 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SIZE)
 | 
						|
         >= PrivateData->StartingAddr + PrivateData->Size)) {
 | 
						|
      MemoryFound = TRUE;
 | 
						|
      DEBUG ((
 | 
						|
        EFI_D_INFO,
 | 
						|
        "RamDiskPublishNfit: RAM disk with reserved meomry type, will publish to NFIT.\n"
 | 
						|
        ));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
 | 
						|
  }
 | 
						|
  FreePool (MemoryMap);
 | 
						|
 | 
						|
  if (!MemoryFound) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Determine whether there is a NFIT already in the ACPI table.
 | 
						|
  //
 | 
						|
  Status      = EFI_SUCCESS;
 | 
						|
  TableIndex  = 0;
 | 
						|
  TableKey    = 0;
 | 
						|
  TableHeader = NULL;
 | 
						|
 | 
						|
  while (!EFI_ERROR (Status)) {
 | 
						|
    Status = mAcpiSdtProtocol->GetAcpiTable (
 | 
						|
                                 TableIndex,
 | 
						|
                                 (EFI_ACPI_SDT_HEADER **)&TableHeader,
 | 
						|
                                 &TableVersion,
 | 
						|
                                 &TableKey
 | 
						|
                                 );
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      TableIndex++;
 | 
						|
 | 
						|
      if (((EFI_ACPI_SDT_HEADER *)TableHeader)->Signature ==
 | 
						|
          EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    //
 | 
						|
    // A NFIT is already in the ACPI table.
 | 
						|
    //
 | 
						|
    DEBUG ((
 | 
						|
      EFI_D_INFO,
 | 
						|
      "RamDiskPublishNfit: A NFIT is already exist in the ACPI Table.\n"
 | 
						|
      ));
 | 
						|
 | 
						|
    NfitHeader = (EFI_ACPI_DESCRIPTION_HEADER *)TableHeader;
 | 
						|
    NfitLen    = NfitHeader->Length + sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE);
 | 
						|
    Nfit       = AllocateZeroPool (NfitLen);
 | 
						|
    if (Nfit == NULL) {
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
    CopyMem (Nfit, TableHeader, NfitHeader->Length);
 | 
						|
 | 
						|
    //
 | 
						|
    // Update the NFIT head pointer.
 | 
						|
    //
 | 
						|
    NfitHeader = (EFI_ACPI_DESCRIPTION_HEADER *)Nfit;
 | 
						|
 | 
						|
    //
 | 
						|
    // Uninstall the origin NFIT from the ACPI table.
 | 
						|
    //
 | 
						|
    Status = mAcpiTableProtocol->UninstallAcpiTable (
 | 
						|
                                   mAcpiTableProtocol,
 | 
						|
                                   TableKey
 | 
						|
                                   );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      FreePool (Nfit);
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Append the System Physical Address (SPA) Range Structure at the end
 | 
						|
    // of the origin NFIT.
 | 
						|
    //
 | 
						|
    SpaRange   = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *)
 | 
						|
                 ((UINT8 *)Nfit + NfitHeader->Length);
 | 
						|
 | 
						|
    //
 | 
						|
    // Update the length field of the NFIT
 | 
						|
    //
 | 
						|
    NfitHeader->Length   = NfitLen;
 | 
						|
 | 
						|
    //
 | 
						|
    // The checksum will be updated after the new contents are appended.
 | 
						|
    //
 | 
						|
    NfitHeader->Checksum = 0;
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // Assumption is made that if no NFIT is in the ACPI table, there is no
 | 
						|
    // NVDIMM root device in the \SB scope.
 | 
						|
    // Therefore, a NVDIMM root device will be reported via Secondary System
 | 
						|
    // Description Table (SSDT).
 | 
						|
    //
 | 
						|
    Status = RamDiskPublishSsdt ();
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // No NFIT is in the ACPI table, we will create one here.
 | 
						|
    //
 | 
						|
    DEBUG ((
 | 
						|
      EFI_D_INFO,
 | 
						|
      "RamDiskPublishNfit: No NFIT is in the ACPI Table, will create one.\n"
 | 
						|
      ));
 | 
						|
 | 
						|
    NfitLen = sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE) +
 | 
						|
              sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE);
 | 
						|
    Nfit    = AllocateZeroPool (NfitLen);
 | 
						|
    if (Nfit == NULL) {
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
 | 
						|
    SpaRange = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *)
 | 
						|
               ((UINT8 *)Nfit + sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE));
 | 
						|
 | 
						|
    NfitHeader                  = (EFI_ACPI_DESCRIPTION_HEADER *)Nfit;
 | 
						|
    NfitHeader->Signature       = EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE;
 | 
						|
    NfitHeader->Length          = NfitLen;
 | 
						|
    NfitHeader->Revision        = EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_REVISION;
 | 
						|
    NfitHeader->Checksum        = 0;
 | 
						|
    NfitHeader->OemRevision     = PcdGet32 (PcdAcpiDefaultOemRevision);
 | 
						|
    NfitHeader->CreatorId       = PcdGet32 (PcdAcpiDefaultCreatorId);
 | 
						|
    NfitHeader->CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision);
 | 
						|
    CurrentData                 = PcdGet64 (PcdAcpiDefaultOemTableId);
 | 
						|
    CopyMem (NfitHeader->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (NfitHeader->OemId));
 | 
						|
    CopyMem (&NfitHeader->OemTableId, &CurrentData, sizeof (UINT64));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill in the content of the SPA Range Structure.
 | 
						|
  //
 | 
						|
  SpaRange->Type   = EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE_TYPE;
 | 
						|
  SpaRange->Length = sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE);
 | 
						|
  SpaRange->SystemPhysicalAddressRangeBase   = PrivateData->StartingAddr;
 | 
						|
  SpaRange->SystemPhysicalAddressRangeLength = PrivateData->Size;
 | 
						|
  CopyGuid (&SpaRange->AddressRangeTypeGUID, &PrivateData->TypeGuid);
 | 
						|
 | 
						|
  Checksum             = CalculateCheckSum8((UINT8 *)Nfit, NfitHeader->Length);
 | 
						|
  NfitHeader->Checksum = Checksum;
 | 
						|
 | 
						|
  //
 | 
						|
  // Publish the NFIT to the ACPI table.
 | 
						|
  // Note, since the NFIT might be modified by other driver, therefore, we
 | 
						|
  // do not track the returning TableKey from the InstallAcpiTable().
 | 
						|
  //
 | 
						|
  Status = mAcpiTableProtocol->InstallAcpiTable (
 | 
						|
                                 mAcpiTableProtocol,
 | 
						|
                                 Nfit,
 | 
						|
                                 NfitHeader->Length,
 | 
						|
                                 &TableKey
 | 
						|
                                 );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  FreePool (Nfit);
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  PrivateData->InNfit = TRUE;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Unpublish the RAM disk NVDIMM Firmware Interface Table (NFIT) from the
 | 
						|
  ACPI table.
 | 
						|
 | 
						|
  @param[in] PrivateData          Points to RAM disk private data.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS             The RAM disk NFIT has been unpublished.
 | 
						|
  @retval others                  The RAM disk NFIT has not been unpublished.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RamDiskUnpublishNfit (
 | 
						|
  IN RAM_DISK_PRIVATE_DATA        *PrivateData
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                                    Status;
 | 
						|
  UINTN                                         TableIndex;
 | 
						|
  VOID                                          *TableHeader;
 | 
						|
  EFI_ACPI_TABLE_VERSION                        TableVersion;
 | 
						|
  UINTN                                         TableKey;
 | 
						|
  EFI_ACPI_DESCRIPTION_HEADER                   *NewNfitHeader;
 | 
						|
  EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE
 | 
						|
                                                *SpaRange;
 | 
						|
  VOID                                          *NewNfit;
 | 
						|
  VOID                                          *NewNfitPtr;
 | 
						|
  EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER            *NfitStructHeader;
 | 
						|
  UINT32                                        NewNfitLen;
 | 
						|
  UINT32                                        RemainLen;
 | 
						|
  UINT8                                         Checksum;
 | 
						|
 | 
						|
  //
 | 
						|
  // Find the NFIT in the ACPI table.
 | 
						|
  //
 | 
						|
  Status      = EFI_SUCCESS;
 | 
						|
  TableIndex  = 0;
 | 
						|
  TableKey    = 0;
 | 
						|
  TableHeader = NULL;
 | 
						|
 | 
						|
  while (!EFI_ERROR (Status)) {
 | 
						|
    Status = mAcpiSdtProtocol->GetAcpiTable (
 | 
						|
                                 TableIndex,
 | 
						|
                                 (EFI_ACPI_SDT_HEADER **)&TableHeader,
 | 
						|
                                 &TableVersion,
 | 
						|
                                 &TableKey
 | 
						|
                                 );
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      TableIndex++;
 | 
						|
 | 
						|
      if (((EFI_ACPI_SDT_HEADER *)TableHeader)->Signature ==
 | 
						|
          EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    //
 | 
						|
    // No NFIT is found in the ACPI table.
 | 
						|
    //
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  NewNfitLen    = ((EFI_ACPI_DESCRIPTION_HEADER *)TableHeader)->Length -
 | 
						|
                  sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE);
 | 
						|
 | 
						|
  //
 | 
						|
  // After removing this RAM disk from the NFIT, if no other structure is in
 | 
						|
  // the NFIT, we just remove the NFIT and the SSDT which is used to report
 | 
						|
  // the NVDIMM root device.
 | 
						|
  //
 | 
						|
  if (NewNfitLen == sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE)) {
 | 
						|
    //
 | 
						|
    // Remove the NFIT.
 | 
						|
    //
 | 
						|
    Status = mAcpiTableProtocol->UninstallAcpiTable (
 | 
						|
                                   mAcpiTableProtocol,
 | 
						|
                                   TableKey
 | 
						|
                                   );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Remove the SSDT which is used by RamDiskDxe driver to report the NVDIMM
 | 
						|
    // root device.
 | 
						|
    // We do not care the return status since this SSDT might already be
 | 
						|
    // uninstalled by other drivers to update the information of the NVDIMM
 | 
						|
    // root device.
 | 
						|
    //
 | 
						|
    if (mRamDiskSsdtTableKeyValid) {
 | 
						|
      mRamDiskSsdtTableKeyValid = FALSE;
 | 
						|
 | 
						|
      mAcpiTableProtocol->UninstallAcpiTable (
 | 
						|
                            mAcpiTableProtocol,
 | 
						|
                            mRamDiskSsdtTableKey
 | 
						|
                            );
 | 
						|
    }
 | 
						|
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  NewNfit = AllocateZeroPool (NewNfitLen);
 | 
						|
  if (NewNfit == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Get a copy of the old NFIT header content.
 | 
						|
  //
 | 
						|
  CopyMem (NewNfit, TableHeader, sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE));
 | 
						|
  NewNfitHeader           = (EFI_ACPI_DESCRIPTION_HEADER *)NewNfit;
 | 
						|
  NewNfitHeader->Length   = NewNfitLen;
 | 
						|
  NewNfitHeader->Checksum = 0;
 | 
						|
 | 
						|
  //
 | 
						|
  // Copy the content of required NFIT structures.
 | 
						|
  //
 | 
						|
  NewNfitPtr       = (UINT8 *)NewNfit + sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE);
 | 
						|
  RemainLen        = NewNfitLen - sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE);
 | 
						|
  NfitStructHeader = (EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *)
 | 
						|
                     ((UINT8 *)TableHeader + sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE));
 | 
						|
  while (RemainLen > 0) {
 | 
						|
    if ((NfitStructHeader->Type == EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE_TYPE) &&
 | 
						|
        (NfitStructHeader->Length == sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE))) {
 | 
						|
      SpaRange = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *)NfitStructHeader;
 | 
						|
 | 
						|
      if ((SpaRange->SystemPhysicalAddressRangeBase == PrivateData->StartingAddr) &&
 | 
						|
          (SpaRange->SystemPhysicalAddressRangeLength == PrivateData->Size) &&
 | 
						|
          (CompareGuid (&SpaRange->AddressRangeTypeGUID, &PrivateData->TypeGuid))) {
 | 
						|
        //
 | 
						|
        // Skip the SPA Range Structure for the RAM disk to be unpublished
 | 
						|
        // from NFIT.
 | 
						|
        //
 | 
						|
        NfitStructHeader = (EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *)
 | 
						|
                           ((UINT8 *)NfitStructHeader + NfitStructHeader->Length);
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Copy the content of origin NFIT.
 | 
						|
    //
 | 
						|
    CopyMem (NewNfitPtr, NfitStructHeader, NfitStructHeader->Length);
 | 
						|
    NewNfitPtr = (UINT8 *)NewNfitPtr + NfitStructHeader->Length;
 | 
						|
 | 
						|
    //
 | 
						|
    // Move to the header of next NFIT structure.
 | 
						|
    //
 | 
						|
    RemainLen       -= NfitStructHeader->Length;
 | 
						|
    NfitStructHeader = (EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *)
 | 
						|
                       ((UINT8 *)NfitStructHeader + NfitStructHeader->Length);
 | 
						|
  }
 | 
						|
 | 
						|
  Checksum                = CalculateCheckSum8((UINT8 *)NewNfit, NewNfitHeader->Length);
 | 
						|
  NewNfitHeader->Checksum = Checksum;
 | 
						|
 | 
						|
  Status = mAcpiTableProtocol->UninstallAcpiTable (
 | 
						|
                                 mAcpiTableProtocol,
 | 
						|
                                 TableKey
 | 
						|
                                 );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    FreePool (NewNfit);
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Publish the NFIT to the ACPI table.
 | 
						|
  // Note, since the NFIT might be modified by other driver, therefore, we
 | 
						|
  // do not track the returning TableKey from the InstallAcpiTable().
 | 
						|
  //
 | 
						|
  Status = mAcpiTableProtocol->InstallAcpiTable (
 | 
						|
                                 mAcpiTableProtocol,
 | 
						|
                                 NewNfit,
 | 
						|
                                 NewNfitLen,
 | 
						|
                                 &TableKey
 | 
						|
                                 );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  FreePool (NewNfit);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Register a RAM disk with specified address, size and type.
 | 
						|
 | 
						|
  @param[in]  RamDiskBase    The base address of registered RAM disk.
 | 
						|
  @param[in]  RamDiskSize    The size of registered RAM disk.
 | 
						|
  @param[in]  RamDiskType    The type of registered RAM disk. The GUID can be
 | 
						|
                             any of the values defined in section 9.3.6.9, or a
 | 
						|
                             vendor defined GUID.
 | 
						|
  @param[in]  ParentDevicePath
 | 
						|
                             Pointer to the parent device path. If there is no
 | 
						|
                             parent device path then ParentDevicePath is NULL.
 | 
						|
  @param[out] DevicePath     On return, points to a pointer to the device path
 | 
						|
                             of the RAM disk device.
 | 
						|
                             If ParentDevicePath is not NULL, the returned
 | 
						|
                             DevicePath is created by appending a RAM disk node
 | 
						|
                             to the parent device path. If ParentDevicePath is
 | 
						|
                             NULL, the returned DevicePath is a RAM disk device
 | 
						|
                             path without appending. This function is
 | 
						|
                             responsible for allocating the buffer DevicePath
 | 
						|
                             with the boot service AllocatePool().
 | 
						|
 | 
						|
  @retval EFI_SUCCESS             The RAM disk is registered successfully.
 | 
						|
  @retval EFI_INVALID_PARAMETER   DevicePath or RamDiskType is NULL.
 | 
						|
                                  RamDiskSize is 0.
 | 
						|
  @retval EFI_ALREADY_STARTED     A Device Path Protocol instance to be created
 | 
						|
                                  is already present in the handle database.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES    The RAM disk register operation fails due to
 | 
						|
                                  resource limitation.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
RamDiskRegister (
 | 
						|
  IN UINT64                       RamDiskBase,
 | 
						|
  IN UINT64                       RamDiskSize,
 | 
						|
  IN EFI_GUID                     *RamDiskType,
 | 
						|
  IN EFI_DEVICE_PATH              *ParentDevicePath     OPTIONAL,
 | 
						|
  OUT EFI_DEVICE_PATH_PROTOCOL    **DevicePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                      Status;
 | 
						|
  RAM_DISK_PRIVATE_DATA           *PrivateData;
 | 
						|
  RAM_DISK_PRIVATE_DATA           *RegisteredPrivateData;
 | 
						|
  MEDIA_RAM_DISK_DEVICE_PATH      *RamDiskDevNode;
 | 
						|
  UINTN                           DevicePathSize;
 | 
						|
  LIST_ENTRY                      *Entry;
 | 
						|
 | 
						|
  if ((0 == RamDiskSize) || (NULL == RamDiskType) || (NULL == DevicePath)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Add check to prevent data read across the memory boundary
 | 
						|
  //
 | 
						|
  if (RamDiskBase + RamDiskSize > ((UINTN) -1) - RAM_DISK_BLOCK_SIZE + 1) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  RamDiskDevNode = NULL;
 | 
						|
 | 
						|
  //
 | 
						|
  // Create a new RAM disk instance and initialize its private data
 | 
						|
  //
 | 
						|
  PrivateData = AllocateCopyPool (
 | 
						|
                  sizeof (RAM_DISK_PRIVATE_DATA),
 | 
						|
                  &mRamDiskPrivateDataTemplate
 | 
						|
                  );
 | 
						|
  if (NULL == PrivateData) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  PrivateData->StartingAddr = RamDiskBase;
 | 
						|
  PrivateData->Size         = RamDiskSize;
 | 
						|
  CopyGuid (&PrivateData->TypeGuid, RamDiskType);
 | 
						|
  InitializeListHead (&PrivateData->ThisInstance);
 | 
						|
 | 
						|
  //
 | 
						|
  // Generate device path information for the registered RAM disk
 | 
						|
  //
 | 
						|
  RamDiskDevNode = AllocateCopyPool (
 | 
						|
                     sizeof (MEDIA_RAM_DISK_DEVICE_PATH),
 | 
						|
                     &mRamDiskDeviceNodeTemplate
 | 
						|
                     );
 | 
						|
  if (NULL == RamDiskDevNode) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ErrorExit;
 | 
						|
  }
 | 
						|
 | 
						|
  RamDiskInitDeviceNode (PrivateData, RamDiskDevNode);
 | 
						|
 | 
						|
  *DevicePath = AppendDevicePathNode (
 | 
						|
                  ParentDevicePath,
 | 
						|
                  (EFI_DEVICE_PATH_PROTOCOL *) RamDiskDevNode
 | 
						|
                  );
 | 
						|
  if (NULL == *DevicePath) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ErrorExit;
 | 
						|
  }
 | 
						|
 | 
						|
  PrivateData->DevicePath = *DevicePath;
 | 
						|
 | 
						|
  //
 | 
						|
  // Check whether the created device path is already present in the handle
 | 
						|
  // database
 | 
						|
  //
 | 
						|
  if (!IsListEmpty(&RegisteredRamDisks)) {
 | 
						|
    DevicePathSize = GetDevicePathSize (PrivateData->DevicePath);
 | 
						|
 | 
						|
    EFI_LIST_FOR_EACH (Entry, &RegisteredRamDisks) {
 | 
						|
      RegisteredPrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry);
 | 
						|
      if (DevicePathSize == GetDevicePathSize (RegisteredPrivateData->DevicePath)) {
 | 
						|
        //
 | 
						|
        // Compare device path
 | 
						|
        //
 | 
						|
        if ((CompareMem (
 | 
						|
               PrivateData->DevicePath,
 | 
						|
               RegisteredPrivateData->DevicePath,
 | 
						|
               DevicePathSize)) == 0) {
 | 
						|
          *DevicePath = NULL;
 | 
						|
          Status      = EFI_ALREADY_STARTED;
 | 
						|
          goto ErrorExit;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill Block IO protocol informations for the RAM disk
 | 
						|
  //
 | 
						|
  RamDiskInitBlockIo (PrivateData);
 | 
						|
 | 
						|
  //
 | 
						|
  // Install EFI_DEVICE_PATH_PROTOCOL & EFI_BLOCK_IO(2)_PROTOCOL on a new
 | 
						|
  // handle
 | 
						|
  //
 | 
						|
  Status = gBS->InstallMultipleProtocolInterfaces (
 | 
						|
                  &PrivateData->Handle,
 | 
						|
                  &gEfiBlockIoProtocolGuid,
 | 
						|
                  &PrivateData->BlockIo,
 | 
						|
                  &gEfiBlockIo2ProtocolGuid,
 | 
						|
                  &PrivateData->BlockIo2,
 | 
						|
                  &gEfiDevicePathProtocolGuid,
 | 
						|
                  PrivateData->DevicePath,
 | 
						|
                  NULL
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    goto ErrorExit;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Insert the newly created one to the registered RAM disk list
 | 
						|
  //
 | 
						|
  InsertTailList (&RegisteredRamDisks, &PrivateData->ThisInstance);
 | 
						|
 | 
						|
  gBS->ConnectController (PrivateData->Handle, NULL, NULL, TRUE);
 | 
						|
 | 
						|
  FreePool (RamDiskDevNode);
 | 
						|
 | 
						|
  if ((mAcpiTableProtocol != NULL) && (mAcpiSdtProtocol != NULL)) {
 | 
						|
    RamDiskPublishNfit (PrivateData);
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
 | 
						|
ErrorExit:
 | 
						|
  if (RamDiskDevNode != NULL) {
 | 
						|
    FreePool (RamDiskDevNode);
 | 
						|
  }
 | 
						|
 | 
						|
  if (PrivateData != NULL) {
 | 
						|
    if (PrivateData->DevicePath) {
 | 
						|
      FreePool (PrivateData->DevicePath);
 | 
						|
    }
 | 
						|
 | 
						|
    FreePool (PrivateData);
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Unregister a RAM disk specified by DevicePath.
 | 
						|
 | 
						|
  @param[in] DevicePath      A pointer to the device path that describes a RAM
 | 
						|
                             Disk device.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS             The RAM disk is unregistered successfully.
 | 
						|
  @retval EFI_INVALID_PARAMETER   DevicePath is NULL.
 | 
						|
  @retval EFI_UNSUPPORTED         The device specified by DevicePath is not a
 | 
						|
                                  valid ramdisk device path and not supported
 | 
						|
                                  by the driver.
 | 
						|
  @retval EFI_NOT_FOUND           The RAM disk pointed by DevicePath doesn't
 | 
						|
                                  exist.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
RamDiskUnregister (
 | 
						|
  IN  EFI_DEVICE_PATH_PROTOCOL    *DevicePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                      *Entry;
 | 
						|
  LIST_ENTRY                      *NextEntry;
 | 
						|
  BOOLEAN                         Found;
 | 
						|
  UINT64                          StartingAddr;
 | 
						|
  UINT64                          EndingAddr;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL        *Header;
 | 
						|
  MEDIA_RAM_DISK_DEVICE_PATH      *RamDiskDevNode;
 | 
						|
  RAM_DISK_PRIVATE_DATA           *PrivateData;
 | 
						|
 | 
						|
  if (NULL == DevicePath) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Locate the RAM disk device node.
 | 
						|
  //
 | 
						|
  RamDiskDevNode = NULL;
 | 
						|
  Header         = DevicePath;
 | 
						|
  do {
 | 
						|
    //
 | 
						|
    // Test if the current device node is a RAM disk.
 | 
						|
    //
 | 
						|
    if ((MEDIA_DEVICE_PATH == Header->Type) &&
 | 
						|
      (MEDIA_RAM_DISK_DP == Header->SubType)) {
 | 
						|
      RamDiskDevNode = (MEDIA_RAM_DISK_DEVICE_PATH *) Header;
 | 
						|
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    Header = NextDevicePathNode (Header);
 | 
						|
  } while ((Header->Type != END_DEVICE_PATH_TYPE));
 | 
						|
 | 
						|
  if (NULL == RamDiskDevNode) {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  Found          = FALSE;
 | 
						|
  StartingAddr   = ReadUnaligned64 ((UINT64 *) &(RamDiskDevNode->StartingAddr[0]));
 | 
						|
  EndingAddr     = ReadUnaligned64 ((UINT64 *) &(RamDiskDevNode->EndingAddr[0]));
 | 
						|
 | 
						|
  if (!IsListEmpty(&RegisteredRamDisks)) {
 | 
						|
    EFI_LIST_FOR_EACH_SAFE (Entry, NextEntry, &RegisteredRamDisks) {
 | 
						|
      PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry);
 | 
						|
 | 
						|
      //
 | 
						|
      // Unregister the RAM disk given by its starting address, ending address
 | 
						|
      // and type guid.
 | 
						|
      //
 | 
						|
      if ((StartingAddr == PrivateData->StartingAddr) &&
 | 
						|
          (EndingAddr == PrivateData->StartingAddr + PrivateData->Size - 1) &&
 | 
						|
          (CompareGuid (&RamDiskDevNode->TypeGuid, &PrivateData->TypeGuid))) {
 | 
						|
        //
 | 
						|
        // Remove the content for this RAM disk in NFIT.
 | 
						|
        //
 | 
						|
        if (PrivateData->InNfit) {
 | 
						|
          RamDiskUnpublishNfit (PrivateData);
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        // Uninstall the EFI_DEVICE_PATH_PROTOCOL & EFI_BLOCK_IO(2)_PROTOCOL
 | 
						|
        //
 | 
						|
        gBS->UninstallMultipleProtocolInterfaces (
 | 
						|
               PrivateData->Handle,
 | 
						|
               &gEfiBlockIoProtocolGuid,
 | 
						|
               &PrivateData->BlockIo,
 | 
						|
               &gEfiBlockIo2ProtocolGuid,
 | 
						|
               &PrivateData->BlockIo2,
 | 
						|
               &gEfiDevicePathProtocolGuid,
 | 
						|
               (EFI_DEVICE_PATH_PROTOCOL *) PrivateData->DevicePath,
 | 
						|
               NULL
 | 
						|
               );
 | 
						|
 | 
						|
        RemoveEntryList (&PrivateData->ThisInstance);
 | 
						|
 | 
						|
        if (RamDiskCreateHii == PrivateData->CreateMethod) {
 | 
						|
          //
 | 
						|
          // If a RAM disk is created within HII, then the RamDiskDxe driver
 | 
						|
          // driver is responsible for freeing the allocated memory for the
 | 
						|
          // RAM disk.
 | 
						|
          //
 | 
						|
          FreePool ((VOID *)(UINTN) PrivateData->StartingAddr);
 | 
						|
        }
 | 
						|
 | 
						|
        FreePool (PrivateData->DevicePath);
 | 
						|
        FreePool (PrivateData);
 | 
						|
        Found = TRUE;
 | 
						|
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (TRUE == Found) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  } else {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
}
 |