ProcTrace.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /** @file
  2. Intel Processor Trace feature.
  3. Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include "CpuCommonFeatures.h"
  7. ///
  8. /// This macro define the max entries in the Topa table.
  9. /// Each entry in the table contains some attribute bits, a pointer to an output region, and the size of the region.
  10. /// The last entry in the table may hold a pointer to the next table. This pointer can either point to the top of the
  11. /// current table (for circular array) or to the base of another table.
  12. /// At least 2 entries are needed because the list of entries must
  13. /// be terminated by an entry with the END bit set to 1, so 2
  14. /// entries are required to use a single valid entry.
  15. ///
  16. #define MAX_TOPA_ENTRY_COUNT 2
  17. ///
  18. /// Processor trace output scheme selection.
  19. ///
  20. typedef enum {
  21. RtitOutputSchemeSingleRange = 0,
  22. RtitOutputSchemeToPA
  23. } RTIT_OUTPUT_SCHEME;
  24. typedef struct {
  25. BOOLEAN TopaSupported;
  26. BOOLEAN SingleRangeSupported;
  27. MSR_IA32_RTIT_CTL_REGISTER RtitCtrl;
  28. MSR_IA32_RTIT_OUTPUT_BASE_REGISTER RtitOutputBase;
  29. MSR_IA32_RTIT_OUTPUT_MASK_PTRS_REGISTER RtitOutputMaskPtrs;
  30. } PROC_TRACE_PROCESSOR_DATA;
  31. typedef struct {
  32. UINT32 NumberOfProcessors;
  33. UINT8 ProcTraceOutputScheme;
  34. UINT32 ProcTraceMemSize;
  35. UINTN *ThreadMemRegionTable;
  36. UINTN AllocatedThreads;
  37. UINTN *TopaMemArray;
  38. PROC_TRACE_PROCESSOR_DATA *ProcessorData;
  39. } PROC_TRACE_DATA;
  40. typedef struct {
  41. RTIT_TOPA_TABLE_ENTRY TopaEntry[MAX_TOPA_ENTRY_COUNT];
  42. } PROC_TRACE_TOPA_TABLE;
  43. /**
  44. Prepares for the data used by CPU feature detection and initialization.
  45. @param[in] NumberOfProcessors The number of CPUs in the platform.
  46. @return Pointer to a buffer of CPU related configuration data.
  47. @note This service could be called by BSP only.
  48. **/
  49. VOID *
  50. EFIAPI
  51. ProcTraceGetConfigData (
  52. IN UINTN NumberOfProcessors
  53. )
  54. {
  55. PROC_TRACE_DATA *ConfigData;
  56. ConfigData = AllocateZeroPool (sizeof (PROC_TRACE_DATA) + sizeof (PROC_TRACE_PROCESSOR_DATA) * NumberOfProcessors);
  57. ASSERT (ConfigData != NULL);
  58. ConfigData->ProcessorData = (PROC_TRACE_PROCESSOR_DATA *)((UINT8 *)ConfigData + sizeof (PROC_TRACE_DATA));
  59. ConfigData->NumberOfProcessors = (UINT32)NumberOfProcessors;
  60. ConfigData->ProcTraceMemSize = PcdGet32 (PcdCpuProcTraceMemSize);
  61. ConfigData->ProcTraceOutputScheme = PcdGet8 (PcdCpuProcTraceOutputScheme);
  62. return ConfigData;
  63. }
  64. /**
  65. Detects if Intel Processor Trace feature supported on current
  66. processor.
  67. @param[in] ProcessorNumber The index of the CPU executing this function.
  68. @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION
  69. structure for the CPU executing this function.
  70. @param[in] ConfigData A pointer to the configuration buffer returned
  71. by CPU_FEATURE_GET_CONFIG_DATA. NULL if
  72. CPU_FEATURE_GET_CONFIG_DATA was not provided in
  73. RegisterCpuFeature().
  74. @retval TRUE Processor Trace feature is supported.
  75. @retval FALSE Processor Trace feature is not supported.
  76. @note This service could be called by BSP/APs.
  77. **/
  78. BOOLEAN
  79. EFIAPI
  80. ProcTraceSupport (
  81. IN UINTN ProcessorNumber,
  82. IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo,
  83. IN VOID *ConfigData OPTIONAL
  84. )
  85. {
  86. PROC_TRACE_DATA *ProcTraceData;
  87. CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX Ebx;
  88. CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF_ECX Ecx;
  89. //
  90. // Check if ProcTraceMemorySize option is enabled (0xFF means disable by user)
  91. //
  92. ProcTraceData = (PROC_TRACE_DATA *)ConfigData;
  93. ASSERT (ProcTraceData != NULL);
  94. if ((ProcTraceData->ProcTraceMemSize > RtitTopaMemorySize128M) ||
  95. (ProcTraceData->ProcTraceOutputScheme > RtitOutputSchemeToPA))
  96. {
  97. return FALSE;
  98. }
  99. //
  100. // Check if Processor Trace is supported
  101. //
  102. AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, 0, NULL, &Ebx.Uint32, NULL, NULL);
  103. if (Ebx.Bits.IntelProcessorTrace == 0) {
  104. return FALSE;
  105. }
  106. AsmCpuidEx (CPUID_INTEL_PROCESSOR_TRACE, CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF, NULL, NULL, &Ecx.Uint32, NULL);
  107. ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported = (BOOLEAN)(Ecx.Bits.RTIT == 1);
  108. ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported = (BOOLEAN)(Ecx.Bits.SingleRangeOutput == 1);
  109. if ((ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported && (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeToPA)) ||
  110. (ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported && (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeSingleRange)))
  111. {
  112. ProcTraceData->ProcessorData[ProcessorNumber].RtitCtrl.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_CTL);
  113. ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_OUTPUT_BASE);
  114. ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_OUTPUT_MASK_PTRS);
  115. return TRUE;
  116. }
  117. return FALSE;
  118. }
  119. /**
  120. Initializes Intel Processor Trace feature to specific state.
  121. @param[in] ProcessorNumber The index of the CPU executing this function.
  122. @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION
  123. structure for the CPU executing this function.
  124. @param[in] ConfigData A pointer to the configuration buffer returned
  125. by CPU_FEATURE_GET_CONFIG_DATA. NULL if
  126. CPU_FEATURE_GET_CONFIG_DATA was not provided in
  127. RegisterCpuFeature().
  128. @param[in] State If TRUE, then the Processor Trace feature must be
  129. enabled.
  130. If FALSE, then the Processor Trace feature must be
  131. disabled.
  132. @retval RETURN_SUCCESS Intel Processor Trace feature is initialized.
  133. **/
  134. RETURN_STATUS
  135. EFIAPI
  136. ProcTraceInitialize (
  137. IN UINTN ProcessorNumber,
  138. IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo,
  139. IN VOID *ConfigData OPTIONAL,
  140. IN BOOLEAN State
  141. )
  142. {
  143. UINT32 MemRegionSize;
  144. UINTN Pages;
  145. UINTN Alignment;
  146. UINTN MemRegionBaseAddr;
  147. UINTN *ThreadMemRegionTable;
  148. UINTN Index;
  149. UINTN TopaTableBaseAddr;
  150. UINTN AlignedAddress;
  151. UINTN *TopaMemArray;
  152. PROC_TRACE_TOPA_TABLE *TopaTable;
  153. PROC_TRACE_DATA *ProcTraceData;
  154. BOOLEAN FirstIn;
  155. MSR_IA32_RTIT_CTL_REGISTER CtrlReg;
  156. MSR_IA32_RTIT_STATUS_REGISTER StatusReg;
  157. MSR_IA32_RTIT_OUTPUT_BASE_REGISTER OutputBaseReg;
  158. MSR_IA32_RTIT_OUTPUT_MASK_PTRS_REGISTER OutputMaskPtrsReg;
  159. RTIT_TOPA_TABLE_ENTRY *TopaEntryPtr;
  160. //
  161. // The scope of the MSR_IA32_RTIT_* is core for below processor type, only program
  162. // MSR_IA32_RTIT_* for thread 0 in each core.
  163. //
  164. if (IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) ||
  165. IS_GOLDMONT_PLUS_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel))
  166. {
  167. if (CpuInfo->ProcessorInfo.Location.Thread != 0) {
  168. return RETURN_SUCCESS;
  169. }
  170. }
  171. ProcTraceData = (PROC_TRACE_DATA *)ConfigData;
  172. ASSERT (ProcTraceData != NULL);
  173. //
  174. // Clear MSR_IA32_RTIT_CTL[0] and IA32_RTIT_STS only if MSR_IA32_RTIT_CTL[0]==1b
  175. //
  176. CtrlReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitCtrl.Uint64;
  177. if (CtrlReg.Bits.TraceEn != 0) {
  178. ///
  179. /// Clear bit 0 in MSR IA32_RTIT_CTL (570)
  180. ///
  181. CtrlReg.Bits.TraceEn = 0;
  182. CPU_REGISTER_TABLE_WRITE64 (
  183. ProcessorNumber,
  184. Msr,
  185. MSR_IA32_RTIT_CTL,
  186. CtrlReg.Uint64
  187. );
  188. ///
  189. /// Clear MSR IA32_RTIT_STS (571h) to all zeros
  190. ///
  191. StatusReg.Uint64 = 0x0;
  192. CPU_REGISTER_TABLE_WRITE64 (
  193. ProcessorNumber,
  194. Msr,
  195. MSR_IA32_RTIT_STATUS,
  196. StatusReg.Uint64
  197. );
  198. }
  199. if (!State) {
  200. return RETURN_SUCCESS;
  201. }
  202. MemRegionBaseAddr = 0;
  203. FirstIn = FALSE;
  204. if (ProcTraceData->ThreadMemRegionTable == NULL) {
  205. FirstIn = TRUE;
  206. DEBUG ((DEBUG_INFO, "Initialize Processor Trace\n"));
  207. }
  208. ///
  209. /// Refer to PROC_TRACE_MEM_SIZE Table for Size Encoding
  210. ///
  211. MemRegionSize = (UINT32)(1 << (ProcTraceData->ProcTraceMemSize + 12));
  212. if (FirstIn) {
  213. DEBUG ((DEBUG_INFO, "ProcTrace: MemSize requested: 0x%X \n", MemRegionSize));
  214. }
  215. if (FirstIn) {
  216. //
  217. // Let BSP allocate and create the necessary memory region (Aligned to the size of
  218. // the memory region from setup option(ProcTraceMemSize) which is an integral multiple of 4kB)
  219. // for all the enabled threads to store Processor Trace debug data. Then Configure the trace
  220. // address base in MSR, IA32_RTIT_OUTPUT_BASE (560h) bits 47:12. Note that all regions must be
  221. // aligned based on their size, not just 4K. Thus a 2M region must have bits 20:12 cleared.
  222. //
  223. ThreadMemRegionTable = (UINTN *)AllocatePool (ProcTraceData->NumberOfProcessors * sizeof (UINTN *));
  224. if (ThreadMemRegionTable == NULL) {
  225. DEBUG ((DEBUG_ERROR, "Allocate ProcTrace ThreadMemRegionTable Failed\n"));
  226. return RETURN_OUT_OF_RESOURCES;
  227. }
  228. ProcTraceData->ThreadMemRegionTable = ThreadMemRegionTable;
  229. for (Index = 0; Index < ProcTraceData->NumberOfProcessors; Index++, ProcTraceData->AllocatedThreads++) {
  230. Pages = EFI_SIZE_TO_PAGES (MemRegionSize);
  231. Alignment = MemRegionSize;
  232. AlignedAddress = (UINTN)AllocateAlignedReservedPages (Pages, Alignment);
  233. if (AlignedAddress == 0) {
  234. DEBUG ((DEBUG_ERROR, "ProcTrace: Out of mem, allocated only for %d threads\n", ProcTraceData->AllocatedThreads));
  235. if (Index == 0) {
  236. //
  237. // Could not allocate for BSP even
  238. //
  239. FreePool ((VOID *)ThreadMemRegionTable);
  240. ThreadMemRegionTable = NULL;
  241. return RETURN_OUT_OF_RESOURCES;
  242. }
  243. break;
  244. }
  245. ThreadMemRegionTable[Index] = AlignedAddress;
  246. DEBUG ((DEBUG_INFO, "ProcTrace: PT MemRegionBaseAddr(aligned) for thread %d: 0x%llX \n", Index, (UINT64)ThreadMemRegionTable[Index]));
  247. }
  248. DEBUG ((DEBUG_INFO, "ProcTrace: Allocated PT mem for %d thread \n", ProcTraceData->AllocatedThreads));
  249. }
  250. if (ProcessorNumber < ProcTraceData->AllocatedThreads) {
  251. MemRegionBaseAddr = ProcTraceData->ThreadMemRegionTable[ProcessorNumber];
  252. } else {
  253. return RETURN_SUCCESS;
  254. }
  255. ///
  256. /// Check Processor Trace output scheme: Single Range output or ToPA table
  257. ///
  258. //
  259. // Single Range output scheme
  260. //
  261. if (ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported &&
  262. (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeSingleRange))
  263. {
  264. if (FirstIn) {
  265. DEBUG ((DEBUG_INFO, "ProcTrace: Enabling Single Range Output scheme \n"));
  266. }
  267. //
  268. // Clear MSR IA32_RTIT_CTL (0x570) ToPA (Bit 8)
  269. //
  270. CtrlReg.Bits.ToPA = 0;
  271. CPU_REGISTER_TABLE_WRITE64 (
  272. ProcessorNumber,
  273. Msr,
  274. MSR_IA32_RTIT_CTL,
  275. CtrlReg.Uint64
  276. );
  277. //
  278. // Program MSR IA32_RTIT_OUTPUT_BASE (0x560) bits[63:7] with the allocated Memory Region
  279. //
  280. OutputBaseReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64;
  281. OutputBaseReg.Bits.Base = (MemRegionBaseAddr >> 7) & 0x01FFFFFF;
  282. OutputBaseReg.Bits.BaseHi = RShiftU64 ((UINT64)MemRegionBaseAddr, 32) & 0xFFFFFFFF;
  283. CPU_REGISTER_TABLE_WRITE64 (
  284. ProcessorNumber,
  285. Msr,
  286. MSR_IA32_RTIT_OUTPUT_BASE,
  287. OutputBaseReg.Uint64
  288. );
  289. //
  290. // Program the Mask bits for the Memory Region to MSR IA32_RTIT_OUTPUT_MASK_PTRS (561h)
  291. //
  292. OutputMaskPtrsReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64;
  293. OutputMaskPtrsReg.Bits.MaskOrTableOffset = ((MemRegionSize - 1) >> 7) & 0x01FFFFFF;
  294. OutputMaskPtrsReg.Bits.OutputOffset = RShiftU64 (MemRegionSize - 1, 32) & 0xFFFFFFFF;
  295. CPU_REGISTER_TABLE_WRITE64 (
  296. ProcessorNumber,
  297. Msr,
  298. MSR_IA32_RTIT_OUTPUT_MASK_PTRS,
  299. OutputMaskPtrsReg.Uint64
  300. );
  301. }
  302. //
  303. // ToPA(Table of physical address) scheme
  304. //
  305. if (ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported &&
  306. (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeToPA))
  307. {
  308. //
  309. // Create ToPA structure aligned at 4KB for each logical thread
  310. // with at least 2 entries by 8 bytes size each. The first entry
  311. // should have the trace output base address in bits 47:12, 6:9
  312. // for Size, bits 4,2 and 0 must be cleared. The second entry
  313. // should have the base address of the table location in bits
  314. // 47:12, bits 4 and 2 must be cleared and bit 0 must be set.
  315. //
  316. if (FirstIn) {
  317. DEBUG ((DEBUG_INFO, "ProcTrace: Enabling ToPA scheme \n"));
  318. //
  319. // Let BSP allocate ToPA table mem for all threads
  320. //
  321. TopaMemArray = (UINTN *)AllocatePool (ProcTraceData->AllocatedThreads * sizeof (UINTN *));
  322. if (TopaMemArray == NULL) {
  323. DEBUG ((DEBUG_ERROR, "ProcTrace: Allocate mem for ToPA Failed\n"));
  324. return RETURN_OUT_OF_RESOURCES;
  325. }
  326. ProcTraceData->TopaMemArray = TopaMemArray;
  327. for (Index = 0; Index < ProcTraceData->AllocatedThreads; Index++) {
  328. Pages = EFI_SIZE_TO_PAGES (sizeof (PROC_TRACE_TOPA_TABLE));
  329. Alignment = 0x1000;
  330. AlignedAddress = (UINTN)AllocateAlignedReservedPages (Pages, Alignment);
  331. if (AlignedAddress == 0) {
  332. if (Index < ProcTraceData->AllocatedThreads) {
  333. ProcTraceData->AllocatedThreads = Index;
  334. }
  335. DEBUG ((DEBUG_ERROR, "ProcTrace: Out of mem, allocated ToPA mem only for %d threads\n", ProcTraceData->AllocatedThreads));
  336. if (Index == 0) {
  337. //
  338. // Could not allocate for BSP even
  339. //
  340. FreePool ((VOID *)TopaMemArray);
  341. TopaMemArray = NULL;
  342. return RETURN_OUT_OF_RESOURCES;
  343. }
  344. break;
  345. }
  346. TopaMemArray[Index] = AlignedAddress;
  347. DEBUG ((DEBUG_INFO, "ProcTrace: Topa table address(aligned) for thread %d is 0x%llX \n", Index, (UINT64)TopaMemArray[Index]));
  348. }
  349. DEBUG ((DEBUG_INFO, "ProcTrace: Allocated ToPA mem for %d thread \n", ProcTraceData->AllocatedThreads));
  350. }
  351. if (ProcessorNumber < ProcTraceData->AllocatedThreads) {
  352. TopaTableBaseAddr = ProcTraceData->TopaMemArray[ProcessorNumber];
  353. } else {
  354. return RETURN_SUCCESS;
  355. }
  356. TopaTable = (PROC_TRACE_TOPA_TABLE *)TopaTableBaseAddr;
  357. TopaEntryPtr = &TopaTable->TopaEntry[0];
  358. TopaEntryPtr->Uint64 = 0;
  359. TopaEntryPtr->Bits.Base = (MemRegionBaseAddr >> 12) & 0x000FFFFF;
  360. TopaEntryPtr->Bits.BaseHi = RShiftU64 ((UINT64)MemRegionBaseAddr, 32) & 0xFFFFFFFF;
  361. TopaEntryPtr->Bits.Size = ProcTraceData->ProcTraceMemSize;
  362. TopaEntryPtr->Bits.END = 0;
  363. TopaEntryPtr = &TopaTable->TopaEntry[1];
  364. TopaEntryPtr->Uint64 = 0;
  365. TopaEntryPtr->Bits.Base = (TopaTableBaseAddr >> 12) & 0x000FFFFF;
  366. TopaEntryPtr->Bits.BaseHi = RShiftU64 ((UINT64)TopaTableBaseAddr, 32) & 0xFFFFFFFF;
  367. TopaEntryPtr->Bits.END = 1;
  368. //
  369. // Program the MSR IA32_RTIT_OUTPUT_BASE (0x560) bits[63:7] with ToPA base
  370. //
  371. OutputBaseReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64;
  372. OutputBaseReg.Bits.Base = (TopaTableBaseAddr >> 7) & 0x01FFFFFF;
  373. OutputBaseReg.Bits.BaseHi = RShiftU64 ((UINT64)TopaTableBaseAddr, 32) & 0xFFFFFFFF;
  374. CPU_REGISTER_TABLE_WRITE64 (
  375. ProcessorNumber,
  376. Msr,
  377. MSR_IA32_RTIT_OUTPUT_BASE,
  378. OutputBaseReg.Uint64
  379. );
  380. //
  381. // Set the MSR IA32_RTIT_OUTPUT_MASK (0x561) bits[63:7] to 0
  382. //
  383. OutputMaskPtrsReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64;
  384. OutputMaskPtrsReg.Bits.MaskOrTableOffset = 0;
  385. OutputMaskPtrsReg.Bits.OutputOffset = 0;
  386. CPU_REGISTER_TABLE_WRITE64 (
  387. ProcessorNumber,
  388. Msr,
  389. MSR_IA32_RTIT_OUTPUT_MASK_PTRS,
  390. OutputMaskPtrsReg.Uint64
  391. );
  392. //
  393. // Enable ToPA output scheme by enabling MSR IA32_RTIT_CTL (0x570) ToPA (Bit 8)
  394. //
  395. CtrlReg.Bits.ToPA = 1;
  396. CPU_REGISTER_TABLE_WRITE64 (
  397. ProcessorNumber,
  398. Msr,
  399. MSR_IA32_RTIT_CTL,
  400. CtrlReg.Uint64
  401. );
  402. }
  403. ///
  404. /// Enable the Processor Trace feature from MSR IA32_RTIT_CTL (570h)
  405. ///
  406. CtrlReg.Bits.OS = 1;
  407. CtrlReg.Bits.User = 1;
  408. CtrlReg.Bits.BranchEn = 1;
  409. CtrlReg.Bits.TraceEn = 1;
  410. CPU_REGISTER_TABLE_WRITE64 (
  411. ProcessorNumber,
  412. Msr,
  413. MSR_IA32_RTIT_CTL,
  414. CtrlReg.Uint64
  415. );
  416. return RETURN_SUCCESS;
  417. }