| /** @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; | |
| } | |
| } |