diff --git a/MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.c b/MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.c new file mode 100644 index 000000000000..25cfa8b61181 --- /dev/null +++ b/MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.c @@ -0,0 +1,1122 @@ +/** @file + CxlFirmwareMgmt Application is used to send and receive FMP commands + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "CxlFwMgmtApp.h" + +/** + Opens the file and read its buffer value + + @param[in] FileName The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] BufferSize size of buffer to be read + + @retval Buffer input file is read and buffer is returned + + **/ +EFI_STATUS +ReadFileToBuffer ( + IN CHAR16 *FileName, + OUT UINTN *BufferSize, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + SHELL_FILE_HANDLE FileHandle; + UINTN FileSize; + VOID *TempBuffer; + UINTN Position; + EFI_SHELL_PROTOCOL *ShellProtocol; + UINTN TempBufferSize; + + FileSize = 0; + Position = 0; + ShellProtocol = NULL; + + Status = gBS->LocateProtocol ( + &gEfiShellProtocolGuid, + NULL, + (VOID **)&ShellProtocol + ); + + if (EFI_ERROR (Status)) { + Print (L"ReadFileToBuffer: Error gEfiShellProtocolGuid %r\n", Status); + ShellProtocol = NULL; + *BufferSize = 0; + *Buffer = NULL; + return Status; + } + + Status = ShellProtocol->OpenFileByName ( + FileName, + &FileHandle, + EFI_FILE_MODE_READ + ); + + if (EFI_ERROR (Status)) { + Print(L"ReadFileToBuffer: Error open file by name %r\n", Status); + return Status; + } + + Status = ShellProtocol->GetFileSize (FileHandle, &FileSize); + + if (EFI_ERROR (Status)) { + ShellProtocol->CloseFile (FileHandle); + return Status; + } + + if (FileSize > CXL_FW_SIZE) { + Print(L"ReadFileToBuffer: Error FileSize = %d is greater then 32 MB\n", FileSize); + Status = EFI_INVALID_PARAMETER; + return Status; + } + + TempBufferSize = FileSize; + TempBuffer = AllocateZeroPool (TempBufferSize); + if (NULL == TempBuffer) { + ShellProtocol->CloseFile (FileHandle); + return Status; + } + + Status = ShellProtocol->SetFilePosition (FileHandle, Position); + if (EFI_ERROR (Status)) { + Print (L"Error in setting position...%r (Position = 0x%X)\n", Status, Position); + return Status; + } + + Status = ShellProtocol->ReadFile ( + FileHandle, + &TempBufferSize, + TempBuffer + ); + + if (EFI_ERROR (Status)) { + ShellProtocol->CloseFile (FileHandle); + return Status; + } + + Status = ShellProtocol->CloseFile (FileHandle); + + *BufferSize = TempBufferSize; + *Buffer = TempBuffer; + return EFI_SUCCESS; +} + +/** + Get lots of info about a device from its handle. + + @param[in] NumOfHandles Number of handle to loop among all + + @retval Handles return handle of firmware management protocol + + **/ +EFI_STATUS +GetHandleInfo ( + UINTN *NumOfHandles, + EFI_HANDLE **Handles + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + + Status = gBS->LocateHandleBuffer( + ByProtocol, + &gEfiFirmwareManagementProtocolGuid, + NULL, + NumOfHandles, + Handles + ); + + if (EFI_ERROR(Status)) { + Print(L"GetHandleInfo: LocateHandleBuffer failed status = %r, NumOfHandles = %d\n", Status, *NumOfHandles); + return Status; + } + + if (0 == *NumOfHandles) { + Print(L"GetHandleInfo: Handle not found status = %r, NoHandles = %d\n", Status, *NumOfHandles); + return EFI_NOT_FOUND; + } + return Status; +} + +/** + Get CXL controller private data structure from firmware management + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Handles Number of handle to loop among all + @param[in] Index Index of handle + + @retval Status Return EFI_SUCCESS on successfully getting the data + + **/ +EFI_STATUS +GetCxlPrivateData ( + CXL_CONTROLLER_PRIVATE_DATA **Private1, + EFI_HANDLE *Handles, + UINTN Index + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *FirmwareMgmt; + + Status = EFI_SUCCESS; + FirmwareMgmt = NULL; + + Status = gBS->HandleProtocol( + Handles[Index], + &gEfiFirmwareManagementProtocolGuid, + (VOID**)&FirmwareMgmt + ); + + if (EFI_ERROR(Status)) { + return Status; + } + + *Private1 = CXL_CONTROLLER_PRIVATE_FROM_FIRMWARE_MGMT(FirmwareMgmt); + if (CXL_CONTROLLER_PRIVATE_DATA_SIGNATURE != (*Private1)->Signature) { + Print(L"\ngetPrivateStr: Error, Private Data is not for CXL device!\n"); + Status = EFI_NOT_FOUND; + } + return Status; +} + +/** + Returns information about the current firmware image(s) of the device. + + @param[in] Bus Bus value in BDF of device. + @param[in] Device Device value in BDF of device. + @param[in] Function Function value in BDF of device. + + @retval Status Return EFI_SUCCESS on successfully getting the data + + **/ +EFI_STATUS +GetImageInfo ( + UINTN Bus, + UINTN Device, + UINTN Function + ) +{ + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN Index; + UINTN NumOfHandles; + UINTN ImageInfoSize; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo; + UINT32 DescriptorVersion; + UINT8 DescriptorCount; + UINTN DescriptorSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + CXL_CONTROLLER_PRIVATE_DATA *Private; + + Status = EFI_SUCCESS; + Handles = NULL; + NumOfHandles = 0; + ImageInfoSize = 0; + ImageInfo = NULL; + DescriptorVersion = 0; + DescriptorCount = 0; + DescriptorSize = 0; + PackageVersion = 0; + PackageVersionName = NULL; + Private = NULL; + + Status = GetHandleInfo(&NumOfHandles, &Handles); + if (Status != EFI_SUCCESS) { + Print(L"GetImageInfo: Fail to locate handle buffer...\n"); + return Status; + } + + for (Index = 0; Index < NumOfHandles; Index++) { + Status = GetCxlPrivateData(&Private, Handles, Index); + if (Status != EFI_SUCCESS) { + Print(L"GetImageInfo: Fail to locate handle buffer...\n"); + continue; + } + + if (Bus == Private->Bus && Device == Private->Device && Function == Private->Function) { + Status = Private->FirmwareMgmt.GetImageInfo( + &Private->FirmwareMgmt, + &ImageInfoSize, + ImageInfo, + &DescriptorVersion, + &DescriptorCount, + &DescriptorSize, + &PackageVersion, + &PackageVersionName + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + ImageInfo = AllocateZeroPool(ImageInfoSize); + if (ImageInfo == NULL) { + DEBUG((EFI_D_ERROR, "GetImageInfo: AllocateZeroPool failed!\n")); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = Private->FirmwareMgmt.GetImageInfo( + &Private->FirmwareMgmt, + &ImageInfoSize, + ImageInfo, + &DescriptorVersion, + &DescriptorCount, + &DescriptorSize, + &PackageVersion, + &PackageVersionName + ); + } + + if (!EFI_ERROR(Status)) { + Print(L"===== Current Firmware Image Information =====\n"); + Print(L"Package Version : %08X\n", PackageVersion); + Print(L"Package Version Name : %s\n", PackageVersionName); + Print(L"Image Index : %d\n", ImageInfo->ImageIndex); + Print(L"Image Type ID : %g\n", ImageInfo->ImageTypeId); + Print(L"Image ID : %016lx\n", ImageInfo->ImageId); + Print(L"Image ID Name : %s\n", ImageInfo->ImageIdName); + Print(L"Version : %d\n", ImageInfo->Version); + Print(L"Version Name : %a\n", ImageInfo->VersionName); + Print(L"Size : %d\n", ImageInfo->Size); + Print(L"Attributes Supported : %d\n", ImageInfo->AttributesSupported); + Print(L"Attributes Setting : %d\n", ImageInfo->AttributesSetting); + Print(L"Compatibilities : %d\n", ImageInfo->Compatibilities); + } else { + Print(L"Calling GetImageInfo Failed with status = %r\n", Status); + } + + break; + } + } + + FreePool(Handles); + + if (NULL != PackageVersionName) { + FreePool(PackageVersionName); + } + + if (NULL != ImageInfo) { + FreePool(ImageInfo); + } + return Status; +} + +/** + Returns information about the firmware package.of device + + @param[in] Bus Bus value in BDF of device. + @param[in] Device Device value in BDF of device. + @param[in] Function Function value in BDF of device. + + @retval Status Return EFI_SUCCESS on successfully getting the data + + **/ +EFI_STATUS +GetPackageInfo ( + UINTN Bus, + UINTN Device, + UINTN Function + ) +{ + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN Index; + UINTN NumOfHandles; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + UINT32 PackageVersionNameMaxLen; + UINT64 AttributesSupported; + UINT64 AttributesSetting; + CXL_CONTROLLER_PRIVATE_DATA *Private; + + Status = EFI_SUCCESS; + Handles = NULL; + NumOfHandles = 0; + PackageVersion = 0; + PackageVersionName = NULL; + PackageVersionNameMaxLen = 0; + AttributesSupported = 0; + AttributesSetting = 0; + Private = NULL; + + Status = GetHandleInfo(&NumOfHandles, &Handles); + if (Status != EFI_SUCCESS) { + Print(L"GetPackageInfo: Fail to locate handle buffer...\n"); + return Status; + } + + for (Index = 0; Index < NumOfHandles; Index++) { + Status = GetCxlPrivateData(&Private, Handles, Index); + if (Status != EFI_SUCCESS) { + Print(L"GetImageInfo: Fail to locate handle buffer...\n"); + continue; + } + + if (Bus == Private->Bus && Device == Private->Device && Function == Private->Function) { + PackageVersionName = AllocateZeroPool(CXL_STRING_BUFFER_WIDTH); + if (PackageVersionName == NULL) { + DEBUG((EFI_D_ERROR, "GetImageInfo: AllocateZeroPool failed!\n")); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = Private->FirmwareMgmt.GetPackageInfo( + &Private->FirmwareMgmt, + &PackageVersion, + &PackageVersionName, + &PackageVersionNameMaxLen, + &AttributesSupported, + &AttributesSetting + ); + + if (!EFI_ERROR(Status)) { + Print(L"Package Version Name : %s\n", PackageVersionName); + Print(L"Package Version : %d\n", PackageVersion); + Print(L"Attributes Supported : %d\n", AttributesSupported); + Print(L"Attributes Setting : %d\n", AttributesSetting); + } else { + Print(L"Calling FMP.GetPackageInfo...%r\n", Status); + } + + if (NULL != PackageVersionName) { + FreePool(PackageVersionName); + } + break; + } + } + + FreePool(Handles); + return Status; +} + +/** + Returns information about the image.of device + + @param[in] Bus Bus value in BDF of device. + @param[in] Device Device value in BDF of device. + @param[in] Function Function value in BDF of device. + @param[in] Slot Slot information on which image info is to be taken + + @retval Status Return EFI_SUCCESS on successfully getting the data + + **/ +EFI_STATUS +GetImage ( + UINTN Bus, + UINTN Device, + UINTN Function, + UINTN Slot + ) +{ + EFI_STATUS Status; + UINT8 ImageIndex; + CHAR16 *Image; + UINTN ImageSize; + EFI_HANDLE *Handles; + UINTN Index; + UINTN NumOfHandles; + CXL_CONTROLLER_PRIVATE_DATA *Private = NULL; + + Status = EFI_SUCCESS; + ImageIndex = Slot; + Image = NULL; + Handles = NULL; + NumOfHandles = 0; + + Status = GetHandleInfo(&NumOfHandles, &Handles); + if (Status != EFI_SUCCESS) { + Print(L"GetImage: Fail to locate handle buffer...\n"); + return Status; + } + + for (Index = 0; Index < NumOfHandles; Index++) { + Status = GetCxlPrivateData(&Private, Handles, Index); + if (Status != EFI_SUCCESS) { + Print(L"GetImage: Fail to locate handle buffer...\n"); + continue; + } + + if (Bus == Private->Bus && Device == Private->Device && Function == Private->Function) { + Status = Private->FirmwareMgmt.GetImage( + &Private->FirmwareMgmt, + ImageIndex, + Image, + &ImageSize + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + Print(L"\nGetImage: Image Allocated with Size = %d\n", ImageSize); + Image = AllocateZeroPool(ImageSize); + if (Image == NULL) { + DEBUG((EFI_D_ERROR, "GetImage: AllocateZeroPool failed!\n")); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = Private->FirmwareMgmt.GetImage( + &Private->FirmwareMgmt, + ImageIndex, + Image, + &ImageSize + ); + } + + if (!EFI_ERROR(Status)) { + Print(L"GetImage, Image Size = %d\n", ImageSize); + } else { + Print(L"Calling FMP GetImage Failed...%r\n", Status); + } + break; + } + } + + if (NULL != Handles) { + FreePool(Handles); + } + + if (NULL != Image) { + FreePool(Image); + } + return Status; +} + +/** + Updates information about the firmware package + + @param[in] Bus Bus value in BDF of device. + @param[in] Device Device value in BDF of device. + @param[in] Function Function value in BDF of device. + + @retval Status Return EFI_SUCCESS on successfully getting the data + + **/ +EFI_STATUS +SetPackageInfo ( + UINTN Bus, + UINTN Device, + UINTN Function + ) +{ + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN Index; + UINTN NumOfHandles; + CONST VOID *Image; + UINTN ImageSize; + CONST VOID *VendorCode; + UINT32 PackageVersion; + CHAR16 PackageVersionName[CXL_STRING_BUFFER_WIDTH]; + CXL_CONTROLLER_PRIVATE_DATA *Private; + + Status = EFI_SUCCESS; + Handles = NULL; + NumOfHandles = 0; + Image = NULL; + ImageSize = 0; + VendorCode = NULL; + PackageVersion = 1; + + Status = GetHandleInfo(&NumOfHandles, &Handles); + if (Status != EFI_SUCCESS) { + Print(L"SetPackageInfo: Fail to locate handle buffer...\n"); + return Status; + } + + for (Index = 0; Index < NumOfHandles; Index++) { + Status = GetCxlPrivateData(&Private, Handles, Index); + if (Status != EFI_SUCCESS) { + Print(L"GetImageInfo: Fail to locate handle buffer...\n"); + continue; + } + + if (Bus == Private->Bus && Device == Private->Device && Function == Private->Function) { + StrCpyS(PackageVersionName, CXL_STRING_BUFFER_WIDTH, CXL_PACKAGE_VERSION_NAME_APP); + Status = Private->FirmwareMgmt.SetPackageInfo( + &Private->FirmwareMgmt, + &Image, + ImageSize, + &VendorCode, + PackageVersion, + PackageVersionName + ); + + if (!EFI_ERROR(Status)) { + Print(L"SetPackageInfo Success\n"); + } else { + Print(L"Calling FMP SetPackageInfo Failed...%r\n", Status); + } + break; + } + } + + FreePool(Handles); + return Status; +} + +/** + Gets BUS, DEVICE, FUNCTION value of all CXL supported device + + @retval Status Return EFI_SUCCESS on successfully getting the data + + **/ +EFI_STATUS +GetCxlDeviceList() +{ + EFI_HANDLE *Handles = NULL; + UINTN Index; + EFI_STATUS Status = EFI_SUCCESS; + UINTN NumOfHandles = 0; + bool printFlag = FALSE; + CXL_CONTROLLER_PRIVATE_DATA *Private = NULL; + + Handles = NULL; + Status = EFI_SUCCESS; + NumOfHandles = 0; + printFlag = FALSE; + Private = NULL; + + Status = GetHandleInfo(&NumOfHandles, &Handles); + if (Status != EFI_SUCCESS) { + Print(L"GetCxlDeviceList: Fail to locate handle buffer...\n"); + return Status; + } + + for (Index = 0; Index < NumOfHandles; Index++) { + Status = GetCxlPrivateData(&Private, Handles, Index); + if (Status != EFI_SUCCESS) { + Print(L"GetImageInfo: Fail to locate handle buffer...\n"); + continue; + } + + if (printFlag == FALSE) { + Print(L"Device BUS DEVICE FUNCTION \n"); + printFlag = TRUE; + } + + Print(L"CXLDevice[%d]: %d %d %d\n", Index, Private->Bus, Private->Device, Private->Function); + } + + FreePool(Handles); + return Status; +} + +/** + Updates the firmware image of the device. + + @param[in] Bus Bus value in BDF of device. + @param[in] Device Device value in BDF of device. + @param[in] Function Function value in BDF of device. + @param[in] Slot Slot information on which image info is to be taken + + @retval Status Return EFI_SUCCESS on successfully getting the data + + **/ +EFI_STATUS +SetImage ( + UINTN Bus, + UINTN Device, + UINTN Function, + UINTN Slot, + CHAR16 *FileName + ) +{ + VOID *Buffer; + UINTN BufferSize; + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN Index; + UINTN NumOfHandles; + CXL_CONTROLLER_PRIVATE_DATA *Private; + + Status = EFI_SUCCESS; + Handles = NULL; + NumOfHandles = 0; + Private = NULL; + + Status = GetHandleInfo(&NumOfHandles, &Handles); + if (Status != EFI_SUCCESS) { + Print(L"SetImage: Fail to locate handle buffer...\n"); + return Status; + } + + for (Index = 0; Index < NumOfHandles; Index++) { + Status = GetCxlPrivateData(&Private, Handles, Index); + if (Status != EFI_SUCCESS) { + Print(L"GetImageInfo: Fail to locate handle buffer...\n"); + continue; + } + + if (Bus == Private->Bus && Device == Private->Device && Function == Private->Function) { + Status = ReadFileToBuffer(FileName, &BufferSize, &Buffer); + if (EFI_SUCCESS != Status) { + Print(L"SetImage: ReadFileToBuffer FMP SetImage Failed...%r\n", Status); + break; + } + + Status = Private->FirmwareMgmt.SetImage( + &Private->FirmwareMgmt, + Slot, + Buffer, + BufferSize, + NULL, + NULL, + NULL + ); + + if (!EFI_ERROR(Status)) { + Print(L"SetImage Success\n"); + } + else { + Print(L"SetImage: Calling FMP SetImage Failed...%r\n", Status); + } + break; + } + } + + FreePool(Handles); + return Status; +} + +/** + Check the firmware image of the device. + + @param[in] Bus Bus value in BDF of device. + @param[in] Device Device value in BDF of device. + @param[in] Function Function value in BDF of device. + @param[in] Slot Slot information on which image info is to be taken + + @retval Status Return EFI_SUCCESS on successfully getting the data + + **/ +EFI_STATUS +CheckImage ( + UINTN Bus, + UINTN Device, + UINTN Function, + UINTN Slot + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + Print(L"CheckImage Command not supported...\n"); + return Status; +} + +/** + Prints help page of the CXL Firmware management application + + **/ +void +PrintHelpPage () +{ + Print (L" -fGetCXLDeviceList : Get CXL Device List (GetCxlDeviceList).\n"); + Print (L" -fimginfo : Get information about current firmware image (GetImageInfo).\n"); + Print (L" -fsetimg : Firmware download and ActivateFw (SetImage).\n"); + Print (L" -fgetimg : Get information about the firmware package (GetImage).\n"); + Print (L" -fchkimg : Check the validity of a firmware image (CheckImage).\n"); + Print (L" -fsetpack : Set information about the firmware package (SetPackageInfo).\n"); + Print (L" -fgetpack : Get information about the firmware package (GetPackageInfo).\n"); +} + +/** + Check the input character is digit or not. + + @param[in] Character Input character + + @retval Status Return value as 0 if input is character else 1 + + **/ +BOOLEAN +IsDigit ( + char Character + ) +{ + return (Character >= '0') && (Character <= '9'); +} + +/** + Check the length of input string. + + @param[in] String Input string value. + + @retval Status Return length of input string + + **/ +int +GetStrLength ( + CHAR16 *String + ) +{ + int Length = 0; + while (*String != '\0') { + Length++; + String++; + } + return Length; +} + +/** + Check the input string is number or not. + + @param[in] String Input string value. + + @retval Return True if string is number else FALSE + + **/ +BOOLEAN +IsNumber ( + CHAR16 *String + ) +{ + int Length = GetStrLength(String); + for (int Index = 0; Index < Length; Index++) { + if (IsDigit(String[Index]) == FALSE) { + return FALSE; + } + } + return TRUE; +} + +/** + Gets Bus device Function (BDF) value of the device + + @retval Bus Bus value in BDF of device. + @retval Device Device value in BDF of device. + @retval Function Function value in BDF of device. + @retval Slot Slot information on which image info is to be taken + + **/ +BOOLEAN +GetBdfValues ( + UINTN Argc, + CHAR16 **Argv, + UINTN *Bus, + UINTN *Device, + UINTN *Function, + UINTN *Slot, + CHAR16 **FileName + ) +{ + CHAR16 *Bus1; + CHAR16 *Dev1; + CHAR16 *Func1; + CHAR16 *Slot1; + CHAR16 *FileName1; + + Bus1 = NULL; + Dev1 = NULL; + Func1 = NULL; + Slot1 = NULL; + FileName1 = NULL; + + Bus1 = Argv[2]; + Dev1 = Argv[3]; + Func1 = Argv[4]; + + *Bus = StrDecimalToUintn(Argv[2]); + *Device = StrDecimalToUintn(Argv[3]); + *Function = StrDecimalToUintn(Argv[4]); + + if (Argc >= 6) { + Slot1 = Argv[5]; + *Slot = StrDecimalToUintn(Argv[5]); + if (IsNumber(Slot1) == FALSE) { + return FALSE; + } + } + + if (Argc == 7) { + *FileName = AllocateZeroPool(CXL_MAX_FILE_NAME_LENGTH); + if (NULL == *FileName) { + DEBUG((EFI_D_ERROR, "GetBdfValues: EFI Out of resources...\n")); + return FALSE; + } + + FileName1 = Argv[6]; + StrCpyS(*FileName, CXL_MAX_FILE_NAME_LENGTH, FileName1); + if (IsNumber(FileName1) == TRUE) { + return FALSE; + } + } + + if (IsNumber(Bus1) == FALSE || IsNumber(Dev1) == FALSE || IsNumber(Func1) == FALSE) { + return FALSE; + } + return TRUE; +} + +/** + Validates input argument value of Bus device Function (BDF) value of the device + + @param[in] Bus Bus value in BDF of device. + @param[in] Device Device value in BDF of device. + @param[in] Function Function value in BDF of device. + @param[in] Slot Slot information on which image info is to be takenBOOLEAN + + @retval True if param are correct otherwise False + **/ +BOOLEAN +ValidateArguments ( + UINTN Argc, + CHAR16 **Argv, + UINTN *Bus, + UINTN *Device, + UINTN *Function, + UINTN *Slot, + CHAR16 **FileName, + CXL_FMP_OPERATION_TYPE OpType + ) +{ + bool IsBdfRequire = TRUE; + switch (OpType) { + case OpTypeDisplayHelp: + IsBdfRequire = FALSE; + break; + + case OpTypeListDevice: + if (Argc != 2) { + Print(L"Invalid argument...\n"); + return FALSE; + } + IsBdfRequire = FALSE; + break; + + case OpTypeFmpGetImgInfo: + if (Argc != 5) { + Print(L"Invalid argument...\n"); + return FALSE; + } + break; + + case OpTypeFmpSetImg: + if (Argc != 7) { + Print(L"Invalid argument...\n"); + return FALSE; + } + break; + + case OpTypeGetImage: + if (Argc != 6) { + Print(L"Invalid argument...\n"); + return FALSE; + } + break; + + case OpTypeFmpCheckImg: + if (Argc != 7) { + Print(L"Invalid argument...\n"); + return FALSE; + } + break; + + case OpTypeFmpGetPkgInfo: + if (Argc != 5) { + Print(L"Invalid argument...\n"); + return FALSE; + } + break; + + case OpTypeSetPkgInfo: + if (Argc != 5) { + Print(L"Invalid argument...\n"); + return FALSE; + } + break; + + default: + return FALSE; + } + + if (IsBdfRequire == TRUE) { + if (FALSE == GetBdfValues(Argc, Argv, Bus, Device, Function, Slot, FileName)) { + Print(L"Invalid argument...\n"); + return FALSE; + } + } + return TRUE; +} + +/** + Get operation type from input arguments in CXL Application + + @retval DisplayHelp, ListDevice, FmpGetImgInfo, GetImage, SetPkgInfo, FmpSetImg, FmpCheckImg, FmpGetPkgInfo + + **/ +CXL_FMP_OPERATION_TYPE +GetOperationType ( + UINTN Argc, + CHAR16 **Argv + ) +{ + CXL_FMP_OPERATION_TYPE OpType; + CHAR16 *String; + + String = NULL; + + if (1 == Argc) { + OpType = OpTypeDisplayHelp; + goto END; + } + + String = Argv[1]; + + if (!StrCmp(String, L"-fGetCXLDeviceList")) { + OpType = OpTypeListDevice; + } else if (!StrCmp(String, L"-fimginfo")) { + OpType = OpTypeFmpGetImgInfo; + } else if (!StrCmp(String, L"-fsetimg")) { + OpType = OpTypeFmpSetImg; + } else if (!StrCmp(String, L"-fgetimg")) { + OpType = OpTypeGetImage; + } else if (!StrCmp(String, L"-fchkimg")) { + OpType = OpTypeFmpCheckImg; + } else if (!StrCmp(String, L"-fsetpack")) { + OpType = OpTypeSetPkgInfo; + } else if (!StrCmp(String, L"-fgetpack")) { + OpType = OpTypeFmpGetPkgInfo; + } else { + Print(L"Invalid argument...\n"); + OpType = OpTypeDisplayHelp; + } + +END: + return OpType; +} + +/** + Parse the argument from input arguments in CXL Application + + @retval Bus Bus value in BDF of device. + @retval Device Device value in BDF of device. + @retval Function Function value in BDF of device. + @retval Slot Slot information on which image info is to be taken + + **/ +CXL_FMP_OPERATION_TYPE +ParseArguments ( + UINTN Argc, + CHAR16 **Argv, + UINTN *Bus, + UINTN *Device, + UINTN *Function, + UINTN *Slot, + CHAR16 **FileName + ) +{ + CXL_FMP_OPERATION_TYPE OpType; + OpType = GetOperationType(Argc, Argv); + if (OpType == OpTypeDisplayHelp || OpType == OpTypeListDevice) { + return OpType; + } + + if (ValidateArguments(Argc, Argv, Bus, Device, Function, Slot, FileName, OpType) == FALSE) { + Print(L"Arguments Validation Fail\n"); + OpType = OpTypeDisplayHelp; + } + return OpType; +} + +/** + CXL Application main function which will call the operation + + @retval Status Return EFI_SUCCESS on successfully calling the requested operation + **/ +EFI_STATUS +EFIAPI +CxlFwMain( + IN UINTN Argc, + IN CHAR16 **Argv + ) +{ + CXL_FMP_OPERATION_TYPE OpType; + UINTN Bus; + UINTN Device; + UINTN Function; + UINTN Slot; + CHAR16 *FileName; + EFI_STATUS Status; + + FileName = NULL; + Status = EFI_SUCCESS; + + OpType = ParseArguments(Argc, Argv, &Bus, &Device, &Function, &Slot, &FileName); + + switch (OpType){ + case OpTypeDisplayHelp: + PrintHelpPage(); + Status = EFI_SUCCESS; + break; + + case OpTypeListDevice: + Status = GetCxlDeviceList(); + break; + + case OpTypeFmpGetImgInfo: + Status = GetImageInfo(Bus, Device, Function); + break; + + case OpTypeFmpSetImg: + Status = SetImage(Bus, Device, Function, Slot, FileName); + break; + + case OpTypeGetImage: + Status = GetImage(Bus, Device, Function, Slot); + break; + + case OpTypeFmpCheckImg: + Status = CheckImage(Bus, Device, Function, Slot); + break; + + case OpTypeFmpGetPkgInfo: + Status = GetPackageInfo(Bus, Device, Function); + break; + + case OpTypeSetPkgInfo: + Status = SetPackageInfo(Bus, Device, Function); + break; + + default: + Print(L"Invalid Operation Type\n"); + break; + } + + if (NULL != FileName) { + FreePool(FileName); + } + return Status; +} + +/** + Shell Entry library of the function + + @param[in] ImageHandle Image Handle for entry lib. + + @retval Status Return EFI_SUCCESS on successfully calling the requested operation + + **/ +EFI_STATUS +EFIAPI +MyShellCEntryLib ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_SHELL_PARAMETERS_PROTOCOL *EfiShellParametersProtocol; + EFI_STATUS Status; + + EfiShellParametersProtocol = NULL; + + Status = SystemTable->BootServices->OpenProtocol(ImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID **)&EfiShellParametersProtocol, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR(Status)) { + Status = CxlFwMain ( EfiShellParametersProtocol->Argc, EfiShellParametersProtocol->Argv); + } else { + ASSERT(FALSE); + } + return Status; +} + diff --git a/MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.h b/MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.h new file mode 100644 index 000000000000..4d435a2f44ce --- /dev/null +++ b/MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.h @@ -0,0 +1,144 @@ +/** @file + Header file for CxlDxe Application + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _CXL_FIRMWARE_MGMT_H_ +#define _CXL_FIRMWARE_MGMT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CXL_CONTROLLER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C','X','L','X') +#define CXL_MAX_FILE_NAME_LENGTH 256 +#define CXL_FW_IMAGE_DESCRIPTOR_COUNT 5 +#define CXL_FW_MAX_SLOTS 5 +#define CXL_STRING_BUFFER_WIDTH 256 +#define CXL_PACKAGE_VERSION_NAME_APP L"CXL Firmware Package Name Application" +#define CXL_FW_SIZE 32768 /* 32 mb */ +#define CXL_FW_REVISION_LENGTH_IN_BYTES 16 + +#define CXL_CONTROLLER_PRIVATE_FROM_FIRMWARE_MGMT(a) \ + CR (a, \ + CXL_CONTROLLER_PRIVATE_DATA, \ + FirmwareMgmt, \ + CXL_CONTROLLER_PRIVATE_DATA_SIGNATURE \ + ) + +// +// CXL Memory Device Firmware management supported command set +// +typedef enum { + OpTypeDisplayHelp, + OpTypeListDevice, + OpTypeFmpGetImgInfo, + OpTypeGetImage, + OpTypeSetPkgInfo, + OpTypeFmpSetImg, + OpTypeFmpCheckImg, + OpTypeFmpGetPkgInfo, + OpTypeFmpMax +} CXL_FMP_OPERATION_TYPE; + +// +// CXL Memory Device Register information +// +typedef struct { + UINT32 RegisterType; + UINT32 BaseAddressRegister; + unsigned long long Offset; + unsigned long MailboxRegistersOffset; +} CXL_REGISTER_MAP; + +// +// CXL Memory Device Firmware slot information +// +typedef struct { + UINT8 NumberOfSlots; + UINTN ImageFileSize[CXL_FW_MAX_SLOTS]; + CHAR16 *ImageFileBuffer[CXL_FW_MAX_SLOTS]; + BOOLEAN IsSetImageDone[CXL_FW_MAX_SLOTS]; + CHAR16 *FirmwareVersion[CXL_FW_MAX_SLOTS]; + EFI_FIRMWARE_IMAGE_DESCRIPTOR FwImageDescriptor[CXL_FW_IMAGE_DESCRIPTOR_COUNT]; +} CXL_SLOT_INFO; + +// +// CXL Memory Device Firmware state +// +typedef struct { + UINT32 State; + BOOLEAN OneShot; + UINT32 NumberOfSlots; + UINT32 CurrentSlot; + UINT32 NextSlot; + UINT8 FwActivationCap; + CHAR16 *FwRevisionSlot1; + CHAR16 *FwRevisionSlot2; + CHAR16 *FwRevisionSlot3; + CHAR16 *FwRevisionSlot4; +} CXL_FW_STATE; + +// +// CXL Memory Device Registers state +// +typedef struct { + UINT32 PayloadSize; + CXL_FW_STATE FwState; +} CXL_MEMDEV_STATE; + +// +// CXL device private data structure +// +typedef struct { + UINT32 Signature; + EFI_HANDLE ControllerHandle; + EFI_HANDLE ImageHandle; + EFI_HANDLE DriverBindingHandle; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + //MailBox Register + CXL_REGISTER_MAP RegisterMap; + CXL_MEMDEV_STATE MemdevState; + CXL_MBOX_CMD MailboxCmd; + + //Image Info + CXL_SLOT_INFO SlotInfo; + + //BDF Value + UINTN Seg; + UINTN Bus; + UINTN Device; + UINTN Function; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + + // Produced protocols + EFI_FIRMWARE_MANAGEMENT_PROTOCOL FirmwareMgmt; +} CXL_CONTROLLER_PRIVATE_DATA; + +#endif //_CXL_FIRMWARE_MGMT_H_ + diff --git a/MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.inf b/MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.inf new file mode 100644 index 000000000000..67b20a7c5f01 --- /dev/null +++ b/MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.inf @@ -0,0 +1,40 @@ +## @file +# +# CxlDxe Application setup file +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CxlFwMgmtApp + FILE_GUID = 05092440-531A-4D13-B3F2-B91EEDA9B95F + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = MyShellCEntryLib + +[Sources] + CxlFwMgmtApp.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint + UefiLib + FileHandleLib + PcdLib + +[Protocols] + gEfiPciIoProtocolGuid + gEfiFirmwareManagementProtocolGuid ## BY_START + gEfiShellParametersProtocolGuid + gEfiDevicePathToTextProtocolGuid + gEfiShellProtocolGuid + gEfiShellParametersProtocolGuid + +[Guids] + gEfiDxeServicesTableGuid + gEfiFileInfoGuid + diff --git a/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.c b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.c new file mode 100644 index 000000000000..97c173ffd203 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.c @@ -0,0 +1,1769 @@ +/** @file + CxlDxe driver is used to discover CXL devices + supports Mailbox functionality + uefi driver name is added + supports Get Fw Info + Sending/Receiving FMP commands + Set Fw Image, Activate Fw image + SetPkgInfo, GetPkgInfo, CheckImg + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "CxlDxe.h" + +// +// CXL Driver Binding Protocol Instance +// +EFI_DRIVER_BINDING_PROTOCOL gCxlDriverBinding = { + CxlDriverBindingSupported, + CxlDriverBindingStart, + CxlDriverBindingStop, + 0x10, + NULL, + NULL +}; + + +/** + Reads MB Control Register to verify doorbell is clear. + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval ReturnValue doorbell is clear or not. + +**/ +BOOLEAN +IsCxlDoorbellBusy ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + BOOLEAN ReturnValue; + UINT32 Value; + + ReturnValue = 0; + Value = 0; + + Status = PciUefiMemReadThirtyTwoBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_CTRL_OFFSET, &Value); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error reading Value for busy doorbell\n", __func__)); + } + ReturnValue = Value & CXL_DEV_MBOX_CTRL_DOORBELL; + return ReturnValue; +} + +/** + Sends Mailbox command + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Return EFI STATUS success or failure + +**/ +EFI_STATUS +CxlSendCmd ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + size_t OutputSize; + size_t MinimumOutput; + + OutputSize = 0; + MinimumOutput = 0; + + if (Private->MailboxCmd.InputSize > Private->MemdevState.PayloadSize || + Private->MailboxCmd.OutputSize > Private->MemdevState.PayloadSize) + { + DEBUG((EFI_D_ERROR, "[%a]: InputSize or sizeout > PayloadSize\n", __func__)); + Status = EFI_INVALID_PARAMETER; + return Status; + } + + OutputSize = Private->MailboxCmd.OutputSize; + MinimumOutput = Private->MailboxCmd.MinimumOutput; + Status = CxlPciMboxSend(Private); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error returned in func CxlPciMboxSend()\n", __func__)); + return Status; + } + + if (Private->MailboxCmd.ReturnCode != CXL_MBOX_CMD_RC_SUCCESS && + Private->MailboxCmd.ReturnCode != CXL_MBOX_CMD_RC_BACKGROUND) + { + //Command Return Codes + switch(Private->MailboxCmd.ReturnCode) { + case CXL_MBOX_CMD_INVALID_INPUT: + DEBUG((EFI_D_ERROR, "[%a]: Invalid Input\n", __func__)); + break; + + case CXL_MBOX_CMD_UNSUPPORTED: + DEBUG((EFI_D_ERROR, "[%a]: Unsupported\n", __func__)); + break; + + case CXL_MBOX_CMD_INTERNAL_ERROR: + DEBUG((EFI_D_ERROR, "[%a]: Internal Error\n", __func__)); + break; + + case CXL_MBOX_CMD_RETRY_REQUIRED: + DEBUG((EFI_D_ERROR, "[%a]: Retry Required\n", __func__)); + break; + + case CXL_MBOX_CMD_BUSY: + DEBUG((EFI_D_ERROR, "[%a]: Busy\n", __func__)); + break; + + case CXL_MBOX_CMD_MEDIA_DISABLED: + DEBUG((EFI_D_ERROR, "[%a]: Media Disabled\n", __func__)); + break; + + case CXL_MBOX_CMD_FW_TRANSFER_IN_PROGRESS: + DEBUG((EFI_D_ERROR, "[%a]: FW Transfer in Progress\n", __func__)); + break; + + case CXL_MBOX_CMD_FW_TRANSFER_OUT_OF_ORDER: + DEBUG((EFI_D_ERROR, "[%a]: FW Transfer Out of Order\n", __func__)); + break; + + case CXL_MBOX_CMD_FW_VERIFICATION_FAILED: + DEBUG((EFI_D_ERROR, "[%a]: FW Verification Failed\n", __func__)); + break; + + case CXL_MBOX_CMD_INVALID_SLOT: + DEBUG((EFI_D_ERROR, "[%a]: Invalid Slot\n", __func__)); + break; + + case CXL_MBOX_CMD_ACTIVATION_FAILED_FW_ROLLED_BACK: + DEBUG((EFI_D_ERROR, "[%a]: Activation Failed, FW Rolled Back\n", __func__)); + break; + + case CXL_MBOX_CMD_COLD_RESET_REQUIRED: + DEBUG((EFI_D_ERROR, "[%a]: Activation Failed, Cold Reset Required\n", __func__)); + break; + + case CXL_MBOX_CMD_INVALID_HANDLE: + DEBUG((EFI_D_ERROR, "[%a]: Invalid Handle\n", __func__)); + break; + + case CXL_MBOX_CMD_INVALID_PHYSICAL_ADDRESS: + DEBUG((EFI_D_ERROR, "[%a]: Invalid Physical Address\n", __func__)); + break; + + case CXL_MBOX_CMD_INJECT_POISON_LIMIT_REACHED: + DEBUG((EFI_D_ERROR, "[%a]: Inject Poison Limit Reached\n", __func__)); + break; + + case CXL_MBOX_CMD_PERMANENT_MEDIA_FAILURE: + DEBUG((EFI_D_ERROR, "[%a]: Permanent Media Failure\n", __func__)); + break; + + case CXL_MBOX_CMD_ABORTED: + DEBUG((EFI_D_ERROR, "[%a]: Aborted\n", __func__)); + break; + + case CXL_MBOX_CMD_INVALID_SECURITY_STATE: + DEBUG((EFI_D_ERROR, "[%a]: Invalid Security State\n", __func__)); + break; + + case CXL_MBOX_CMD_INCORRECT_PASSPHRASE: + DEBUG((EFI_D_ERROR, "[%a]: Incorrect Passphrase\n", __func__)); + break; + + default: + DEBUG((EFI_D_ERROR, "[%a]: Error \n", __func__)); + break; + } + + Status = EFI_LOAD_ERROR; + return Status; + } + + if (!OutputSize) { + Status = EFI_SUCCESS; + return Status; + } + + /* + * Variable sized output needs to at least satisfy the caller's + * minimum if not the fully requested Size. + */ + if (MinimumOutput == 0) { + MinimumOutput = OutputSize; + } + + if (Private->MailboxCmd.OutputSize < MinimumOutput) { + DEBUG ((EFI_D_ERROR, "[%a]: OutputSize less than MinimumOutput \n", __func__)); + Status = EFI_LOAD_ERROR; + return Status; + } + + Status = EFI_SUCCESS; + return Status; +} + +/** + Retrieve information about the device FW (Opcode 0200h) + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Possible Command Return Codes Success, Unsupported, Internal Error, + Retry Required, Invalid Payload Length +**/ +EFI_STATUS +CxlMemGetFwInfo ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + DEBUG((EFI_D_INFO, "CxlMemGetFwInfo: UEFI Driver Get FW Info (Opcode 0200h) get called!\n")); + + EFI_STATUS Status; + CXL_MAILBOX_GET_FW_INFO GetFwInfo; + + Private->MailboxCmd = (CXL_MBOX_CMD) { + .Opcode = CxlMboxOpGetFwInfo, + .OutputSize = sizeof(GetFwInfo), + .OutputPayload = &GetFwInfo, + }; + + Status = CxlSendCmd(Private); + if (Status != EFI_SUCCESS){ + DEBUG((EFI_D_ERROR, "CxlMemGetFwInfo: Error CxlSendCmd\n")); + return Status; + } + + for (int Index = 0; Index < CXL_FW_MAX_SLOTS - 1; Index++) { + Private->SlotInfo.FirmwareVersion[Index] = AllocateZeroPool(CXL_FW_REVISION_LENGTH_IN_BYTES + 1); + if (Private->SlotInfo.FirmwareVersion[Index] == NULL) { + DEBUG((EFI_D_ERROR, "[%a]: Resource, memory cannot be allocated\n", __func__)); + return EFI_INVALID_PARAMETER; + } + } + + Private->MemdevState.FwState.FwRevisionSlot1 = AllocateZeroPool(CXL_FW_REVISION_LENGTH_IN_BYTES + 1); + Private->MemdevState.FwState.FwRevisionSlot2 = AllocateZeroPool(CXL_FW_REVISION_LENGTH_IN_BYTES + 1); + Private->MemdevState.FwState.FwRevisionSlot3 = AllocateZeroPool(CXL_FW_REVISION_LENGTH_IN_BYTES + 1); + Private->MemdevState.FwState.FwRevisionSlot4 = AllocateZeroPool(CXL_FW_REVISION_LENGTH_IN_BYTES + 1); + if (Private->MemdevState.FwState.FwRevisionSlot1 == NULL + || Private->MemdevState.FwState.FwRevisionSlot2 == NULL + || Private->MemdevState.FwState.FwRevisionSlot3 == NULL + || Private->MemdevState.FwState.FwRevisionSlot4 == NULL) { + DEBUG((EFI_D_ERROR, "[%a]: Resource, memory cannot be allocated\n", __func__)); + return EFI_INVALID_PARAMETER; + } + + //FW Slots Supported + Private->MemdevState.FwState.NumberOfSlots = GetFwInfo.NumberOfSlots; + Private->SlotInfo.NumberOfSlots = GetFwInfo.NumberOfSlots; + + //FW Slot Info + Private->MemdevState.FwState.CurrentSlot = GetFieldValues(GetFwInfo.SlotInfo, 2, 0); + Private->MemdevState.FwState.NextSlot = GetFieldValues(GetFwInfo.SlotInfo, 5, 3); + + //FW Activation Capabilities + Private->MemdevState.FwState.FwActivationCap = GetFieldValues(GetFwInfo.SlotInfo, 0, 0); + + //Slot 1-4 FW Revision + CopyMem(Private->MemdevState.FwState.FwRevisionSlot1, GetFwInfo.SlotOneFwRevision, sizeof(GetFwInfo.SlotOneFwRevision)); + CopyMem(Private->MemdevState.FwState.FwRevisionSlot2, GetFwInfo.SlotTwoFwRevision, sizeof(GetFwInfo.SlotTwoFwRevision)); + CopyMem(Private->MemdevState.FwState.FwRevisionSlot3, GetFwInfo.SlotThreeFwRevision, sizeof(GetFwInfo.SlotThreeFwRevision)); + CopyMem(Private->MemdevState.FwState.FwRevisionSlot4, GetFwInfo.SlotFourFwRevision, sizeof(GetFwInfo.SlotFourFwRevision)); + + //Slot 1-4 FW Revision + CopyMem(Private->SlotInfo.FirmwareVersion[0], GetFwInfo.SlotOneFwRevision, sizeof(GetFwInfo.SlotOneFwRevision)); + CopyMem(Private->SlotInfo.FirmwareVersion[1], GetFwInfo.SlotTwoFwRevision, sizeof(GetFwInfo.SlotTwoFwRevision)); + CopyMem(Private->SlotInfo.FirmwareVersion[2], GetFwInfo.SlotThreeFwRevision, sizeof(GetFwInfo.SlotThreeFwRevision)); + CopyMem(Private->SlotInfo.FirmwareVersion[3], GetFwInfo.SlotFourFwRevision, sizeof(GetFwInfo.SlotFourFwRevision)); + + DEBUG((EFI_D_INFO, "CxlMemGetFwInfo: Total Slots = %u\n", Private->MemdevState.FwState.NumberOfSlots)); + DEBUG((EFI_D_INFO, "CxlMemGetFwInfo: Current Slots = %u\n", Private->MemdevState.FwState.CurrentSlot)); + DEBUG((EFI_D_INFO, "CxlMemGetFwInfo: Next Slots = %u\n", Private->MemdevState.FwState.NextSlot)); + DEBUG((EFI_D_INFO, "CxlMemGetFwInfo: Activation Cap = %u\n", Private->MemdevState.FwState.FwActivationCap)); + + DEBUG((EFI_D_INFO, "CxlMemGetFwInfo: FW Version Slot 1 = %a\n", Private->MemdevState.FwState.FwRevisionSlot1)); + DEBUG((EFI_D_INFO, "CxlMemGetFwInfo: FW Version Slot 2 = %a\n", Private->MemdevState.FwState.FwRevisionSlot2)); + DEBUG((EFI_D_INFO, "CxlMemGetFwInfo: FW Version Slot 3 = %a\n", Private->MemdevState.FwState.FwRevisionSlot3)); + DEBUG((EFI_D_INFO, "CxlMemGetFwInfo: FW Version Slot 4 = %a\n", Private->MemdevState.FwState.FwRevisionSlot4)); + + Status = EFI_SUCCESS; + return Status; +} + +/** + Activate FW command make a FW previously stored on the device with the Transfer FW command as active FW + (Opcode 0202h) + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Possible Command Return Codes Success, Unsupported, Internal Error, + Retry Required, Invalid Payload Length + +**/ +EFI_STATUS +CxlMemActivateFw ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + DEBUG((EFI_D_INFO, "CxlMemActivateFw: UEFI Driver Activate FW (Opcode 0202h) get called!\n")); + + EFI_STATUS Status; + CXL_MAILBOX_ACTIVATE_FW ActivateFw; + + int Slot = Private->MemdevState.FwState.NextSlot; + if (Slot == 0 || Slot > Private->MemdevState.FwState.NumberOfSlots) { + DEBUG((EFI_D_ERROR, "[CxlMemActivateFw] Error, Slot = %d, num of slots = %d \n", Slot, Private->MemdevState.FwState.NumberOfSlots)); + Status = EFI_INVALID_PARAMETER; + return Status; + } + + /* Only offline activation supported for now */ + ActivateFw.Action = CXL_FW_ACTIVATE_METHOD_ON_NEXT_COLD_RESET; + ActivateFw.Slot = Slot; + + Private->MailboxCmd = (CXL_MBOX_CMD){ + .Opcode = CxlMboxOpActivateFw, + .InputSize = sizeof(ActivateFw), + .InputPayload = &ActivateFw, + }; + + Status = CxlSendCmd(Private); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "CxlMemActivateFw: Error CxlSendCmd\n")); + return Status; + } + + Status = EFI_SUCCESS; + return Status; +} + +/** + Transfer all or part of a FW package from the caller to the device (Opcode 0201h) + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Possible Command Return Codes Success, Unsupported, Internal Error, + Retry Required, Invalid Payload Length +**/ +EFI_STATUS +CxlMemTransferFw ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 NextSlot, + const UINT8 *Data, + UINT32 Offset, + UINT32 Size, + UINT32 *Written + ) +{ + DEBUG((EFI_D_INFO, "CxlMemTransferFw: UEFI Driver Transfer FW (Opcode 0201h) get called!\n")); + + EFI_STATUS Status; + CXL_MAILBOX_TRANSFER_FW *TransferFw; + UINT32 CurrentSize; + UINT32 Remaining; + size_t InputSize; + int ChunkCount; + int ChunkSize; + BOOLEAN IsLastChunk; + + *Written = 0; + CurrentSize = 0; + Remaining = 0; + InputSize = 0; + ChunkCount = 0; + ChunkSize = 0; + IsLastChunk = FALSE; + + /* Offset has to be aligned to 128B */ + if (!IS_ALIGNED(Offset, CXL_FW_TRANSFER_ALIGNMENT)) { + DEBUG((EFI_D_ERROR, "[CxlMemTransferFw] Error, misaligned Offset for FW TransferFw slice (%u) \n", Offset)); + Status = EFI_LOAD_ERROR; + return Status; + } + + Private->MemdevState.FwState.OneShot = ((sizeof(*TransferFw) + Size) < (Private->MemdevState.PayloadSize)); + /* + * Pick TransferFw Size based on MemdevState.PayloadSize @Size must bw 128-byte + * aligned, ->PayloadSize is a power of 2 starting at 256 bytes, and + * sizeof(*TransferFw) is 128. These constraints imply that @CurrentSize + * will always be 128b aligned. + * sizeof(size_t) = 8 + */ + + //CurrentSize = minimumOfTwoSizes(Size, Private->MemdevState.PayloadSize - sizeof(*TransferFw)); + CurrentSize = MIN(Size, Private->MemdevState.PayloadSize - sizeof(*TransferFw)); + Remaining = Size; + InputSize = (sizeof(*TransferFw) + (sizeof(UINT8) * CurrentSize)); + + Private->MemdevState.FwState.NextSlot = NextSlot; + + TransferFw = (CXL_MAILBOX_TRANSFER_FW*)AllocatePool(InputSize); + if (!TransferFw) { + DEBUG((EFI_D_ERROR, "CxlMemTransferFw: Error in AllocatePool\n")); + Status = EFI_LOAD_ERROR; + return Status; + } + + if (Private->MemdevState.FwState.OneShot) { + TransferFw->Offset = (Offset / CXL_FW_TRANSFER_ALIGNMENT); + CopyMem(TransferFw->Data, Data + Offset, CurrentSize); + + TransferFw->Action = CXL_FW_TRANSFER_ACTION_FULL; + TransferFw->Slot = Private->MemdevState.FwState.NextSlot; + + ZeroMem(&Private->MailboxCmd, sizeof(CXL_MBOX_CMD)); + Private->MailboxCmd = (CXL_MBOX_CMD){ + .Opcode = CxlMboxOpTransferFw, + .InputSize = InputSize, + .InputPayload = TransferFw, + .PollInterval = 1000, + .poll_count = 30, + }; + + Status = CxlSendCmd(Private); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "CxlMemTransferFw: Error returned from func CxlSendCmd()\n")); + goto OUT_FREE; + } + } else { + GetChunkCount(Size, CurrentSize, &ChunkCount, &ChunkSize); + for (int Index = 0; Index < ChunkCount; Index++) + { + if (Offset == 0) { + TransferFw->Action = CXL_FW_TRANSFER_ACTION_INITIATE; + } else if (Remaining < ChunkSize) { + IsLastChunk = TRUE; + TransferFw->Action = CXL_FW_TRANSFER_ACTION_END; + } else { + TransferFw->Action = CXL_FW_TRANSFER_ACTION_CONTINUE; + } + + TransferFw->Slot = Private->MemdevState.FwState.NextSlot; + + //This is done as Offset is multiplied by CXL_FW_TRANSFER_ALIGNMENT in Qemu or hw + TransferFw->Offset = (Offset / CXL_FW_TRANSFER_ALIGNMENT); + CopyMem(TransferFw->Data, Data + Offset, CurrentSize); + + ZeroMem(&Private->MailboxCmd, sizeof(CXL_MBOX_CMD)); + Private->MailboxCmd = (CXL_MBOX_CMD){ + .Opcode = CxlMboxOpTransferFw, + .InputSize = InputSize, + .InputPayload = TransferFw, + .PollInterval = 1000, + .poll_count = 30, + }; + + Status = CxlSendCmd(Private); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "CxlMemTransferFw: Error returned from func CxlSendCmd()\n")); + goto OUT_FREE; + } + + if (IsLastChunk == TRUE) { + Offset = 0; + Remaining = 0; + break; + } + + Offset = Offset + ChunkSize; + Remaining = Remaining - ChunkSize; + if (CXL_QEMU) { + gBS->Stall(1000000); + } + } + } + + DEBUG((EFI_D_INFO, "[CxlMemTransferFw] Transfer Fw completed on Slot = %d\n", Private->MemdevState.FwState.NextSlot)); + *Written = CurrentSize; + + /* Activate FW if OneShot or if the last slice was Written */ + if (Private->MemdevState.FwState.OneShot || Remaining == 0) { + DEBUG((EFI_D_INFO, "[CxlMemTransferFw] Activating firmware on Slot: %d\n", Private->MemdevState.FwState.NextSlot)); + if (CXL_QEMU) { + gBS->Stall(10000000); + } + + Status = CxlMemActivateFw(Private); + if (Status != EFI_SUCCESS) { + goto OUT_FREE; + } + } + Status = EFI_SUCCESS; + +OUT_FREE: + + if (TransferFw) { + FreePool(TransferFw); + } + return Status; +} + +/** + Finds PCI Express Designated Vendor-Specific Extended Capability + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start Start position + @param[in] Capability Extended capability + + @retval Position Position of input capability + +**/ +UINT32 +CxlFindNextExtendedCapability ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 Start, + UINT32 Capability + ) +{ + UINT32 Header; + UINT32 TimeToLive; + UINT32 Position; + + Header = 0; + TimeToLive = 0; + Position = CXL_PCI_CFG_SPACE_SIZE; + + /* minimum 8 bytes per capability */ + TimeToLive = (CXL_PCI_CFG_SPACE_EXP_SIZE - CXL_PCI_CFG_SPACE_SIZE) / 8; + + if (Start) { + Position = Start; + } + + if (PciUefiReadConfigWord(Private, Position, &Header) != EFI_SUCCESS){ + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiReadConfigWord\n", __func__)); + return 0; + } + /* + * If we have no capabilities, this is indicated by Capability ID, + * Capability version and next pointer all being 0. + */ + if (Header == 0) { + DEBUG((EFI_D_ERROR, "[%a]: Error, Header\n", __func__)); + return 0; + } + + while (TimeToLive-- > 0) { + if (CXL_PCI_EXT_CAP_ID(Header) == Capability && Position != Start) { + return Position; + } + + Position = CXL_PCI_EXT_CAP_NEXT(Header); + if (Position < CXL_PCI_CFG_SPACE_SIZE) { + break; + } + if (PciUefiReadConfigWord(Private, Position, &Header) != EFI_SUCCESS) { + break; + } + } + return 0; +} + +/** + Finds Offset of Vendor id and Dvsec, Dvsec Vendor ID field is set to 1E98h to indicate these + Capability structures are defined by the CXL specification. + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Vendor Vendor ID + @param[in] Dvsec Dvsec Id + + @retval Position Return Offset at which DVSEC Vendor ID 1E98h and DVSEC ID 0008h get matched + +**/ +UINT32 +CxlFindDvsecCapability ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 Vendor, + UINT32 Dvsec + ) +{ + UINT32 Position = 0; + + /* CXL_PCI_EXT_CAP_ID_DVSEC: 23h is Extended Capability struct id for DVSEC */ + Position = CxlFindNextExtendedCapability(Private, 0, CXL_PCI_EXT_CAP_ID_DVSEC); + if (!Position){ + DEBUG((EFI_D_ERROR, "[%a]: Error, Position = 0\n", __func__)); + return 0; + } + + while (Position) { + UINT32 VendorData = 0; + UINT32 DvsecData = 0; + UINT32 vendorId = 0; + UINT32 DvsecId = 0; + EFI_STATUS Status; + Status = PciUefiReadConfigWord(Private, Position + CXL_PCI_DVSEC_HEADER1, &VendorData); //DVSEC Header 1 (Offset 04h) Bit Loc 15:0, get DVSEC Vendor ID 1E98h + if (Status != EFI_SUCCESS){ + return 0; + } + + Status = PciUefiReadConfigWord(Private, Position + CXL_PCI_DVSEC_HEADER2, &DvsecData); //DVSEC Header 2 (Offset 08h) Bit Loc 15:0, get DVSEC ID 0008h + if (Status != EFI_SUCCESS){ + return 0; + } + + vendorId = GetFieldValues(VendorData, 15, 0); + DvsecId = GetFieldValues(DvsecData, 15, 0); + if (Vendor == vendorId && Dvsec == DvsecId) { + /* + Return Offset at which DVSEC Vendor ID 1E98h and DVSEC ID 0008h get matched + This required Register Locator DVSEC, if not, search next extended capability + */ + return Position; + } + Position = CxlFindNextExtendedCapability(Private, Position, CXL_PCI_EXT_CAP_ID_DVSEC); + } + return 0; +} + +/** + Finds RegisterType, BaseAddressRegister, and RegisterOffset for input Register low and high + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] RegisterLow Register Low + @param[in] RegisterHigh Register High + @param[in] Type Register block identifier type + + @retval Position Return Offset at which DVSEC Vendor ID 1E98h and DVSEC ID 0008h get matched + +**/ +BOOLEAN +CxlDecodeRegblock ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 RegisterLow, + UINT32 RegisterHigh, + CXL_REG_BLOCK_IDENTIFIER Type + ) +{ + //Get Register BIR, 8.1.9.1, Register Offset Low (Offset: Varies) + /************************************************************************************* + Indicates which one of a Functions BARs, located beginning at Offset + 10h in Configuration Space, or entry in the Enhanced Allocation capability with a + matching BAR Equivalent Indicator (BEI), is used to RegisterMap the CXL registers into + Memory Space. + + Defined encodings are: + BAR0 - 10h to 18h + 000b = Base Address Register 10h + 001b = Base Address Register 14h + + BAR1 + 010b = Base Address Register 18h + 011b = Base Address Register 1Ch + + BAR2 + 100b = Base Address Register 20h + 101b = Base Address Register 24h + + All other encodings are reserved + *************************************************************************************/ + + UINT32 RegisterType; + UINT32 BAR; + UINT32 BaseAddressRegister; + UINT64 Offset; + + RegisterType = 0; + BAR = 0; + + BaseAddressRegister = GetFieldValues(RegisterLow, 2, 0); + + /* + BAR0: BaseAddressRegister = 0,1 + BAR1: BaseAddressRegister = 2,3 + BAR2: BaseAddressRegister = 4,5 + BAR3: BaseAddressRegister = 6,7 + */ + if (BaseAddressRegister == 0 || BaseAddressRegister == 1) { + BAR = 0; + } else if (BaseAddressRegister == 2 || BaseAddressRegister == 3) { + BAR = 1; + } else if (BaseAddressRegister == 4 || BaseAddressRegister == 5) { + BAR = 2; + } else { + BAR = 3; + } + + /*********************************************************************************** + 8.1.9.2 Register Offset High + ***********************************************************************************/ + Offset = ((UINT64)RegisterHigh << 32) | (RegisterLow & CXL_GENMASK(31, 16)); + + //Get Register Block Identifier which Identifies the Type of CXL registers. + //Register Block Identifier 03h is CXL Device Registers + RegisterType = GetFieldValues(RegisterLow, 15, 8); + + if (RegisterType == Type) { + Private->RegisterMap.RegisterType = GetFieldValues(RegisterLow, 15, 8); + Private->RegisterMap.BaseAddressRegister = BAR; + Private->RegisterMap.RegisterOffset = Offset; + } else { + return FALSE; + } + return TRUE; +} + +/** + Finds Register low and high based on Register block identifier and calls CxlDecodeRegblock + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Type Register block identifier type + + @retval Status Return EFI_SUCCESS on successfully calling CxlDecodeRegblock + +**/ +EFI_STATUS +CxlFindRegblockInstance ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + CXL_REG_BLOCK_IDENTIFIER Type + ) +{ + EFI_STATUS Status; + UINT32 RegisterLocatorSize; + UINT32 RegisterBlocks; + UINT32 DVSEC_Header1; + UINT32 RegisterLocator; + UINT32 LoopCount; + UINT32 RegisterLow; + UINT32 RegisterHigh; + + RegisterLocatorSize = 0; + RegisterBlocks = 0; + DVSEC_Header1 = 0; + RegisterLocator = 0; + LoopCount = 0; + + //Get Dvsec Capability with id 8, which is RegisterValue locator + RegisterLocator = CxlFindDvsecCapability(Private, CXL_PCI_DVSEC_VENDOR_ID, CXL_DVSEC_ID_REGISTER_LOCATOR); + if (RegisterLocator == 0) { + DEBUG((EFI_D_ERROR, "[%a]: Error in CxlFindDvsecCapability\n", __func__)); + return EFI_UNSUPPORTED; + } + + //Read Register Locator DVSEC - Header1 + Status = PciUefiReadConfigWord(Private, RegisterLocator + CXL_PCI_DVSEC_HEADER1, &DVSEC_Header1); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiReadConfigWord\n", __func__)); + return Status; + } + + //Get DVSEC length from Bit position 31:20 (Table 8-9), from above read Designated Vendor-specific Header 1 + RegisterLocatorSize = GetFieldValues(DVSEC_Header1, 31, 20); + + //Get Register Block 1 - Register Offset Low, By adding 0Ch to base Offset + RegisterLocator += CXL_DVSEC_REG_LOCATOR_BLOCK1_OFFSET; + + //Get Num of Reg Blocks, (Total Size - Reg Block Offset) and each block is of Size 8 + RegisterBlocks = (RegisterLocatorSize - CXL_DVSEC_REG_LOCATOR_BLOCK1_OFFSET) / 8; + + //Loop for each Reg block and get Register Offset Low and high + for (LoopCount = 0; LoopCount < RegisterBlocks; LoopCount++, RegisterLocator += 8) { + RegisterLow = 0; + RegisterHigh = 0; + Status = PciUefiReadConfigWord(Private, RegisterLocator, &RegisterLow); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiReadConfigWord, RegisterLow\n", __func__)); + return Status; + } + + Status = PciUefiReadConfigWord(Private, RegisterLocator + 4, &RegisterHigh); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiReadConfigWord, RegisterHigh\n", __func__)); + return Status; + } + + if (!CxlDecodeRegblock(Private, RegisterLow, RegisterHigh, Type)) { + continue; + } + } + return EFI_SUCCESS; +} + +/** + Finds CXL Mailbox register, The mailbox registers provide the ability to issue a command to the device + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Return EFI_SUCCESS on successfully calling CxlDecodeRegblock + +**/ +EFI_STATUS +CxlGetMboxRegs ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT32 Capability; + UINT32 CapabilityCount; + UINT64 CapabilityArray; + UINT32 Value; + UINT32 Offset; + UINT32 Length; + UINT16 CapabilityId; + + Capability = 0; + CapabilityCount = 0; + CapabilityArray = 0; + Value = 0; + + Status = PciUefiMemReadSixtyFourBits(Private, CXL_DEV_CAP_ARRAY_OFFSET, &CapabilityArray); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error getting CapabilityArray\n", __func__)); + return Status; + } + + if (GetFieldValues(CapabilityArray, 15, 0) != CXL_DEV_CAP_ARRAY_CAP_ID) { + DEBUG((EFI_D_ERROR, "[%a]: Error CXL_DEV_CAP_ARRAY_CAP_ID not matching\n", __func__)); + return EFI_NOT_FOUND; + } + + CapabilityCount = GetFieldValues(CapabilityArray, 47, 32); + + for (Capability = 1; Capability <= CapabilityCount; Capability++) { + Offset = 0; + Length = 0; + CapabilityId = 0; + + Status = PciUefiMemReadThirtyTwoBits(Private, Capability * 0x10, &Value); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error reading Value for CapabilityId\n", __func__)); + return Status; + } + + CapabilityId = GetFieldValues(Value, 15, 0); + + //8.2.8.2 CXL Device Capability Header Register + Status = PciUefiMemReadThirtyTwoBits(Private, Capability * 0x10 + 0x4, &Offset); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error reading Offset\n", __func__)); + return Status; + } + + Status = PciUefiMemReadThirtyTwoBits(Private, Capability * 0x10 + 0x8, &Length); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error reading length\n", __func__)); + return Status; + } + + switch (CapabilityId) { + case CXL_DEVICE_CAPABILITY_ID_PRIMARY_MAILBOX: + Private->RegisterMap.MailboxRegistersOffset = Offset; + break; + + case CXL_DEVICE_CAPABILITY_ID_SECONDARY_MAILBOX: + break; + + default: + if (CapabilityId >= 0x8000) { + DEBUG((EFI_D_INFO, "[%a]: CapabilityId >= 0x8000\n", __func__)); + } + break; + } + } + return EFI_SUCCESS; +} + +/** + Reads MB Control Register to verify doorbell is clear, and wait for it till it is not clear + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval BOOLEAN Return 0 in case of bit is clear else 1 + +**/ +UINT32 +CxlMboxWaitForDoorbell ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + int Count = 0; + while (IsCxlDoorbellBusy(Private)) { + gBS->Stall(1000); //1000Microsecond, 1 ms + Count++; + + /* Check again in case preempted before timeout test */ + if (!IsCxlDoorbellBusy(Private)) { + break; + } + + if (Count == CXL_MAILBOX_TIMEOUT_MS) { + return 1; + } + } + return 0; +} + +/** + Checks Mailbox background command gets complete or not + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval BOOLEAN Return 0 in case of bit is clear else 1 + +**/ +static BOOLEAN +CxlMboxBackgroundComplete ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + + EFI_STATUS Status; + UINT64 RegisterValue; + UINT64 BackgroundStatus; + + Status = PciUefiMemReadSixtyFourBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_BG_CMD_STATUS_OFFSET, &RegisterValue); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error reading RegisterValue for background status cmd\n", __func__)); + return 0; + } + + BackgroundStatus = GetFieldValues(RegisterValue, 22, 16); + if (BackgroundStatus == 100) { + DEBUG((EFI_D_ERROR, "[%a]: BackgroundStatus = 100\n", __func__)); + return 0; + } + return 1; +} + +/** + Issue a command to the device using mailbox registers + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Return EFI_SUCCESS on successfully calling CxlDecodeRegblock + +**/ +EFI_STATUS +CxlPciMboxSend ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + /* + * 1. Caller reads MB Control Register to verify doorbell is clear + * 2. Caller writes Command Register + * 3. Caller writes Command Payload Registers if input payload is non-empty + * 4. Caller writes MB Control Register to set doorbell + * 5. Caller either polls for doorbell to be clear or waits for interrupt if configured + * 6. Caller reads MB Status Register to fetch Return code + * 7. If command successful, Caller reads Command Register to get Payload Length + * 8. If output payload is non-empty, host reads Command Payload Registers + * + * Hardware is free to do whatever it wants before the doorbell is rung, + * and isn't allowed to change anything after it clears the doorbell. As + * such, steps 2 and 3 can happen in any order, and steps 6, 7, 8 can + * also happen in any order (though some orders might not make sense). + */ + + //8.2.8.4.5 Command Register (Mailbox Registers Capability Offset + 08h) + + EFI_STATUS Status; + UINT64 CommandRegister; + UINT64 StatusRegister; + size_t OutputLength; + UINT32 Value; + int Index; + size_t MinimumNumber; + UINT64 BackgroundStatusRegister; + + CommandRegister = 0; + StatusRegister = 0; + OutputLength = 0; + + /* #1 */ + if (IsCxlDoorbellBusy(Private)) { + DEBUG((EFI_D_ERROR, "[%a]: Error mailbox queue busy\n", __func__)); + return EFI_TIMEOUT; + } + + CommandRegister = Private->MailboxCmd.Opcode; + if (Private->MailboxCmd.InputSize) { + if (!Private->MailboxCmd.InputPayload){ + DEBUG((EFI_D_ERROR, "[%a]: Error InputPayload is 0\n", __func__)); + return EFI_BAD_BUFFER_SIZE; + } + + //Place bits of InputSize starting 16th bits of UINT64 CommandRegister + CommandRegister |= (Private->MailboxCmd.InputSize << 16); + + /*Read Payload buffer in n Bytes*/ + Status = PciUefiMemWriteNBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_PAYLOAD_OFFSET, Private->MailboxCmd.InputPayload, Private->MailboxCmd.InputSize); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiMemWriteNBits\n", __func__)); + return Status; + } + } + + /* #2, #3 */ + Status = PciUefiMemWriteSixtyFourBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_CMD_OFFSET, &CommandRegister); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiMemWriteSixtyFourBits\n", __func__)); + return Status; + } + + /* #4 */ + Value = CXL_DEV_MBOX_CTRL_DOORBELL; + Status = PciUefiMemWriteThirtyTwoBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_CTRL_OFFSET, &Value); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiMemWriteThirtyTwoBits\n", __func__)); + return Status; + } + + /* #5 */ + if (CxlMboxWaitForDoorbell(Private) != 0) { + DEBUG((EFI_D_ERROR, "[%a]: Error, Mailbox timeout\n", __func__)); + Status = EFI_TIMEOUT; + return Status; + } + + /* #6 */ + Status = PciUefiMemReadSixtyFourBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_STATUS_OFFSET, &StatusRegister); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiMemReadSixtyFourBits\n", __func__)); + return Status; + } + + Private->MailboxCmd.ReturnCode = GetFieldValues(StatusRegister, 47, 32); + + if (Private->MailboxCmd.ReturnCode == CXL_MBOX_CMD_RC_BACKGROUND) { + Index = 0; + BackgroundStatusRegister = 0; + + for (Index = 0; Index < Private->MailboxCmd.poll_count; Index++) { + gBS->Stall(Private->MailboxCmd.PollInterval * 1000); //Microsecond + if (CxlMboxBackgroundComplete(Private)) { + break; + } + } + + if (!CxlMboxBackgroundComplete(Private)) { + DEBUG((EFI_D_ERROR, "[%a]: Error, Mailbox timeout\n", __func__)); + return EFI_TIMEOUT; + } + + Status = PciUefiMemReadSixtyFourBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_BG_CMD_STATUS_OFFSET, &BackgroundStatusRegister); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiMemReadSixtyFourBits\n", __func__)); + return Status; + } + + Private->MailboxCmd.ReturnCode = GetFieldValues(BackgroundStatusRegister, 47, 32); + } + + if (Private->MailboxCmd.ReturnCode != CXL_MBOX_CMD_RC_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Completed but caller must check ReturnCode\n", __func__)); + return EFI_SUCCESS; + } + + //8.2.8.4.5 Command Register (Mailbox Registers Capability Offset + 08h) + Status = PciUefiMemReadSixtyFourBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_CMD_OFFSET, &CommandRegister); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in PciUefiMemReadSixtyFourBits\n", __func__)); + return Status; + } + + //Payload Length: (36:16) + OutputLength = GetFieldValues(CommandRegister, 36, 16); + + /* #8 */ + if (OutputLength && Private->MailboxCmd.OutputPayload) { + MinimumNumber = MinimumOfThreeValues(Private->MailboxCmd.OutputSize, Private->MemdevState.PayloadSize, OutputLength); + + /*Read Payload buffer in n Bytes*/ + char Buffer[MinimumNumber]; + Status = PciUefiMemReadNBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_PAYLOAD_OFFSET, Buffer, MinimumNumber); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error, Reading Buffer[n] in func PciUefiMemReadNBits()\n", __func__)); + return Status; + } + + Private->MailboxCmd.OutputSize = MinimumNumber; + MinimumNumber = MinimumOfThreeValues(Private->MailboxCmd.OutputSize, Private->MemdevState.PayloadSize, OutputLength); + CopyMem(Private->MailboxCmd.OutputPayload, Buffer, MinimumNumber); + } else { + Private->MailboxCmd.OutputSize = 0; + } + return EFI_SUCCESS; +} + +/** + Setup Mailbox register Payload size, Mailbox register provide the ability to issue a command to the device + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Return EFI_SUCCESS on successfully calling CxlDecodeRegblock + +**/ +EFI_STATUS +CxlSetMailboxPayloadSize ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT32 Capability = 0; + + //8.2.8.4.3 Mailbox Capabilities Register + Status = PciUefiMemReadThirtyTwoBits(Private, Private->RegisterMap.MailboxRegistersOffset + CXL_DEV_MBOX_CAPS_OFFSET, &Capability); + if (Status != EFI_SUCCESS) { + return Status; + } + + if (CxlMboxWaitForDoorbell(Private) != 0) { + DEBUG((EFI_D_ERROR, "[%a]: Error, Mailbox timeout\n", __func__)); + Status = EFI_TIMEOUT; + return Status; + } + + Private->MemdevState.PayloadSize = (UINT32)1 << GetFieldValues(Capability, 4, 0); + Private->MemdevState.PayloadSize = MIN(Private->MemdevState.PayloadSize, CXL_SZ_1M); + + if (Private->MemdevState.PayloadSize < 256) { + DEBUG((EFI_D_ERROR, "[%a]: Mailbox is too small\n", __func__)); + Status = EFI_LOAD_ERROR; + return Status; + } + + Status = EFI_SUCCESS; + return Status; +} + +/** + Setup Mailbox register which provide the ability to issue a command to the device + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Return EFI_SUCCESS on successfully calling CxlDecodeRegblock + +**/ +EFI_STATUS +EFIAPI +CxlMailBoxSetup ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + Status = CxlFindRegblockInstance(Private, CxlRbiMemdev); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error CxlFindRegblockInstance\n", __func__)); + FreePool(Private); + return Status; + } + + Status = CxlGetMboxRegs(Private); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error CxlGetMboxRegs\n", __func__)); + FreePool(Private); + return Status; + } + + Status = CxlSetMailboxPayloadSize(Private); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error CxlSetMailboxPayloadSize\n", __func__)); + FreePool(Private); + return Status; + } + + //Get FW Info + Status = CxlMemGetFwInfo(Private); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[CxlMailBoxSetup]: Error CxlMemGetFwInfo()\n")); + FreePool(Private); + return Status; + } + + InitializeFwImageDescriptor(Private); + + CopyMem(&Private->FirmwareMgmt, &gCxlFirmwareManagement, sizeof(EFI_FIRMWARE_MANAGEMENT_PROTOCOL)); + return Status; +} + +/** + Tests to see if this driver supports a given controller. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +CxlDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_STATUS RegStatus; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT8 ClassCode[3]; + UINT32 ExtCapOffset; + UINT32 NextExtCapOffset; + UINT32 PcieExtCapAndDvsecHeader[PcieDvsecHeaderMax]; + + // Ensure driver won't be started multiple times + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "[%a]: [CXL-%08X] CallerGuid OpenProtocol Returning status = %r\n", __func__, Controller, Status)); + return EFI_ALREADY_STARTED; + } + + // Attempt to Open PCI I/O Protocol + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID**)&PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "[%a]: [CXL-%08X] PciGuid OpenProtocol Returning status = %r\n", __func__, Controller, Status)); + return Status; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + PCI_CLASSCODE_OFFSET, + sizeof(ClassCode), + ClassCode + ); + + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "[%a]: [CXL-%08X] CallerGuid OpenProtocol Returning status = %r\n", __func__, Controller, Status)); + goto EXIT; + } + + if ((ClassCode[0] != CXL_MEMORY_PROGIF) || (ClassCode[1] != CXL_MEMORY_SUB_CLASS) || (ClassCode[2] != CXL_MEMORY_CLASS)) { + DEBUG((EFI_D_ERROR, "[%a]: UNSUPPORTED Class [CXL-%08X] \n", __func__, Controller)); + Status = EFI_UNSUPPORTED; + goto EXIT; + } + + DEBUG((EFI_D_INFO, "[%a]: SUPPORTED ClassCode0 = %d\n", __func__, ClassCode[0])); + DEBUG((EFI_D_INFO, "[%a]: SUPPORTED ClassCode1 = %d\n", __func__, ClassCode[1])); + DEBUG((EFI_D_INFO, "[%a]: SUPPORTED ClassCode2 = %d\n", __func__, ClassCode[2])); + + Status = EFI_UNSUPPORTED; + NextExtCapOffset = CXL_PCIE_EXTENDED_CAP_OFFSET; + do { + ExtCapOffset = NextExtCapOffset; + DEBUG((EFI_D_INFO, "[%a]: ExtCapOffset = %d \n", __func__, ExtCapOffset)); + RegStatus = PciIo->Pci.Read( + PciIo, + EfiPciIoWidthUint32, + ExtCapOffset, + PcieDvsecHeaderMax, + PcieExtCapAndDvsecHeader + ); + + if (EFI_ERROR (RegStatus)) { + DEBUG((EFI_D_ERROR, "[%a]: Failed to read PCI IO for Ext. capability \n", __func__)); + goto EXIT; + } + + /* Check whether this is a CXL device */ + if (CXL_IS_DVSEC(PcieExtCapAndDvsecHeader[PcieDvsecHeader1])) { + Status = EFI_SUCCESS; + break; + } + + NextExtCapOffset = CXL_PCIE_EXTENDED_CAP_NEXT( + PcieExtCapAndDvsecHeader[PcieExtCapHeader] + ); + } while (NextExtCapOffset); + + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "[%a]: ****[CXL-%08X] Error: Non CXL Device****\n", __func__, Controller)); + } + +EXIT: + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +CxlDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + DEBUG((EFI_D_INFO, "CxlBindStop\n")); + + EFI_STATUS Status; + CXL_CONTROLLER_PRIVATE_DATA *Private; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *FMP; + + Status = gBS->OpenProtocol( + Controller, + &gEfiFirmwareManagementProtocolGuid, + (VOID**)&FMP, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR(Status)) { + Private = CXL_CONTROLLER_PRIVATE_FROM_FIRMWARE_MGMT(FMP); + gBS->UninstallMultipleProtocolInterfaces( + Controller, + &gEfiFirmwareManagementProtocolGuid, + FMP, + NULL, + NULL, + &gEfiFirmwareManagementProtocolGuid, + &Private->FirmwareMgmt, + NULL + ); + + for (int Index = 0; Index < CXL_FW_MAX_SLOTS - 1; Index++) { + FreePool(Private->SlotInfo.FirmwareVersion[Index]); + } + + FreePool(Private->MemdevState.FwState.FwRevisionSlot1); + FreePool(Private->MemdevState.FwState.FwRevisionSlot2); + FreePool(Private->MemdevState.FwState.FwRevisionSlot3); + FreePool(Private->MemdevState.FwState.FwRevisionSlot4); + + FreePool(Private); + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +CxlDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + CXL_CONTROLLER_PRIVATE_DATA *Private; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID**)&ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) { + DEBUG((EFI_D_ERROR, "[%a]: [CXL-%08X] path OpenProtocol Returning status = %r\n", __func__, Controller, Status)); + return Status; + } + + Status = gBS->OpenProtocol( + Controller, + &gEfiPciIoProtocolGuid, + (VOID**)&PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + DEBUG((EFI_D_ERROR, "[%a]: [CXL-%08X] PciIo OpenProtocol Returning status = %r\n", __func__, Controller, Status)); + goto EXIT; + } + + if (Status == EFI_ALREADY_STARTED) { + DEBUG((EFI_D_ERROR, "[%a]: [CXL-%08X] PciIo EFI_ALREADY_STARTED status = %r\n", __func__, Controller, Status)); + } else { + Private = AllocateZeroPool(sizeof(CXL_CONTROLLER_PRIVATE_DATA)); + if (Private == NULL) { + DEBUG((EFI_D_ERROR, "[%a]: Allocating for CXL Private Data failed!\n", __func__)); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + Private->PackageVersionName = AllocateZeroPool(CXL_STRING_BUFFER_WIDTH); + if (Private->PackageVersionName == NULL) { + DEBUG((EFI_D_ERROR, "[%a]: Allocating for CXL PackageVersionName Data failed!\n", __func__)); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + Private->Signature = CXL_CONTROLLER_PRIVATE_DATA_SIGNATURE; + Private->ControllerHandle = Controller; + Private->ImageHandle = This->DriverBindingHandle; + Private->DriverBindingHandle = This->DriverBindingHandle; + Private->PciIo = PciIo; + Private->ParentDevicePath = ParentDevicePath; + StrCpyS(Private->PackageVersionName, CXL_STRING_BUFFER_WIDTH, CXL_PACKAGE_VERSION_NAME); + Private->PackageVersion = CXL_PACKAGE_VERSION_FFFFFFFE; + + DEBUG((EFI_D_ERROR, "[%a]: [CXL-%08X] Allocation Completed status = %r\n", __func__, Controller, Status)); + + Status = PciIo->GetLocation(PciIo, &Private->Seg, &Private->Bus, &Private->Device, &Private->Function); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error PciIo GetLocation\n", __func__)); + FreePool(Private); + return Status; + } + + DEBUG((EFI_D_INFO, "[%a]: Bus = %d, DEVICE = %d, FUNCTION = %d\n", __func__, Private->Bus, Private->Device, Private->Function)); + + Status = CxlMailBoxSetup(Private); + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "[%a]: Error in setting up MailBox\n", __func__)); + return Status; + } + + Status = gBS->InstallMultipleProtocolInterfaces( + &Private->ControllerHandle, + &gEfiFirmwareManagementProtocolGuid, + &Private->FirmwareMgmt, + NULL + ); + + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "[CxlDriverBindingStart]: [CXL-%08X] Protocol Install failed status = %r\n", Controller, Status)); + FreePool(Private); + goto EXIT; + } else { + DEBUG((EFI_D_INFO, "[CxlDriverBindingStart]: [CXL-%08X] Protocol Install completed status = %r\n", Controller, Status)); + } + } + return EFI_SUCCESS; + +EXIT: + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + DEBUG((EFI_D_INFO, "[%a]: [CXL-%08X] Completed status = %r\n", __func__, Controller, Status)); + return Status; +} + +// +// CXLDXE Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL CxlDxeComponentName = { + CxlDxeComponentNameGetDriverName, + CxlDxeComponentNameGetControllerName, + "eng" +}; + +// +// CXLDXE Driver Name Table +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL CxlDxeComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)CxlDxeComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)CxlDxeComponentNameGetControllerName, + "en" +}; + +// +// CXLDXE Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE CxlDxeDriverNameTable[] = { + { "eng;en", L"UEFI CXL Driver" }, + { NULL, NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +CxlDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2( + Language, + This->SupportedLanguages, + CxlDxeDriverNameTable, + DriverName, + (BOOLEAN)(This == &CxlDxeComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +CxlDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_SUCCESS; +} + +/** + The entry point for CXL driver, used to install CXL driver on the ImageHandle. + + @param ImageHandle The firmware allocated handle for this driver image. + @param SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS Driver loaded. + @retval other Driver not loaded. + +**/ +EFI_STATUS +EFIAPI +CxlDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + DEBUG((EFI_D_INFO, "[%a]: Driver entery point get called\n", __func__)); + + Status = EfiLibInstallAllDriverProtocols2 ( + ImageHandle, + SystemTable, + &gCxlDriverBinding, + ImageHandle, + &CxlDxeComponentName, + &CxlDxeComponentName2, + NULL, + NULL, + NULL, + NULL + ); + + DEBUG((EFI_D_INFO, "[%a]: CXL Installing DriverBinding status = %r\n", __func__, Status)); + ASSERT_EFI_ERROR (Status); + gRT = SystemTable->RuntimeServices; + return Status; +} + diff --git a/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.h b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.h new file mode 100644 index 000000000000..69d1016d3ba8 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.h @@ -0,0 +1,571 @@ +/** @file + Header file for CxlDxe driver + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _EFI_CXLDXE_H_ +#define _EFI_CXLDXE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CXL_MEMORY_CLASS 0x05 +#define CXL_MEMORY_SUB_CLASS 0x02 +#define CXL_MEMORY_PROGIF 0x10 +#define CXL_PCIE_EXTENDED_CAP_NEXT(n) ((n) >> (CXL_PCIE_EXTENDED_NEXT_CAP_OFFSET_SHIFT)) +#define CXL_IS_DVSEC(n) (((n) & (0xFFFF)) == CXL_PCI_DVSEC_VENDOR_ID) +#define CXL_CONTROLLER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C','X','L','X') +#define CXL_PCI_EXT_CAP_ID(Header) (Header & 0x0000ffff) +#define CXL_PCI_EXT_CAP_NEXT(Header) ((Header >> 20) & 0xffc) +#define CXL_DEV_CAP_ARRAY_OFFSET 0x0 +#define CXL_DEV_CAP_ARRAY_CAP_ID 0 +#define CXL_BIT(nr) ((UINT32)1 << nr) +#define CXL_DEV_MBOX_CTRL_DOORBELL CXL_BIT(0) +#define CXL_SZ_1M 0x00100000 +#define CXL_STRING_BUFFER_WIDTH 256 +#define CXL_FW_IMAGE_ID 1 +#define CXL_FW_VERSION 1 +#define CXL_PACKAGE_VERSION_FFFFFFFE 0xFFFFFFFE +#define CXL_FIRMWARE_IMAGE_ID_NAME L"CXL Firmware Version 1.0" +#define CXL_PACKAGE_VERSION_NAME L"CXL Firmware Package Version Name UEFI Driver" +#define CXL_FW_SIZE 32768 /* 32 mb */ +#define CXL_BITS_PER_LONG 32 +#define CXL_UL (UINTN) +#define CXL_QEMU 1 +#define CXL_FW_REVISION_LENGTH_IN_BYTES 16 + +#define CXL_GENMASK(h, l) \ + (((~CXL_UL(0)) - (CXL_UL(1) << (l)) + 1) & \ + (~CXL_UL(0) >> (CXL_BITS_PER_LONG - 1 - (h)))) + +#define CXL_CONTROLLER_PRIVATE_FROM_FIRMWARE_MGMT(a) \ + CR (a, \ + CXL_CONTROLLER_PRIVATE_DATA, \ + FirmwareMgmt, \ + CXL_CONTROLLER_PRIVATE_DATA_SIGNATURE \ + ) + +// +// CXL Memory Device Register information +// +typedef struct { + UINT32 RegisterType; + UINT32 BaseAddressRegister; + unsigned long long RegisterOffset; + unsigned long MailboxRegistersOffset; +} CXL_REGISTER_MAP; + +// +// CXL Memory Device Firmware slot information +// +typedef struct { + UINT8 NumberOfSlots; + UINTN ImageFileSize[CXL_FW_MAX_SLOTS]; + CHAR16 *ImageFileBuffer[CXL_FW_MAX_SLOTS]; + BOOLEAN IsSetImageDone[CXL_FW_MAX_SLOTS]; + CHAR16 *FirmwareVersion[CXL_FW_MAX_SLOTS]; + EFI_FIRMWARE_IMAGE_DESCRIPTOR FwImageDescriptor[CXL_FW_IMAGE_DESCRIPTOR_COUNT]; +} CXL_SLOT_INFO; + +// +// CXL Memory Device Firmware state +// +typedef struct { + UINT32 State; + BOOLEAN OneShot; + UINT32 NumberOfSlots; + UINT32 CurrentSlot; + UINT32 NextSlot; + UINT8 FwActivationCap; + CHAR16 *FwRevisionSlot1; + CHAR16 *FwRevisionSlot2; + CHAR16 *FwRevisionSlot3; + CHAR16 *FwRevisionSlot4; +} CXL_FW_STATE; + +// +// CXL Memory Device Registers state +// +typedef struct { + UINT32 PayloadSize; + CXL_FW_STATE FwState; +} CXL_MEMDEV_STATE; + +// +// CXL device private data structure +// +typedef struct { + UINT32 Signature; + EFI_HANDLE ControllerHandle; + EFI_HANDLE ImageHandle; + EFI_HANDLE DriverBindingHandle; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + //MailBox Register + CXL_REGISTER_MAP RegisterMap; + CXL_MEMDEV_STATE MemdevState; + CXL_MBOX_CMD MailboxCmd; + + //Image Info + CXL_SLOT_INFO SlotInfo; + + //BDF Value + UINTN Seg; + UINTN Bus; + UINTN Device; + UINTN Function; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + + // Produced protocols + EFI_FIRMWARE_MANAGEMENT_PROTOCOL FirmwareMgmt; +} CXL_CONTROLLER_PRIVATE_DATA; + +extern EFI_FIRMWARE_MANAGEMENT_PROTOCOL gCxlFirmwareManagement; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +CxlDxeComponentNameGetDriverName( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +CxlDxeComponentNameGetControllerName( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the State of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened State, then it must not be closed with CloseProtocol(). This is required + to guarantee the State of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +CxlDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + + @param[in] ControllerHandle The handle of the controller to Start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + + @retval Others The driver failded to Start the device. + +**/ +EFI_STATUS +EFIAPI +CxlDriverBindingStart( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +CxlDriverBindingStop( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + + +/** + Returns minimum among the input values + + @param[in] ValOne Input value one + @param[in] ValTwo Input value Two + @param[in] ValThree Input value Three + + @retval Minimum Returns minimum value among the given input values + + **/ +UINT64 MinimumOfThreeValues(UINT64 a, UINT64 b, UINT64 c); + +/** + Returns the number of chunk from firmware file, FW Transfer should take less time, therefore chunk Size is maximum + + @param[in] FileSize Size of Firmware file + @param[in] MaxPayloadSize Maximum Payload Size supported by mailbox + + @retval ChunkCount Number of Chunks of perticular size + @retval ChunkSize Chunks size to be transferred + + **/ +void GetChunkCount(int FileSize, int MaxPayloadSize, int *ChunkCount, int *ChunkSize); + +/** + Returns bits value from input value + + @param[in] RegisterValue Input register value from where bits has to extracted + @param[in] PositionOne starting bits position + @param[in] PositionTwo ending bits position + + @retval LastPositionBits Value of bits from position one to two + + **/ +UINT64 GetFieldValues(UINT64 RegisterValue, UINT32 PositionOne, UINT32 PositionTwo); + +/** + Initialize Firmware Image Descriptor with default values, which were to be updated in later function calls + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval void No value is returned + + **/ +void InitializeFwImageDescriptor(CXL_CONTROLLER_PRIVATE_DATA *Private); + +/** + Reads EFI PCI i/o protocol values + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS PciUefiReadConfigWord(CXL_CONTROLLER_PRIVATE_DATA *Private, UINT32 Start, UINT32 *Value); + +/** + Reads EFI PCI i/o protocol values of thirty two bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS PciUefiMemReadThirtyTwoBits(CXL_CONTROLLER_PRIVATE_DATA *Private, UINT32 Start, UINT32 *Value); + +/** + Reads EFI PCI i/o protocol values of sixty four bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS PciUefiMemReadSixtyFourBits(CXL_CONTROLLER_PRIVATE_DATA *Private, UINT32 Start, UINT64 *Value); + +/** + Reads EFI PCI i/o protocol values of N bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS PciUefiMemReadNBits(CXL_CONTROLLER_PRIVATE_DATA *Private, UINT32 Start, CHAR8 Buffer[], UINT32 Size); + +/** + Write EFI PCI i/o protocol values of thirty two bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS PciUefiMemWriteThirtyTwoBits(CXL_CONTROLLER_PRIVATE_DATA *Private, UINT32 Start, UINT32 *Value); + +/** + Write EFI PCI i/o protocol values of sixty four bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS PciUefiMemWriteSixtyFourBits(CXL_CONTROLLER_PRIVATE_DATA *Private, UINT32 Start, UINT64 *Value); + +/** + Write EFI PCI i/o protocol values of N bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS PciUefiMemWriteNBits(CXL_CONTROLLER_PRIVATE_DATA *Private, UINT32 Start, CHAR8 Buffer[], UINT32 Size); + +/** + Retrieve information about the device FW (Opcode 0200h) + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Possible Command Return Codes Success, Unsupported, Internal Error, + Retry Required, Invalid Payload Length + +**/ +EFI_STATUS CxlMemGetFwInfo(CXL_CONTROLLER_PRIVATE_DATA *Private); + +/** + Transfer all or part of a FW package from the caller to the device (Opcode 0201h) + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Possible Command Return Codes Success, Unsupported, Internal Error, + Retry Required, Invalid Payload Length + +**/ +EFI_STATUS CxlMemTransferFw(CXL_CONTROLLER_PRIVATE_DATA *Private, UINT32 NextSlot, const UINT8 *Data, UINT32 Offset, UINT32 Size, UINT32 *Written); + +/** + Activate FW command make a FW previously stored on the device with the Transfer FW command as active FW + (Opcode 0202h) + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Possible Command Return Codes Success, Unsupported, Internal Error, + Retry Required, Invalid Payload Length + +**/ +EFI_STATUS CxlMemActivateFw(CXL_CONTROLLER_PRIVATE_DATA *Private); + +/** + Issue a command to the device using mailbox registers + + @param[in] Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval Status Return EFI_SUCCESS on successfully calling CxlDecodeRegblock + +**/ +EFI_STATUS CxlPciMboxSend(CXL_CONTROLLER_PRIVATE_DATA *Private); +#endif // _EFI_CXLDXE_H_ + diff --git a/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.inf b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.inf new file mode 100644 index 000000000000..887e0c5bf54b --- /dev/null +++ b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.inf @@ -0,0 +1,39 @@ +## @file +# +# CxlDxe driver setup file +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = CxlDxe + FILE_GUID = 29e54f5e-365a-4235-ba62-c832d7660852 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = CxlDxeEntryPoint + +[Sources.common] + CxlDxe.c + CxlDxe.h + CxlDxeFwMgmt.c + CxlDxeUtil.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + DevicePathLib + DxeServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gEfiPciIoProtocolGuid ##CONSUMES + gEfiFirmwareManagementProtocolGuid ## BY_START + +[Depex] + gEfiPciEnumerationCompleteProtocolGuid + diff --git a/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxeFwMgmt.c b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxeFwMgmt.c new file mode 100644 index 000000000000..cc0f3ff8ee05 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxeFwMgmt.c @@ -0,0 +1,398 @@ +/** @file + Firmware Management Protocol - supports Get Fw Info, Sending/Receiving FMP commands + supports Set Fw Image, Activate Fw image + SetPkgInfo, GetPkgInfo, CheckImg + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "CxlDxe.h" + +/** + Returns information about the current firmware image(s) of the device. + + This function allows a copy of the current firmware image to be created and saved. + The saved copy could later been used, for example, in firmware image recovery or rollback. + + @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. + @param[in, out] ImageInfoSize A pointer to the Size, in bytes, of the ImageInfo buffer. + On input, this is the Size of the buffer allocated by the caller. + On output, it is the Size of the buffer returned by the firmware + if the buffer was large enough, or the Size of the buffer needed + to contain the image(s) information if the buffer was too small. + @param[in, out] ImageInfo A pointer to the buffer in which firmware places the current image(s) + information. The information is an array of EFI_FIRMWARE_IMAGE_DESCRIPTORs. + @param[out] DescriptorVersion A pointer to the location in which firmware returns the version number + associated with the EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[out] DescriptorCount A pointer to the location in which firmware returns the number of + descriptors or firmware images within this device. + @param[out] DescriptorSize A pointer to the location in which firmware returns the Size, in bytes, + of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[out] PackageVersion A version number that represents all the firmware images in the device. + The format is Vendor specific and new version must have a greater Value + than the old version. If PackageVersion is not supported, the Value is + 0xFFFFFFFF. A Value of 0xFFFFFFFE indicates that package version comparison + is to be performed using PackageVersionName. A Value of 0xFFFFFFFD indicates + that package version update is in progress. + @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing the + package version name. The buffer is allocated by this function with + AllocatePool(), and it is the caller's responsibility to free it with a call + to FreePool(). + + @retval EFI_SUCCESS The device was successfully updated with the new image. + @retval EFI_BUFFER_TOO_SMALL The ImageInfo buffer was too small. The current buffer Size + needed to hold the image(s) information is returned in ImageInfoSize. + @retval EFI_INVALID_PARAMETER ImageInfoSize is NULL. + @retval EFI_DEVICE_ERROR Valid information could not be returned. Possible corrupted image. + +**/ +EFI_STATUS +EFIAPI +CxlFirmwareMgmtGetImageInfo ( + IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, + IN OUT UINTN *ImageInfoSize, + IN OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo, + OUT UINT32 *DescriptorVersion, + OUT UINT8 *DescriptorCount, + OUT UINTN *DescriptorSize, + OUT UINT32 *PackageVersion, + OUT CHAR16 **PackageVersionName + ) +{ + DEBUG((EFI_D_INFO, "CxlFirmwareMgmtGetImageInfo: Entering CxlFirmwareMgmtGetImageInfo...\n")); + CXL_CONTROLLER_PRIVATE_DATA *Private; + Private = CXL_CONTROLLER_PRIVATE_FROM_FIRMWARE_MGMT(This); + + if(((Private->SlotInfo.NumberOfSlots) * sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR)) > *ImageInfoSize){ + *ImageInfoSize = (Private->SlotInfo.NumberOfSlots * sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR)); + return EFI_BUFFER_TOO_SMALL; + } + + if (NULL == ImageInfoSize || + NULL == ImageInfo || + NULL == DescriptorVersion || + NULL == DescriptorCount || + NULL == PackageVersionName || + NULL == DescriptorSize || + NULL == PackageVersion) + { + DEBUG((EFI_D_ERROR, "CxlFirmwareMgmtGetImageInfo: EFI Invalid param...\n")); + return EFI_INVALID_PARAMETER; + } + + *PackageVersionName = AllocateZeroPool(CXL_STRING_BUFFER_WIDTH); + if (NULL == *PackageVersionName) { + DEBUG((EFI_D_ERROR, "CxlFirmwareMgmtGetImageInfo: EFI Out of resources...\n")); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem(ImageInfo, &Private->SlotInfo.FwImageDescriptor, Private->SlotInfo.NumberOfSlots * sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR)); + + *DescriptorVersion = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; + *DescriptorCount = Private->SlotInfo.NumberOfSlots; + *DescriptorSize = sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR); + + StrCpyS(*PackageVersionName, CXL_STRING_BUFFER_WIDTH, Private->PackageVersionName); + *PackageVersion = Private->PackageVersion; + return EFI_SUCCESS; +} + +/** + Retrieves a copy of the current firmware image of the device. + + This function allows a copy of the current firmware image to be created and saved. + The saved copy could later been used, for example, in firmware image recovery or rollback. + + @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. + @param[in] ImageIndex A unique number identifying the firmware image(s) within the device. + The number is between 1 and DescriptorCount. + @param[in, out] Image Points to the buffer where the current image is copied to. + @param[in, out] ImageSize On entry, points to the size of the buffer pointed to by Image, in bytes. + On return, points to the length of the image, in bytes. + + @retval EFI_SUCCESS The device was successfully updated with the new image. + @retval EFI_BUFFER_TOO_SMALL The buffer specified by ImageSize is too small to hold the + image. The current buffer size needed to hold the image is returned + in ImageSize. + @retval EFI_INVALID_PARAMETER The Image was NULL. + @retval EFI_NOT_FOUND The current image is not copied to the buffer. + @retval EFI_UNSUPPORTED The operation is not supported. + @retval EFI_SECURITY_VIOLATION The operation could not be performed due to an authentication failure. + +**/ +EFI_STATUS +EFIAPI +CxlFirmwareMgmtGetImage ( + IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, + IN UINT8 ImageIndex, + IN OUT VOID *Image, + IN OUT UINTN *ImageSize + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + CXL_CONTROLLER_PRIVATE_DATA *Private = NULL; + + Private = CXL_CONTROLLER_PRIVATE_FROM_FIRMWARE_MGMT(This); + if (ImageIndex > Private->SlotInfo.NumberOfSlots || Private->SlotInfo.IsSetImageDone[ImageIndex] != TRUE) + { + DEBUG((EFI_D_ERROR, "[%a]: ImageIndex = %d, NumberOfSlots = %d, IsSetImageDone = %d \n", + __func__, ImageIndex, Private->SlotInfo.NumberOfSlots, Private->SlotInfo.IsSetImageDone[ImageIndex])); + return EFI_INVALID_PARAMETER; + } + + if (Private->SlotInfo.ImageFileSize[ImageIndex] > *ImageSize) { + *ImageSize = Private->SlotInfo.ImageFileSize[ImageIndex]; + return EFI_BUFFER_TOO_SMALL; + } + + if (Image != NULL) { + CopyMem(Image, Private->SlotInfo.ImageFileBuffer[ImageIndex], sizeof(*ImageSize)); + } else { + *ImageSize = Private->SlotInfo.ImageFileSize[ImageIndex]; + return EFI_BUFFER_TOO_SMALL; + } + return Status; +} + +/** + Updates the firmware image of the device. + + This function updates the hardware with the new firmware image. + This function returns EFI_UNSUPPORTED if the firmware image is not updatable. + If the firmware image is updatable, the function should perform the following minimal validations + before proceeding to do the firmware image update. + - Validate the image authentication if image has attribute + IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED. The function returns + EFI_SECURITY_VIOLATION if the validation fails. + - Validate the image is a supported image for this device. The function returns EFI_ABORTED if + the image is unsupported. The function can optionally provide more detailed information on + why the image is not a supported image. + - Validate the data from VendorCode if not null. Image validation must be performed before + VendorCode data validation. VendorCode data is ignored or considered invalid if image + validation failed. The function returns EFI_ABORTED if the data is invalid. + + VendorCode enables vendor to implement vendor-specific firmware image update policy. Null if + the caller did not specify the policy or use the default policy. As an example, vendor can implement + a policy to allow an option to force a firmware image update when the abort reason is due to the new + firmware image version is older than the current firmware image version or bad image checksum. + Sensitive operations such as those wiping the entire firmware image and render the device to be + non-functional should be encoded in the image itself rather than passed with the VendorCode. + AbortReason enables vendor to have the option to provide a more detailed description of the abort + reason to the caller. + + @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. + @param[in] ImageIndex A unique number identifying the firmware image(s) within the device. + The number is between 1 and DescriptorCount. + @param[in] Image Points to the new image. + @param[in] ImageSize Size of the new image in bytes. + @param[in] VendorCode This enables vendor to implement vendor-specific firmware image update policy. + Null indicates the caller did not specify the policy or use the default policy. + @param[in] Progress A function used by the driver to report the progress of the firmware update. + @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more + details for the aborted operation. The buffer is allocated by this function + with AllocatePool(), and it is the caller's responsibility to free it with a + call to FreePool(). + + @retval EFI_SUCCESS The device was successfully updated with the new image. + @retval EFI_ABORTED The operation is aborted. + @retval EFI_INVALID_PARAMETER The Image was NULL. + @retval EFI_UNSUPPORTED The operation is not supported. + @retval EFI_SECURITY_VIOLATION The operation could not be performed due to an authentication failure. + +**/ +EFI_STATUS +EFIAPI +CxlFirmwareMgmtSetImage ( + IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, + IN UINT8 ImageIndex, + IN CONST VOID *Image, + IN UINTN ImageSize, + IN CONST VOID *VendorCode, + IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress, + OUT CHAR16 **AbortReason + ) +{ + EFI_STATUS Status; + CXL_CONTROLLER_PRIVATE_DATA *Private = NULL; + UINT32 Written; + + Private = CXL_CONTROLLER_PRIVATE_FROM_FIRMWARE_MGMT(This); + if (ImageIndex > Private->SlotInfo.NumberOfSlots) { + DEBUG((EFI_D_ERROR, "CxlFirmwareMgmtSetImage: ImageIndex = %d is greater then Total slots = %d \n", + ImageIndex, Private->SlotInfo.NumberOfSlots)); + Status = EFI_INVALID_PARAMETER; + return Status; + } + + Status = CxlMemTransferFw(Private, ImageIndex, Image, 0, ImageSize, &Written); + + if (Status != EFI_SUCCESS) { + DEBUG((EFI_D_ERROR, "CxlFirmwareMgmtSetImage: UEFI Driver Transfer FW (Opcode 0201h) Failed!\n")); + return Status; + } + + Private->SlotInfo.ImageFileBuffer[ImageIndex] = AllocateZeroPool(ImageSize); + if (Private->SlotInfo.ImageFileBuffer[ImageIndex] == NULL) { + DEBUG((EFI_D_ERROR, "CxlFirmwareMgmtSetImage: AllocateZeroPool failed!\n")); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Private->SlotInfo.ImageFileSize[ImageIndex] = ImageSize; + CopyMem(Private->SlotInfo.ImageFileBuffer[ImageIndex], Image, sizeof(ImageSize)); + Private->SlotInfo.IsSetImageDone[ImageIndex] = TRUE; + + DEBUG((EFI_D_INFO, "[CxlFirmwareMgmtSetImage] SetImage returns...%r\n", Status)); + return Status; +} + +/** + Checks if the firmware image is valid for the device. + + This function allows firmware update application to validate the firmware image without + invoking the SetImage() first. + + @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. + @param[in] ImageIndex A unique number identifying the firmware image(s) within the device. + The number is between 1 and DescriptorCount. + @param[in] Image Points to the new image. + @param[in] ImageSize Size of the new image in bytes. + @param[out] ImageUpdatable Indicates if the new image is valid for update. It also provides, + if available, additional information if the image is invalid. + + @retval EFI_UNSUPPORTED The operation is not supported. + +**/ +EFI_STATUS +EFIAPI +CxlFirmwareMgmtCheckImage ( + IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, + IN UINT8 ImageIndex, + IN CONST VOID *Image, + IN UINTN ImageSize, + OUT UINT32 *ImageUpdatable + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Returns information about the firmware package. + + This function returns package information. + + @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. + @param[out] PackageVersion A version number that represents all the firmware images in the device. + The format is vendor specific and new version must have a greater value + than the old version. If PackageVersion is not supported, the value is + 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version + comparison is to be performed using PackageVersionName. A value of + 0xFFFFFFFD indicates that package version update is in progress. + @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing + the package version name. The buffer is allocated by this function with + AllocatePool(), and it is the caller's responsibility to free it with a + call to FreePool(). + @param[out] PackageVersionNameMaxLen The maximum length of package version name if device supports update of + package version name. A value of 0 indicates the device does not support + update of package version name. Length is the number of Unicode characters, + including the terminating null character. + @param[out] AttributesSupported Package attributes that are supported by this device. See 'Package Attribute + Definitions' for possible returned values of this parameter. A value of 1 + indicates the attribute is supported and the current setting value is + indicated in AttributesSetting. A value of 0 indicates the attribute is not + supported and the current setting value in AttributesSetting is meaningless. + @param[out] AttributesSetting Package attributes. See 'Package Attribute Definitions' for possible returned + values of this parameter + + @retval EFI_SUCCESS The package information was successfully returned. + @retval EFI_UNSUPPORTED The operation is not supported. + +**/ +EFI_STATUS +EFIAPI +CxlFirmwareMgmtGetPackageInfo ( + IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, + OUT UINT32 *PackageVersion, + OUT CHAR16 **PackageVersionName, + OUT UINT32 *PackageVersionNameMaxLen, + OUT UINT64 *AttributesSupported, + OUT UINT64 *AttributesSetting + ) +{ + DEBUG((EFI_D_INFO, "CxlFirmwareMgmtGetPackageInfo: Entering CxlFirmwareMgmtGetPackageInfo...\n")); + CXL_CONTROLLER_PRIVATE_DATA *Private; + Private = CXL_CONTROLLER_PRIVATE_FROM_FIRMWARE_MGMT(This); + + if (Private->PackageVersionName != NULL) { + StrCpyS(*PackageVersionName, CXL_STRING_BUFFER_WIDTH, Private->PackageVersionName); + } + + *PackageVersion = Private->PackageVersion; + *AttributesSupported = Private->SlotInfo.FwImageDescriptor[0].AttributesSupported; + *AttributesSetting = Private->SlotInfo.FwImageDescriptor[0].AttributesSetting; + + return EFI_SUCCESS; +} + +/** + Updates information about the firmware package. + + This function updates package information. + This function returns EFI_UNSUPPORTED if the package information is not updatable. + VendorCode enables vendor to implement vendor-specific package information update policy. + Null if the caller did not specify this policy or use the default policy. + + @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. + @param[in] Image Points to the authentication image. + Null if authentication is not required. + @param[in] ImageSize Size of the authentication image in bytes. + 0 if authentication is not required. + @param[in] VendorCode This enables vendor to implement vendor-specific firmware + image update policy. + Null indicates the caller did not specify this policy or use + the default policy. + @param[in] PackageVersion The new package version. + @param[in] PackageVersionName A pointer to the new null-terminated Unicode string representing + the package version name. + The string length is equal to or less than the value returned in + PackageVersionNameMaxLen. + + @retval EFI_SUCCESS The device was successfully updated with the new package + information. + @retval EFI_INVALID_PARAMETER The PackageVersionName length is longer than the value + returned in PackageVersionNameMaxLen. + @retval EFI_UNSUPPORTED The operation is not supported. + @retval EFI_SECURITY_VIOLATION The operation could not be performed due to an authentication failure. + +**/ +EFI_STATUS +EFIAPI +CxlFirmwareMgmtSetPackageInfo ( + IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, + IN CONST VOID *Image, + IN UINTN ImageSize, + IN CONST VOID *VendorCode, + IN UINT32 PackageVersion, + IN CONST CHAR16 *PackageVersionName + ) +{ + DEBUG((EFI_D_INFO, "CxlFirmwareMgmtSetPackageInfo: Entering CxlFirmwareMgmtSetPackageInfo...\n")); + CXL_CONTROLLER_PRIVATE_DATA *Private; + Private = CXL_CONTROLLER_PRIVATE_FROM_FIRMWARE_MGMT(This); + StrCpyS(Private->PackageVersionName, CXL_STRING_BUFFER_WIDTH, PackageVersionName); + Private->PackageVersion = PackageVersion; + return EFI_SUCCESS; +} + +// +// Template of the private context structure for the Firmware Management Protocol instance +// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_FIRMWARE_MANAGEMENT_PROTOCOL gCxlFirmwareManagement = { + CxlFirmwareMgmtGetImageInfo, + CxlFirmwareMgmtGetImage, + CxlFirmwareMgmtSetImage, + CxlFirmwareMgmtCheckImage, + CxlFirmwareMgmtGetPackageInfo, + CxlFirmwareMgmtSetPackageInfo +}; + diff --git a/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxeUtil.c b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxeUtil.c new file mode 100644 index 000000000000..0f40751585da --- /dev/null +++ b/MdeModulePkg/Bus/Pci/CxlDxe/CxlDxeUtil.c @@ -0,0 +1,419 @@ +/** @file + CxlDxe driver utility file + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "CxlDxe.h" + +/** + Returns minimum among the input values + + @param[in] ValOne Input value one + @param[in] ValTwo Input value Two + @param[in] ValThree Input value Three + + @retval Minimum Returns minimum value among the given input values + + **/ +inline +UINT64 +MinimumOfThreeValues ( + UINT64 ValOne, + UINT64 ValTwo, + UINT64 ValThree + ) +{ + UINT64 Minimum = MIN(ValOne, MIN(ValTwo, ValThree)); + return Minimum; +} + +/** + Returns the number of chunk from firmware file, FW Transfer should take less time, therefore chunk Size is maximum + + @param[in] FileSize Size of Firmware file + @param[in] MaxPayloadSize Maximum Payload Size supported by mailbox + + @retval ChunkCount Number of Chunks of perticular size + @retval ChunkSize Chunks size to be transferred + + **/ +void +GetChunkCount ( + int FileSize, + int MaxPayloadSize, + int *ChunkCount, + int *ChunkSize + ) +{ + /******************************************************************************* + * FW Transfer should take less time, so we made chunk Size maximum + * else chunk Size can be CXL_FW_TRANSFER_ALIGNMENT + ********************************************************************************/ + + int Count; + int Size; + + Count = 0; + Size = 0; + + if (MaxPayloadSize % CXL_FW_TRANSFER_ALIGNMENT == 0) { + Size = MaxPayloadSize; + } else { + Size = (MaxPayloadSize - (MaxPayloadSize % CXL_FW_TRANSFER_ALIGNMENT)); + } + + Count = (FileSize / Size); + if (FileSize % Size) { + //Add 1 to Count for remaining chunk + Count = Count + 1; + } + + *ChunkCount = Count; + *ChunkSize = Size; +} + +/** + Returns bits value from input value + + @param[in] RegisterValue Input register value from where bits has to extracted + @param[in] PositionOne starting bits position + @param[in] PositionTwo ending bits position + + @retval LastPositionBits Value of bits from position one to two + + **/ +UINT64 +GetFieldValues ( + UINT64 RegisterValue, + UINT32 PositionOne, + UINT32 PositionTwo + ) +{ + UINT32 Position = PositionOne - PositionTwo + 1; //Num of bits + RegisterValue = RegisterValue >> PositionTwo; //Right shift to make P2 position as 0 bit position + UINT64 Mask = (1 << Position) - 1; //Crate mask + UINT64 LastPositionBits = RegisterValue & Mask; + return LastPositionBits; +} + +/** + Initialize Firmware Image Descriptor with default values, which were to be updated in later function calls + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + + @retval void No value is returned + + **/ +void +InitializeFwImageDescriptor ( + CXL_CONTROLLER_PRIVATE_DATA *Private + ) +{ + for (int Index = 0; Index < Private->SlotInfo.NumberOfSlots; Index++) { + Private->SlotInfo.FwImageDescriptor[Index].ImageIndex = Index; /* This should start from 1 */ + Private->SlotInfo.FwImageDescriptor[Index].ImageId = CXL_FW_IMAGE_ID; + Private->SlotInfo.FwImageDescriptor[Index].ImageIdName = CXL_FIRMWARE_IMAGE_ID_NAME; + Private->SlotInfo.FwImageDescriptor[Index].Version = CXL_FW_VERSION; + + if (Private->SlotInfo.FirmwareVersion[Index][0] != '\0') { + Private->SlotInfo.FwImageDescriptor[Index].VersionName = AllocateZeroPool(CXL_FW_REVISION_LENGTH_IN_BYTES); + if (Private->SlotInfo.FwImageDescriptor[Index].VersionName == NULL) { + DEBUG((EFI_D_ERROR, "InitializeFwImageDescriptor: AllocateZeroPool failed!\n")); + return; + } + + StrnCpyS ( + Private->SlotInfo.FwImageDescriptor[Index].VersionName, + CXL_FW_REVISION_LENGTH_IN_BYTES + 1, + Private->SlotInfo.FirmwareVersion[Index], + StrLen(Private->SlotInfo.FirmwareVersion[Index]) + ); + } + + Private->SlotInfo.FwImageDescriptor[Index].Size = Private->SlotInfo.ImageFileSize[Index]; + Private->SlotInfo.FwImageDescriptor[Index].AttributesSupported = 1; + Private->SlotInfo.FwImageDescriptor[Index].AttributesSetting = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED; + Private->SlotInfo.FwImageDescriptor[Index].Compatibilities = IMAGE_COMPATIBILITY_CHECK_SUPPORTED; + Private->SlotInfo.FwImageDescriptor[Index].LowestSupportedImageVersion = 0; + Private->SlotInfo.FwImageDescriptor[Index].LastAttemptVersion = 0; + Private->SlotInfo.FwImageDescriptor[Index].LastAttemptStatus = EFI_SUCCESS; + } +} + +/** + Reads EFI PCI i/o protocol values + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS +PciUefiReadConfigWord ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 Start, + UINT32 *Value + ) +{ + EFI_STATUS Status; + UINT32 Offset; + + Offset = Start; + + Status = Private->PciIo->Pci.Read( + Private->PciIo, + EfiPciIoWidthUint32, + Offset, + 1, + Value + ); + + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "[%a]: Failed to read PCI IO for Ext. capability\n", __func__)); + } + return Status; +} + +/** + Reads EFI PCI i/o protocol values of thirty two bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS +PciUefiMemReadThirtyTwoBits ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 Start, + UINT32 *Value + ) +{ + EFI_STATUS Status; + UINT32 BarIndex; + UINT32 ReadValue; + + BarIndex = Private->RegisterMap.BaseAddressRegister; + ReadValue = 0; + + Status = Private->PciIo->Mem.Read( + Private->PciIo, + EfiPciIoWidthUint32, + BarIndex, + Start, + 1, + &ReadValue + ); + + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "[%a]: Failed to read PCI Mem\n", __func__)); + return Status; + } + + *Value = ReadValue; + return Status; +} + +/** + Reads EFI PCI i/o protocol values of sixty four bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS +PciUefiMemReadSixtyFourBits ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 Start, + UINT64 *Value + ) +{ + EFI_STATUS Status; + UINT32 BarIndex; + UINT64 ReadValue; + + BarIndex = Private->RegisterMap.BaseAddressRegister; + ReadValue = 0; + + Status = Private->PciIo->Mem.Read( + Private->PciIo, + EfiPciIoWidthUint64, + BarIndex, + Start, + 1, + &ReadValue + ); + + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "[%a]: Failed to read PCI Mem\n", __func__)); + return Status; + } + + *Value = ReadValue; + return Status; +} + +/** + Reads EFI PCI i/o protocol values of N bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS +PciUefiMemReadNBits ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 Start, + CHAR8 Buffer[], + UINT32 Size) +{ + EFI_STATUS Status; + UINT32 BarIndex; + UINT32 Offset; + + BarIndex = Private->RegisterMap.BaseAddressRegister; + Offset = Start; + + for (int Index = 0; Index < Size; Index++) { + Status = Private->PciIo->Mem.Read( + Private->PciIo, + EfiPciIoWidthUint8, + BarIndex, + Offset, + 1, + &Buffer[Index] + ); + + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "[%a]: Read err in Buffer[%d] \n", __func__, Index)); + break; + } + Offset += 1; + } + return Status; +} + +/** + Write EFI PCI i/o protocol values of thirty two bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS +PciUefiMemWriteThirtyTwoBits ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 Start, + UINT32 *Value) +{ + EFI_STATUS Status; + UINT32 BarIndex; + UINT32 WriteValue; + + BarIndex = Private->RegisterMap.BaseAddressRegister; + WriteValue = *Value; + + Status = Private->PciIo->Mem.Write( + Private->PciIo, + EfiPciIoWidthUint32, + BarIndex, + Start, + 1, + &WriteValue + ); + + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "[%a]: Failed to write PCI Mem\n", __func__)); + } + return Status; +} + +/** + Write EFI PCI i/o protocol values of sixty four bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS +PciUefiMemWriteSixtyFourBits ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 Start, + UINT64 *Value + ) +{ + EFI_STATUS Status; + UINT32 BarIndex; + UINT64 WriteValue; + + BarIndex = Private->RegisterMap.BaseAddressRegister; + WriteValue = *Value; + + Status = Private->PciIo->Mem.Write( + Private->PciIo, + EfiPciIoWidthUint64, + BarIndex, + Start, + 1, + &WriteValue + ); + + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "[%a]: Failed to write PCI Mem\n", __func__)); + } + return Status; +} + +/** + Write EFI PCI i/o protocol values of N bits + + @param Private The pointer to the CXL_CONTROLLER_PRIVATE_DATA data structure. + @param[in] Start starting bits position + + @retval Value of PCI IO for Extended capability + + **/ +EFI_STATUS +PciUefiMemWriteNBits ( + CXL_CONTROLLER_PRIVATE_DATA *Private, + UINT32 Start, + CHAR8 Buffer[], + UINT32 Size + ) +{ + EFI_STATUS Status; + UINT32 BarIndex; + UINT32 Offset; + + BarIndex = Private->RegisterMap.BaseAddressRegister; + Offset = Start; + + for (int Index = 0; Index < Size; Index++) { + Status = Private->PciIo->Mem.Write( + Private->PciIo, + EfiPciIoWidthUint8, + BarIndex, + Offset, + 1, + &Buffer[Index] + ); + + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "[%a]: Read err in Buffer[%d] \n", __func__, Index)); + break; + } + Offset += 1; + } + return Status; +} + diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index ebceafcdd0ff..f0ade0c9351e 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -206,6 +206,7 @@ [Components] MdeModulePkg/Application/HelloWorld/HelloWorld.inf + MdeModulePkg/Application/CxlFwMgmtApp/CxlFwMgmtApp.inf MdeModulePkg/Application/DumpDynPcd/DumpDynPcd.inf MdeModulePkg/Application/MemoryProfileInfo/MemoryProfileInfo.inf @@ -234,6 +235,7 @@ MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf + MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.inf MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf diff --git a/MdePkg/Include/IndustryStandard/Cxl20.h b/MdePkg/Include/IndustryStandard/Cxl20.h index 574f78688180..f1f35fe12bc3 100755 --- a/MdePkg/Include/IndustryStandard/Cxl20.h +++ b/MdePkg/Include/IndustryStandard/Cxl20.h @@ -102,6 +102,136 @@ #define CXL_MEM_DEVICE_MEDIA_STATUS_ERROR 0x2 #define CXL_MEM_DEVICE_MEDIA_STATUS_DISABLED 0x3 +// +// Register Locator DVSEC +// Compute Express Link Specification Revision 2.0 - Chapter 8.1.9 +// +#define CXL_PCIE_EXTENDED_CAP_OFFSET 0x100 +#define CXL_PCIE_EXTENDED_NEXT_CAP_OFFSET_SHIFT 20 +#define CXL_PCI_CFG_SPACE_SIZE 256 +#define CXL_PCI_CFG_SPACE_EXP_SIZE 4096 +#define CXL_PCI_DVSEC_HEADER1 0x4 /* Designated Vendor-Specific Header1 */ +#define CXL_PCI_DVSEC_HEADER2 0x8 /* Designated Vendor-Specific Header2 */ +#define CXL_PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */ +#define CXL_DVSEC_REG_LOCATOR_BLOCK1_OFFSET 0xC +#define CXL_PCI_DVSEC_VENDOR_ID 0x1E98 + +// +// Transfer FW +// Compute Express Link Specification Revision 2.0 - Chapter 8.2.9.2.2 +// +#define CXL_FW_TRANSFER_ALIGNMENT 128 +#define CXL_FW_TRANSFER_ACTION_FULL 0x0 +#define CXL_FW_TRANSFER_ACTION_INITIATE 0x1 +#define CXL_FW_TRANSFER_ACTION_CONTINUE 0x2 +#define CXL_FW_TRANSFER_ACTION_END 0x3 +#define CXL_FW_TRANSFER_ACTION_ABORT 0x4 + +// +// Activate FW +// Compute Express Link Specification Revision 2.0 - Chapter 8.2.9.2.3 +// +#define CXL_FW_ACTIVATE_METHOD_ONLINE 0x0 +#define CXL_FW_ACTIVATE_METHOD_ON_NEXT_COLD_RESET 0x1 + +// +// Get FW Info +// Compute Express Link Specification Revision 2.0 - Chapter 8.2.9.2.1 +// +#define CXL_FW_MAX_SLOTS 5 +#define CXL_FW_IMAGE_DESCRIPTOR_COUNT 5 + +// +// Mailbox Registers +// Compute Express Link Specification Revision 2.0 - Chapter 8.2.8.4 +// +#define CXL_DEV_MBOX_CAPS_OFFSET 0x00 +#define CXL_DEV_MBOX_CTRL_OFFSET 0x04 +#define CXL_DEV_MBOX_CMD_OFFSET 0x08 +#define CXL_DEV_MBOX_STATUS_OFFSET 0x10 +#define CXL_DEV_MBOX_BG_CMD_STATUS_OFFSET 0x18 +#define CXL_DEV_MBOX_PAYLOAD_OFFSET 0x20 +#define CXL_MAILBOX_TIMEOUT_MS 2000 + +// +// Command Return Codes +// Compute Express Link Specification Revision 2.0 - Chapter 8.2.8.4.5.1 +// +#define CXL_MBOX_CMD_RC_SUCCESS 0 +#define CXL_MBOX_CMD_RC_BACKGROUND 1 +#define CXL_MBOX_CMD_INVALID_INPUT 2 +#define CXL_MBOX_CMD_UNSUPPORTED 3 +#define CXL_MBOX_CMD_INTERNAL_ERROR 4 +#define CXL_MBOX_CMD_RETRY_REQUIRED 5 +#define CXL_MBOX_CMD_BUSY 6 +#define CXL_MBOX_CMD_MEDIA_DISABLED 7 +#define CXL_MBOX_CMD_FW_TRANSFER_IN_PROGRESS 8 +#define CXL_MBOX_CMD_FW_TRANSFER_OUT_OF_ORDER 9 +#define CXL_MBOX_CMD_FW_VERIFICATION_FAILED 10 +#define CXL_MBOX_CMD_INVALID_SLOT 11 +#define CXL_MBOX_CMD_ACTIVATION_FAILED_FW_ROLLED_BACK 12 +#define CXL_MBOX_CMD_COLD_RESET_REQUIRED 13 +#define CXL_MBOX_CMD_INVALID_HANDLE 14 +#define CXL_MBOX_CMD_INVALID_PHYSICAL_ADDRESS 15 +#define CXL_MBOX_CMD_INJECT_POISON_LIMIT_REACHED 16 +#define CXL_MBOX_CMD_PERMANENT_MEDIA_FAILURE 17 +#define CXL_MBOX_CMD_ABORTED 18 +#define CXL_MBOX_CMD_INVALID_SECURITY_STATE 19 +#define CXL_MBOX_CMD_INCORRECT_PASSPHRASE 20 +#define CXL_MBOX_CMD_UNSUPPORTED_MAILBOX 21 +#define CXL_MBOX_CMD_INVALID_PAYLOAD_LENGTH 22 + +// +// Register Locator DVSEC +// Compute Express Link Specification Revision 2.0 - Chapter 8.1.9 +// +typedef enum { + PcieExtCapHeader = 0, + PcieDvsecHeader1, + PcieDvsecHeader2, + PcieDvsecHeaderMax +} CXL_PCIE_DVSEC_HEADER_ENUM; + +// +// Register Block Identifier - Identifies the type of CXL registers. +// Compute Express Link Specification Revision 2.0 - Chapter 8.1.9.1 +// +typedef enum { + CxlRbiEmpty = 0, + CxlRbiComponent, + CxlRbiVirt, + CxlRbiMemdev, + CxlRbiMax +} CXL_REG_BLOCK_IDENTIFIER; + +// +// CXL Device Command Opcodes. +// Compute Express Link Specification Revision 2.0 - Chapter 8.2.9 +// +typedef enum { + CxlMboxOpInvalid = 0x0000, + CxlMboxOpGetFwInfo = 0x0200, + CxlMboxOpTransferFw = 0x0201, + CxlMboxOpActivateFw = 0x0202, + CxlMboxOpMax = 0x10000 +} CXL_OPCODE; + +// +// CXL Device Mailbox Registers +// Compute Express Link Specification Revision 2.0 - Chapter 8.2.8.4 +// +typedef struct { + UINT16 Opcode; + void *InputPayload; + void *OutputPayload; + UINT64 InputSize; + UINT64 OutputSize; + UINT64 MinimumOutput; + UINT32 poll_count; + UINT32 PollInterval; + UINT16 ReturnCode; +} CXL_MBOX_CMD; + // // Ensure proper structure formats // @@ -458,6 +588,35 @@ typedef union { UINT64 Uint64; } CXL_MEMORY_DEVICE_STATUS_REGISTER; +// +// Firmware Update +// Compute Express Link Specification Revision 2.0 - Chapter 8.2.9.2 +// +typedef struct { + UINT8 NumberOfSlots; + UINT8 SlotInfo; + UINT8 ActivationCapabilities; + UINT8 Reserved[13]; + char SlotOneFwRevision[16]; + char SlotTwoFwRevision[16]; + char SlotThreeFwRevision[16]; + char SlotFourFwRevision[16]; +} CXL_MAILBOX_GET_FW_INFO; + +typedef struct { + UINT8 Action; + UINT8 Slot; + UINT8 Reserved[2]; + UINT32 Offset; + UINT8 Reserved2[0x78]; + UINT8 Data[]; +} CXL_MAILBOX_TRANSFER_FW; + +typedef struct { + UINT8 Action; + UINT8 Slot; +} CXL_MAILBOX_ACTIVATE_FW; + #pragma pack() #endif diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 1305dab60da7..6d8310a779b6 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -912,6 +912,7 @@ MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf + MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.inf MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index 9405cd732762..528075ff9920 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -300,6 +300,7 @@ INF MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf INF MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf INF MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf INF MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf +INF MdeModulePkg/Bus/Pci/CxlDxe/CxlDxe.inf INF MdeModulePkg/Bus/Pci/SataControllerDxe/SataControllerDxe.inf INF MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf INF MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf