]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.c
MdeModulePkg: Change OPTIONAL keyword usage style
[mirror_edk2.git] / MdeModulePkg / Library / SmmCorePerformanceLib / SmmCorePerformanceLib.c
CommitLineData
d042c6e8 1/** @file\r
2 Performance library instance used by SMM Core.\r
3\r
4 This library provides the performance measurement interfaces and initializes performance\r
5 logging for the SMM phase.\r
f0da4d7d 6 It initializes SMM phase performance logging by publishing the SMM Performance and PerformanceEx Protocol,\r
d042c6e8 7 which is consumed by SmmPerformanceLib to logging performance data in SMM phase.\r
8\r
9 This library is mainly used by SMM Core to start performance logging to ensure that\r
f0da4d7d 10 SMM Performance and PerformanceEx Protocol are installed at the very beginning of SMM phase.\r
d042c6e8 11\r
ccd2f6b0 12 Caution: This module requires additional review when modified.\r
13 This driver will have external input - performance data and communicate buffer in SMM mode.\r
14 This external input must be validated carefully to avoid security issue like\r
15 buffer overflow, integer overflow.\r
16\r
17 SmmPerformanceHandlerEx(), SmmPerformanceHandler() will receive untrusted input and do basic validation.\r
18\r
466ebdd2 19Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.<BR>\r
9d510e61 20SPDX-License-Identifier: BSD-2-Clause-Patent\r
d042c6e8 21\r
22**/\r
23\r
24\r
25#include "SmmCorePerformanceLibInternal.h"\r
26\r
2001f84e
DB
27#define STRING_SIZE (FPDT_STRING_EVENT_RECORD_NAME_LENGTH * sizeof (CHAR8))\r
28#define FIRMWARE_RECORD_BUFFER 0x1000\r
29#define CACHE_HANDLE_GUID_COUNT 0x100\r
d042c6e8 30\r
2001f84e 31SMM_BOOT_PERFORMANCE_TABLE *mSmmBootPerformanceTable = NULL;\r
d042c6e8 32\r
2001f84e
DB
33typedef struct {\r
34 EFI_HANDLE Handle;\r
35 CHAR8 NameString[FPDT_STRING_EVENT_RECORD_NAME_LENGTH];\r
36 EFI_GUID ModuleGuid;\r
37} HANDLE_GUID_MAP;\r
d042c6e8 38\r
2001f84e
DB
39HANDLE_GUID_MAP mCacheHandleGuidTable[CACHE_HANDLE_GUID_COUNT];\r
40UINTN mCachePairCount = 0;\r
d042c6e8 41\r
dd3907f2 42UINT32 mPerformanceLength = sizeof (SMM_BOOT_PERFORMANCE_TABLE);\r
2001f84e 43UINT32 mMaxPerformanceLength = 0;\r
6b4d58a1 44UINT32 mLoadImageCount = 0;\r
2001f84e
DB
45BOOLEAN mFpdtDataIsReported = FALSE;\r
46BOOLEAN mLackSpaceIsReport = FALSE;\r
47CHAR8 *mPlatformLanguage = NULL;\r
48SPIN_LOCK mSmmFpdtLock;\r
49PERFORMANCE_PROPERTY mPerformanceProperty;\r
6b4d58a1 50UINT32 mCachedLength = 0;\r
466ebdd2 51UINT32 mBootRecordSize = 0;\r
d042c6e8 52\r
d042c6e8 53//\r
137fb13d 54// Interfaces for SMM PerformanceMeasurement Protocol.\r
d042c6e8 55//\r
137fb13d
BD
56EDKII_PERFORMANCE_MEASUREMENT_PROTOCOL mPerformanceMeasurementInterface = {\r
57 CreatePerformanceMeasurement,\r
f0da4d7d
SZ
58};\r
59\r
6b4d58a1
BD
60/**\r
61 Return the pointer to the FPDT record in the allocated memory.\r
62\r
63 @param RecordSize The size of FPDT record.\r
64 @param FpdtRecordPtr Pointer the FPDT record in the allocated memory.\r
65\r
66 @retval EFI_SUCCESS Successfully get the pointer to the FPDT record.\r
67 @retval EFI_OUT_OF_RESOURCES Ran out of space to store the records.\r
68**/\r
69EFI_STATUS\r
70GetFpdtRecordPtr (\r
71 IN UINT8 RecordSize,\r
72 IN OUT FPDT_RECORD_PTR *FpdtRecordPtr\r
73)\r
74{\r
75 if (mFpdtDataIsReported) {\r
76 //\r
77 // Append Boot records after Smm boot performance records have been reported.\r
78 //\r
79 if (mPerformanceLength + RecordSize > mMaxPerformanceLength) {\r
80 if (!mLackSpaceIsReport) {\r
81 DEBUG ((DEBUG_INFO, "SmmCorePerformanceLib: No enough space to save boot records\n"));\r
82 mLackSpaceIsReport = TRUE;\r
83 }\r
84 return EFI_OUT_OF_RESOURCES;\r
85 } else {\r
86 //\r
87 // Covert buffer to FPDT Ptr Union type.\r
88 //\r
89 FpdtRecordPtr->RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8*)mSmmBootPerformanceTable + mSmmBootPerformanceTable->Header.Length);\r
90 }\r
91 } else {\r
92 //\r
93 // Check if pre-allocated buffer is full\r
94 //\r
95 if (mPerformanceLength + RecordSize > mMaxPerformanceLength) {\r
96 mSmmBootPerformanceTable = ReallocatePool (\r
97 mPerformanceLength,\r
dd3907f2 98 mPerformanceLength + RecordSize + FIRMWARE_RECORD_BUFFER,\r
6b4d58a1
BD
99 mSmmBootPerformanceTable\r
100 );\r
101\r
102 if (mSmmBootPerformanceTable == NULL) {\r
103 return EFI_OUT_OF_RESOURCES;\r
104 }\r
dd3907f2
DB
105 mSmmBootPerformanceTable->Header.Length = mPerformanceLength;\r
106 mMaxPerformanceLength = mPerformanceLength + RecordSize + FIRMWARE_RECORD_BUFFER;\r
6b4d58a1
BD
107 }\r
108 //\r
109 // Covert buffer to FPDT Ptr Union type.\r
110 //\r
111 FpdtRecordPtr->RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8*)mSmmBootPerformanceTable + mSmmBootPerformanceTable->Header.Length);\r
112 }\r
113 FpdtRecordPtr->RecordHeader->Length = 0;\r
114 return EFI_SUCCESS;\r
115}\r
116\r
117\r
2001f84e
DB
118/**\r
119Check whether the Token is a known one which is uesed by core.\r
120\r
121@param Token Pointer to a Null-terminated ASCII string\r
122\r
123@retval TRUE Is a known one used by core.\r
124@retval FALSE Not a known one.\r
125\r
126**/\r
127BOOLEAN\r
128IsKnownTokens (\r
129 IN CONST CHAR8 *Token\r
130 )\r
131{\r
c9faac27
DB
132 if (Token == NULL) {\r
133 return FALSE;\r
134 }\r
135\r
2001f84e
DB
136 if (AsciiStrCmp (Token, SEC_TOK) == 0 ||\r
137 AsciiStrCmp (Token, PEI_TOK) == 0 ||\r
138 AsciiStrCmp (Token, DXE_TOK) == 0 ||\r
139 AsciiStrCmp (Token, BDS_TOK) == 0 ||\r
140 AsciiStrCmp (Token, DRIVERBINDING_START_TOK) == 0 ||\r
141 AsciiStrCmp (Token, DRIVERBINDING_SUPPORT_TOK) == 0 ||\r
142 AsciiStrCmp (Token, DRIVERBINDING_STOP_TOK) == 0 ||\r
143 AsciiStrCmp (Token, LOAD_IMAGE_TOK) == 0 ||\r
144 AsciiStrCmp (Token, START_IMAGE_TOK) == 0 ||\r
145 AsciiStrCmp (Token, PEIM_TOK) == 0) {\r
146 return TRUE;\r
147 } else {\r
148 return FALSE;\r
149 }\r
150}\r
cfb0aba7 151\r
d042c6e8 152/**\r
2001f84e 153Check whether the ID is a known one which map to the known Token.\r
d042c6e8 154\r
2001f84e 155@param Identifier 32-bit identifier.\r
d042c6e8 156\r
2001f84e
DB
157@retval TRUE Is a known one used by core.\r
158@retval FALSE Not a known one.\r
159\r
160**/\r
161BOOLEAN\r
162IsKnownID (\r
163 IN UINT32 Identifier\r
164 )\r
165{\r
166 if (Identifier == MODULE_START_ID ||\r
167 Identifier == MODULE_END_ID ||\r
168 Identifier == MODULE_LOADIMAGE_START_ID ||\r
169 Identifier == MODULE_LOADIMAGE_END_ID ||\r
170 Identifier == MODULE_DB_START_ID ||\r
171 Identifier == MODULE_DB_END_ID ||\r
172 Identifier == MODULE_DB_SUPPORT_START_ID ||\r
173 Identifier == MODULE_DB_SUPPORT_END_ID ||\r
174 Identifier == MODULE_DB_STOP_START_ID ||\r
175 Identifier == MODULE_DB_STOP_END_ID) {\r
176 return TRUE;\r
177 } else {\r
178 return FALSE;\r
179 }\r
180}\r
181\r
182/**\r
6b4d58a1 183 Get the FPDT record identifier.\r
2001f84e 184\r
6b4d58a1
BD
185 @param Attribute The attribute of the Record.\r
186 PerfStartEntry: Start Record.\r
187 PerfEndEntry: End Record.\r
188 @param Handle Pointer to environment specific context used to identify the component being measured.\r
189 @param String Pointer to a Null-terminated ASCII string that identifies the component being measured.\r
190 @param ProgressID On return, pointer to the ProgressID.\r
d042c6e8 191\r
6b4d58a1
BD
192 @retval EFI_SUCCESS Get record info successfully.\r
193 @retval EFI_INVALID_PARAMETER No matched FPDT record.\r
d042c6e8 194\r
195**/\r
2001f84e 196EFI_STATUS\r
6b4d58a1
BD
197GetFpdtRecordId (\r
198 IN PERF_MEASUREMENT_ATTRIBUTE Attribute,\r
199 IN CONST VOID *Handle,\r
200 IN CONST CHAR8 *String,\r
201 OUT UINT16 *ProgressID\r
d042c6e8 202 )\r
203{\r
2001f84e 204 //\r
6b4d58a1 205 // Token to Id.\r
2001f84e 206 //\r
6b4d58a1
BD
207 if (String != NULL) {\r
208 if (AsciiStrCmp (String, START_IMAGE_TOK) == 0) { // "StartImage:"\r
209 if (Attribute == PerfStartEntry) {\r
210 *ProgressID = MODULE_START_ID;\r
2001f84e 211 } else {\r
6b4d58a1 212 *ProgressID = MODULE_END_ID;\r
2001f84e 213 }\r
6b4d58a1
BD
214 } else if (AsciiStrCmp (String, LOAD_IMAGE_TOK) == 0) { // "LoadImage:"\r
215 if (Attribute == PerfStartEntry) {\r
216 *ProgressID = MODULE_LOADIMAGE_START_ID;\r
2001f84e 217 } else {\r
6b4d58a1 218 *ProgressID = MODULE_LOADIMAGE_END_ID;\r
2001f84e
DB
219 }\r
220 } else { // Pref used in Modules\r
6b4d58a1
BD
221 if (Attribute == PerfStartEntry) {\r
222 *ProgressID = PERF_INMODULE_START_ID;\r
2001f84e 223 } else {\r
6b4d58a1 224 *ProgressID = PERF_INMODULE_END_ID;\r
2001f84e
DB
225 }\r
226 }\r
6b4d58a1
BD
227 } else if (Handle != NULL) { // Pref used in Modules\r
228 if (Attribute == PerfStartEntry) {\r
229 *ProgressID = PERF_INMODULE_START_ID;\r
2001f84e 230 } else {\r
6b4d58a1 231 *ProgressID = PERF_INMODULE_END_ID;\r
2001f84e
DB
232 }\r
233 } else {\r
234 return EFI_UNSUPPORTED;\r
d042c6e8 235 }\r
2001f84e 236 return EFI_SUCCESS;\r
d042c6e8 237}\r
238\r
239/**\r
2001f84e
DB
240 Get a human readable module name and module guid for the given image handle.\r
241 If module name can't be found, "" string will return.\r
242 If module guid can't be found, Zero Guid will return.\r
243\r
244 @param Handle Image handle or Controller handle.\r
245 @param NameString The ascii string will be filled into it. If not found, null string will return.\r
246 @param BufferSize Size of the input NameString buffer.\r
247 @param ModuleGuid Point to the guid buffer to store the got module guid value.\r
248\r
249 @retval EFI_SUCCESS Successfully get module name and guid.\r
250 @retval EFI_INVALID_PARAMETER The input parameter NameString is NULL.\r
251 @retval other value Module Name can't be got.\r
252**/\r
253EFI_STATUS\r
254EFIAPI\r
255GetModuleInfoFromHandle (\r
256 IN EFI_HANDLE Handle,\r
257 OUT CHAR8 *NameString,\r
258 IN UINTN BufferSize,\r
259 OUT EFI_GUID *ModuleGuid OPTIONAL\r
260 )\r
261{\r
262 EFI_STATUS Status;\r
263 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
264 EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;\r
265 CHAR8 *PdbFileName;\r
266 EFI_GUID *TempGuid;\r
267 UINTN StartIndex;\r
268 UINTN Index;\r
269 INTN Count;\r
270 BOOLEAN ModuleGuidIsGet;\r
271 UINTN StringSize;\r
272 CHAR16 *StringPtr;\r
273 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFilePath;\r
274\r
275 if (NameString == NULL || BufferSize == 0) {\r
276 return EFI_INVALID_PARAMETER;\r
277 }\r
d042c6e8 278\r
2001f84e
DB
279 //\r
280 // Try to get the ModuleGuid and name string form the caached array.\r
281 //\r
282 if (mCachePairCount > 0) {\r
283 for (Count = mCachePairCount - 1; Count >= 0; Count--) {\r
284 if (Handle == mCacheHandleGuidTable[Count].Handle) {\r
285 CopyGuid (ModuleGuid, &mCacheHandleGuidTable[Count].ModuleGuid);\r
286 AsciiStrCpyS (NameString, FPDT_STRING_EVENT_RECORD_NAME_LENGTH, mCacheHandleGuidTable[Count].NameString);\r
287 return EFI_SUCCESS;\r
288 }\r
289 }\r
290 }\r
291\r
292 Status = EFI_INVALID_PARAMETER;\r
293 LoadedImage = NULL;\r
294 ModuleGuidIsGet = FALSE;\r
d042c6e8 295\r
2001f84e
DB
296 //\r
297 // Initialize GUID as zero value.\r
298 //\r
299 TempGuid = &gZeroGuid;\r
300 //\r
301 // Initialize it as "" string.\r
302 //\r
303 NameString[0] = 0;\r
304\r
305 if (Handle != NULL) {\r
306 //\r
307 // Try Handle as ImageHandle.\r
308 //\r
309 Status = gBS->HandleProtocol (\r
310 Handle,\r
311 &gEfiLoadedImageProtocolGuid,\r
312 (VOID**) &LoadedImage\r
313 );\r
314\r
315 if (EFI_ERROR (Status)) {\r
316 //\r
317 // Try Handle as Controller Handle\r
318 //\r
319 Status = gBS->OpenProtocol (\r
320 Handle,\r
321 &gEfiDriverBindingProtocolGuid,\r
322 (VOID **) &DriverBinding,\r
323 NULL,\r
324 NULL,\r
325 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
326 );\r
327 if (!EFI_ERROR (Status)) {\r
328 //\r
329 // Get Image protocol from ImageHandle\r
330 //\r
331 Status = gBS->HandleProtocol (\r
332 DriverBinding->ImageHandle,\r
333 &gEfiLoadedImageProtocolGuid,\r
334 (VOID**) &LoadedImage\r
335 );\r
336 }\r
337 }\r
338 }\r
339\r
340 if (!EFI_ERROR (Status) && LoadedImage != NULL) {\r
341 //\r
342 // Get Module Guid from DevicePath.\r
343 //\r
344 if (LoadedImage->FilePath != NULL &&\r
345 LoadedImage->FilePath->Type == MEDIA_DEVICE_PATH &&\r
346 LoadedImage->FilePath->SubType == MEDIA_PIWG_FW_FILE_DP\r
347 ) {\r
348 //\r
349 // Determine GUID associated with module logging performance\r
350 //\r
351 ModuleGuidIsGet = TRUE;\r
352 FvFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LoadedImage->FilePath;\r
353 TempGuid = &FvFilePath->FvFileName;\r
354 }\r
355\r
356 //\r
357 // Method 1 Get Module Name from PDB string.\r
358 //\r
359 PdbFileName = PeCoffLoaderGetPdbPointer (LoadedImage->ImageBase);\r
360 if (PdbFileName != NULL && BufferSize > 0) {\r
361 StartIndex = 0;\r
362 for (Index = 0; PdbFileName[Index] != 0; Index++) {\r
363 if ((PdbFileName[Index] == '\\') || (PdbFileName[Index] == '/')) {\r
364 StartIndex = Index + 1;\r
365 }\r
366 }\r
367 //\r
368 // Copy the PDB file name to our temporary string.\r
369 // If the length is bigger than BufferSize, trim the redudant characters to avoid overflow in array boundary.\r
370 //\r
371 for (Index = 0; Index < BufferSize - 1; Index++) {\r
372 NameString[Index] = PdbFileName[Index + StartIndex];\r
373 if (NameString[Index] == 0 || NameString[Index] == '.') {\r
374 NameString[Index] = 0;\r
375 break;\r
376 }\r
377 }\r
378\r
379 if (Index == BufferSize - 1) {\r
380 NameString[Index] = 0;\r
381 }\r
382 //\r
383 // Module Name is got.\r
384 //\r
385 goto Done;\r
386 }\r
387 }\r
388\r
389 if (ModuleGuidIsGet) {\r
390 //\r
391 // Method 2 Try to get the image's FFS UI section by image GUID\r
392 //\r
393 StringPtr = NULL;\r
394 StringSize = 0;\r
395 Status = GetSectionFromAnyFv (\r
396 TempGuid,\r
397 EFI_SECTION_USER_INTERFACE,\r
398 0,\r
399 (VOID **) &StringPtr,\r
400 &StringSize\r
401 );\r
402\r
403 if (!EFI_ERROR (Status)) {\r
404 //\r
405 // Method 3. Get the name string from FFS UI section\r
406 //\r
407 for (Index = 0; Index < BufferSize - 1 && StringPtr[Index] != 0; Index++) {\r
408 NameString[Index] = (CHAR8) StringPtr[Index];\r
409 }\r
410 NameString[Index] = 0;\r
411 FreePool (StringPtr);\r
412 }\r
413 }\r
414\r
415Done:\r
416 //\r
417 // Copy Module Guid\r
418 //\r
419 if (ModuleGuid != NULL) {\r
420 CopyGuid (ModuleGuid, TempGuid);\r
421 if (IsZeroGuid(TempGuid) && (Handle != NULL) && !ModuleGuidIsGet) {\r
422 // Handle is GUID\r
423 CopyGuid (ModuleGuid, (EFI_GUID *) Handle);\r
424 }\r
425 }\r
426\r
427 //\r
428 // Cache the Handle and Guid pairs.\r
429 //\r
430 if (mCachePairCount < CACHE_HANDLE_GUID_COUNT) {\r
431 mCacheHandleGuidTable[mCachePairCount].Handle = Handle;\r
432 CopyGuid (&mCacheHandleGuidTable[mCachePairCount].ModuleGuid, ModuleGuid);\r
433 AsciiStrCpyS (mCacheHandleGuidTable[mCachePairCount].NameString, FPDT_STRING_EVENT_RECORD_NAME_LENGTH, NameString);\r
434 mCachePairCount ++;\r
435 }\r
436\r
437 return Status;\r
438}\r
439\r
440/**\r
6b4d58a1
BD
441 Copies the string from Source into Destination and updates Length with the\r
442 size of the string.\r
2001f84e 443\r
6b4d58a1
BD
444 @param Destination - destination of the string copy\r
445 @param Source - pointer to the source string which will get copied\r
446 @param Length - pointer to a length variable to be updated\r
447\r
448**/\r
449VOID\r
450CopyStringIntoPerfRecordAndUpdateLength (\r
451 IN OUT CHAR8 *Destination,\r
452 IN CONST CHAR8 *Source,\r
453 IN OUT UINT8 *Length\r
454 )\r
455{\r
456 UINTN StringLen;\r
457 UINTN DestMax;\r
458\r
459 ASSERT (Source != NULL);\r
460\r
461 if (PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) {\r
462 DestMax = STRING_SIZE;\r
463 } else {\r
464 DestMax = AsciiStrSize (Source);\r
465 if (DestMax > STRING_SIZE) {\r
466 DestMax = STRING_SIZE;\r
467 }\r
468 }\r
469 StringLen = AsciiStrLen (Source);\r
470 if (StringLen >= DestMax) {\r
471 StringLen = DestMax -1;\r
472 }\r
d042c6e8 473\r
6b4d58a1
BD
474 AsciiStrnCpyS(Destination, DestMax, Source, StringLen);\r
475 *Length += (UINT8)DestMax;\r
476\r
477 return;\r
478}\r
479\r
480/**\r
481 Create performance record with event description and a timestamp.\r
482\r
483 @param CallerIdentifier - Image handle or pointer to caller ID GUID.\r
484 @param Guid - Pointer to a GUID.\r
485 @param String - Pointer to a string describing the measurement.\r
486 @param Ticker - 64-bit time stamp.\r
487 @param Address - Pointer to a location in memory relevant to the measurement.\r
488 @param PerfId - Performance identifier describing the type of measurement.\r
489 @param Attribute - The attribute of the measurement. According to attribute can create a start\r
490 record for PERF_START/PERF_START_EX, or a end record for PERF_END/PERF_END_EX,\r
491 or a general record for other Perf macros.\r
492\r
493 @retval EFI_SUCCESS - Successfully created performance record.\r
494 @retval EFI_OUT_OF_RESOURCES - Ran out of space to store the records.\r
495 @retval EFI_INVALID_PARAMETER - Invalid parameter passed to function - NULL\r
496 pointer or invalid PerfId.\r
497\r
498 @retval EFI_SUCCESS - Successfully created performance record\r
499 @retval EFI_OUT_OF_RESOURCES - Ran out of space to store the records\r
500 @retval EFI_INVALID_PARAMETER - Invalid parameter passed to function - NULL\r
501 pointer or invalid PerfId\r
d042c6e8 502\r
503**/\r
504EFI_STATUS\r
6b4d58a1 505InsertFpdtRecord (\r
e3917e22
MK
506 IN CONST VOID *CallerIdentifier OPTIONAL,\r
507 IN CONST VOID *Guid OPTIONAL,\r
508 IN CONST CHAR8 *String OPTIONAL,\r
6b4d58a1 509 IN UINT64 Ticker,\r
e3917e22 510 IN UINT64 Address OPTIONAL,\r
6b4d58a1
BD
511 IN UINT16 PerfId,\r
512 IN PERF_MEASUREMENT_ATTRIBUTE Attribute\r
d042c6e8 513 )\r
6b4d58a1 514\r
d042c6e8 515{\r
6b4d58a1 516 EFI_STATUS Status;\r
2001f84e
DB
517 EFI_GUID ModuleGuid;\r
518 CHAR8 ModuleName[FPDT_STRING_EVENT_RECORD_NAME_LENGTH];\r
2001f84e 519 FPDT_RECORD_PTR FpdtRecordPtr;\r
6b4d58a1 520 FPDT_RECORD_PTR CachedFpdtRecordPtr;\r
2001f84e 521 UINT64 TimeStamp;\r
2001f84e 522 CONST CHAR8 *StringPtr;\r
6b4d58a1
BD
523 UINTN DestMax;\r
524 UINTN StringLen;\r
525 UINT16 ProgressId;\r
2001f84e
DB
526\r
527 StringPtr = NULL;\r
2001f84e 528 ZeroMem (ModuleName, sizeof (ModuleName));\r
d042c6e8 529\r
2001f84e 530 //\r
6b4d58a1
BD
531 // 1. Get the Perf Id for records from PERF_START/PERF_END, PERF_START_EX/PERF_END_EX.\r
532 // notes: For other Perf macros (Attribute == PerfEntry), their Id is known.\r
2001f84e 533 //\r
6b4d58a1 534 if (Attribute != PerfEntry) {\r
d042c6e8 535 //\r
6b4d58a1
BD
536 // If PERF_START_EX()/PERF_END_EX() have specified the ProgressID,it has high priority.\r
537 // !!! Note: If the Perf is not the known Token used in the core but have same\r
538 // ID with the core Token, this case will not be supported.\r
539 // And in currtnt usage mode, for the unkown ID, there is a general rule:\r
540 // If it is start pref: the lower 4 bits of the ID should be 0.\r
541 // If it is end pref: the lower 4 bits of the ID should not be 0.\r
542 // If input ID doesn't follow the rule, we will adjust it.\r
d042c6e8 543 //\r
6b4d58a1
BD
544 if ((PerfId != 0) && (IsKnownID (PerfId)) && (!IsKnownTokens (String))) {\r
545 return EFI_INVALID_PARAMETER;\r
546 } else if ((PerfId != 0) && (!IsKnownID (PerfId)) && (!IsKnownTokens (String))) {\r
547 if ((Attribute == PerfStartEntry) && ((PerfId & 0x000F) != 0)) {\r
548 PerfId &= 0xFFF0;\r
549 } else if ((Attribute == PerfEndEntry) && ((PerfId & 0x000F) == 0)) {\r
550 PerfId += 1;\r
2001f84e 551 }\r
6b4d58a1
BD
552 }\r
553 if (PerfId == 0) {\r
2001f84e 554 //\r
6b4d58a1 555 // Get ProgressID form the String Token.\r
2001f84e 556 //\r
6b4d58a1
BD
557 Status = GetFpdtRecordId (Attribute, CallerIdentifier, String, &ProgressId);\r
558 if (EFI_ERROR (Status)) {\r
559 return Status;\r
2001f84e 560 }\r
6b4d58a1 561 PerfId = ProgressId;\r
d042c6e8 562 }\r
2001f84e 563 }\r
d042c6e8 564\r
2001f84e 565 //\r
6b4d58a1
BD
566 // 2. Get the buffer to store the FPDT record.\r
567 //\r
568 Status = GetFpdtRecordPtr (FPDT_MAX_PERF_RECORD_SIZE, &FpdtRecordPtr);\r
569 if (EFI_ERROR (Status)) {\r
570 return Status;\r
571 }\r
572\r
573 //\r
574 // 3. Get the TimeStamp.\r
2001f84e
DB
575 //\r
576 if (Ticker == 0) {\r
577 Ticker = GetPerformanceCounter ();\r
578 TimeStamp = GetTimeInNanoSecond (Ticker);\r
579 } else if (Ticker == 1) {\r
580 TimeStamp = 0;\r
581 } else {\r
582 TimeStamp = GetTimeInNanoSecond (Ticker);\r
d042c6e8 583 }\r
584\r
2001f84e 585 //\r
6b4d58a1 586 // 4. Fill in the FPDT record according to different Performance Identifier.\r
2001f84e 587 //\r
6b4d58a1
BD
588 switch (PerfId) {\r
589 case MODULE_START_ID:\r
590 case MODULE_END_ID:\r
3522ea73 591 GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid);\r
6b4d58a1
BD
592 StringPtr = ModuleName;\r
593 //\r
594 // Cache the offset of start image start record and use to update the start image end record if needed.\r
595 //\r
596 if (PerfId == MODULE_START_ID && Attribute == PerfEntry) {\r
597 mCachedLength = mSmmBootPerformanceTable->Header.Length;\r
598 }\r
599 if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) {\r
600 FpdtRecordPtr.GuidEvent->Header.Type = FPDT_GUID_EVENT_TYPE;\r
601 FpdtRecordPtr.GuidEvent->Header.Length = sizeof (FPDT_GUID_EVENT_RECORD);\r
602 FpdtRecordPtr.GuidEvent->Header.Revision = FPDT_RECORD_REVISION_1;\r
603 FpdtRecordPtr.GuidEvent->ProgressID = PerfId;\r
604 FpdtRecordPtr.GuidEvent->Timestamp = TimeStamp;\r
605 CopyMem (&FpdtRecordPtr.GuidEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.GuidEvent->Guid));\r
606 if (CallerIdentifier == NULL && PerfId == MODULE_END_ID && mCachedLength != 0) {\r
607 CachedFpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8*)mSmmBootPerformanceTable + mCachedLength);\r
608 CopyMem (&FpdtRecordPtr.GuidEvent->Guid, &CachedFpdtRecordPtr.GuidEvent->Guid, sizeof (FpdtRecordPtr.GuidEvent->Guid));\r
609 mCachedLength = 0;\r
610 }\r
611 }\r
612 break;\r
d042c6e8 613\r
6b4d58a1
BD
614 case MODULE_LOADIMAGE_START_ID:\r
615 case MODULE_LOADIMAGE_END_ID:\r
3522ea73 616 GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid);\r
6b4d58a1
BD
617 StringPtr = ModuleName;\r
618 if (PerfId == MODULE_LOADIMAGE_START_ID) {\r
619 mLoadImageCount++;\r
620 //\r
621 // Cache the offset of load image start record and use to be updated by the load image end record if needed.\r
622 //\r
623 if (CallerIdentifier == NULL && Attribute == PerfEntry) {\r
624 mCachedLength = mSmmBootPerformanceTable->Header.Length;\r
625 }\r
626 }\r
627 if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) {\r
628 FpdtRecordPtr.GuidQwordEvent->Header.Type = FPDT_GUID_QWORD_EVENT_TYPE;\r
629 FpdtRecordPtr.GuidQwordEvent->Header.Length = sizeof (FPDT_GUID_QWORD_EVENT_RECORD);\r
630 FpdtRecordPtr.GuidQwordEvent->Header.Revision = FPDT_RECORD_REVISION_1;\r
631 FpdtRecordPtr.GuidQwordEvent->ProgressID = PerfId;\r
632 FpdtRecordPtr.GuidQwordEvent->Timestamp = TimeStamp;\r
633 FpdtRecordPtr.GuidQwordEvent->Qword = mLoadImageCount;\r
634 CopyMem (&FpdtRecordPtr.GuidQwordEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.GuidQwordEvent->Guid));\r
635 if (PerfId == MODULE_LOADIMAGE_END_ID && mCachedLength != 0) {\r
636 CachedFpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8*)mSmmBootPerformanceTable + mCachedLength);\r
637 CopyMem (&CachedFpdtRecordPtr.GuidQwordEvent->Guid, &ModuleGuid, sizeof (CachedFpdtRecordPtr.GuidQwordEvent->Guid));\r
638 mCachedLength = 0;\r
639 }\r
640 }\r
2001f84e
DB
641 break;\r
642\r
6b4d58a1
BD
643 case PERF_EVENTSIGNAL_START_ID:\r
644 case PERF_EVENTSIGNAL_END_ID:\r
645 case PERF_CALLBACK_START_ID:\r
646 case PERF_CALLBACK_END_ID:\r
981b7edc 647 if (String == NULL || Guid == NULL) {\r
6b4d58a1
BD
648 return EFI_INVALID_PARAMETER;\r
649 }\r
6b4d58a1 650 StringPtr = String;\r
981b7edc
DB
651 if (AsciiStrLen (String) == 0) {\r
652 StringPtr = "unknown name";\r
653 }\r
6b4d58a1
BD
654 if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) {\r
655 FpdtRecordPtr.DualGuidStringEvent->Header.Type = FPDT_DUAL_GUID_STRING_EVENT_TYPE;\r
656 FpdtRecordPtr.DualGuidStringEvent->Header.Length = sizeof (FPDT_DUAL_GUID_STRING_EVENT_RECORD);\r
657 FpdtRecordPtr.DualGuidStringEvent->Header.Revision = FPDT_RECORD_REVISION_1;\r
658 FpdtRecordPtr.DualGuidStringEvent->ProgressID = PerfId;\r
659 FpdtRecordPtr.DualGuidStringEvent->Timestamp = TimeStamp;\r
660 CopyMem (&FpdtRecordPtr.DualGuidStringEvent->Guid1, CallerIdentifier, sizeof (FpdtRecordPtr.DualGuidStringEvent->Guid1));\r
661 CopyMem (&FpdtRecordPtr.DualGuidStringEvent->Guid2, Guid, sizeof (FpdtRecordPtr.DualGuidStringEvent->Guid2));\r
662 CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DualGuidStringEvent->String, StringPtr, &FpdtRecordPtr.DualGuidStringEvent->Header.Length);\r
663 }\r
664 break;\r
2001f84e 665\r
6b4d58a1
BD
666 case PERF_EVENT_ID:\r
667 case PERF_FUNCTION_START_ID:\r
668 case PERF_FUNCTION_END_ID:\r
669 case PERF_INMODULE_START_ID:\r
670 case PERF_INMODULE_END_ID:\r
671 case PERF_CROSSMODULE_START_ID:\r
672 case PERF_CROSSMODULE_END_ID:\r
3522ea73 673 GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid);\r
6b4d58a1
BD
674 if (String != NULL) {\r
675 StringPtr = String;\r
676 } else {\r
677 StringPtr = ModuleName;\r
678 }\r
679 if (AsciiStrLen (StringPtr) == 0) {\r
680 StringPtr = "unknown name";\r
2001f84e 681 }\r
6b4d58a1
BD
682 if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) {\r
683 FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE;\r
684 FpdtRecordPtr.DynamicStringEvent->Header.Length = sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD);\r
685 FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1;\r
686 FpdtRecordPtr.DynamicStringEvent->ProgressID = PerfId;\r
687 FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp;\r
688 CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid));\r
689 CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DynamicStringEvent->String, StringPtr, &FpdtRecordPtr.DynamicStringEvent->Header.Length);\r
690 }\r
691 break;\r
692\r
693 default:\r
694 if (Attribute != PerfEntry) {\r
3522ea73 695 GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid);\r
6b4d58a1
BD
696 if (String != NULL) {\r
697 StringPtr = String;\r
698 } else {\r
699 StringPtr = ModuleName;\r
700 }\r
701 if (AsciiStrLen (StringPtr) == 0) {\r
702 StringPtr = "unknown name";\r
703 }\r
704 if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) {\r
705 FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE;\r
706 FpdtRecordPtr.DynamicStringEvent->Header.Length = sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD);\r
707 FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1;\r
708 FpdtRecordPtr.DynamicStringEvent->ProgressID = PerfId;\r
709 FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp;\r
710 CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid));\r
711 CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DynamicStringEvent->String, StringPtr, &FpdtRecordPtr.DynamicStringEvent->Header.Length);\r
2001f84e 712 }\r
2001f84e 713 } else {\r
6b4d58a1 714 return EFI_INVALID_PARAMETER;\r
2001f84e
DB
715 }\r
716 break;\r
6b4d58a1 717 }\r
2001f84e 718\r
6b4d58a1
BD
719 //\r
720 // 4.2 When PcdEdkiiFpdtStringRecordEnableOnly==TRUE, create string record for all Perf entries.\r
721 //\r
722 if (PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) {\r
723 if (StringPtr == NULL) {\r
724 return EFI_INVALID_PARAMETER;\r
725 }\r
726 FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE;\r
727 FpdtRecordPtr.DynamicStringEvent->Header.Length = sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD);\r
728 FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1;\r
729 FpdtRecordPtr.DynamicStringEvent->ProgressID = PerfId;\r
730 FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp;\r
981b7edc
DB
731 if (Guid != NULL) {\r
732 //\r
733 // Cache the event guid in string event record.\r
734 //\r
735 CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, Guid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid));\r
736 } else {\r
737 CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid));\r
738 }\r
6b4d58a1
BD
739 if (AsciiStrLen (StringPtr) == 0) {\r
740 StringPtr = "unknown name";\r
741 }\r
742 CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DynamicStringEvent->String, StringPtr, &FpdtRecordPtr.DynamicStringEvent->Header.Length);\r
2001f84e 743\r
6b4d58a1
BD
744 if ((PerfId == MODULE_LOADIMAGE_START_ID) || (PerfId == MODULE_END_ID)) {\r
745 FpdtRecordPtr.DynamicStringEvent->Header.Length = (UINT8)(sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD)+ STRING_SIZE);\r
746 }\r
747 if ((PerfId == MODULE_LOADIMAGE_END_ID || PerfId == MODULE_END_ID) && mCachedLength != 0) {\r
748 CachedFpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8*)mSmmBootPerformanceTable + mCachedLength);\r
749 if (PerfId == MODULE_LOADIMAGE_END_ID) {\r
750 DestMax = CachedFpdtRecordPtr.DynamicStringEvent->Header.Length - sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD);\r
751 StringLen = AsciiStrLen (StringPtr);\r
752 if (StringLen >= DestMax) {\r
753 StringLen = DestMax -1;\r
754 }\r
755 CopyMem (&CachedFpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (CachedFpdtRecordPtr.DynamicStringEvent->Guid));\r
756 AsciiStrnCpyS (CachedFpdtRecordPtr.DynamicStringEvent->String, DestMax, StringPtr, StringLen);\r
757 } else if (PerfId == MODULE_END_ID) {\r
758 DestMax = FpdtRecordPtr.DynamicStringEvent->Header.Length - sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD);\r
759 StringLen = AsciiStrLen (CachedFpdtRecordPtr.DynamicStringEvent->String);\r
760 if (StringLen >= DestMax) {\r
761 StringLen = DestMax -1;\r
762 }\r
763 CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &CachedFpdtRecordPtr.DynamicStringEvent->Guid, sizeof (CachedFpdtRecordPtr.DynamicStringEvent->Guid));\r
764 AsciiStrnCpyS (FpdtRecordPtr.DynamicStringEvent->String, DestMax, CachedFpdtRecordPtr.DynamicStringEvent->String, StringLen);\r
765 }\r
766 mCachedLength = 0;\r
767 }\r
d042c6e8 768 }\r
769\r
2001f84e 770 //\r
6b4d58a1 771 // 5. Update the length of the used buffer after fill in the record.\r
2001f84e
DB
772 //\r
773 mPerformanceLength += FpdtRecordPtr.RecordHeader->Length;\r
774 mSmmBootPerformanceTable->Header.Length += FpdtRecordPtr.RecordHeader->Length;\r
f0da4d7d 775\r
2001f84e
DB
776 return EFI_SUCCESS;\r
777}\r
d042c6e8 778\r
2001f84e 779/**\r
466ebdd2 780 Communication service SMI Handler entry.\r
f0da4d7d 781\r
466ebdd2 782 This SMI handler provides services for report MM boot records.\r
ccd2f6b0 783\r
466ebdd2
DB
784 Caution: This function may receive untrusted input.\r
785 Communicate buffer and buffer size are external input, so this function will do basic validation.\r
786\r
787 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().\r
788 @param[in] RegisterContext Points to an optional handler context which was specified when the\r
789 handler was registered.\r
790 @param[in, out] CommBuffer A pointer to a collection of data in memory that will\r
791 be conveyed from a non-MM environment into an MM environment.\r
792 @param[in, out] CommBufferSize The size of the CommBuffer.\r
793\r
794 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers\r
795 should still be called.\r
796 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should\r
797 still be called.\r
798 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still\r
799 be called.\r
800 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.\r
f0da4d7d 801\r
d042c6e8 802**/\r
803EFI_STATUS\r
804EFIAPI\r
466ebdd2
DB
805FpdtSmiHandler (\r
806 IN EFI_HANDLE DispatchHandle,\r
807 IN CONST VOID *RegisterContext,\r
808 IN OUT VOID *CommBuffer,\r
809 IN OUT UINTN *CommBufferSize\r
d042c6e8 810 )\r
811{\r
466ebdd2
DB
812 EFI_STATUS Status;\r
813 SMM_BOOT_RECORD_COMMUNICATE *SmmCommData;\r
814 UINTN BootRecordOffset;\r
815 UINTN BootRecordSize;\r
816 VOID *BootRecordData;\r
817 UINTN TempCommBufferSize;\r
818 UINT8 *BootRecordBuffer;\r
819\r
820 //\r
821 // If input is invalid, stop processing this SMI\r
822 //\r
823 if (CommBuffer == NULL || CommBufferSize == NULL) {\r
824 return EFI_SUCCESS;\r
825 }\r
826\r
827 TempCommBufferSize = *CommBufferSize;\r
828\r
829 if(TempCommBufferSize < sizeof (SMM_BOOT_RECORD_COMMUNICATE)) {\r
830 return EFI_SUCCESS;\r
831 }\r
832\r
833 if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {\r
834 DEBUG ((DEBUG_ERROR, "FpdtSmiHandler: MM communication data buffer in MMRAM or overflow!\n"));\r
835 return EFI_SUCCESS;\r
836 }\r
837\r
838 SmmCommData = (SMM_BOOT_RECORD_COMMUNICATE*)CommBuffer;\r
839\r
840 Status = EFI_SUCCESS;\r
841\r
842 switch (SmmCommData->Function) {\r
843 case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE :\r
844 if (mSmmBootPerformanceTable != NULL) {\r
845 mBootRecordSize = mSmmBootPerformanceTable->Header.Length - sizeof (SMM_BOOT_PERFORMANCE_TABLE);\r
846 }\r
847 SmmCommData->BootRecordSize = mBootRecordSize;\r
848 break;\r
849\r
850 case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA :\r
851 Status = EFI_UNSUPPORTED;\r
852 break;\r
853\r
854 case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA_BY_OFFSET :\r
855 BootRecordOffset = SmmCommData->BootRecordOffset;\r
856 BootRecordData = SmmCommData->BootRecordData;\r
857 BootRecordSize = SmmCommData->BootRecordSize;\r
858 if (BootRecordData == NULL || BootRecordOffset >= mBootRecordSize) {\r
859 Status = EFI_INVALID_PARAMETER;\r
860 break;\r
861 }\r
862\r
863 //\r
864 // Sanity check\r
865 //\r
866 if (BootRecordSize > mBootRecordSize - BootRecordOffset) {\r
867 BootRecordSize = mBootRecordSize - BootRecordOffset;\r
868 }\r
869 SmmCommData->BootRecordSize = BootRecordSize;\r
870 if (!SmmIsBufferOutsideSmmValid ((UINTN)BootRecordData, BootRecordSize)) {\r
871 DEBUG ((DEBUG_ERROR, "FpdtSmiHandler: MM Data buffer in MMRAM or overflow!\n"));\r
872 Status = EFI_ACCESS_DENIED;\r
873 break;\r
874 }\r
875 BootRecordBuffer = ((UINT8 *) (mSmmBootPerformanceTable)) + sizeof (SMM_BOOT_PERFORMANCE_TABLE);\r
876 CopyMem (\r
877 (UINT8*)BootRecordData,\r
878 BootRecordBuffer + BootRecordOffset,\r
879 BootRecordSize\r
2001f84e 880 );\r
466ebdd2
DB
881 mFpdtDataIsReported = TRUE;\r
882 break;\r
883\r
884 default:\r
885 Status = EFI_UNSUPPORTED;\r
f0da4d7d 886 }\r
466ebdd2
DB
887\r
888 SmmCommData->ReturnStatus = Status;\r
889\r
d042c6e8 890 return EFI_SUCCESS;\r
891}\r
892\r
893/**\r
2001f84e
DB
894 SmmBase2 protocol notify callback function, when SMST and SMM memory service get initialized\r
895 this function is callbacked to initialize the Smm Performance Lib\r
d042c6e8 896\r
897 @param Event The event of notify protocol.\r
898 @param Context Notify event context.\r
899\r
900**/\r
901VOID\r
902EFIAPI\r
903InitializeSmmCorePerformanceLib (\r
904 IN EFI_EVENT Event,\r
905 IN VOID *Context\r
906 )\r
907{\r
d042c6e8 908 EFI_HANDLE Handle;\r
466ebdd2 909 EFI_HANDLE SmiHandle;\r
2001f84e 910 EFI_STATUS Status;\r
cfb0aba7 911 PERFORMANCE_PROPERTY *PerformanceProperty;\r
d042c6e8 912\r
913 //\r
914 // Initialize spin lock\r
915 //\r
2001f84e 916 InitializeSpinLock (&mSmmFpdtLock);\r
d042c6e8 917\r
d042c6e8 918 //\r
2001f84e 919 // Install the protocol interfaces for SMM performance library instance.\r
d042c6e8 920 //\r
2001f84e 921 Handle = NULL;\r
d042c6e8 922 Status = gSmst->SmmInstallProtocolInterface (\r
2001f84e 923 &Handle,\r
137fb13d 924 &gEdkiiSmmPerformanceMeasurementProtocolGuid,\r
f0da4d7d 925 EFI_NATIVE_INTERFACE,\r
137fb13d 926 &mPerformanceMeasurementInterface\r
f0da4d7d
SZ
927 );\r
928 ASSERT_EFI_ERROR (Status);\r
929\r
466ebdd2
DB
930 //\r
931 // Register SMI handler.\r
932 //\r
933 SmiHandle = NULL;\r
934 Status = gSmst->SmiHandlerRegister (FpdtSmiHandler, &gEfiFirmwarePerformanceGuid, &SmiHandle);\r
935 ASSERT_EFI_ERROR (Status);\r
936\r
9802d6d5 937 Status = EfiGetSystemConfigurationTable (&gPerformanceProtocolGuid, (VOID **) &PerformanceProperty);\r
cfb0aba7
SZ
938 if (EFI_ERROR (Status)) {\r
939 //\r
940 // Install configuration table for performance property.\r
941 //\r
942 mPerformanceProperty.Revision = PERFORMANCE_PROPERTY_REVISION;\r
943 mPerformanceProperty.Reserved = 0;\r
944 mPerformanceProperty.Frequency = GetPerformanceCounterProperties (\r
945 &mPerformanceProperty.TimerStartValue,\r
946 &mPerformanceProperty.TimerEndValue\r
947 );\r
948 Status = gBS->InstallConfigurationTable (&gPerformanceProtocolGuid, &mPerformanceProperty);\r
949 ASSERT_EFI_ERROR (Status);\r
950 }\r
d042c6e8 951}\r
952\r
953/**\r
2001f84e 954 The constructor function initializes the Performance Measurement Enable flag and\r
f0da4d7d 955 registers SmmBase2 protocol notify callback.\r
d042c6e8 956 It will ASSERT() if one of these operations fails and it will always return EFI_SUCCESS.\r
957\r
958 @param ImageHandle The firmware allocated handle for the EFI image.\r
959 @param SystemTable A pointer to the EFI System Table.\r
960\r
961 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.\r
962\r
963**/\r
964EFI_STATUS\r
965EFIAPI\r
966SmmCorePerformanceLibConstructor (\r
967 IN EFI_HANDLE ImageHandle,\r
968 IN EFI_SYSTEM_TABLE *SystemTable\r
969 )\r
970{\r
971 EFI_STATUS Status;\r
972 EFI_EVENT Event;\r
973 VOID *Registration;\r
974\r
2001f84e 975 if (!PerformanceMeasurementEnabled ()) {\r
d042c6e8 976 //\r
977 // Do not initialize performance infrastructure if not required.\r
978 //\r
979 return EFI_SUCCESS;\r
980 }\r
981\r
982 //\r
983 // Create the events to do the library init.\r
984 //\r
985 Status = gBS->CreateEvent (\r
986 EVT_NOTIFY_SIGNAL,\r
987 TPL_CALLBACK,\r
988 InitializeSmmCorePerformanceLib,\r
989 NULL,\r
990 &Event\r
991 );\r
992 ASSERT_EFI_ERROR (Status);\r
993\r
994 //\r
995 // Register for protocol notifications on this event\r
996 //\r
997 Status = gBS->RegisterProtocolNotify (\r
998 &gEfiSmmBase2ProtocolGuid,\r
999 Event,\r
1000 &Registration\r
1001 );\r
1002\r
1003 ASSERT_EFI_ERROR (Status);\r
1004\r
1005 return EFI_SUCCESS;\r
1006}\r
1007\r
137fb13d
BD
1008/**\r
1009 Create performance record with event description and a timestamp.\r
1010\r
1011 @param CallerIdentifier - Image handle or pointer to caller ID GUID.\r
1012 @param Guid - Pointer to a GUID.\r
1013 @param String - Pointer to a string describing the measurement.\r
1014 @param TimeStamp - 64-bit time stamp.\r
1015 @param Address - Pointer to a location in memory relevant to the measurement.\r
1016 @param Identifier - Performance identifier describing the type of measurement.\r
1017 @param Attribute - The attribute of the measurement. According to attribute can create a start\r
1018 record for PERF_START/PERF_START_EX, or a end record for PERF_END/PERF_END_EX,\r
1019 or a general record for other Perf macros.\r
1020\r
1021 @retval EFI_SUCCESS - Successfully created performance record.\r
1022 @retval EFI_OUT_OF_RESOURCES - Ran out of space to store the records.\r
1023 @retval EFI_INVALID_PARAMETER - Invalid parameter passed to function - NULL\r
1024 pointer or invalid PerfId.\r
1025**/\r
1026EFI_STATUS\r
1027EFIAPI\r
1028CreatePerformanceMeasurement(\r
e3917e22
MK
1029 IN CONST VOID *CallerIdentifier OPTIONAL,\r
1030 IN CONST VOID *Guid OPTIONAL,\r
1031 IN CONST CHAR8 *String OPTIONAL,\r
1032 IN UINT64 TimeStamp OPTIONAL,\r
1033 IN UINT64 Address OPTIONAL,\r
137fb13d
BD
1034 IN UINT32 Identifier,\r
1035 IN PERF_MEASUREMENT_ATTRIBUTE Attribute\r
1036 )\r
1037{\r
1038 EFI_STATUS Status;\r
1039\r
6b4d58a1
BD
1040 Status = EFI_SUCCESS;\r
1041\r
137fb13d 1042 AcquireSpinLock (&mSmmFpdtLock);\r
6b4d58a1 1043 Status = InsertFpdtRecord (CallerIdentifier, Guid, String, TimeStamp, Address, (UINT16)Identifier, Attribute);\r
137fb13d
BD
1044 ReleaseSpinLock (&mSmmFpdtLock);\r
1045 return Status;\r
1046}\r
1047\r
d042c6e8 1048/**\r
1049 Adds a record at the end of the performance measurement log\r
1050 that records the start time of a performance measurement.\r
1051\r
1052 Adds a record to the end of the performance measurement log\r
f0da4d7d 1053 that contains the Handle, Token, Module and Identifier.\r
d042c6e8 1054 The end time of the new record must be set to zero.\r
1055 If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record.\r
1056 If TimeStamp is zero, the start time in the record is filled in with the value\r
1057 read from the current time stamp.\r
1058\r
1059 @param Handle Pointer to environment specific context used\r
1060 to identify the component being measured.\r
1061 @param Token Pointer to a Null-terminated ASCII string\r
1062 that identifies the component being measured.\r
1063 @param Module Pointer to a Null-terminated ASCII string\r
1064 that identifies the module being measured.\r
1065 @param TimeStamp 64-bit time stamp.\r
f0da4d7d
SZ
1066 @param Identifier 32-bit identifier. If the value is 0, the created record\r
1067 is same as the one created by StartPerformanceMeasurement.\r
d042c6e8 1068\r
1069 @retval RETURN_SUCCESS The start of the measurement was recorded.\r
1070 @retval RETURN_OUT_OF_RESOURCES There are not enough resources to record the measurement.\r
1071\r
1072**/\r
1073RETURN_STATUS\r
1074EFIAPI\r
f0da4d7d 1075StartPerformanceMeasurementEx (\r
e3917e22
MK
1076 IN CONST VOID *Handle OPTIONAL,\r
1077 IN CONST CHAR8 *Token OPTIONAL,\r
1078 IN CONST CHAR8 *Module OPTIONAL,\r
f0da4d7d
SZ
1079 IN UINT64 TimeStamp,\r
1080 IN UINT32 Identifier\r
d042c6e8 1081 )\r
1082{\r
137fb13d
BD
1083 CONST CHAR8 *String;\r
1084\r
1085 if (Token != NULL) {\r
1086 String = Token;\r
1087 } else if (Module != NULL) {\r
1088 String = Module;\r
1089 } else {\r
1090 String = NULL;\r
1091 }\r
1092\r
1093 return (RETURN_STATUS)CreatePerformanceMeasurement (Handle, NULL, String, TimeStamp, 0, Identifier, PerfStartEntry);\r
d042c6e8 1094}\r
1095\r
1096/**\r
1097 Searches the performance measurement log from the beginning of the log\r
1098 for the first matching record that contains a zero end time and fills in a valid end time.\r
1099\r
1100 Searches the performance measurement log from the beginning of the log\r
2001f84e 1101 for the first record that matches Handle, Token, Module and Identifier and has an end time value of zero.\r
d042c6e8 1102 If the record can not be found then return RETURN_NOT_FOUND.\r
1103 If the record is found and TimeStamp is not zero,\r
1104 then the end time in the record is filled in with the value specified by TimeStamp.\r
1105 If the record is found and TimeStamp is zero, then the end time in the matching record\r
1106 is filled in with the current time stamp value.\r
1107\r
1108 @param Handle Pointer to environment specific context used\r
1109 to identify the component being measured.\r
1110 @param Token Pointer to a Null-terminated ASCII string\r
1111 that identifies the component being measured.\r
1112 @param Module Pointer to a Null-terminated ASCII string\r
1113 that identifies the module being measured.\r
1114 @param TimeStamp 64-bit time stamp.\r
f0da4d7d
SZ
1115 @param Identifier 32-bit identifier. If the value is 0, the found record\r
1116 is same as the one found by EndPerformanceMeasurement.\r
d042c6e8 1117\r
1118 @retval RETURN_SUCCESS The end of the measurement was recorded.\r
1119 @retval RETURN_NOT_FOUND The specified measurement record could not be found.\r
1120\r
1121**/\r
1122RETURN_STATUS\r
1123EFIAPI\r
f0da4d7d 1124EndPerformanceMeasurementEx (\r
e3917e22
MK
1125 IN CONST VOID *Handle OPTIONAL,\r
1126 IN CONST CHAR8 *Token OPTIONAL,\r
1127 IN CONST CHAR8 *Module OPTIONAL,\r
f0da4d7d
SZ
1128 IN UINT64 TimeStamp,\r
1129 IN UINT32 Identifier\r
d042c6e8 1130 )\r
1131{\r
137fb13d
BD
1132 CONST CHAR8 *String;\r
1133\r
1134 if (Token != NULL) {\r
1135 String = Token;\r
1136 } else if (Module != NULL) {\r
1137 String = Module;\r
1138 } else {\r
1139 String = NULL;\r
1140 }\r
1141\r
1142 return (RETURN_STATUS)CreatePerformanceMeasurement (Handle, NULL, String, TimeStamp, 0, Identifier, PerfEndEntry);\r
d042c6e8 1143}\r
1144\r
1145/**\r
1146 Attempts to retrieve a performance measurement log entry from the performance measurement log.\r
f0da4d7d
SZ
1147 It can also retrieve the log created by StartPerformanceMeasurement and EndPerformanceMeasurement,\r
1148 and then assign the Identifier with 0.\r
d042c6e8 1149\r
2001f84e
DB
1150 !!! Not Support!!!\r
1151\r
d042c6e8 1152 Attempts to retrieve the performance log entry specified by LogEntryKey. If LogEntryKey is\r
1153 zero on entry, then an attempt is made to retrieve the first entry from the performance log,\r
1154 and the key for the second entry in the log is returned. If the performance log is empty,\r
1155 then no entry is retrieved and zero is returned. If LogEntryKey is not zero, then the performance\r
1156 log entry associated with LogEntryKey is retrieved, and the key for the next entry in the log is\r
1157 returned. If LogEntryKey is the key for the last entry in the log, then the last log entry is\r
1158 retrieved and an implementation specific non-zero key value that specifies the end of the performance\r
1159 log is returned. If LogEntryKey is equal this implementation specific non-zero key value, then no entry\r
1160 is retrieved and zero is returned. In the cases where a performance log entry can be returned,\r
f0da4d7d 1161 the log entry is returned in Handle, Token, Module, StartTimeStamp, EndTimeStamp and Identifier.\r
d042c6e8 1162 If LogEntryKey is not a valid log entry key for the performance measurement log, then ASSERT().\r
1163 If Handle is NULL, then ASSERT().\r
1164 If Token is NULL, then ASSERT().\r
1165 If Module is NULL, then ASSERT().\r
1166 If StartTimeStamp is NULL, then ASSERT().\r
1167 If EndTimeStamp is NULL, then ASSERT().\r
f0da4d7d 1168 If Identifier is NULL, then ASSERT().\r
d042c6e8 1169\r
1170 @param LogEntryKey On entry, the key of the performance measurement log entry to retrieve.\r
1171 0, then the first performance measurement log entry is retrieved.\r
1172 On exit, the key of the next performance log entry.\r
1173 @param Handle Pointer to environment specific context used to identify the component\r
1174 being measured.\r
1175 @param Token Pointer to a Null-terminated ASCII string that identifies the component\r
1176 being measured.\r
1177 @param Module Pointer to a Null-terminated ASCII string that identifies the module\r
1178 being measured.\r
1179 @param StartTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement\r
1180 was started.\r
1181 @param EndTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement\r
1182 was ended.\r
f0da4d7d 1183 @param Identifier Pointer to the 32-bit identifier that was recorded.\r
d042c6e8 1184\r
1185 @return The key for the next performance log entry (in general case).\r
1186\r
1187**/\r
1188UINTN\r
1189EFIAPI\r
f0da4d7d 1190GetPerformanceMeasurementEx (\r
2001f84e 1191 IN UINTN LogEntryKey,\r
d042c6e8 1192 OUT CONST VOID **Handle,\r
1193 OUT CONST CHAR8 **Token,\r
1194 OUT CONST CHAR8 **Module,\r
1195 OUT UINT64 *StartTimeStamp,\r
f0da4d7d
SZ
1196 OUT UINT64 *EndTimeStamp,\r
1197 OUT UINT32 *Identifier\r
d042c6e8 1198 )\r
1199{\r
2001f84e 1200 return 0;\r
d042c6e8 1201}\r
1202\r
f0da4d7d
SZ
1203/**\r
1204 Adds a record at the end of the performance measurement log\r
1205 that records the start time of a performance measurement.\r
1206\r
1207 Adds a record to the end of the performance measurement log\r
1208 that contains the Handle, Token, and Module.\r
1209 The end time of the new record must be set to zero.\r
1210 If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record.\r
1211 If TimeStamp is zero, the start time in the record is filled in with the value\r
1212 read from the current time stamp.\r
1213\r
1214 @param Handle Pointer to environment specific context used\r
1215 to identify the component being measured.\r
1216 @param Token Pointer to a Null-terminated ASCII string\r
1217 that identifies the component being measured.\r
1218 @param Module Pointer to a Null-terminated ASCII string\r
1219 that identifies the module being measured.\r
1220 @param TimeStamp 64-bit time stamp.\r
1221\r
1222 @retval RETURN_SUCCESS The start of the measurement was recorded.\r
1223 @retval RETURN_OUT_OF_RESOURCES There are not enough resources to record the measurement.\r
1224\r
1225**/\r
1226RETURN_STATUS\r
1227EFIAPI\r
1228StartPerformanceMeasurement (\r
e3917e22
MK
1229 IN CONST VOID *Handle OPTIONAL,\r
1230 IN CONST CHAR8 *Token OPTIONAL,\r
1231 IN CONST CHAR8 *Module OPTIONAL,\r
f0da4d7d
SZ
1232 IN UINT64 TimeStamp\r
1233 )\r
1234{\r
137fb13d 1235 return StartPerformanceMeasurementEx (Handle, Token, Module, TimeStamp, 0);\r
f0da4d7d
SZ
1236}\r
1237\r
1238/**\r
1239 Searches the performance measurement log from the beginning of the log\r
1240 for the first matching record that contains a zero end time and fills in a valid end time.\r
1241\r
1242 Searches the performance measurement log from the beginning of the log\r
1243 for the first record that matches Handle, Token, and Module and has an end time value of zero.\r
1244 If the record can not be found then return RETURN_NOT_FOUND.\r
1245 If the record is found and TimeStamp is not zero,\r
1246 then the end time in the record is filled in with the value specified by TimeStamp.\r
1247 If the record is found and TimeStamp is zero, then the end time in the matching record\r
1248 is filled in with the current time stamp value.\r
1249\r
1250 @param Handle Pointer to environment specific context used\r
1251 to identify the component being measured.\r
1252 @param Token Pointer to a Null-terminated ASCII string\r
1253 that identifies the component being measured.\r
1254 @param Module Pointer to a Null-terminated ASCII string\r
1255 that identifies the module being measured.\r
1256 @param TimeStamp 64-bit time stamp.\r
1257\r
1258 @retval RETURN_SUCCESS The end of the measurement was recorded.\r
1259 @retval RETURN_NOT_FOUND The specified measurement record could not be found.\r
1260\r
1261**/\r
1262RETURN_STATUS\r
1263EFIAPI\r
1264EndPerformanceMeasurement (\r
e3917e22
MK
1265 IN CONST VOID *Handle OPTIONAL,\r
1266 IN CONST CHAR8 *Token OPTIONAL,\r
1267 IN CONST CHAR8 *Module OPTIONAL,\r
f0da4d7d
SZ
1268 IN UINT64 TimeStamp\r
1269 )\r
1270{\r
137fb13d 1271 return EndPerformanceMeasurementEx (Handle, Token, Module, TimeStamp, 0);\r
f0da4d7d
SZ
1272}\r
1273\r
1274/**\r
1275 Attempts to retrieve a performance measurement log entry from the performance measurement log.\r
1276 It can also retrieve the log created by StartPerformanceMeasurementEx and EndPerformanceMeasurementEx,\r
1277 and then eliminate the Identifier.\r
1278\r
2001f84e
DB
1279 !!! Not Support!!!\r
1280\r
f0da4d7d
SZ
1281 Attempts to retrieve the performance log entry specified by LogEntryKey. If LogEntryKey is\r
1282 zero on entry, then an attempt is made to retrieve the first entry from the performance log,\r
1283 and the key for the second entry in the log is returned. If the performance log is empty,\r
1284 then no entry is retrieved and zero is returned. If LogEntryKey is not zero, then the performance\r
1285 log entry associated with LogEntryKey is retrieved, and the key for the next entry in the log is\r
1286 returned. If LogEntryKey is the key for the last entry in the log, then the last log entry is\r
1287 retrieved and an implementation specific non-zero key value that specifies the end of the performance\r
1288 log is returned. If LogEntryKey is equal this implementation specific non-zero key value, then no entry\r
1289 is retrieved and zero is returned. In the cases where a performance log entry can be returned,\r
1290 the log entry is returned in Handle, Token, Module, StartTimeStamp, and EndTimeStamp.\r
1291 If LogEntryKey is not a valid log entry key for the performance measurement log, then ASSERT().\r
1292 If Handle is NULL, then ASSERT().\r
1293 If Token is NULL, then ASSERT().\r
1294 If Module is NULL, then ASSERT().\r
1295 If StartTimeStamp is NULL, then ASSERT().\r
1296 If EndTimeStamp is NULL, then ASSERT().\r
1297\r
1298 @param LogEntryKey On entry, the key of the performance measurement log entry to retrieve.\r
1299 0, then the first performance measurement log entry is retrieved.\r
1300 On exit, the key of the next performance log entry.\r
1301 @param Handle Pointer to environment specific context used to identify the component\r
1302 being measured.\r
1303 @param Token Pointer to a Null-terminated ASCII string that identifies the component\r
1304 being measured.\r
1305 @param Module Pointer to a Null-terminated ASCII string that identifies the module\r
1306 being measured.\r
1307 @param StartTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement\r
1308 was started.\r
1309 @param EndTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement\r
1310 was ended.\r
1311\r
1312 @return The key for the next performance log entry (in general case).\r
1313\r
1314**/\r
1315UINTN\r
1316EFIAPI\r
1317GetPerformanceMeasurement (\r
1318 IN UINTN LogEntryKey,\r
1319 OUT CONST VOID **Handle,\r
1320 OUT CONST CHAR8 **Token,\r
1321 OUT CONST CHAR8 **Module,\r
1322 OUT UINT64 *StartTimeStamp,\r
1323 OUT UINT64 *EndTimeStamp\r
1324 )\r
1325{\r
2001f84e 1326 return 0;\r
f0da4d7d
SZ
1327}\r
1328\r
d042c6e8 1329/**\r
1330 Returns TRUE if the performance measurement macros are enabled.\r
1331\r
1332 This function returns TRUE if the PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of\r
1333 PcdPerformanceLibraryPropertyMask is set. Otherwise FALSE is returned.\r
1334\r
1335 @retval TRUE The PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of\r
1336 PcdPerformanceLibraryPropertyMask is set.\r
1337 @retval FALSE The PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of\r
1338 PcdPerformanceLibraryPropertyMask is clear.\r
1339\r
1340**/\r
1341BOOLEAN\r
1342EFIAPI\r
1343PerformanceMeasurementEnabled (\r
1344 VOID\r
1345 )\r
1346{\r
2001f84e 1347 return (BOOLEAN) ((PcdGet8(PcdPerformanceLibraryPropertyMask) & PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED) != 0);\r
d042c6e8 1348}\r
6b4d58a1
BD
1349\r
1350/**\r
1351 Create performance record with event description and a timestamp.\r
1352\r
1353 @param CallerIdentifier - Image handle or pointer to caller ID GUID\r
1354 @param Guid - Pointer to a GUID\r
1355 @param String - Pointer to a string describing the measurement\r
1356 @param Address - Pointer to a location in memory relevant to the measurement\r
1357 @param Identifier - Performance identifier describing the type of measurement\r
1358\r
1359 @retval RETURN_SUCCESS - Successfully created performance record\r
1360 @retval RETURN_OUT_OF_RESOURCES - Ran out of space to store the records\r
1361 @retval RETURN_INVALID_PARAMETER - Invalid parameter passed to function - NULL\r
1362 pointer or invalid PerfId\r
1363\r
1364**/\r
1365RETURN_STATUS\r
1366EFIAPI\r
1367LogPerformanceMeasurement (\r
1368 IN CONST VOID *CallerIdentifier,\r
e3917e22
MK
1369 IN CONST VOID *Guid OPTIONAL,\r
1370 IN CONST CHAR8 *String OPTIONAL,\r
1371 IN UINT64 Address OPTIONAL,\r
6b4d58a1
BD
1372 IN UINT32 Identifier\r
1373 )\r
1374{\r
1375 return (RETURN_STATUS)CreatePerformanceMeasurement (CallerIdentifier, Guid, String, 0, Address, Identifier, PerfEntry);\r
1376}\r
1377\r
1378/**\r
1379 Check whether the specified performance measurement can be logged.\r
1380\r
1381 This function returns TRUE when the PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of PcdPerformanceLibraryPropertyMask is set\r
1382 and the Type disable bit in PcdPerformanceLibraryPropertyMask is not set.\r
1383\r
1384 @param Type - Type of the performance measurement entry.\r
1385\r
1386 @retval TRUE The performance measurement can be logged.\r
1387 @retval FALSE The performance measurement can NOT be logged.\r
1388\r
1389**/\r
1390BOOLEAN\r
1391EFIAPI\r
1392LogPerformanceMeasurementEnabled (\r
1393 IN CONST UINTN Type\r
1394 )\r
1395{\r
1396 //\r
1397 // When Performance measurement is enabled and the type is not filtered, the performance can be logged.\r
1398 //\r
1399 if (PerformanceMeasurementEnabled () && (PcdGet8(PcdPerformanceLibraryPropertyMask) & Type) == 0) {\r
1400 return TRUE;\r
1401 }\r
1402 return FALSE;\r
1403}\r