/** Save this command in the circular history buffer. Older commands are overwritten with newer commands. @param Cmd Command line to archive the history of. @return None **/ VOID SetCmdHistory ( IN CHAR8 *Cmd ) { // Don't bother adding empty commands to the list if (AsciiStrLen(Cmd) != 0) { // First entry if (mCmdHistoryStart == -1) { mCmdHistoryStart = 0; mCmdHistoryEnd = 0; } else { // Record the new command at the next index RingBufferIncrement(&mCmdHistoryStart); // If the next index runs into the end index, shuffle end back by one if (mCmdHistoryStart == mCmdHistoryEnd) { RingBufferIncrement(&mCmdHistoryEnd); } } // Copy the new command line into the ring buffer AsciiStrnCpyS (&mCmdHistory[mCmdHistoryStart][0], MAX_CMD_LINE, Cmd, MAX_CMD_LINE); } // Reset the command history for the next up arrow press mCmdHistoryCurrent = mCmdHistoryStart; }
/** Creates a record for the beginning of a performance measurement. Creates a record that contains the Handle, Token, Module and Identifier. If TimeStamp is not zero, then TimeStamp is added to the record as the start time. If TimeStamp is zero, then this function reads the current time stamp and adds that time stamp value to the record as the start time. @param Handle Pointer to environment specific context used to identify the component being measured. @param Token Pointer to a Null-terminated ASCII string that identifies the component being measured. @param Module Pointer to a Null-terminated ASCII string that identifies the module being measured. @param TimeStamp 64-bit time stamp. @param Identifier 32-bit identifier. If the value is 0, the created record is same as the one created by StartPerformanceMeasurement. @retval RETURN_SUCCESS The start of the measurement was recorded. @retval RETURN_OUT_OF_RESOURCES There are not enough resources to record the measurement. **/ RETURN_STATUS EFIAPI StartPerformanceMeasurementEx ( IN CONST VOID *Handle, OPTIONAL IN CONST CHAR8 *Token, OPTIONAL IN CONST CHAR8 *Module, OPTIONAL IN UINT64 TimeStamp, IN UINT32 Identifier ) { PEI_PERFORMANCE_LOG_HEADER *PeiPerformanceLog; UINT32 *PeiPerformanceIdArray; PEI_PERFORMANCE_LOG_ENTRY *LogEntryArray; UINT32 Index; UINT16 PeiPerformanceLogEntries; PeiPerformanceLogEntries = (UINT16) (PcdGet16 (PcdMaxPeiPerformanceLogEntries16) != 0 ? PcdGet16 (PcdMaxPeiPerformanceLogEntries16) : PcdGet8 (PcdMaxPeiPerformanceLogEntries)); InternalGetPerformanceHobLog (&PeiPerformanceLog, &PeiPerformanceIdArray); if (PeiPerformanceLog->NumberOfEntries >= PeiPerformanceLogEntries) { DEBUG ((DEBUG_ERROR, "PEI performance log array out of resources\n")); return RETURN_OUT_OF_RESOURCES; } Index = PeiPerformanceLog->NumberOfEntries++; LogEntryArray = (PEI_PERFORMANCE_LOG_ENTRY *) (PeiPerformanceLog + 1); LogEntryArray[Index].Handle = (EFI_PHYSICAL_ADDRESS) (UINTN) Handle; if (Token != NULL) { AsciiStrnCpyS (LogEntryArray[Index].Token, PEI_PERFORMANCE_STRING_SIZE, Token, PEI_PERFORMANCE_STRING_LENGTH); } if (Module != NULL) { AsciiStrnCpyS (LogEntryArray[Index].Module, PEI_PERFORMANCE_STRING_SIZE, Module, PEI_PERFORMANCE_STRING_LENGTH); } LogEntryArray[Index].EndTimeStamp = 0; PeiPerformanceIdArray[Index] = Identifier; if (TimeStamp == 0) { TimeStamp = GetPerformanceCounter (); } LogEntryArray[Index].StartTimeStamp = TimeStamp; return RETURN_SUCCESS; }
/** This is a callback function when packets are received or transmitted in Mtftp driver. A callback function that is provided by the caller to intercept the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory(). @param[in] This Pointer to EFI_MTFTP4_PROTOCOL. @param[in] Token Pointer to EFI_MTFTP4_TOKEN. @param[in] PacketLen Length of EFI_MTFTP4_PACKET. @param[in] Packet Pointer to EFI_MTFTP4_PACKET to be checked. @retval EFI_SUCCESS The current operation succeeeded. @retval EFI_ABORTED Abort the current transfer process. **/ EFI_STATUS EFIAPI PxeBcMtftp4CheckPacket ( IN EFI_MTFTP4_PROTOCOL *This, IN EFI_MTFTP4_TOKEN *Token, IN UINT16 PacketLen, IN EFI_MTFTP4_PACKET *Packet ) { PXEBC_PRIVATE_DATA *Private; EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; EFI_STATUS Status; Private = (PXEBC_PRIVATE_DATA *) Token->Context; Callback = Private->PxeBcCallback; Status = EFI_SUCCESS; if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) { // // Store the tftp error message into mode data and set the received flag. // Private->Mode.TftpErrorReceived = TRUE; Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; AsciiStrnCpyS ( Private->Mode.TftpError.ErrorString, PXE_MTFTP_ERROR_STRING_LENGTH, (CHAR8 *) Packet->Error.ErrorMessage, PXE_MTFTP_ERROR_STRING_LENGTH - 1 ); Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; } if (Callback != NULL) { // // Callback to user if has when received any tftp packet. // Status = Callback->Callback ( Callback, Private->Function, TRUE, PacketLen, (EFI_PXE_BASE_CODE_PACKET *) Packet ); if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { // // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. // Status = EFI_ABORTED; } else { // // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. // Status = EFI_SUCCESS; } } return Status; }
/** Collect the keyboard input for a cmd line. Carriage Return, New Line, or ESC terminates the command line. You can edit the command line via left arrow, delete and backspace and they all back up and erase the command line. No edit of command line is possible without deletion at this time! The up arrow and down arrow fill Cmd with information from the history buffer. @param Cmd Command line to return @param CmdMaxSize Maximum size of Cmd @return The Status of EblGetCharKey() **/ EFI_STATUS GetCmd ( IN OUT CHAR8 *Cmd, IN UINTN CmdMaxSize ) { EFI_STATUS Status; UINTN Index; UINTN Index2; CHAR8 Char; CHAR8 *History; EFI_INPUT_KEY Key; for (Index = 0; Index < CmdMaxSize - 1;) { Status = EblGetCharKey (&Key, 0, NULL); if (EFI_ERROR (Status)) { Cmd[Index] = '\0'; AsciiPrint ("\n"); return Status; } Char = (CHAR8)Key.UnicodeChar; if ((Char == '\n') || (Char == '\r') || (Char == 0x7f)) { Cmd[Index] = '\0'; if (FixedPcdGetBool(PcdEmbeddedShellCharacterEcho) == TRUE) { AsciiPrint ("\n\r"); } return EFI_SUCCESS; } else if ((Char == '\b') || (Key.ScanCode == SCAN_LEFT) || (Key.ScanCode == SCAN_DELETE)){ if (Index != 0) { Index--; // // Update the display // AsciiPrint ("\b \b"); } } else if ((Key.ScanCode == SCAN_UP) || Key.ScanCode == SCAN_DOWN) { History = GetCmdHistory (Key.ScanCode); // // Clear display line // for (Index2 = 0; Index2 < Index; Index2++) { AsciiPrint ("\b \b"); } AsciiPrint (History); Index = AsciiStrLen (History); AsciiStrnCpyS (Cmd, CmdMaxSize, History, CmdMaxSize); } else { Cmd[Index++] = Char; if (FixedPcdGetBool(PcdEmbeddedShellCharacterEcho) == TRUE) { AsciiPrint ("%c", Char); } } } return EFI_SUCCESS; }
/** Get the file name portion of the Pdb File Name. The portion of the Pdb File Name between the last backslash and either a following period or the end of the string is copied into AsciiBuffer. The name is truncated, if necessary, to ensure that AsciiBuffer is not overrun. @param[in] PdbFileName Pdb file name. @param[out] AsciiBuffer The resultant Ascii File Name. **/ VOID GetShortPdbFileName ( IN CHAR8 *PdbFileName, OUT CHAR8 *AsciiBuffer ) { UINTN IndexPdb; // Current work location within a Pdb string. UINTN IndexBuffer; // Current work location within a Buffer string. UINTN StartIndex; UINTN EndIndex; ZeroMem (AsciiBuffer, PROFILE_NAME_STRING_LENGTH + 1); if (PdbFileName == NULL) { AsciiStrnCpyS (AsciiBuffer, PROFILE_NAME_STRING_LENGTH + 1, " ", 1); } else { StartIndex = 0; for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++); for (IndexPdb = 0; PdbFileName[IndexPdb] != 0; IndexPdb++) { if ((PdbFileName[IndexPdb] == '\\') || (PdbFileName[IndexPdb] == '/')) { StartIndex = IndexPdb + 1; } if (PdbFileName[IndexPdb] == '.') { EndIndex = IndexPdb; } } IndexBuffer = 0; for (IndexPdb = StartIndex; IndexPdb < EndIndex; IndexPdb++) { AsciiBuffer[IndexBuffer] = PdbFileName[IndexPdb]; IndexBuffer++; if (IndexBuffer >= PROFILE_NAME_STRING_LENGTH) { AsciiBuffer[PROFILE_NAME_STRING_LENGTH] = 0; break; } } } }
/** This function is to get size of a file using Tftp. @param[in] Private Pointer to PxeBc private data. @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. @param[in] Filename Pointer to boot file name. @param[in] BlockSize Pointer to required block size. @param[in, out] BufferSize Pointer to buffer size. @retval EFI_SUCCESS Successfully obtained the size of file. @retval EFI_NOT_FOUND Parse the tftp options failed. @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. @retval Others Did not obtain the size of the file. **/ EFI_STATUS PxeBcMtftp4GetFileSize ( IN PXEBC_PRIVATE_DATA *Private, IN EFI_MTFTP4_CONFIG_DATA *Config, IN UINT8 *Filename, IN UINTN *BlockSize, IN OUT UINT64 *BufferSize ) { EFI_MTFTP4_PROTOCOL *Mtftp4; EFI_MTFTP4_OPTION ReqOpt[2]; EFI_MTFTP4_PACKET *Packet; EFI_MTFTP4_OPTION *Option; UINT32 PktLen; UINT8 OptBuf[128]; UINT32 OptCnt; EFI_STATUS Status; *BufferSize = 0; Status = EFI_DEVICE_ERROR; Mtftp4 = Private->Mtftp4; Packet = NULL; Option = NULL; PktLen = 0; OptCnt = 1; Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; Status = Mtftp4->Configure (Mtftp4, Config); if (EFI_ERROR (Status)) { return Status; } // // Build the required options for get info. // ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX]; PxeBcUintnToAscDec (0, OptBuf, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); ReqOpt[0].ValueStr = OptBuf; if (BlockSize != NULL) { ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1); PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX - (AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1)); OptCnt++; } Status = Mtftp4->GetInfo ( Mtftp4, NULL, Filename, NULL, (UINT8) OptCnt, ReqOpt, &PktLen, &Packet ); if (EFI_ERROR (Status)) { if (Status == EFI_TFTP_ERROR) { // // Store the tftp error message into mode data and set the received flag. // Private->Mode.TftpErrorReceived = TRUE; Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; AsciiStrnCpyS ( Private->Mode.TftpError.ErrorString, PXE_MTFTP_ERROR_STRING_LENGTH, (CHAR8 *) Packet->Error.ErrorMessage, PXE_MTFTP_ERROR_STRING_LENGTH - 1 ); Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; } goto ON_ERROR; } // // Parse the options in the reply packet. // OptCnt = 0; Status = Mtftp4->ParseOptions ( Mtftp4, PktLen, Packet, (UINT32 *) &OptCnt, &Option ); if (EFI_ERROR (Status)) { goto ON_ERROR; } // // Parse out the value of "tsize" option. // Status = EFI_NOT_FOUND; while (OptCnt != 0) { if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) { *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr)); Status = EFI_SUCCESS; } OptCnt--; } FreePool (Option); ON_ERROR: if (Packet != NULL) { FreePool (Packet); } Mtftp4->Configure (Mtftp4, NULL); return Status; }
/** Writes performance data of booting into the allocated memory. OS can process these records. @param Event The triggered event. @param Context Context for this event. **/ VOID EFIAPI WriteBootToOsPerformanceData ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; UINT32 LimitCount; EFI_HANDLE *Handles; UINTN NoHandles; CHAR8 GaugeString[PERF_TOKEN_SIZE]; UINT8 *Ptr; UINT32 Index; UINT64 Ticker; UINT64 Freq; UINT32 Duration; UINTN LogEntryKey; CONST VOID *Handle; CONST CHAR8 *Token; CONST CHAR8 *Module; UINT64 StartTicker; UINT64 EndTicker; UINT64 StartValue; UINT64 EndValue; BOOLEAN CountUp; UINTN EntryIndex; UINTN NumPerfEntries; // // List of flags indicating PerfEntry contains DXE handle // BOOLEAN *PerfEntriesAsDxeHandle; UINTN VarSize; // // Record the performance data for End of BDS // PERF_END(NULL, "BDS", NULL, 0); // // Retrieve time stamp count as early as possible // Ticker = GetPerformanceCounter (); Freq = GetPerformanceCounterProperties (&StartValue, &EndValue); Freq = DivU64x32 (Freq, 1000); mPerfHeader.CpuFreq = Freq; // // Record BDS raw performance data // if (EndValue >= StartValue) { mPerfHeader.BDSRaw = Ticker - StartValue; CountUp = TRUE; } else { mPerfHeader.BDSRaw = StartValue - Ticker; CountUp = FALSE; } if (mAcpiLowMemoryBase == 0x0FFFFFFFF) { VarSize = sizeof (EFI_PHYSICAL_ADDRESS); Status = gRT->GetVariable ( L"PerfDataMemAddr", &gPerformanceProtocolGuid, NULL, &VarSize, &mAcpiLowMemoryBase ); if (EFI_ERROR (Status)) { // // Fail to get the variable, return. // return; } } // // Put Detailed performance data into memory // Handles = NULL; Status = gBS->LocateHandleBuffer ( AllHandles, NULL, NULL, &NoHandles, &Handles ); if (EFI_ERROR (Status)) { return ; } Ptr = (UINT8 *) ((UINT32) mAcpiLowMemoryBase + sizeof (PERF_HEADER)); LimitCount = (UINT32) (PERF_DATA_MAX_LENGTH - sizeof (PERF_HEADER)) / sizeof (PERF_DATA); NumPerfEntries = 0; LogEntryKey = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { NumPerfEntries++; } PerfEntriesAsDxeHandle = AllocateZeroPool (NumPerfEntries * sizeof (BOOLEAN)); ASSERT (PerfEntriesAsDxeHandle != NULL); // // Get DXE drivers performance // for (Index = 0; Index < NoHandles; Index++) { Ticker = 0; LogEntryKey = 0; EntryIndex = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { if (Handle == Handles[Index] && !PerfEntriesAsDxeHandle[EntryIndex]) { PerfEntriesAsDxeHandle[EntryIndex] = TRUE; } EntryIndex++; if ((Handle == Handles[Index]) && (EndTicker != 0)) { if (StartTicker == 1) { StartTicker = StartValue; } if (EndTicker == 1) { EndTicker = StartValue; } Ticker += CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); } } Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); if (Duration > 0) { GetNameFromHandle (Handles[Index], GaugeString); AsciiStrCpyS (mPerfData.Token, PERF_TOKEN_SIZE, GaugeString); mPerfData.Duration = Duration; CopyMem (Ptr, &mPerfData, sizeof (PERF_DATA)); Ptr += sizeof (PERF_DATA); mPerfHeader.Count++; if (mPerfHeader.Count == LimitCount) { goto Done; } } } // // Get inserted performance data // LogEntryKey = 0; EntryIndex = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { if (!PerfEntriesAsDxeHandle[EntryIndex] && EndTicker != 0) { ZeroMem (&mPerfData, sizeof (PERF_DATA)); AsciiStrnCpyS (mPerfData.Token, PERF_TOKEN_SIZE, Token, PERF_TOKEN_LENGTH); if (StartTicker == 1) { StartTicker = StartValue; } if (EndTicker == 1) { EndTicker = StartValue; } Ticker = CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); mPerfData.Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); CopyMem (Ptr, &mPerfData, sizeof (PERF_DATA)); Ptr += sizeof (PERF_DATA); mPerfHeader.Count++; if (mPerfHeader.Count == LimitCount) { goto Done; } } EntryIndex++; } Done: FreePool (Handles); FreePool (PerfEntriesAsDxeHandle); mPerfHeader.Signiture = PERFORMANCE_SIGNATURE; // // Put performance data to Reserved memory // CopyMem ( (UINTN *) (UINTN) mAcpiLowMemoryBase, &mPerfHeader, sizeof (PERF_HEADER) ); return ; }
/** Adds a record at the end of the performance measurement log that records the start time of a performance measurement. Adds a record to the end of the performance measurement log that contains the Handle, Token, Module and Identifier. The end time of the new record must be set to zero. If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record. If TimeStamp is zero, the start time in the record is filled in with the value read from the current time stamp. @param Handle Pointer to environment specific context used to identify the component being measured. @param Token Pointer to a Null-terminated ASCII string that identifies the component being measured. @param Module Pointer to a Null-terminated ASCII string that identifies the module being measured. @param TimeStamp 64-bit time stamp. @param Identifier 32-bit identifier. If the value is 0, the created record is same as the one created by StartGauge of PERFORMANCE_PROTOCOL. @retval EFI_SUCCESS The data was read correctly from the device. @retval EFI_OUT_OF_RESOURCES There are not enough resources to record the measurement. **/ EFI_STATUS EFIAPI StartGaugeEx ( IN CONST VOID *Handle, OPTIONAL IN CONST CHAR8 *Token, OPTIONAL IN CONST CHAR8 *Module, OPTIONAL IN UINT64 TimeStamp, IN UINT32 Identifier ) { GAUGE_DATA_ENTRY_EX *GaugeEntryExArray; UINTN GaugeDataSize; GAUGE_DATA_HEADER *NewGaugeData; UINTN OldGaugeDataSize; GAUGE_DATA_HEADER *OldGaugeData; UINT32 Index; AcquireSpinLock (&mSmmPerfLock); Index = mGaugeData->NumberOfEntries; if (Index >= mMaxGaugeRecords) { // // Try to enlarge the scale of gauge array. // OldGaugeData = mGaugeData; OldGaugeDataSize = sizeof (GAUGE_DATA_HEADER) + sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords; GaugeDataSize = sizeof (GAUGE_DATA_HEADER) + sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords * 2; NewGaugeData = AllocateZeroPool (GaugeDataSize); if (NewGaugeData == NULL) { ReleaseSpinLock (&mSmmPerfLock); return EFI_OUT_OF_RESOURCES; } mGaugeData = NewGaugeData; mMaxGaugeRecords *= 2; // // Initialize new data array and migrate old data one. // mGaugeData = CopyMem (mGaugeData, OldGaugeData, OldGaugeDataSize); FreePool (OldGaugeData); } GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1); GaugeEntryExArray[Index].Handle = (EFI_PHYSICAL_ADDRESS) (UINTN) Handle; if (Token != NULL) { AsciiStrnCpyS (GaugeEntryExArray[Index].Token, SMM_PERFORMANCE_STRING_SIZE, Token, SMM_PERFORMANCE_STRING_LENGTH); } if (Module != NULL) { AsciiStrnCpyS (GaugeEntryExArray[Index].Module, SMM_PERFORMANCE_STRING_SIZE, Module, SMM_PERFORMANCE_STRING_LENGTH); } GaugeEntryExArray[Index].EndTimeStamp = 0; GaugeEntryExArray[Index].Identifier = Identifier; if (TimeStamp == 0) { TimeStamp = GetPerformanceCounter (); } GaugeEntryExArray[Index].StartTimeStamp = TimeStamp; mGaugeData->NumberOfEntries++; ReleaseSpinLock (&mSmmPerfLock); return EFI_SUCCESS; }
/** Convert PEI performance log to FPDT String boot record. @param IsStart TRUE if the performance log is start log. @param Handle Pointer to environment specific context used to identify the component being measured. @param Token Pointer to a Null-terminated ASCII string that identifies the component being measured. @param Module Pointer to a Null-terminated ASCII string that identifies the module being measured. @param Ticker 64-bit time stamp. @param Identifier 32-bit identifier. If the value is 0, the created record is same as the one created by StartGauge of PERFORMANCE_PROTOCOL. @retval EFI_SUCCESS Add FPDT boot record. @retval EFI_OUT_OF_RESOURCES There are not enough resources to record the measurement. @retval EFI_UNSUPPORTED No matched FPDT record. **/ EFI_STATUS InsertPeiFpdtMeasurement ( IN BOOLEAN IsStart, IN CONST VOID *Handle, OPTIONAL IN CONST CHAR8 *Token, OPTIONAL IN CONST CHAR8 *Module, OPTIONAL IN UINT64 Ticker, IN UINT32 Identifier ) { EFI_HOB_GUID_TYPE *GuidHob; UINTN PeiPerformanceSize; UINT8 *PeiFirmwarePerformance; FPDT_PEI_EXT_PERF_HEADER *PeiPerformanceLogHeader; FPDT_RECORD_PTR FpdtRecordPtr; FPDT_BASIC_RECORD_INFO RecordInfo; CONST VOID *ModuleGuid; UINTN DestMax; UINTN StrLength; CONST CHAR8 *StringPtr; EFI_STATUS Status; UINT16 PeiPerformanceLogEntries; UINT64 TimeStamp; StringPtr = NULL; FpdtRecordPtr.RecordHeader = NULL; PeiPerformanceLogHeader = NULL; // // Get record info (type, size, ProgressID and Module Guid). // Status = GetFpdtRecordInfo (IsStart, Handle, Token, Module, &RecordInfo); if (EFI_ERROR (Status)) { return Status; } // // If PERF_START()/PERF_END() have specified the ProgressID,it has high priority. // !!! Note: If the Perf is not the known Token used in the core but have same // ID with the core Token, this case will not be supported. // And in currtnt usage mode, for the unkown ID, there is a general rule: // If it is start pref: the lower 4 bits of the ID should be 0. // If it is end pref: the lower 4 bits of the ID should not be 0. // If input ID doesn't follow the rule, we will adjust it. // if ((Identifier != 0) && (IsKnownID (Identifier)) && (!IsKnownTokens (Token))) { return EFI_UNSUPPORTED; } else if ((Identifier != 0) && (!IsKnownID (Identifier)) && (!IsKnownTokens (Token))) { if (IsStart && ((Identifier & 0x000F) != 0)) { Identifier &= 0xFFF0; } else if ((!IsStart) && ((Identifier & 0x000F) == 0)) { Identifier += 1; } RecordInfo.ProgressID = (UINT16)Identifier; } // // Get the number of PeiPerformanceLogEntries form PCD. // PeiPerformanceLogEntries = (UINT16) (PcdGet16 (PcdMaxPeiPerformanceLogEntries16) != 0 ? PcdGet16 (PcdMaxPeiPerformanceLogEntries16) : PcdGet8 (PcdMaxPeiPerformanceLogEntries)); // // Create GUID HOB Data. // GuidHob = GetFirstGuidHob (&gEdkiiFpdtExtendedFirmwarePerformanceGuid); PeiFirmwarePerformance = NULL; while (GuidHob != NULL) { // // PEI Performance HOB was found, then return the existing one. // PeiFirmwarePerformance = (UINT8*)GET_GUID_HOB_DATA (GuidHob); PeiPerformanceLogHeader = (FPDT_PEI_EXT_PERF_HEADER *)PeiFirmwarePerformance; if (!PeiPerformanceLogHeader->HobIsFull && PeiPerformanceLogHeader->SizeOfAllEntries + RecordInfo.RecordSize > PeiPerformanceLogEntries * MAX_RECORD_SIZE) { PeiPerformanceLogHeader->HobIsFull = TRUE; } if (!PeiPerformanceLogHeader->HobIsFull && PeiPerformanceLogHeader->SizeOfAllEntries + RecordInfo.RecordSize <= PeiPerformanceLogEntries * MAX_RECORD_SIZE) { FpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)(PeiFirmwarePerformance + sizeof (FPDT_PEI_EXT_PERF_HEADER) + PeiPerformanceLogHeader->SizeOfAllEntries); break; } // // Previous HOB is used, then find next one. // GuidHob = GetNextGuidHob (&gEdkiiFpdtExtendedFirmwarePerformanceGuid, GET_NEXT_HOB (GuidHob)); } if (GuidHob == NULL) { // // PEI Performance HOB was not found, then build one. // PeiPerformanceSize = sizeof (FPDT_PEI_EXT_PERF_HEADER) + MAX_RECORD_SIZE * PeiPerformanceLogEntries; PeiFirmwarePerformance = (UINT8*)BuildGuidHob (&gEdkiiFpdtExtendedFirmwarePerformanceGuid, PeiPerformanceSize); if (PeiFirmwarePerformance != NULL) { ZeroMem (PeiFirmwarePerformance, PeiPerformanceSize); } PeiPerformanceLogHeader = (FPDT_PEI_EXT_PERF_HEADER *)PeiFirmwarePerformance; FpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)(PeiFirmwarePerformance + sizeof (FPDT_PEI_EXT_PERF_HEADER)); } if (PeiFirmwarePerformance == NULL) { // // there is no enough resource to store performance data // return EFI_OUT_OF_RESOURCES; } // // Get the TimeStamp. // if (Ticker == 0) { Ticker = GetPerformanceCounter (); TimeStamp = GetTimeInNanoSecond (Ticker); } else if (Ticker == 1) { TimeStamp = 0; } else { TimeStamp = GetTimeInNanoSecond (Ticker); } // // Get the ModuleGuid. // if (Handle != NULL) { ModuleGuid = Handle; } else { ModuleGuid = &gEfiCallerIdGuid; } switch (RecordInfo.Type) { case FPDT_GUID_EVENT_TYPE: FpdtRecordPtr.GuidEvent->Header.Type = FPDT_GUID_EVENT_TYPE; FpdtRecordPtr.GuidEvent->Header.Length = RecordInfo.RecordSize;; FpdtRecordPtr.GuidEvent->Header.Revision = FPDT_RECORD_REVISION_1; FpdtRecordPtr.GuidEvent->ProgressID = RecordInfo.ProgressID; FpdtRecordPtr.GuidEvent->Timestamp = TimeStamp; CopyMem (&FpdtRecordPtr.GuidEvent->Guid, ModuleGuid, sizeof (EFI_GUID)); PeiPerformanceLogHeader->SizeOfAllEntries += RecordInfo.RecordSize; break; case FPDT_GUID_QWORD_EVENT_TYPE: FpdtRecordPtr.GuidQwordEvent->Header.Type = FPDT_GUID_QWORD_EVENT_TYPE; FpdtRecordPtr.GuidQwordEvent->Header.Length = RecordInfo.RecordSize;; FpdtRecordPtr.GuidQwordEvent->Header.Revision = FPDT_RECORD_REVISION_1; FpdtRecordPtr.GuidQwordEvent->ProgressID = RecordInfo.ProgressID; FpdtRecordPtr.GuidQwordEvent->Timestamp = TimeStamp; PeiPerformanceLogHeader->LoadImageCount++; FpdtRecordPtr.GuidQwordEvent->Qword = PeiPerformanceLogHeader->LoadImageCount; CopyMem (&FpdtRecordPtr.GuidQwordEvent->Guid, ModuleGuid, sizeof (EFI_GUID)); PeiPerformanceLogHeader->SizeOfAllEntries += RecordInfo.RecordSize; break; case FPDT_DYNAMIC_STRING_EVENT_TYPE: FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE; FpdtRecordPtr.DynamicStringEvent->Header.Length = RecordInfo.RecordSize; FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1; FpdtRecordPtr.DynamicStringEvent->ProgressID = RecordInfo.ProgressID; FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp; CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, ModuleGuid, sizeof (EFI_GUID)); PeiPerformanceLogHeader->SizeOfAllEntries += RecordInfo.RecordSize; if (Token != NULL) { StringPtr = Token; } else if (Module != NULL) { StringPtr = Module; } if (StringPtr != NULL && AsciiStrLen (StringPtr) != 0) { DestMax = (RecordInfo.RecordSize - sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD)) / sizeof (CHAR8); StrLength = AsciiStrLen (StringPtr); if (StrLength >= DestMax) { StrLength = DestMax -1; } AsciiStrnCpyS (FpdtRecordPtr.DynamicStringEvent->String, DestMax, StringPtr, StrLength); } else { AsciiStrCpyS (FpdtRecordPtr.DynamicStringEvent->String, FPDT_STRING_EVENT_RECORD_NAME_LENGTH, "unknown name"); } break; default: // // Record is not supported in current PEI phase, return EFI_ABORTED // return EFI_UNSUPPORTED; } return EFI_SUCCESS; }