]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - UefiCpuPkg/CpuDxe/CpuDxe.c
UefiCpuPkg: Apply uncrustify changes
[mirror_edk2.git] / UefiCpuPkg / CpuDxe / CpuDxe.c
... / ...
CommitLineData
1/** @file\r
2 CPU DXE Module to produce CPU ARCH Protocol.\r
3\r
4 Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include "CpuDxe.h"\r
10#include "CpuMp.h"\r
11#include "CpuPageTable.h"\r
12\r
13//\r
14// Global Variables\r
15//\r
16BOOLEAN InterruptState = FALSE;\r
17EFI_HANDLE mCpuHandle = NULL;\r
18BOOLEAN mIsFlushingGCD;\r
19BOOLEAN mIsAllocatingPageTable = FALSE;\r
20UINT64 mValidMtrrAddressMask;\r
21UINT64 mValidMtrrBitsMask;\r
22UINT64 mTimerPeriod = 0;\r
23\r
24FIXED_MTRR mFixedMtrrTable[] = {\r
25 {\r
26 MSR_IA32_MTRR_FIX64K_00000,\r
27 0,\r
28 0x10000\r
29 },\r
30 {\r
31 MSR_IA32_MTRR_FIX16K_80000,\r
32 0x80000,\r
33 0x4000\r
34 },\r
35 {\r
36 MSR_IA32_MTRR_FIX16K_A0000,\r
37 0xA0000,\r
38 0x4000\r
39 },\r
40 {\r
41 MSR_IA32_MTRR_FIX4K_C0000,\r
42 0xC0000,\r
43 0x1000\r
44 },\r
45 {\r
46 MSR_IA32_MTRR_FIX4K_C8000,\r
47 0xC8000,\r
48 0x1000\r
49 },\r
50 {\r
51 MSR_IA32_MTRR_FIX4K_D0000,\r
52 0xD0000,\r
53 0x1000\r
54 },\r
55 {\r
56 MSR_IA32_MTRR_FIX4K_D8000,\r
57 0xD8000,\r
58 0x1000\r
59 },\r
60 {\r
61 MSR_IA32_MTRR_FIX4K_E0000,\r
62 0xE0000,\r
63 0x1000\r
64 },\r
65 {\r
66 MSR_IA32_MTRR_FIX4K_E8000,\r
67 0xE8000,\r
68 0x1000\r
69 },\r
70 {\r
71 MSR_IA32_MTRR_FIX4K_F0000,\r
72 0xF0000,\r
73 0x1000\r
74 },\r
75 {\r
76 MSR_IA32_MTRR_FIX4K_F8000,\r
77 0xF8000,\r
78 0x1000\r
79 },\r
80};\r
81\r
82EFI_CPU_ARCH_PROTOCOL gCpu = {\r
83 CpuFlushCpuDataCache,\r
84 CpuEnableInterrupt,\r
85 CpuDisableInterrupt,\r
86 CpuGetInterruptState,\r
87 CpuInit,\r
88 CpuRegisterInterruptHandler,\r
89 CpuGetTimerValue,\r
90 CpuSetMemoryAttributes,\r
91 1, // NumberOfTimers\r
92 4 // DmaBufferAlignment\r
93};\r
94\r
95//\r
96// CPU Arch Protocol Functions\r
97//\r
98\r
99/**\r
100 Flush CPU data cache. If the instruction cache is fully coherent\r
101 with all DMA operations then function can just return EFI_SUCCESS.\r
102\r
103 @param This Protocol instance structure\r
104 @param Start Physical address to start flushing from.\r
105 @param Length Number of bytes to flush. Round up to chipset\r
106 granularity.\r
107 @param FlushType Specifies the type of flush operation to perform.\r
108\r
109 @retval EFI_SUCCESS If cache was flushed\r
110 @retval EFI_UNSUPPORTED If flush type is not supported.\r
111 @retval EFI_DEVICE_ERROR If requested range could not be flushed.\r
112\r
113**/\r
114EFI_STATUS\r
115EFIAPI\r
116CpuFlushCpuDataCache (\r
117 IN EFI_CPU_ARCH_PROTOCOL *This,\r
118 IN EFI_PHYSICAL_ADDRESS Start,\r
119 IN UINT64 Length,\r
120 IN EFI_CPU_FLUSH_TYPE FlushType\r
121 )\r
122{\r
123 if (FlushType == EfiCpuFlushTypeWriteBackInvalidate) {\r
124 AsmWbinvd ();\r
125 return EFI_SUCCESS;\r
126 } else if (FlushType == EfiCpuFlushTypeInvalidate) {\r
127 AsmInvd ();\r
128 return EFI_SUCCESS;\r
129 } else {\r
130 return EFI_UNSUPPORTED;\r
131 }\r
132}\r
133\r
134/**\r
135 Enables CPU interrupts.\r
136\r
137 @param This Protocol instance structure\r
138\r
139 @retval EFI_SUCCESS If interrupts were enabled in the CPU\r
140 @retval EFI_DEVICE_ERROR If interrupts could not be enabled on the CPU.\r
141\r
142**/\r
143EFI_STATUS\r
144EFIAPI\r
145CpuEnableInterrupt (\r
146 IN EFI_CPU_ARCH_PROTOCOL *This\r
147 )\r
148{\r
149 EnableInterrupts ();\r
150\r
151 InterruptState = TRUE;\r
152 return EFI_SUCCESS;\r
153}\r
154\r
155/**\r
156 Disables CPU interrupts.\r
157\r
158 @param This Protocol instance structure\r
159\r
160 @retval EFI_SUCCESS If interrupts were disabled in the CPU.\r
161 @retval EFI_DEVICE_ERROR If interrupts could not be disabled on the CPU.\r
162\r
163**/\r
164EFI_STATUS\r
165EFIAPI\r
166CpuDisableInterrupt (\r
167 IN EFI_CPU_ARCH_PROTOCOL *This\r
168 )\r
169{\r
170 DisableInterrupts ();\r
171\r
172 InterruptState = FALSE;\r
173 return EFI_SUCCESS;\r
174}\r
175\r
176/**\r
177 Return the state of interrupts.\r
178\r
179 @param This Protocol instance structure\r
180 @param State Pointer to the CPU's current interrupt state\r
181\r
182 @retval EFI_SUCCESS If interrupts were disabled in the CPU.\r
183 @retval EFI_INVALID_PARAMETER State is NULL.\r
184\r
185**/\r
186EFI_STATUS\r
187EFIAPI\r
188CpuGetInterruptState (\r
189 IN EFI_CPU_ARCH_PROTOCOL *This,\r
190 OUT BOOLEAN *State\r
191 )\r
192{\r
193 if (State == NULL) {\r
194 return EFI_INVALID_PARAMETER;\r
195 }\r
196\r
197 *State = InterruptState;\r
198 return EFI_SUCCESS;\r
199}\r
200\r
201/**\r
202 Generates an INIT to the CPU.\r
203\r
204 @param This Protocol instance structure\r
205 @param InitType Type of CPU INIT to perform\r
206\r
207 @retval EFI_SUCCESS If CPU INIT occurred. This value should never be\r
208 seen.\r
209 @retval EFI_DEVICE_ERROR If CPU INIT failed.\r
210 @retval EFI_UNSUPPORTED Requested type of CPU INIT not supported.\r
211\r
212**/\r
213EFI_STATUS\r
214EFIAPI\r
215CpuInit (\r
216 IN EFI_CPU_ARCH_PROTOCOL *This,\r
217 IN EFI_CPU_INIT_TYPE InitType\r
218 )\r
219{\r
220 return EFI_UNSUPPORTED;\r
221}\r
222\r
223/**\r
224 Registers a function to be called from the CPU interrupt handler.\r
225\r
226 @param This Protocol instance structure\r
227 @param InterruptType Defines which interrupt to hook. IA-32\r
228 valid range is 0x00 through 0xFF\r
229 @param InterruptHandler A pointer to a function of type\r
230 EFI_CPU_INTERRUPT_HANDLER that is called\r
231 when a processor interrupt occurs. A null\r
232 pointer is an error condition.\r
233\r
234 @retval EFI_SUCCESS If handler installed or uninstalled.\r
235 @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler\r
236 for InterruptType was previously installed.\r
237 @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for\r
238 InterruptType was not previously installed.\r
239 @retval EFI_UNSUPPORTED The interrupt specified by InterruptType\r
240 is not supported.\r
241\r
242**/\r
243EFI_STATUS\r
244EFIAPI\r
245CpuRegisterInterruptHandler (\r
246 IN EFI_CPU_ARCH_PROTOCOL *This,\r
247 IN EFI_EXCEPTION_TYPE InterruptType,\r
248 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler\r
249 )\r
250{\r
251 return RegisterCpuInterruptHandler (InterruptType, InterruptHandler);\r
252}\r
253\r
254/**\r
255 Returns a timer value from one of the CPU's internal timers. There is no\r
256 inherent time interval between ticks but is a function of the CPU frequency.\r
257\r
258 @param This - Protocol instance structure.\r
259 @param TimerIndex - Specifies which CPU timer is requested.\r
260 @param TimerValue - Pointer to the returned timer value.\r
261 @param TimerPeriod - A pointer to the amount of time that passes\r
262 in femtoseconds (10-15) for each increment\r
263 of TimerValue. If TimerValue does not\r
264 increment at a predictable rate, then 0 is\r
265 returned. The amount of time that has\r
266 passed between two calls to GetTimerValue()\r
267 can be calculated with the formula\r
268 (TimerValue2 - TimerValue1) * TimerPeriod.\r
269 This parameter is optional and may be NULL.\r
270\r
271 @retval EFI_SUCCESS - If the CPU timer count was returned.\r
272 @retval EFI_UNSUPPORTED - If the CPU does not have any readable timers.\r
273 @retval EFI_DEVICE_ERROR - If an error occurred while reading the timer.\r
274 @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.\r
275\r
276**/\r
277EFI_STATUS\r
278EFIAPI\r
279CpuGetTimerValue (\r
280 IN EFI_CPU_ARCH_PROTOCOL *This,\r
281 IN UINT32 TimerIndex,\r
282 OUT UINT64 *TimerValue,\r
283 OUT UINT64 *TimerPeriod OPTIONAL\r
284 )\r
285{\r
286 UINT64 BeginValue;\r
287 UINT64 EndValue;\r
288\r
289 if (TimerValue == NULL) {\r
290 return EFI_INVALID_PARAMETER;\r
291 }\r
292\r
293 if (TimerIndex != 0) {\r
294 return EFI_INVALID_PARAMETER;\r
295 }\r
296\r
297 *TimerValue = AsmReadTsc ();\r
298\r
299 if (TimerPeriod != NULL) {\r
300 if (mTimerPeriod == 0) {\r
301 //\r
302 // Read time stamp counter before and after delay of 100 microseconds\r
303 //\r
304 BeginValue = AsmReadTsc ();\r
305 MicroSecondDelay (100);\r
306 EndValue = AsmReadTsc ();\r
307 //\r
308 // Calculate the actual frequency\r
309 //\r
310 mTimerPeriod = DivU64x64Remainder (\r
311 MultU64x32 (\r
312 1000 * 1000 * 1000,\r
313 100\r
314 ),\r
315 EndValue - BeginValue,\r
316 NULL\r
317 );\r
318 }\r
319\r
320 *TimerPeriod = mTimerPeriod;\r
321 }\r
322\r
323 return EFI_SUCCESS;\r
324}\r
325\r
326/**\r
327 A minimal wrapper function that allows MtrrSetAllMtrrs() to be passed to\r
328 EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() as Procedure.\r
329\r
330 @param[in] Buffer Pointer to an MTRR_SETTINGS object, to be passed to\r
331 MtrrSetAllMtrrs().\r
332**/\r
333VOID\r
334EFIAPI\r
335SetMtrrsFromBuffer (\r
336 IN VOID *Buffer\r
337 )\r
338{\r
339 MtrrSetAllMtrrs (Buffer);\r
340}\r
341\r
342/**\r
343 Implementation of SetMemoryAttributes() service of CPU Architecture Protocol.\r
344\r
345 This function modifies the attributes for the memory region specified by BaseAddress and\r
346 Length from their current attributes to the attributes specified by Attributes.\r
347\r
348 @param This The EFI_CPU_ARCH_PROTOCOL instance.\r
349 @param BaseAddress The physical address that is the start address of a memory region.\r
350 @param Length The size in bytes of the memory region.\r
351 @param Attributes The bit mask of attributes to set for the memory region.\r
352\r
353 @retval EFI_SUCCESS The attributes were set for the memory region.\r
354 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by\r
355 BaseAddress and Length cannot be modified.\r
356 @retval EFI_INVALID_PARAMETER Length is zero.\r
357 Attributes specified an illegal combination of attributes that\r
358 cannot be set together.\r
359 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
360 the memory resource range.\r
361 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory\r
362 resource range specified by BaseAddress and Length.\r
363 The bit mask of attributes is not support for the memory resource\r
364 range specified by BaseAddress and Length.\r
365\r
366**/\r
367EFI_STATUS\r
368EFIAPI\r
369CpuSetMemoryAttributes (\r
370 IN EFI_CPU_ARCH_PROTOCOL *This,\r
371 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
372 IN UINT64 Length,\r
373 IN UINT64 Attributes\r
374 )\r
375{\r
376 RETURN_STATUS Status;\r
377 MTRR_MEMORY_CACHE_TYPE CacheType;\r
378 EFI_STATUS MpStatus;\r
379 EFI_MP_SERVICES_PROTOCOL *MpService;\r
380 MTRR_SETTINGS MtrrSettings;\r
381 UINT64 CacheAttributes;\r
382 UINT64 MemoryAttributes;\r
383 MTRR_MEMORY_CACHE_TYPE CurrentCacheType;\r
384\r
385 //\r
386 // If this function is called because GCD SetMemorySpaceAttributes () is called\r
387 // by RefreshGcdMemoryAttributes (), then we are just synchronizing GCD memory\r
388 // map with MTRR values. So there is no need to modify MTRRs, just return immediately\r
389 // to avoid unnecessary computing.\r
390 //\r
391 if (mIsFlushingGCD) {\r
392 DEBUG ((DEBUG_VERBOSE, " Flushing GCD\n"));\r
393 return EFI_SUCCESS;\r
394 }\r
395\r
396 //\r
397 // During memory attributes updating, new pages may be allocated to setup\r
398 // smaller granularity of page table. Page allocation action might then cause\r
399 // another calling of CpuSetMemoryAttributes() recursively, due to memory\r
400 // protection policy configured (such as PcdDxeNxMemoryProtectionPolicy).\r
401 // Since this driver will always protect memory used as page table by itself,\r
402 // there's no need to apply protection policy requested from memory service.\r
403 // So it's safe to just return EFI_SUCCESS if this time of calling is caused\r
404 // by page table memory allocation.\r
405 //\r
406 if (mIsAllocatingPageTable) {\r
407 DEBUG ((DEBUG_VERBOSE, " Allocating page table memory\n"));\r
408 return EFI_SUCCESS;\r
409 }\r
410\r
411 CacheAttributes = Attributes & EFI_CACHE_ATTRIBUTE_MASK;\r
412 MemoryAttributes = Attributes & EFI_MEMORY_ATTRIBUTE_MASK;\r
413\r
414 if (Attributes != (CacheAttributes | MemoryAttributes)) {\r
415 return EFI_INVALID_PARAMETER;\r
416 }\r
417\r
418 if (CacheAttributes != 0) {\r
419 if (!IsMtrrSupported ()) {\r
420 return EFI_UNSUPPORTED;\r
421 }\r
422\r
423 switch (CacheAttributes) {\r
424 case EFI_MEMORY_UC:\r
425 CacheType = CacheUncacheable;\r
426 break;\r
427\r
428 case EFI_MEMORY_WC:\r
429 CacheType = CacheWriteCombining;\r
430 break;\r
431\r
432 case EFI_MEMORY_WT:\r
433 CacheType = CacheWriteThrough;\r
434 break;\r
435\r
436 case EFI_MEMORY_WP:\r
437 CacheType = CacheWriteProtected;\r
438 break;\r
439\r
440 case EFI_MEMORY_WB:\r
441 CacheType = CacheWriteBack;\r
442 break;\r
443\r
444 default:\r
445 return EFI_INVALID_PARAMETER;\r
446 }\r
447\r
448 CurrentCacheType = MtrrGetMemoryAttribute (BaseAddress);\r
449 if (CurrentCacheType != CacheType) {\r
450 //\r
451 // call MTRR library function\r
452 //\r
453 Status = MtrrSetMemoryAttribute (\r
454 BaseAddress,\r
455 Length,\r
456 CacheType\r
457 );\r
458\r
459 if (!RETURN_ERROR (Status)) {\r
460 MpStatus = gBS->LocateProtocol (\r
461 &gEfiMpServiceProtocolGuid,\r
462 NULL,\r
463 (VOID **)&MpService\r
464 );\r
465 //\r
466 // Synchronize the update with all APs\r
467 //\r
468 if (!EFI_ERROR (MpStatus)) {\r
469 MtrrGetAllMtrrs (&MtrrSettings);\r
470 MpStatus = MpService->StartupAllAPs (\r
471 MpService, // This\r
472 SetMtrrsFromBuffer, // Procedure\r
473 FALSE, // SingleThread\r
474 NULL, // WaitEvent\r
475 0, // TimeoutInMicrosecsond\r
476 &MtrrSettings, // ProcedureArgument\r
477 NULL // FailedCpuList\r
478 );\r
479 ASSERT (MpStatus == EFI_SUCCESS || MpStatus == EFI_NOT_STARTED);\r
480 }\r
481 }\r
482\r
483 if (EFI_ERROR (Status)) {\r
484 return Status;\r
485 }\r
486 }\r
487 }\r
488\r
489 //\r
490 // Set memory attribute by page table\r
491 //\r
492 return AssignMemoryPageAttributes (NULL, BaseAddress, Length, MemoryAttributes, NULL);\r
493}\r
494\r
495/**\r
496 Initializes the valid bits mask and valid address mask for MTRRs.\r
497\r
498 This function initializes the valid bits mask and valid address mask for MTRRs.\r
499\r
500**/\r
501VOID\r
502InitializeMtrrMask (\r
503 VOID\r
504 )\r
505{\r
506 UINT32 RegEax;\r
507 UINT8 PhysicalAddressBits;\r
508\r
509 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
510\r
511 if (RegEax >= 0x80000008) {\r
512 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
513\r
514 PhysicalAddressBits = (UINT8)RegEax;\r
515 } else {\r
516 PhysicalAddressBits = 36;\r
517 }\r
518\r
519 mValidMtrrBitsMask = LShiftU64 (1, PhysicalAddressBits) - 1;\r
520 mValidMtrrAddressMask = mValidMtrrBitsMask & 0xfffffffffffff000ULL;\r
521}\r
522\r
523/**\r
524 Gets GCD Mem Space type from MTRR Type.\r
525\r
526 This function gets GCD Mem Space type from MTRR Type.\r
527\r
528 @param MtrrAttributes MTRR memory type\r
529\r
530 @return GCD Mem Space type\r
531\r
532**/\r
533UINT64\r
534GetMemorySpaceAttributeFromMtrrType (\r
535 IN UINT8 MtrrAttributes\r
536 )\r
537{\r
538 switch (MtrrAttributes) {\r
539 case MTRR_CACHE_UNCACHEABLE:\r
540 return EFI_MEMORY_UC;\r
541 case MTRR_CACHE_WRITE_COMBINING:\r
542 return EFI_MEMORY_WC;\r
543 case MTRR_CACHE_WRITE_THROUGH:\r
544 return EFI_MEMORY_WT;\r
545 case MTRR_CACHE_WRITE_PROTECTED:\r
546 return EFI_MEMORY_WP;\r
547 case MTRR_CACHE_WRITE_BACK:\r
548 return EFI_MEMORY_WB;\r
549 default:\r
550 return 0;\r
551 }\r
552}\r
553\r
554/**\r
555 Searches memory descriptors covered by given memory range.\r
556\r
557 This function searches into the Gcd Memory Space for descriptors\r
558 (from StartIndex to EndIndex) that contains the memory range\r
559 specified by BaseAddress and Length.\r
560\r
561 @param MemorySpaceMap Gcd Memory Space Map as array.\r
562 @param NumberOfDescriptors Number of descriptors in map.\r
563 @param BaseAddress BaseAddress for the requested range.\r
564 @param Length Length for the requested range.\r
565 @param StartIndex Start index into the Gcd Memory Space Map.\r
566 @param EndIndex End index into the Gcd Memory Space Map.\r
567\r
568 @retval EFI_SUCCESS Search successfully.\r
569 @retval EFI_NOT_FOUND The requested descriptors does not exist.\r
570\r
571**/\r
572EFI_STATUS\r
573SearchGcdMemorySpaces (\r
574 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,\r
575 IN UINTN NumberOfDescriptors,\r
576 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
577 IN UINT64 Length,\r
578 OUT UINTN *StartIndex,\r
579 OUT UINTN *EndIndex\r
580 )\r
581{\r
582 UINTN Index;\r
583\r
584 *StartIndex = 0;\r
585 *EndIndex = 0;\r
586 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
587 if ((BaseAddress >= MemorySpaceMap[Index].BaseAddress) &&\r
588 (BaseAddress < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))\r
589 {\r
590 *StartIndex = Index;\r
591 }\r
592\r
593 if ((BaseAddress + Length - 1 >= MemorySpaceMap[Index].BaseAddress) &&\r
594 (BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))\r
595 {\r
596 *EndIndex = Index;\r
597 return EFI_SUCCESS;\r
598 }\r
599 }\r
600\r
601 return EFI_NOT_FOUND;\r
602}\r
603\r
604/**\r
605 Sets the attributes for a specified range in Gcd Memory Space Map.\r
606\r
607 This function sets the attributes for a specified range in\r
608 Gcd Memory Space Map.\r
609\r
610 @param MemorySpaceMap Gcd Memory Space Map as array\r
611 @param NumberOfDescriptors Number of descriptors in map\r
612 @param BaseAddress BaseAddress for the range\r
613 @param Length Length for the range\r
614 @param Attributes Attributes to set\r
615\r
616 @retval EFI_SUCCESS Memory attributes set successfully\r
617 @retval EFI_NOT_FOUND The specified range does not exist in Gcd Memory Space\r
618\r
619**/\r
620EFI_STATUS\r
621SetGcdMemorySpaceAttributes (\r
622 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,\r
623 IN UINTN NumberOfDescriptors,\r
624 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
625 IN UINT64 Length,\r
626 IN UINT64 Attributes\r
627 )\r
628{\r
629 EFI_STATUS Status;\r
630 UINTN Index;\r
631 UINTN StartIndex;\r
632 UINTN EndIndex;\r
633 EFI_PHYSICAL_ADDRESS RegionStart;\r
634 UINT64 RegionLength;\r
635\r
636 //\r
637 // Get all memory descriptors covered by the memory range\r
638 //\r
639 Status = SearchGcdMemorySpaces (\r
640 MemorySpaceMap,\r
641 NumberOfDescriptors,\r
642 BaseAddress,\r
643 Length,\r
644 &StartIndex,\r
645 &EndIndex\r
646 );\r
647 if (EFI_ERROR (Status)) {\r
648 return Status;\r
649 }\r
650\r
651 //\r
652 // Go through all related descriptors and set attributes accordingly\r
653 //\r
654 for (Index = StartIndex; Index <= EndIndex; Index++) {\r
655 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {\r
656 continue;\r
657 }\r
658\r
659 //\r
660 // Calculate the start and end address of the overlapping range\r
661 //\r
662 if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {\r
663 RegionStart = BaseAddress;\r
664 } else {\r
665 RegionStart = MemorySpaceMap[Index].BaseAddress;\r
666 }\r
667\r
668 if (BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) {\r
669 RegionLength = BaseAddress + Length - RegionStart;\r
670 } else {\r
671 RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;\r
672 }\r
673\r
674 //\r
675 // Set memory attributes according to MTRR attribute and the original attribute of descriptor\r
676 //\r
677 gDS->SetMemorySpaceAttributes (\r
678 RegionStart,\r
679 RegionLength,\r
680 (MemorySpaceMap[Index].Attributes & ~EFI_CACHE_ATTRIBUTE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)\r
681 );\r
682 }\r
683\r
684 return EFI_SUCCESS;\r
685}\r
686\r
687/**\r
688 Refreshes the GCD Memory Space attributes according to MTRRs.\r
689\r
690 This function refreshes the GCD Memory Space attributes according to MTRRs.\r
691\r
692**/\r
693VOID\r
694RefreshMemoryAttributesFromMtrr (\r
695 VOID\r
696 )\r
697{\r
698 EFI_STATUS Status;\r
699 UINTN Index;\r
700 UINTN SubIndex;\r
701 UINT64 RegValue;\r
702 EFI_PHYSICAL_ADDRESS BaseAddress;\r
703 UINT64 Length;\r
704 UINT64 Attributes;\r
705 UINT64 CurrentAttributes;\r
706 UINT8 MtrrType;\r
707 UINTN NumberOfDescriptors;\r
708 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
709 UINT64 DefaultAttributes;\r
710 VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR];\r
711 MTRR_FIXED_SETTINGS MtrrFixedSettings;\r
712 UINT32 FirmwareVariableMtrrCount;\r
713 UINT8 DefaultMemoryType;\r
714\r
715 FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount ();\r
716 ASSERT (FirmwareVariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR);\r
717\r
718 MemorySpaceMap = NULL;\r
719\r
720 //\r
721 // Initialize the valid bits mask and valid address mask for MTRRs\r
722 //\r
723 InitializeMtrrMask ();\r
724\r
725 //\r
726 // Get the memory attribute of variable MTRRs\r
727 //\r
728 MtrrGetMemoryAttributeInVariableMtrr (\r
729 mValidMtrrBitsMask,\r
730 mValidMtrrAddressMask,\r
731 VariableMtrr\r
732 );\r
733\r
734 //\r
735 // Get the memory space map from GCD\r
736 //\r
737 Status = gDS->GetMemorySpaceMap (\r
738 &NumberOfDescriptors,\r
739 &MemorySpaceMap\r
740 );\r
741 ASSERT_EFI_ERROR (Status);\r
742\r
743 DefaultMemoryType = (UINT8)MtrrGetDefaultMemoryType ();\r
744 DefaultAttributes = GetMemorySpaceAttributeFromMtrrType (DefaultMemoryType);\r
745\r
746 //\r
747 // Set default attributes to all spaces.\r
748 //\r
749 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
750 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {\r
751 continue;\r
752 }\r
753\r
754 gDS->SetMemorySpaceAttributes (\r
755 MemorySpaceMap[Index].BaseAddress,\r
756 MemorySpaceMap[Index].Length,\r
757 (MemorySpaceMap[Index].Attributes & ~EFI_CACHE_ATTRIBUTE_MASK) |\r
758 (MemorySpaceMap[Index].Capabilities & DefaultAttributes)\r
759 );\r
760 }\r
761\r
762 //\r
763 // Go for variable MTRRs with WB attribute\r
764 //\r
765 for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {\r
766 if (VariableMtrr[Index].Valid &&\r
767 (VariableMtrr[Index].Type == MTRR_CACHE_WRITE_BACK))\r
768 {\r
769 SetGcdMemorySpaceAttributes (\r
770 MemorySpaceMap,\r
771 NumberOfDescriptors,\r
772 VariableMtrr[Index].BaseAddress,\r
773 VariableMtrr[Index].Length,\r
774 EFI_MEMORY_WB\r
775 );\r
776 }\r
777 }\r
778\r
779 //\r
780 // Go for variable MTRRs with the attribute except for WB and UC attributes\r
781 //\r
782 for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {\r
783 if (VariableMtrr[Index].Valid &&\r
784 (VariableMtrr[Index].Type != MTRR_CACHE_WRITE_BACK) &&\r
785 (VariableMtrr[Index].Type != MTRR_CACHE_UNCACHEABLE))\r
786 {\r
787 Attributes = GetMemorySpaceAttributeFromMtrrType ((UINT8)VariableMtrr[Index].Type);\r
788 SetGcdMemorySpaceAttributes (\r
789 MemorySpaceMap,\r
790 NumberOfDescriptors,\r
791 VariableMtrr[Index].BaseAddress,\r
792 VariableMtrr[Index].Length,\r
793 Attributes\r
794 );\r
795 }\r
796 }\r
797\r
798 //\r
799 // Go for variable MTRRs with UC attribute\r
800 //\r
801 for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) {\r
802 if (VariableMtrr[Index].Valid &&\r
803 (VariableMtrr[Index].Type == MTRR_CACHE_UNCACHEABLE))\r
804 {\r
805 SetGcdMemorySpaceAttributes (\r
806 MemorySpaceMap,\r
807 NumberOfDescriptors,\r
808 VariableMtrr[Index].BaseAddress,\r
809 VariableMtrr[Index].Length,\r
810 EFI_MEMORY_UC\r
811 );\r
812 }\r
813 }\r
814\r
815 //\r
816 // Go for fixed MTRRs\r
817 //\r
818 Attributes = 0;\r
819 BaseAddress = 0;\r
820 Length = 0;\r
821 MtrrGetFixedMtrr (&MtrrFixedSettings);\r
822 for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {\r
823 RegValue = MtrrFixedSettings.Mtrr[Index];\r
824 //\r
825 // Check for continuous fixed MTRR sections\r
826 //\r
827 for (SubIndex = 0; SubIndex < 8; SubIndex++) {\r
828 MtrrType = (UINT8)RShiftU64 (RegValue, SubIndex * 8);\r
829 CurrentAttributes = GetMemorySpaceAttributeFromMtrrType (MtrrType);\r
830 if (Length == 0) {\r
831 //\r
832 // A new MTRR attribute begins\r
833 //\r
834 Attributes = CurrentAttributes;\r
835 } else {\r
836 //\r
837 // If fixed MTRR attribute changed, then set memory attribute for previous attribute\r
838 //\r
839 if (CurrentAttributes != Attributes) {\r
840 SetGcdMemorySpaceAttributes (\r
841 MemorySpaceMap,\r
842 NumberOfDescriptors,\r
843 BaseAddress,\r
844 Length,\r
845 Attributes\r
846 );\r
847 BaseAddress = mFixedMtrrTable[Index].BaseAddress + mFixedMtrrTable[Index].Length * SubIndex;\r
848 Length = 0;\r
849 Attributes = CurrentAttributes;\r
850 }\r
851 }\r
852\r
853 Length += mFixedMtrrTable[Index].Length;\r
854 }\r
855 }\r
856\r
857 //\r
858 // Handle the last fixed MTRR region\r
859 //\r
860 SetGcdMemorySpaceAttributes (\r
861 MemorySpaceMap,\r
862 NumberOfDescriptors,\r
863 BaseAddress,\r
864 Length,\r
865 Attributes\r
866 );\r
867\r
868 //\r
869 // Free memory space map allocated by GCD service GetMemorySpaceMap ()\r
870 //\r
871 if (MemorySpaceMap != NULL) {\r
872 FreePool (MemorySpaceMap);\r
873 }\r
874}\r
875\r
876/**\r
877 Check if paging is enabled or not.\r
878**/\r
879BOOLEAN\r
880IsPagingAndPageAddressExtensionsEnabled (\r
881 VOID\r
882 )\r
883{\r
884 IA32_CR0 Cr0;\r
885 IA32_CR4 Cr4;\r
886\r
887 Cr0.UintN = AsmReadCr0 ();\r
888 Cr4.UintN = AsmReadCr4 ();\r
889\r
890 return ((Cr0.Bits.PG != 0) && (Cr4.Bits.PAE != 0));\r
891}\r
892\r
893/**\r
894 Refreshes the GCD Memory Space attributes according to MTRRs and Paging.\r
895\r
896 This function refreshes the GCD Memory Space attributes according to MTRRs\r
897 and page tables.\r
898\r
899**/\r
900VOID\r
901RefreshGcdMemoryAttributes (\r
902 VOID\r
903 )\r
904{\r
905 mIsFlushingGCD = TRUE;\r
906\r
907 if (IsMtrrSupported ()) {\r
908 RefreshMemoryAttributesFromMtrr ();\r
909 }\r
910\r
911 if (IsPagingAndPageAddressExtensionsEnabled ()) {\r
912 RefreshGcdMemoryAttributesFromPaging ();\r
913 }\r
914\r
915 mIsFlushingGCD = FALSE;\r
916}\r
917\r
918/**\r
919 Initialize Interrupt Descriptor Table for interrupt handling.\r
920\r
921**/\r
922VOID\r
923InitInterruptDescriptorTable (\r
924 VOID\r
925 )\r
926{\r
927 EFI_STATUS Status;\r
928 EFI_VECTOR_HANDOFF_INFO *VectorInfoList;\r
929 EFI_VECTOR_HANDOFF_INFO *VectorInfo;\r
930\r
931 VectorInfo = NULL;\r
932 Status = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **)&VectorInfoList);\r
933 if ((Status == EFI_SUCCESS) && (VectorInfoList != NULL)) {\r
934 VectorInfo = VectorInfoList;\r
935 }\r
936\r
937 Status = InitializeCpuInterruptHandlers (VectorInfo);\r
938 ASSERT_EFI_ERROR (Status);\r
939}\r
940\r
941/**\r
942 Callback function for idle events.\r
943\r
944 @param Event Event whose notification function is being invoked.\r
945 @param Context The pointer to the notification function's context,\r
946 which is implementation-dependent.\r
947\r
948**/\r
949VOID\r
950EFIAPI\r
951IdleLoopEventCallback (\r
952 IN EFI_EVENT Event,\r
953 IN VOID *Context\r
954 )\r
955{\r
956 CpuSleep ();\r
957}\r
958\r
959/**\r
960 Ensure the compatibility of a memory space descriptor with the MMIO aperture.\r
961\r
962 The memory space descriptor can come from the GCD memory space map, or it can\r
963 represent a gap between two neighboring memory space descriptors. In the\r
964 latter case, the GcdMemoryType field is expected to be\r
965 EfiGcdMemoryTypeNonExistent.\r
966\r
967 If the memory space descriptor already has type\r
968 EfiGcdMemoryTypeMemoryMappedIo, and its capabilities are a superset of the\r
969 required capabilities, then no action is taken -- it is by definition\r
970 compatible with the aperture.\r
971\r
972 Otherwise, the intersection of the memory space descriptor is calculated with\r
973 the aperture. If the intersection is the empty set (no overlap), no action is\r
974 taken; the memory space descriptor is compatible with the aperture.\r
975\r
976 Otherwise, the type of the descriptor is investigated again. If the type is\r
977 EfiGcdMemoryTypeNonExistent (representing a gap, or a genuine descriptor with\r
978 such a type), then an attempt is made to add the intersection as MMIO space\r
979 to the GCD memory space map, with the specified capabilities. This ensures\r
980 continuity for the aperture, and the descriptor is deemed compatible with the\r
981 aperture.\r
982\r
983 Otherwise, the memory space descriptor is incompatible with the MMIO\r
984 aperture.\r
985\r
986 @param[in] Base Base address of the aperture.\r
987 @param[in] Length Length of the aperture.\r
988 @param[in] Capabilities Capabilities required by the aperture.\r
989 @param[in] Descriptor The descriptor to ensure compatibility with the\r
990 aperture for.\r
991\r
992 @retval EFI_SUCCESS The descriptor is compatible. The GCD memory\r
993 space map may have been updated, for\r
994 continuity within the aperture.\r
995 @retval EFI_INVALID_PARAMETER The descriptor is incompatible.\r
996 @return Error codes from gDS->AddMemorySpace().\r
997**/\r
998EFI_STATUS\r
999IntersectMemoryDescriptor (\r
1000 IN UINT64 Base,\r
1001 IN UINT64 Length,\r
1002 IN UINT64 Capabilities,\r
1003 IN CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor\r
1004 )\r
1005{\r
1006 UINT64 IntersectionBase;\r
1007 UINT64 IntersectionEnd;\r
1008 EFI_STATUS Status;\r
1009\r
1010 if ((Descriptor->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&\r
1011 ((Descriptor->Capabilities & Capabilities) == Capabilities))\r
1012 {\r
1013 return EFI_SUCCESS;\r
1014 }\r
1015\r
1016 IntersectionBase = MAX (Base, Descriptor->BaseAddress);\r
1017 IntersectionEnd = MIN (\r
1018 Base + Length,\r
1019 Descriptor->BaseAddress + Descriptor->Length\r
1020 );\r
1021 if (IntersectionBase >= IntersectionEnd) {\r
1022 //\r
1023 // The descriptor and the aperture don't overlap.\r
1024 //\r
1025 return EFI_SUCCESS;\r
1026 }\r
1027\r
1028 if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeNonExistent) {\r
1029 Status = gDS->AddMemorySpace (\r
1030 EfiGcdMemoryTypeMemoryMappedIo,\r
1031 IntersectionBase,\r
1032 IntersectionEnd - IntersectionBase,\r
1033 Capabilities\r
1034 );\r
1035\r
1036 DEBUG ((\r
1037 EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE,\r
1038 "%a: %a: add [%Lx, %Lx): %r\n",\r
1039 gEfiCallerBaseName,\r
1040 __FUNCTION__,\r
1041 IntersectionBase,\r
1042 IntersectionEnd,\r
1043 Status\r
1044 ));\r
1045 return Status;\r
1046 }\r
1047\r
1048 DEBUG ((\r
1049 DEBUG_ERROR,\r
1050 "%a: %a: desc [%Lx, %Lx) type %u cap %Lx conflicts "\r
1051 "with aperture [%Lx, %Lx) cap %Lx\n",\r
1052 gEfiCallerBaseName,\r
1053 __FUNCTION__,\r
1054 Descriptor->BaseAddress,\r
1055 Descriptor->BaseAddress + Descriptor->Length,\r
1056 (UINT32)Descriptor->GcdMemoryType,\r
1057 Descriptor->Capabilities,\r
1058 Base,\r
1059 Base + Length,\r
1060 Capabilities\r
1061 ));\r
1062 return EFI_INVALID_PARAMETER;\r
1063}\r
1064\r
1065/**\r
1066 Add MMIO space to GCD.\r
1067 The routine checks the GCD database and only adds those which are\r
1068 not added in the specified range to GCD.\r
1069\r
1070 @param Base Base address of the MMIO space.\r
1071 @param Length Length of the MMIO space.\r
1072 @param Capabilities Capabilities of the MMIO space.\r
1073\r
1074 @retval EFI_SUCCESS The MMIO space was added successfully.\r
1075**/\r
1076EFI_STATUS\r
1077AddMemoryMappedIoSpace (\r
1078 IN UINT64 Base,\r
1079 IN UINT64 Length,\r
1080 IN UINT64 Capabilities\r
1081 )\r
1082{\r
1083 EFI_STATUS Status;\r
1084 UINTN Index;\r
1085 UINTN NumberOfDescriptors;\r
1086 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
1087\r
1088 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
1089 if (EFI_ERROR (Status)) {\r
1090 DEBUG ((\r
1091 DEBUG_ERROR,\r
1092 "%a: %a: GetMemorySpaceMap(): %r\n",\r
1093 gEfiCallerBaseName,\r
1094 __FUNCTION__,\r
1095 Status\r
1096 ));\r
1097 return Status;\r
1098 }\r
1099\r
1100 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
1101 Status = IntersectMemoryDescriptor (\r
1102 Base,\r
1103 Length,\r
1104 Capabilities,\r
1105 &MemorySpaceMap[Index]\r
1106 );\r
1107 if (EFI_ERROR (Status)) {\r
1108 goto FreeMemorySpaceMap;\r
1109 }\r
1110 }\r
1111\r
1112 DEBUG_CODE_BEGIN ();\r
1113 //\r
1114 // Make sure there are adjacent descriptors covering [Base, Base + Length).\r
1115 // It is possible that they have not been merged; merging can be prevented\r
1116 // by allocation and different capabilities.\r
1117 //\r
1118 UINT64 CheckBase;\r
1119 EFI_STATUS CheckStatus;\r
1120 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;\r
1121\r
1122 for (CheckBase = Base;\r
1123 CheckBase < Base + Length;\r
1124 CheckBase = Descriptor.BaseAddress + Descriptor.Length)\r
1125 {\r
1126 CheckStatus = gDS->GetMemorySpaceDescriptor (CheckBase, &Descriptor);\r
1127 ASSERT_EFI_ERROR (CheckStatus);\r
1128 ASSERT (Descriptor.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo);\r
1129 ASSERT ((Descriptor.Capabilities & Capabilities) == Capabilities);\r
1130 }\r
1131\r
1132 DEBUG_CODE_END ();\r
1133\r
1134FreeMemorySpaceMap:\r
1135 FreePool (MemorySpaceMap);\r
1136\r
1137 return Status;\r
1138}\r
1139\r
1140/**\r
1141 Add and allocate CPU local APIC memory mapped space.\r
1142\r
1143 @param[in]ImageHandle Image handle this driver.\r
1144\r
1145**/\r
1146VOID\r
1147AddLocalApicMemorySpace (\r
1148 IN EFI_HANDLE ImageHandle\r
1149 )\r
1150{\r
1151 EFI_STATUS Status;\r
1152 EFI_PHYSICAL_ADDRESS BaseAddress;\r
1153\r
1154 BaseAddress = (EFI_PHYSICAL_ADDRESS)GetLocalApicBaseAddress ();\r
1155 Status = AddMemoryMappedIoSpace (BaseAddress, SIZE_4KB, EFI_MEMORY_UC);\r
1156 ASSERT_EFI_ERROR (Status);\r
1157\r
1158 //\r
1159 // Try to allocate APIC memory mapped space, does not check return\r
1160 // status because it may be allocated by other driver, or DXE Core if\r
1161 // this range is built into Memory Allocation HOB.\r
1162 //\r
1163 Status = gDS->AllocateMemorySpace (\r
1164 EfiGcdAllocateAddress,\r
1165 EfiGcdMemoryTypeMemoryMappedIo,\r
1166 0,\r
1167 SIZE_4KB,\r
1168 &BaseAddress,\r
1169 ImageHandle,\r
1170 NULL\r
1171 );\r
1172 if (EFI_ERROR (Status)) {\r
1173 DEBUG ((\r
1174 DEBUG_INFO,\r
1175 "%a: %a: AllocateMemorySpace() Status - %r\n",\r
1176 gEfiCallerBaseName,\r
1177 __FUNCTION__,\r
1178 Status\r
1179 ));\r
1180 }\r
1181}\r
1182\r
1183/**\r
1184 Initialize the state information for the CPU Architectural Protocol.\r
1185\r
1186 @param ImageHandle Image handle this driver.\r
1187 @param SystemTable Pointer to the System Table.\r
1188\r
1189 @retval EFI_SUCCESS Thread can be successfully created\r
1190 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure\r
1191 @retval EFI_DEVICE_ERROR Cannot create the thread\r
1192\r
1193**/\r
1194EFI_STATUS\r
1195EFIAPI\r
1196InitializeCpu (\r
1197 IN EFI_HANDLE ImageHandle,\r
1198 IN EFI_SYSTEM_TABLE *SystemTable\r
1199 )\r
1200{\r
1201 EFI_STATUS Status;\r
1202 EFI_EVENT IdleLoopEvent;\r
1203\r
1204 InitializePageTableLib ();\r
1205\r
1206 InitializeFloatingPointUnits ();\r
1207\r
1208 //\r
1209 // Make sure interrupts are disabled\r
1210 //\r
1211 DisableInterrupts ();\r
1212\r
1213 //\r
1214 // Init GDT for DXE\r
1215 //\r
1216 InitGlobalDescriptorTable ();\r
1217\r
1218 //\r
1219 // Setup IDT pointer, IDT and interrupt entry points\r
1220 //\r
1221 InitInterruptDescriptorTable ();\r
1222\r
1223 //\r
1224 // Install CPU Architectural Protocol\r
1225 //\r
1226 Status = gBS->InstallMultipleProtocolInterfaces (\r
1227 &mCpuHandle,\r
1228 &gEfiCpuArchProtocolGuid,\r
1229 &gCpu,\r
1230 NULL\r
1231 );\r
1232 ASSERT_EFI_ERROR (Status);\r
1233\r
1234 //\r
1235 // Refresh GCD memory space map according to MTRR value.\r
1236 //\r
1237 RefreshGcdMemoryAttributes ();\r
1238\r
1239 //\r
1240 // Add and allocate local APIC memory mapped space\r
1241 //\r
1242 AddLocalApicMemorySpace (ImageHandle);\r
1243\r
1244 //\r
1245 // Setup a callback for idle events\r
1246 //\r
1247 Status = gBS->CreateEventEx (\r
1248 EVT_NOTIFY_SIGNAL,\r
1249 TPL_NOTIFY,\r
1250 IdleLoopEventCallback,\r
1251 NULL,\r
1252 &gIdleLoopEventGuid,\r
1253 &IdleLoopEvent\r
1254 );\r
1255 ASSERT_EFI_ERROR (Status);\r
1256\r
1257 InitializeMpSupport ();\r
1258\r
1259 return Status;\r
1260}\r