proc.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. proc.c
  5. Abstract:
  6. This module implements support for processor devices in ACPI.
  7. Author:
  8. Evan Green 28-Sep-2015
  9. Environment:
  10. Kernel
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/kernel/driver.h>
  16. #include "acpip.h"
  17. #include "proc.h"
  18. #include "namespce.h"
  19. #include "resdesc.h"
  20. //
  21. // ---------------------------------------------------------------- Definitions
  22. //
  23. //
  24. // ------------------------------------------------------ Data Type Definitions
  25. //
  26. //
  27. // ----------------------------------------------- Internal Function Prototypes
  28. //
  29. KSTATUS
  30. AcpipCreateGlobalProcessorContext (
  31. VOID
  32. );
  33. VOID
  34. AcpipProcessorInitializeCStates (
  35. PACPI_OBJECT NamespaceObject,
  36. PACPI_PROCESSOR_CONTEXT Device
  37. );
  38. KSTATUS
  39. AcpipInitializeCStatesOnProcessor (
  40. PPM_IDLE_STATE_INTERFACE Interface,
  41. PPM_IDLE_PROCESSOR_STATE Processor
  42. );
  43. KSTATUS
  44. AcpipProcessorGetOsProcessorId (
  45. ULONG AcpiId,
  46. PULONG OsId
  47. );
  48. ULONG
  49. AcpipGetProcessorCount (
  50. VOID
  51. );
  52. //
  53. // -------------------------------------------------------------------- Globals
  54. //
  55. //
  56. // Set this boolean to print processor power enumeration information.
  57. //
  58. BOOL AcpiDebugProcessorPowerEnumeration = FALSE;
  59. //
  60. // Remember the number of processors and how many have successfully started.
  61. //
  62. ULONG AcpiProcessorCount;
  63. ULONG AcpiInitializedProcessorCount;
  64. PACPI_PROCESSOR_GLOBAL_CONTEXT AcpiProcessor;
  65. //
  66. // ------------------------------------------------------------------ Functions
  67. //
  68. KSTATUS
  69. AcpipProcessorStart (
  70. PACPI_DEVICE_CONTEXT Device
  71. )
  72. /*++
  73. Routine Description:
  74. This routine starts an ACPI processor object.
  75. Arguments:
  76. Device - Supplies a pointer to the ACPI information associated with
  77. the processor device.
  78. Return Value:
  79. Status code.
  80. --*/
  81. {
  82. ULONG AcpiId;
  83. ULONG BlockAddress;
  84. ULONG BlockSize;
  85. PACPI_OBJECT NamespaceObject;
  86. ULONG OsId;
  87. PACPI_PROCESSOR_CONTEXT Processor;
  88. ULONG ReadyCount;
  89. UINTN Size;
  90. KSTATUS Status;
  91. PACPI_OBJECT UidMethod;
  92. PACPI_OBJECT UidReturnValue;
  93. Processor = NULL;
  94. UidReturnValue = NULL;
  95. if (Device->Processor != NULL) {
  96. Status = STATUS_SUCCESS;
  97. goto ProcessorStartEnd;
  98. }
  99. if (AcpiProcessor == NULL) {
  100. Status = AcpipCreateGlobalProcessorContext();
  101. if (!KSUCCESS(Status)) {
  102. goto ProcessorStartEnd;
  103. }
  104. }
  105. //
  106. // Perform architecture specific initialization before evaluating processor
  107. // methods, such as calling _OSC.
  108. //
  109. Status = AcpipArchInitializeProcessorManagement(Device->NamespaceObject);
  110. if (!KSUCCESS(Status)) {
  111. goto ProcessorStartEnd;
  112. }
  113. //
  114. // Get the ACPI processor ID.
  115. //
  116. AcpiId = -1;
  117. BlockAddress = 0;
  118. BlockSize = 0;
  119. NamespaceObject = Device->NamespaceObject;
  120. if (NamespaceObject->Type == AcpiObjectProcessor) {
  121. AcpiId = NamespaceObject->U.Processor.ProcessorId;
  122. BlockAddress = NamespaceObject->U.Processor.ProcessorBlockAddress;
  123. BlockSize = NamespaceObject->U.Processor.ProcessorBlockLength;
  124. } else {
  125. ASSERT(NamespaceObject->Type == AcpiObjectDevice);
  126. //
  127. // Attempt to find and execute the _UID function.
  128. //
  129. UidMethod = AcpipFindNamedObject(NamespaceObject, ACPI_METHOD__UID);
  130. if (UidMethod == NULL) {
  131. Status = STATUS_DEVICE_NOT_CONNECTED;
  132. goto ProcessorStartEnd;
  133. }
  134. Status = AcpiExecuteMethod(UidMethod, NULL, 0, 0, &UidReturnValue);
  135. if (!KSUCCESS(Status)) {
  136. goto ProcessorStartEnd;
  137. }
  138. if (UidReturnValue == NULL) {
  139. Status = STATUS_INVALID_CONFIGURATION;
  140. goto ProcessorStartEnd;
  141. }
  142. //
  143. // Convert to a device ID string if needed.
  144. //
  145. if (UidReturnValue->Type == AcpiObjectInteger) {
  146. AcpiId = UidReturnValue->U.Integer.Value;
  147. } else if (UidReturnValue->Type == AcpiObjectString) {
  148. //
  149. // Consider supporting processor strings if needed.
  150. //
  151. ASSERT(FALSE);
  152. Status = STATUS_NOT_SUPPORTED;
  153. goto ProcessorStartEnd;
  154. }
  155. }
  156. //
  157. // Match the ACPI processor ID with an entry in the MADT, and determine the
  158. // OS processor number from that.
  159. //
  160. Status = AcpipProcessorGetOsProcessorId(AcpiId, &OsId);
  161. if (!KSUCCESS(Status)) {
  162. goto ProcessorStartEnd;
  163. }
  164. ASSERT(OsId < AcpiProcessor->ProcessorCount);
  165. Processor = &(AcpiProcessor->Processors[OsId]);
  166. Processor->BlockAddress = BlockAddress;
  167. Processor->BlockSize = BlockSize;
  168. Processor->AcpiId = AcpiId;
  169. Processor->OsId = OsId;
  170. AcpipProcessorInitializeCStates(NamespaceObject, Processor);
  171. //
  172. // This processor device is initialized. If this was the last one, then
  173. // register processor power management facilities.
  174. //
  175. ReadyCount = RtlAtomicAdd32(&(AcpiProcessor->StartedProcessorCount), 1) + 1;
  176. ASSERT(ReadyCount <= AcpiProcessor->ProcessorCount);
  177. if (ReadyCount == AcpiProcessor->ProcessorCount) {
  178. //
  179. // Register C-State handlers. Failure is not fatal.
  180. //
  181. if (Processor->CStateCount != 0) {
  182. Size = sizeof(PM_IDLE_STATE_INTERFACE);
  183. KeGetSetSystemInformation(SystemInformationPm,
  184. PmInformationIdleStateHandlers,
  185. &(AcpiProcessor->CStateInterface),
  186. &Size,
  187. TRUE);
  188. }
  189. }
  190. Status = STATUS_SUCCESS;
  191. ProcessorStartEnd:
  192. if (UidReturnValue != NULL) {
  193. AcpipObjectReleaseReference(UidReturnValue);
  194. }
  195. return Status;
  196. }
  197. //
  198. // --------------------------------------------------------- Internal Functions
  199. //
  200. KSTATUS
  201. AcpipCreateGlobalProcessorContext (
  202. VOID
  203. )
  204. /*++
  205. Routine Description:
  206. This routine creates and initializes the global ACPI processor context.
  207. Arguments:
  208. None.
  209. Return Value:
  210. None. Failure to initialize C-States is not fatal.
  211. --*/
  212. {
  213. UINTN AllocationSize;
  214. PACPI_PROCESSOR_GLOBAL_CONTEXT Context;
  215. ULONG ProcessorCount;
  216. KSTATUS Status;
  217. ProcessorCount = AcpipGetProcessorCount();
  218. AllocationSize = sizeof(ACPI_PROCESSOR_GLOBAL_CONTEXT) +
  219. (sizeof(ACPI_PROCESSOR_CONTEXT) * ProcessorCount);
  220. Context = MmAllocateNonPagedPool(AllocationSize, ACPI_ALLOCATION_TAG);
  221. if (Context == NULL) {
  222. Status = STATUS_INSUFFICIENT_RESOURCES;
  223. goto CreateGlobalProcessorContextEnd;
  224. }
  225. RtlZeroMemory(Context, AllocationSize);
  226. Context->ProcessorCount = ProcessorCount;
  227. Context->Processors = (PACPI_PROCESSOR_CONTEXT)(Context + 1);
  228. Context->CStateInterface.InitializeIdleStates =
  229. AcpipInitializeCStatesOnProcessor;
  230. Context->CStateInterface.EnterIdleState = AcpipEnterCState;
  231. Context->CStateInterface.Context = Context;
  232. Status = STATUS_SUCCESS;
  233. CreateGlobalProcessorContextEnd:
  234. if (!KSUCCESS(Status)) {
  235. if (Context != NULL) {
  236. MmFreeNonPagedPool(Context);
  237. }
  238. }
  239. //
  240. // If device enumeration is parallelized, this should be an atomic compare
  241. // exchange and then a subsequent free if the exchange is lost.
  242. //
  243. ASSERT(AcpiProcessor == NULL);
  244. AcpiProcessor = Context;
  245. return Status;
  246. }
  247. VOID
  248. AcpipProcessorInitializeCStates (
  249. PACPI_OBJECT NamespaceObject,
  250. PACPI_PROCESSOR_CONTEXT Device
  251. )
  252. /*++
  253. Routine Description:
  254. This routine attempts to find and initialize the OS logical processor
  255. number for the given processor device.
  256. Arguments:
  257. NamespaceObject - Supplies the processor namespace object.
  258. Device - Supplies a pointer to the ACPI information associated with
  259. the processor device. On success, the OS ID will be filled in.
  260. Return Value:
  261. None. Failure to initialize C-States is not fatal.
  262. --*/
  263. {
  264. PACPI_CSTATE AcpiCState;
  265. ULONG Count;
  266. PACPI_OBJECT CountObject;
  267. PACPI_OBJECT Cst;
  268. ULONG CStateIndex;
  269. PACPI_OBJECT CStateLatencyObject;
  270. PACPI_OBJECT CStatePowerObject;
  271. PACPI_OBJECT CStateRegister;
  272. ACPI_CSTATE_TYPE CStateType;
  273. PACPI_OBJECT CStateTypeObject;
  274. PACPI_OBJECT CstMethod;
  275. ULONG Latency;
  276. PPM_IDLE_STATE OsCState;
  277. ULONG Power;
  278. PACPI_OBJECT StatePackage;
  279. KSTATUS Status;
  280. Cst = NULL;
  281. if (AcpiDebugProcessorPowerEnumeration != FALSE) {
  282. RtlDebugPrint("Processor %d (ACPI %d) C-States\n",
  283. Device->OsId,
  284. Device->AcpiId);
  285. }
  286. //
  287. // Attempt to find and execute the _CST function.
  288. //
  289. CstMethod = AcpipFindNamedObject(NamespaceObject, ACPI_METHOD__CST);
  290. if (CstMethod == NULL) {
  291. Status = STATUS_NOT_SUPPORTED;
  292. goto ProcessorInitializeCStatesEnd;
  293. }
  294. Status = AcpiExecuteMethod(CstMethod, NULL, 0, 0, &Cst);
  295. if (!KSUCCESS(Status)) {
  296. goto ProcessorInitializeCStatesEnd;
  297. }
  298. if ((Cst == NULL) || (Cst->Type != AcpiObjectPackage) ||
  299. (Cst->U.Package.ElementCount < 2)) {
  300. Status = STATUS_INVALID_CONFIGURATION;
  301. goto ProcessorInitializeCStatesEnd;
  302. }
  303. CountObject = Cst->U.Package.Array[0];
  304. Count = CountObject->U.Integer.Value;
  305. if ((CountObject->Type != AcpiObjectInteger) ||
  306. (Count + 1 > Cst->U.Package.ElementCount)) {
  307. Status = STATUS_INVALID_CONFIGURATION;
  308. goto ProcessorInitializeCStatesEnd;
  309. }
  310. if (Count > ACPI_MAX_CSTATES) {
  311. RtlDebugPrint("%d C-States!\n", Count);
  312. Count = ACPI_MAX_CSTATES;
  313. }
  314. Device->HighestNonC3 = 0;
  315. //
  316. // Loop over all the enumerated C-states.
  317. //
  318. for (CStateIndex = 0; CStateIndex < Count; CStateIndex += 1) {
  319. StatePackage = Cst->U.Package.Array[1 + CStateIndex];
  320. if ((StatePackage->Type != AcpiObjectPackage) ||
  321. (StatePackage->U.Package.ElementCount < 4)) {
  322. Status = STATUS_INVALID_CONFIGURATION;
  323. goto ProcessorInitializeCStatesEnd;
  324. }
  325. CStateRegister = StatePackage->U.Package.Array[0];
  326. CStateTypeObject = StatePackage->U.Package.Array[1];
  327. CStateLatencyObject = StatePackage->U.Package.Array[2];
  328. CStatePowerObject = StatePackage->U.Package.Array[3];
  329. if ((CStateRegister->Type != AcpiObjectBuffer) ||
  330. (CStateTypeObject->Type != AcpiObjectInteger) ||
  331. (CStateTypeObject->Type != AcpiObjectInteger) ||
  332. (CStatePowerObject->Type != AcpiObjectInteger)) {
  333. Status = STATUS_INVALID_CONFIGURATION;
  334. goto ProcessorInitializeCStatesEnd;
  335. }
  336. CStateType = CStateTypeObject->U.Integer.Value;
  337. Latency = CStateLatencyObject->U.Integer.Value;
  338. Power = CStatePowerObject->U.Integer.Value;
  339. AcpiCState = &(Device->AcpiCStates[CStateIndex]);
  340. OsCState = &(Device->OsCStates[CStateIndex]);
  341. Status = AcpipParseGenericAddress(CStateRegister,
  342. &(AcpiCState->Register));
  343. if (!KSUCCESS(Status)) {
  344. goto ProcessorInitializeCStatesEnd;
  345. }
  346. //
  347. // Determine if this is Intel-specific fixed function hardware.
  348. //
  349. if ((AcpiCState->Register.AddressSpaceId ==
  350. AddressSpaceFixedHardware) &&
  351. (AcpiCState->Register.RegisterBitWidth ==
  352. ACPI_FIXED_HARDWARE_INTEL)) {
  353. switch (AcpiCState->Register.RegisterBitOffset) {
  354. case ACPI_FIXED_HARDWARE_INTEL_CST_HALT:
  355. AcpiCState->Flags |= ACPI_CSTATE_HALT;
  356. break;
  357. case ACPI_FIXED_HARDWARE_INTEL_CST_IO_HALT:
  358. AcpiCState->Flags |= ACPI_CSTATE_IO_HALT;
  359. break;
  360. case ACPI_FIXED_HARDWARE_INTEL_CST_MWAIT:
  361. AcpiCState->Flags |= ACPI_CSTATE_MWAIT;
  362. if ((AcpiCState->Register.AccessSize &
  363. ACPI_INTEL_MWAIT_BUS_MASTER_AVOIDANCE) != 0) {
  364. AcpiCState->Flags |= ACPI_CSTATE_BUS_MASTER_AVOIDANCE;
  365. }
  366. break;
  367. }
  368. }
  369. AcpiCState->Type = CStateType;
  370. AcpiCState->Latency = Latency;
  371. AcpiCState->Power = Power;
  372. if (AcpiDebugProcessorPowerEnumeration != FALSE) {
  373. RtlDebugPrint("C%d: Type %d Latency %d us, Power %d mw\n",
  374. CStateIndex + 1,
  375. CStateType,
  376. Latency,
  377. Power);
  378. }
  379. //
  380. // Remember the highest C-state that is not C3 in case there is a
  381. // fallback from a C3 transition that couldn't happen.
  382. //
  383. if ((CStateType < AcpiC3) && (CStateIndex > Device->HighestNonC3)) {
  384. Device->HighestNonC3 = CStateIndex;
  385. }
  386. //
  387. // Initialize the target residency to twice the latency, assuming it
  388. // takes that much time to get in and get out.
  389. //
  390. OsCState->Name[0] = 'C';
  391. OsCState->Name[1] = '0' + CStateIndex + 1;
  392. OsCState->Name[2] = '\0';
  393. OsCState->ExitLatency = KeConvertMicrosecondsToTimeTicks(Latency);
  394. OsCState->TargetResidency = OsCState->ExitLatency * 2;
  395. }
  396. Device->CStateCount = Count;
  397. Status = STATUS_SUCCESS;
  398. ProcessorInitializeCStatesEnd:
  399. if (!KSUCCESS(Status)) {
  400. if (AcpiDebugProcessorPowerEnumeration != FALSE) {
  401. RtlDebugPrint("ACPI: C-State init failed on P%d: %x\n",
  402. Device->AcpiId,
  403. Status);
  404. }
  405. }
  406. if (Cst != NULL) {
  407. AcpipObjectReleaseReference(Cst);
  408. }
  409. return;
  410. }
  411. KSTATUS
  412. AcpipInitializeCStatesOnProcessor (
  413. PPM_IDLE_STATE_INTERFACE Interface,
  414. PPM_IDLE_PROCESSOR_STATE Processor
  415. )
  416. /*++
  417. Routine Description:
  418. This routine is called on a particular processor to initialize processor
  419. C-State support.
  420. Arguments:
  421. Interface - Supplies a pointer to the interface.
  422. Processor - Supplies a pointer to the context for this processor.
  423. Return Value:
  424. Status code.
  425. --*/
  426. {
  427. PACPI_PROCESSOR_CONTEXT Context;
  428. Context = &(AcpiProcessor->Processors[Processor->ProcessorNumber]);
  429. Processor->Context = Context;
  430. Processor->States = Context->OsCStates;
  431. Processor->StateCount = Context->CStateCount;
  432. return STATUS_SUCCESS;
  433. }
  434. KSTATUS
  435. AcpipProcessorGetOsProcessorId (
  436. ULONG AcpiId,
  437. PULONG OsId
  438. )
  439. /*++
  440. Routine Description:
  441. This routine attempts to find and initialize the OS logical processor
  442. number for the given processor device.
  443. Arguments:
  444. AcpiId - Supplies the ACPI processor ID to look up.
  445. OsId - Supplies a pointer where the OS processor index will be returned on
  446. success.
  447. Return Value:
  448. Status code.
  449. --*/
  450. {
  451. BOOL Active;
  452. PMADT_GENERIC_ENTRY CurrentEntry;
  453. BOOL Found;
  454. PMADT_GIC Gic;
  455. PMADT_LOCAL_APIC LocalApic;
  456. PMADT MadtTable;
  457. ULONG PhysicalId;
  458. KSTATUS Status;
  459. MadtTable = AcpiFindTable(MADT_SIGNATURE, NULL);
  460. if (MadtTable == NULL) {
  461. return STATUS_NOT_FOUND;
  462. }
  463. //
  464. // Find an MADT entry that matches this processor ID.
  465. //
  466. Found = FALSE;
  467. PhysicalId = -1;
  468. CurrentEntry = (PMADT_GENERIC_ENTRY)(MadtTable + 1);
  469. while ((UINTN)CurrentEntry <
  470. ((UINTN)MadtTable + MadtTable->Header.Length)) {
  471. if ((CurrentEntry->Type == MadtEntryTypeLocalApic) &&
  472. (CurrentEntry->Length == sizeof(MADT_LOCAL_APIC))) {
  473. LocalApic = (PMADT_LOCAL_APIC)CurrentEntry;
  474. if ((LocalApic->Flags & MADT_LOCAL_APIC_FLAG_ENABLED) != 0) {
  475. if (LocalApic->AcpiProcessorId == AcpiId) {
  476. PhysicalId = LocalApic->ApicId;
  477. Found = TRUE;
  478. break;
  479. }
  480. }
  481. } else if ((CurrentEntry->Type == MadtEntryTypeGic) &&
  482. (CurrentEntry->Length == sizeof(MADT_GIC))) {
  483. Gic = (PMADT_GIC)CurrentEntry;
  484. if ((Gic->Flags & MADT_LOCAL_GIC_FLAG_ENABLED) != 0) {
  485. if (Gic->AcpiProcessorId == AcpiId) {
  486. PhysicalId = Gic->GicId;
  487. Found = TRUE;
  488. break;
  489. }
  490. }
  491. }
  492. CurrentEntry = (PMADT_GENERIC_ENTRY)((PUCHAR)CurrentEntry +
  493. CurrentEntry->Length);
  494. }
  495. if (Found == FALSE) {
  496. return STATUS_NOT_FOUND;
  497. }
  498. Status = HlGetProcessorIndexFromId(PhysicalId, OsId, &Active);
  499. if (!KSUCCESS(Status)) {
  500. return Status;
  501. }
  502. if (Active == FALSE) {
  503. return STATUS_NOT_STARTED;
  504. }
  505. return STATUS_SUCCESS;
  506. }
  507. ULONG
  508. AcpipGetProcessorCount (
  509. VOID
  510. )
  511. /*++
  512. Routine Description:
  513. This routine determines the number of processors in the system.
  514. Arguments:
  515. None.
  516. Return Value:
  517. Returns the maximum number of processors in the system.
  518. --*/
  519. {
  520. PMADT_GENERIC_ENTRY CurrentEntry;
  521. PMADT_GIC Gic;
  522. PMADT_LOCAL_APIC LocalApic;
  523. PMADT MadtTable;
  524. ULONG ProcessorCount;
  525. MadtTable = AcpiFindTable(MADT_SIGNATURE, NULL);
  526. if (MadtTable == NULL) {
  527. return 1;
  528. }
  529. ProcessorCount = 0;
  530. CurrentEntry = (PMADT_GENERIC_ENTRY)(MadtTable + 1);
  531. while ((UINTN)CurrentEntry <
  532. ((UINTN)MadtTable + MadtTable->Header.Length)) {
  533. if ((CurrentEntry->Type == MadtEntryTypeLocalApic) &&
  534. (CurrentEntry->Length == sizeof(MADT_LOCAL_APIC))) {
  535. LocalApic = (PMADT_LOCAL_APIC)CurrentEntry;
  536. if ((LocalApic->Flags & MADT_LOCAL_APIC_FLAG_ENABLED) != 0) {
  537. ProcessorCount += 1;
  538. }
  539. } else if ((CurrentEntry->Type == MadtEntryTypeGic) &&
  540. (CurrentEntry->Length == sizeof(MADT_GIC))) {
  541. Gic = (PMADT_GIC)CurrentEntry;
  542. if ((Gic->Flags & MADT_LOCAL_GIC_FLAG_ENABLED) != 0) {
  543. ProcessorCount += 1;
  544. }
  545. }
  546. CurrentEntry = (PMADT_GENERIC_ENTRY)((PUCHAR)CurrentEntry +
  547. CurrentEntry->Length);
  548. }
  549. return ProcessorCount;
  550. }