/** @file Get BIOS version, product name and CCB version from BVDT region. ;****************************************************************************** ;* Copyright (c) 2012 - 2020, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include #include #include #include #include #include /** Converts ASCII characters to Unicode. @param UnicodeStr The Unicode string to be written to. The buffer must be large enough. @param AsciiStr The ASCII string to be converted. @return The address to the Unicode string - same as UnicodeStr. **/ STATIC CHAR16 * BvdtLibAscii2Unicode ( OUT CHAR16 *UnicodeStr, IN CHAR8 *AsciiStr ) { CHAR16 *Str; Str = UnicodeStr; while (TRUE) { *(UnicodeStr++) = (CHAR16) *AsciiStr; if (*(AsciiStr++) == '\0') { return Str; } } } /** Compare the ASCII strings in length. @param String - Compare to String2 @param String2 - Compare to String @param Length - Number of ASCII characters to compare @return == 0 - The substring of String and String2 is identical. > 0 - The substring of String sorts lexicographically after String2 < 0 - The substring of String sorts lexicographically before String2 **/ STATIC INTN BvdtLibAsciiStrnCmp ( IN CHAR8 *String, IN CHAR8 *String2, IN UINTN Length ) { if (Length == 0) { return 0; } while ((*String != '\0') && (*String == *String2) && (Length > 1)) { String++; String2++; Length--; } return *String - *String2; } /** Converts an 8-bit BCD value to an 8-bit value. @param[in] Value The 8-bit BCD value to convert to an 8-bit value. @return The 8-bit value is returned. **/ STATIC UINT8 BcdToDecimal ( IN UINT8 Value ) { ASSERT (Value < 0xa0); ASSERT ((Value & 0xf) < 0xa); return (UINT8) ((Value >> 4) * 10 + (Value & 0xf)); } /** Check if it is a leap year @param[in] Year Year value @retval TRUE It is a leap year @retval FALSE It is NOT a leap year **/ STATIC BOOLEAN IsLeapYear ( IN UINT16 Year ) { if (Year % 4 == 0) { if (Year % 100 == 0) { if (Year % 400 == 0) { return TRUE; } else { return FALSE; } } else { return TRUE; } } else { return FALSE; } } /** Check if Day field of input EFI time is valid @param[in] EfiTime The Day field of input EFI time is to be checked @retval TRUE Day field of input EFI time is valid. @retval FALSE Day field of input EFI time is NOT valid. **/ STATIC BOOLEAN IsDayValid ( IN EFI_TIME *EfiTime ) { UINT8 DayOfMonth[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if (EfiTime->Month < 1 || EfiTime->Month > 12) { return FALSE; } if (EfiTime->Day < 1 || EfiTime->Day > DayOfMonth[EfiTime->Month - 1] || (EfiTime->Month == 2 && (!IsLeapYear (EfiTime->Year) && EfiTime->Day > 28))) { return FALSE; } return TRUE; } /** Check if input EFI time is valid @param[in] EfiTime The EFI time is to be checked @retval TRUE Input EFI time is valid. @retval FALSE Input EFI time is NOT valid. **/ STATIC EFI_STATUS IsEfiTimeValid ( IN EFI_TIME *EfiTime ) { if (EfiTime->Year < 1900 || EfiTime->Year > 9999 || EfiTime->Month < 1 || EfiTime->Month > 12 || (!IsDayValid (EfiTime)) || EfiTime->Hour > 23 || EfiTime->Minute > 59 || EfiTime->Second > 59) { return FALSE; } return TRUE; } /** Get BVDT date data @param[in] BvdtDatePtr Pointer to BVDT date data @param[out] EfiTime A pointer to storage BVDT date in EFI time structure @retval EFI_SUCCESS Successfully get BVDT date data @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ STATIC EFI_STATUS GetBvdtDate ( IN UINT8 *BvdtDatePtr, OUT EFI_TIME *EfiTime ) { if (BvdtDatePtr == NULL || EfiTime == NULL) { return EFI_INVALID_PARAMETER; } EfiTime->Year = (UINT16) BcdToDecimal (BvdtDatePtr[0]) + 2000; EfiTime->Month = BcdToDecimal (BvdtDatePtr[1]); EfiTime->Day = BcdToDecimal (BvdtDatePtr[2]); return EFI_SUCCESS; } /** Get BVDT time data @param[in] BvdtTimePtr Pointer to BVDT time data @param[out] EfiTime A pointer to storage BVDT time in EFI time structure @retval EFI_SUCCESS Successfully get BVDT time data @retval EFI_INVALID_PARAMETER Input parameter is NULL **/ STATIC EFI_STATUS GetBvdtTime ( IN UINT8 *BvdtTimePtr, OUT EFI_TIME *EfiTime ) { if (BvdtTimePtr == NULL || EfiTime == NULL) { return EFI_INVALID_PARAMETER; } EfiTime->Hour = BcdToDecimal (BvdtTimePtr[0]); EfiTime->Minute = BcdToDecimal (BvdtTimePtr[1]); EfiTime->Second = BcdToDecimal (BvdtTimePtr[2]); return EFI_SUCCESS; } /** Get the pointer of BIOS release date infomation from BVDT @return The pointer of BIOS release date infomation or NULL if not found **/ STATIC UINT8 * GetBiosReleaseDatePtr ( VOID ) { UINT8 RelaseDateTag[] = RELEASE_DATE_TAG; UINT8 *Bvdt; UINTN Index; UINT64 BvdtSize; BvdtSize = FdmGetNAtSize(&gH2OFlashMapRegionBvdtGuid, 1); Bvdt = (UINT8 *)(UINTN)FdmGetNAtAddr(&gH2OFlashMapRegionBvdtGuid, 1); if (Bvdt == NULL) { return NULL; } // // Search for "$RDATE" from BVDT dynamic signature start // for (Index = MULTI_BIOS_VERSION_OFFSET; Index < BvdtSize; Index++) { if (CompareMem(Bvdt + Index, RelaseDateTag, sizeof(RelaseDateTag)) == 0) { return (Bvdt + Index + sizeof (RelaseDateTag)); } } return NULL; } /** Get BIOS version, product name, CCB version, multiple BIOS version, multiple product name or multiple CCB verion from BVDT region. @param[in] Type Information type of BVDT. @param[in, out] StrBufferLen Input : string buffer length Output: length of BVDT information string. @param[out] StrBuffer BVDT information string. @retval EFI_SUCCESS Successly get string. @retval EFI_BUFFER_TOO_SMALL Buffer was too small. The current length of information string needed to hold the string is returned in BufferSize. @retval EFI_INVALID_PARAMETER Input invalid type of BVDT information. BufferSize or Buffer is NULL. @retval EFI_NOT_FOUND Can not find information of multiple version, multiple product name or multiple CCB verion or BVDT build time is invalid **/ EFI_STATUS EFIAPI GetBvdtInfo ( IN BVDT_TYPE Type, IN OUT UINTN *StrBufferLen, OUT CHAR16 *StrBuffer ) { UINTN StrLen; UINT64 BvdtAddr; CHAR8 *StrPtr; CHAR8 MultiBiosVerSignature[] = {'$', 'V', 'E', 'R', 'E', 'X'}; CHAR8 MultiProductNameSignature[] = {'$', 'P', 'R', 'O', 'D', 'E', 'X'}; CHAR8 MultiCcbVerSignature[] = {'$', 'C', 'C', 'B', 'V', 'E', 'X'}; CHAR8 AsciiStrBuffer[BVDT_MAX_STR_SIZE]; EFI_TIME EfiTime; if ((StrBuffer == NULL) || (StrBufferLen == NULL)) { return EFI_INVALID_PARAMETER; } BvdtAddr = FdmGetNAtAddr(&gH2OFlashMapRegionBvdtGuid, 1); if (BvdtAddr == 0) { return EFI_NOT_FOUND; } switch (Type) { case BvdtBuildDate: case BvdtBuildTime: ZeroMem (&EfiTime, sizeof (EfiTime)); GetBvdtDate ((UINT8 *) (UINTN) (BvdtAddr + BIOS_BUILD_DATE_OFFSET), &EfiTime); GetBvdtTime ((UINT8 *) (UINTN) (BvdtAddr + BIOS_BUILD_TIME_OFFSET), &EfiTime); if (!IsEfiTimeValid (&EfiTime)) { return EFI_NOT_FOUND; } if (Type == BvdtBuildDate) { AsciiSPrint (AsciiStrBuffer, sizeof (AsciiStrBuffer), "%02d/%02d/%04d", EfiTime.Month, EfiTime.Day, EfiTime.Year); } else { AsciiSPrint (AsciiStrBuffer, sizeof (AsciiStrBuffer), "%02d:%02d:%02d", EfiTime.Hour, EfiTime.Minute, EfiTime.Second); } StrPtr = AsciiStrBuffer; break; case BvdtBiosVer: StrPtr = (CHAR8 *) (UINTN) (BvdtAddr + BIOS_VERSION_OFFSET); break; case BvdtProductName: StrPtr = (CHAR8 *) (UINTN) (BvdtAddr + PRODUCT_NAME_OFFSET); break; case BvdtCcbVer: StrPtr = (CHAR8 *) (UINTN) (BvdtAddr + CCB_VERSION_OFFSET); break; case BvdtMultiBiosVer: StrPtr = (CHAR8 *) (UINTN) (BvdtAddr + MULTI_BIOS_VERSION_OFFSET); if (BvdtLibAsciiStrnCmp (StrPtr, MultiBiosVerSignature, sizeof (MultiBiosVerSignature))) { return EFI_NOT_FOUND; } StrPtr = (CHAR8 *) ((UINTN) StrPtr + sizeof (MultiBiosVerSignature)); break; case BvdtMultiProductName: StrPtr = (CHAR8 *) (UINTN) (BvdtAddr + MULTI_BIOS_VERSION_OFFSET); if (!BvdtLibAsciiStrnCmp (StrPtr, MultiBiosVerSignature, sizeof (MultiBiosVerSignature))) { StrPtr = (CHAR8 *) ((UINTN) StrPtr + AsciiStrSize (StrPtr)); } if (BvdtLibAsciiStrnCmp (StrPtr, MultiProductNameSignature, sizeof (MultiProductNameSignature))) { return EFI_NOT_FOUND; } StrPtr = (CHAR8 *) ((UINTN) StrPtr + sizeof (MultiProductNameSignature)); break; case BvdtMultiCcbVer: StrPtr = (CHAR8 *) (UINTN) (BvdtAddr + MULTI_BIOS_VERSION_OFFSET); if (!BvdtLibAsciiStrnCmp (StrPtr, MultiBiosVerSignature, sizeof (MultiBiosVerSignature))) { StrPtr = (CHAR8 *) ((UINTN) StrPtr + AsciiStrSize (StrPtr)); } if (!BvdtLibAsciiStrnCmp (StrPtr, MultiProductNameSignature, sizeof (MultiProductNameSignature))) { StrPtr = (CHAR8 *) ((UINTN) StrPtr + AsciiStrSize (StrPtr)); } if (BvdtLibAsciiStrnCmp (StrPtr, MultiCcbVerSignature, sizeof (MultiCcbVerSignature))) { return EFI_NOT_FOUND; } StrPtr = (CHAR8 *) ((UINTN) StrPtr + sizeof (MultiCcbVerSignature)); break; case BvdtReleaseDate: ZeroMem (&EfiTime, sizeof (EfiTime)); GetBvdtDate (GetBiosReleaseDatePtr (), &EfiTime); if (!IsEfiTimeValid (&EfiTime)) { return EFI_NOT_FOUND; } AsciiSPrint (AsciiStrBuffer, sizeof (AsciiStrBuffer), "%02d/%02d/%04d", EfiTime.Month, EfiTime.Day, EfiTime.Year); StrPtr = AsciiStrBuffer; break; default: return EFI_INVALID_PARAMETER; } StrLen = AsciiStrSize (StrPtr); if (StrLen > *StrBufferLen) { *StrBufferLen = StrLen; return EFI_BUFFER_TOO_SMALL; } *StrBufferLen = StrLen; BvdtLibAscii2Unicode (StrBuffer, StrPtr); return EFI_SUCCESS; } /** Get ESRT System Firmware GUID and Version information from BVDT $ESRT tag @param[out] FirmwareGuid Pointer to the system firmware version GUID @param[out] FirmwareVersion Pointer to the system firmware version @retval EFI_SUCCESS System firmware GUID and system firmware version are successfully retrieved EFI_NOT_FOUND Unable to find system firmware GUID or system firmware version in the BVDT table **/ EFI_STATUS EFIAPI GetEsrtFirmwareInfo ( OUT EFI_GUID *FirmwareGuid, OUT UINT32 *FirmwareVersion ) { UINTN Index; UINT8 *Bvdt; UINT8 *EsrtTagPtr; UINT8 EsrtTag[] = ESRT_TAG; UINT64 BvdtSize; if (FirmwareGuid == NULL || FirmwareVersion == NULL) { return EFI_INVALID_PARAMETER; } Bvdt = (UINT8 *)(UINTN) FdmGetNAtAddr (&gH2OFlashMapRegionBvdtGuid, 1); if (Bvdt == NULL) { return EFI_NOT_FOUND; } BvdtSize = FdmGetNAtSize (&gH2OFlashMapRegionBvdtGuid, 1); // // Search for "$ESRT" from BVDT dynamic signature start // for (Index = MULTI_BIOS_VERSION_OFFSET; Index < BvdtSize; Index++) { if (CompareMem(Bvdt + Index, EsrtTag, sizeof(EsrtTag)) == 0) { EsrtTagPtr = Bvdt + Index; *FirmwareVersion = *(UINT32 *)(EsrtTagPtr + sizeof(EsrtTag)); *FirmwareGuid = *(EFI_GUID *)(EsrtTagPtr + sizeof(EsrtTag) + sizeof(UINT32)); return EFI_SUCCESS; } } return EFI_NOT_FOUND; }