]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c
UefiCpuPkg/PiSmmCpuDxeSmm: Remove PSD layout assumptions
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / PiSmmCpuDxeSmm.c
1 /** @file
2 Agent Module to load other modules to deploy SMM Entry Vector for X86 CPU.
3
4 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "PiSmmCpuDxeSmm.h"
16
17 //
18 // SMM CPU Private Data structure that contains SMM Configuration Protocol
19 // along its supporting fields.
20 //
21 SMM_CPU_PRIVATE_DATA mSmmCpuPrivateData = {
22 SMM_CPU_PRIVATE_DATA_SIGNATURE, // Signature
23 NULL, // SmmCpuHandle
24 NULL, // Pointer to ProcessorInfo array
25 NULL, // Pointer to Operation array
26 NULL, // Pointer to CpuSaveStateSize array
27 NULL, // Pointer to CpuSaveState array
28 { {0} }, // SmmReservedSmramRegion
29 {
30 SmmStartupThisAp, // SmmCoreEntryContext.SmmStartupThisAp
31 0, // SmmCoreEntryContext.CurrentlyExecutingCpu
32 0, // SmmCoreEntryContext.NumberOfCpus
33 NULL, // SmmCoreEntryContext.CpuSaveStateSize
34 NULL // SmmCoreEntryContext.CpuSaveState
35 },
36 NULL, // SmmCoreEntry
37 {
38 mSmmCpuPrivateData.SmmReservedSmramRegion, // SmmConfiguration.SmramReservedRegions
39 RegisterSmmEntry // SmmConfiguration.RegisterSmmEntry
40 },
41 };
42
43 CPU_HOT_PLUG_DATA mCpuHotPlugData = {
44 CPU_HOT_PLUG_DATA_REVISION_1, // Revision
45 0, // Array Length of SmBase and APIC ID
46 NULL, // Pointer to APIC ID array
47 NULL, // Pointer to SMBASE array
48 0, // Reserved
49 0, // SmrrBase
50 0 // SmrrSize
51 };
52
53 //
54 // Global pointer used to access mSmmCpuPrivateData from outside and inside SMM
55 //
56 SMM_CPU_PRIVATE_DATA *gSmmCpuPrivate = &mSmmCpuPrivateData;
57
58 //
59 // SMM Relocation variables
60 //
61 volatile BOOLEAN *mRebased;
62 volatile BOOLEAN mIsBsp;
63
64 ///
65 /// Handle for the SMM CPU Protocol
66 ///
67 EFI_HANDLE mSmmCpuHandle = NULL;
68
69 ///
70 /// SMM CPU Protocol instance
71 ///
72 EFI_SMM_CPU_PROTOCOL mSmmCpu = {
73 SmmReadSaveState,
74 SmmWriteSaveState
75 };
76
77 EFI_CPU_INTERRUPT_HANDLER mExternalVectorTable[EXCEPTION_VECTOR_NUMBER];
78
79 //
80 // SMM stack information
81 //
82 UINTN mSmmStackArrayBase;
83 UINTN mSmmStackArrayEnd;
84 UINTN mSmmStackSize;
85
86 UINTN mMaxNumberOfCpus = 1;
87 UINTN mNumberOfCpus = 1;
88
89 //
90 // SMM ready to lock flag
91 //
92 BOOLEAN mSmmReadyToLock = FALSE;
93
94 //
95 // Global used to cache PCD for SMM Code Access Check enable
96 //
97 BOOLEAN mSmmCodeAccessCheckEnable = FALSE;
98
99 //
100 // Spin lock used to serialize setting of SMM Code Access Check feature
101 //
102 SPIN_LOCK *mConfigSmmCodeAccessCheckLock = NULL;
103
104 /**
105 Initialize IDT to setup exception handlers for SMM.
106
107 **/
108 VOID
109 InitializeSmmIdt (
110 VOID
111 )
112 {
113 EFI_STATUS Status;
114 BOOLEAN InterruptState;
115 IA32_DESCRIPTOR DxeIdtr;
116
117 //
118 // There are 32 (not 255) entries in it since only processor
119 // generated exceptions will be handled.
120 //
121 gcSmiIdtr.Limit = (sizeof(IA32_IDT_GATE_DESCRIPTOR) * 32) - 1;
122 //
123 // Allocate page aligned IDT, because it might be set as read only.
124 //
125 gcSmiIdtr.Base = (UINTN)AllocateCodePages (EFI_SIZE_TO_PAGES(gcSmiIdtr.Limit + 1));
126 ASSERT (gcSmiIdtr.Base != 0);
127 ZeroMem ((VOID *)gcSmiIdtr.Base, gcSmiIdtr.Limit + 1);
128
129 //
130 // Disable Interrupt and save DXE IDT table
131 //
132 InterruptState = SaveAndDisableInterrupts ();
133 AsmReadIdtr (&DxeIdtr);
134 //
135 // Load SMM temporary IDT table
136 //
137 AsmWriteIdtr (&gcSmiIdtr);
138 //
139 // Setup SMM default exception handlers, SMM IDT table
140 // will be updated and saved in gcSmiIdtr
141 //
142 Status = InitializeCpuExceptionHandlers (NULL);
143 ASSERT_EFI_ERROR (Status);
144 //
145 // Restore DXE IDT table and CPU interrupt
146 //
147 AsmWriteIdtr ((IA32_DESCRIPTOR *) &DxeIdtr);
148 SetInterruptState (InterruptState);
149 }
150
151 /**
152 Search module name by input IP address and output it.
153
154 @param CallerIpAddress Caller instruction pointer.
155
156 **/
157 VOID
158 DumpModuleInfoByIp (
159 IN UINTN CallerIpAddress
160 )
161 {
162 UINTN Pe32Data;
163 EFI_IMAGE_DOS_HEADER *DosHdr;
164 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
165 VOID *PdbPointer;
166 UINT64 DumpIpAddress;
167
168 //
169 // Find Image Base
170 //
171 Pe32Data = CallerIpAddress & ~(SIZE_4KB - 1);
172 while (Pe32Data != 0) {
173 DosHdr = (EFI_IMAGE_DOS_HEADER *) Pe32Data;
174 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
175 //
176 // DOS image header is present, so read the PE header after the DOS image header.
177 //
178 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
179 //
180 // Make sure PE header address does not overflow and is less than the initial address.
181 //
182 if (((UINTN)Hdr.Pe32 > Pe32Data) && ((UINTN)Hdr.Pe32 < CallerIpAddress)) {
183 if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
184 //
185 // It's PE image.
186 //
187 break;
188 }
189 }
190 }
191
192 //
193 // Not found the image base, check the previous aligned address
194 //
195 Pe32Data -= SIZE_4KB;
196 }
197
198 DumpIpAddress = CallerIpAddress;
199 DEBUG ((EFI_D_ERROR, "It is invoked from the instruction before IP(0x%lx)", DumpIpAddress));
200
201 if (Pe32Data != 0) {
202 PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *) Pe32Data);
203 if (PdbPointer != NULL) {
204 DEBUG ((EFI_D_ERROR, " in module (%a)", PdbPointer));
205 }
206 }
207 }
208
209 /**
210 Read information from the CPU save state.
211
212 @param This EFI_SMM_CPU_PROTOCOL instance
213 @param Width The number of bytes to read from the CPU save state.
214 @param Register Specifies the CPU register to read form the save state.
215 @param CpuIndex Specifies the zero-based index of the CPU save state.
216 @param Buffer Upon return, this holds the CPU register value read from the save state.
217
218 @retval EFI_SUCCESS The register was read from Save State
219 @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor
220 @retval EFI_INVALID_PARAMTER This or Buffer is NULL.
221
222 **/
223 EFI_STATUS
224 EFIAPI
225 SmmReadSaveState (
226 IN CONST EFI_SMM_CPU_PROTOCOL *This,
227 IN UINTN Width,
228 IN EFI_SMM_SAVE_STATE_REGISTER Register,
229 IN UINTN CpuIndex,
230 OUT VOID *Buffer
231 )
232 {
233 EFI_STATUS Status;
234
235 //
236 // Retrieve pointer to the specified CPU's SMM Save State buffer
237 //
238 if ((CpuIndex >= gSmst->NumberOfCpus) || (Buffer == NULL)) {
239 return EFI_INVALID_PARAMETER;
240 }
241
242 //
243 // Check for special EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID
244 //
245 if (Register == EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID) {
246 //
247 // The pseudo-register only supports the 64-bit size specified by Width.
248 //
249 if (Width != sizeof (UINT64)) {
250 return EFI_INVALID_PARAMETER;
251 }
252 //
253 // If the processor is in SMM at the time the SMI occurred,
254 // the pseudo register value for EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID is returned in Buffer.
255 // Otherwise, EFI_NOT_FOUND is returned.
256 //
257 if (*(mSmmMpSyncData->CpuData[CpuIndex].Present)) {
258 *(UINT64 *)Buffer = gSmmCpuPrivate->ProcessorInfo[CpuIndex].ProcessorId;
259 return EFI_SUCCESS;
260 } else {
261 return EFI_NOT_FOUND;
262 }
263 }
264
265 if (!(*(mSmmMpSyncData->CpuData[CpuIndex].Present))) {
266 return EFI_INVALID_PARAMETER;
267 }
268
269 Status = SmmCpuFeaturesReadSaveStateRegister (CpuIndex, Register, Width, Buffer);
270 if (Status == EFI_UNSUPPORTED) {
271 Status = ReadSaveStateRegister (CpuIndex, Register, Width, Buffer);
272 }
273 return Status;
274 }
275
276 /**
277 Write data to the CPU save state.
278
279 @param This EFI_SMM_CPU_PROTOCOL instance
280 @param Width The number of bytes to read from the CPU save state.
281 @param Register Specifies the CPU register to write to the save state.
282 @param CpuIndex Specifies the zero-based index of the CPU save state
283 @param Buffer Upon entry, this holds the new CPU register value.
284
285 @retval EFI_SUCCESS The register was written from Save State
286 @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor
287 @retval EFI_INVALID_PARAMTER ProcessorIndex or Width is not correct
288
289 **/
290 EFI_STATUS
291 EFIAPI
292 SmmWriteSaveState (
293 IN CONST EFI_SMM_CPU_PROTOCOL *This,
294 IN UINTN Width,
295 IN EFI_SMM_SAVE_STATE_REGISTER Register,
296 IN UINTN CpuIndex,
297 IN CONST VOID *Buffer
298 )
299 {
300 EFI_STATUS Status;
301
302 //
303 // Retrieve pointer to the specified CPU's SMM Save State buffer
304 //
305 if ((CpuIndex >= gSmst->NumberOfCpus) || (Buffer == NULL)) {
306 return EFI_INVALID_PARAMETER;
307 }
308
309 //
310 // Writes to EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID are ignored
311 //
312 if (Register == EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID) {
313 return EFI_SUCCESS;
314 }
315
316 if (!mSmmMpSyncData->CpuData[CpuIndex].Present) {
317 return EFI_INVALID_PARAMETER;
318 }
319
320 Status = SmmCpuFeaturesWriteSaveStateRegister (CpuIndex, Register, Width, Buffer);
321 if (Status == EFI_UNSUPPORTED) {
322 Status = WriteSaveStateRegister (CpuIndex, Register, Width, Buffer);
323 }
324 return Status;
325 }
326
327
328 /**
329 C function for SMI handler. To change all processor's SMMBase Register.
330
331 **/
332 VOID
333 EFIAPI
334 SmmInitHandler (
335 VOID
336 )
337 {
338 UINT32 ApicId;
339 UINTN Index;
340
341 //
342 // Update SMM IDT entries' code segment and load IDT
343 //
344 AsmWriteIdtr (&gcSmiIdtr);
345 ApicId = GetApicId ();
346
347 ASSERT (mNumberOfCpus <= mMaxNumberOfCpus);
348
349 for (Index = 0; Index < mNumberOfCpus; Index++) {
350 if (ApicId == (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId) {
351 //
352 // Initialize SMM specific features on the currently executing CPU
353 //
354 SmmCpuFeaturesInitializeProcessor (
355 Index,
356 mIsBsp,
357 gSmmCpuPrivate->ProcessorInfo,
358 &mCpuHotPlugData
359 );
360
361 if (!mSmmS3Flag) {
362 //
363 // Check XD and BTS features on each processor on normal boot
364 //
365 CheckFeatureSupported ();
366 }
367
368 if (mIsBsp) {
369 //
370 // BSP rebase is already done above.
371 // Initialize private data during S3 resume
372 //
373 InitializeMpSyncData ();
374 }
375
376 //
377 // Hook return after RSM to set SMM re-based flag
378 //
379 SemaphoreHook (Index, &mRebased[Index]);
380
381 return;
382 }
383 }
384 ASSERT (FALSE);
385 }
386
387 /**
388 Relocate SmmBases for each processor.
389
390 Execute on first boot and all S3 resumes
391
392 **/
393 VOID
394 EFIAPI
395 SmmRelocateBases (
396 VOID
397 )
398 {
399 UINT8 BakBuf[BACK_BUF_SIZE];
400 SMRAM_SAVE_STATE_MAP BakBuf2;
401 SMRAM_SAVE_STATE_MAP *CpuStatePtr;
402 UINT8 *U8Ptr;
403 UINT32 ApicId;
404 UINTN Index;
405 UINTN BspIndex;
406
407 //
408 // Make sure the reserved size is large enough for procedure SmmInitTemplate.
409 //
410 ASSERT (sizeof (BakBuf) >= gcSmmInitSize);
411
412 //
413 // Patch ASM code template with current CR0, CR3, and CR4 values
414 //
415 gSmmCr0 = (UINT32)AsmReadCr0 ();
416 gSmmCr3 = (UINT32)AsmReadCr3 ();
417 gSmmCr4 = (UINT32)AsmReadCr4 ();
418
419 //
420 // Patch GDTR for SMM base relocation
421 //
422 gcSmiInitGdtr.Base = gcSmiGdtr.Base;
423 gcSmiInitGdtr.Limit = gcSmiGdtr.Limit;
424
425 U8Ptr = (UINT8*)(UINTN)(SMM_DEFAULT_SMBASE + SMM_HANDLER_OFFSET);
426 CpuStatePtr = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET);
427
428 //
429 // Backup original contents at address 0x38000
430 //
431 CopyMem (BakBuf, U8Ptr, sizeof (BakBuf));
432 CopyMem (&BakBuf2, CpuStatePtr, sizeof (BakBuf2));
433
434 //
435 // Load image for relocation
436 //
437 CopyMem (U8Ptr, gcSmmInitTemplate, gcSmmInitSize);
438
439 //
440 // Retrieve the local APIC ID of current processor
441 //
442 ApicId = GetApicId ();
443
444 //
445 // Relocate SM bases for all APs
446 // This is APs' 1st SMI - rebase will be done here, and APs' default SMI handler will be overridden by gcSmmInitTemplate
447 //
448 mIsBsp = FALSE;
449 BspIndex = (UINTN)-1;
450 for (Index = 0; Index < mNumberOfCpus; Index++) {
451 mRebased[Index] = FALSE;
452 if (ApicId != (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId) {
453 SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId);
454 //
455 // Wait for this AP to finish its 1st SMI
456 //
457 while (!mRebased[Index]);
458 } else {
459 //
460 // BSP will be Relocated later
461 //
462 BspIndex = Index;
463 }
464 }
465
466 //
467 // Relocate BSP's SMM base
468 //
469 ASSERT (BspIndex != (UINTN)-1);
470 mIsBsp = TRUE;
471 SendSmiIpi (ApicId);
472 //
473 // Wait for the BSP to finish its 1st SMI
474 //
475 while (!mRebased[BspIndex]);
476
477 //
478 // Restore contents at address 0x38000
479 //
480 CopyMem (CpuStatePtr, &BakBuf2, sizeof (BakBuf2));
481 CopyMem (U8Ptr, BakBuf, sizeof (BakBuf));
482 }
483
484 /**
485 SMM Ready To Lock event notification handler.
486
487 The CPU S3 data is copied to SMRAM for security and mSmmReadyToLock is set to
488 perform additional lock actions that must be performed from SMM on the next SMI.
489
490 @param[in] Protocol Points to the protocol's unique identifier.
491 @param[in] Interface Points to the interface instance.
492 @param[in] Handle The handle on which the interface was installed.
493
494 @retval EFI_SUCCESS Notification handler runs successfully.
495 **/
496 EFI_STATUS
497 EFIAPI
498 SmmReadyToLockEventNotify (
499 IN CONST EFI_GUID *Protocol,
500 IN VOID *Interface,
501 IN EFI_HANDLE Handle
502 )
503 {
504 GetAcpiCpuData ();
505
506 //
507 // Set SMM ready to lock flag and return
508 //
509 mSmmReadyToLock = TRUE;
510 return EFI_SUCCESS;
511 }
512
513 /**
514 The module Entry Point of the CPU SMM driver.
515
516 @param ImageHandle The firmware allocated handle for the EFI image.
517 @param SystemTable A pointer to the EFI System Table.
518
519 @retval EFI_SUCCESS The entry point is executed successfully.
520 @retval Other Some error occurs when executing this entry point.
521
522 **/
523 EFI_STATUS
524 EFIAPI
525 PiCpuSmmEntry (
526 IN EFI_HANDLE ImageHandle,
527 IN EFI_SYSTEM_TABLE *SystemTable
528 )
529 {
530 EFI_STATUS Status;
531 EFI_MP_SERVICES_PROTOCOL *MpServices;
532 UINTN NumberOfEnabledProcessors;
533 UINTN Index;
534 VOID *Buffer;
535 UINTN BufferPages;
536 UINTN TileCodeSize;
537 UINTN TileDataSize;
538 UINTN TileSize;
539 UINT8 *Stacks;
540 VOID *Registration;
541 UINT32 RegEax;
542 UINT32 RegEdx;
543 UINTN FamilyId;
544 UINTN ModelId;
545 UINT32 Cr3;
546
547 //
548 // Initialize Debug Agent to support source level debug in SMM code
549 //
550 InitializeDebugAgent (DEBUG_AGENT_INIT_SMM, NULL, NULL);
551
552 //
553 // Report the start of CPU SMM initialization.
554 //
555 REPORT_STATUS_CODE (
556 EFI_PROGRESS_CODE,
557 EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_PC_SMM_INIT
558 );
559
560 //
561 // Fix segment address of the long-mode-switch jump
562 //
563 if (sizeof (UINTN) == sizeof (UINT64)) {
564 gSmmJmpAddr.Segment = LONG_MODE_CODE_SEGMENT;
565 }
566
567 //
568 // Find out SMRR Base and SMRR Size
569 //
570 FindSmramInfo (&mCpuHotPlugData.SmrrBase, &mCpuHotPlugData.SmrrSize);
571
572 //
573 // Get MP Services Protocol
574 //
575 Status = SystemTable->BootServices->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices);
576 ASSERT_EFI_ERROR (Status);
577
578 //
579 // Use MP Services Protocol to retrieve the number of processors and number of enabled processors
580 //
581 Status = MpServices->GetNumberOfProcessors (MpServices, &mNumberOfCpus, &NumberOfEnabledProcessors);
582 ASSERT_EFI_ERROR (Status);
583 ASSERT (mNumberOfCpus <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
584
585 //
586 // If support CPU hot plug, PcdCpuSmmEnableBspElection should be set to TRUE.
587 // A constant BSP index makes no sense because it may be hot removed.
588 //
589 DEBUG_CODE (
590 if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
591
592 ASSERT (FeaturePcdGet (PcdCpuSmmEnableBspElection));
593 }
594 );
595
596 //
597 // Save the PcdCpuSmmCodeAccessCheckEnable value into a global variable.
598 //
599 mSmmCodeAccessCheckEnable = PcdGetBool (PcdCpuSmmCodeAccessCheckEnable);
600 DEBUG ((EFI_D_INFO, "PcdCpuSmmCodeAccessCheckEnable = %d\n", mSmmCodeAccessCheckEnable));
601
602 //
603 // If support CPU hot plug, we need to allocate resources for possibly hot-added processors
604 //
605 if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
606 mMaxNumberOfCpus = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
607 } else {
608 mMaxNumberOfCpus = mNumberOfCpus;
609 }
610 gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus = mMaxNumberOfCpus;
611
612 //
613 // The CPU save state and code for the SMI entry point are tiled within an SMRAM
614 // allocated buffer. The minimum size of this buffer for a uniprocessor system
615 // is 32 KB, because the entry point is SMBASE + 32KB, and CPU save state area
616 // just below SMBASE + 64KB. If more than one CPU is present in the platform,
617 // then the SMI entry point and the CPU save state areas can be tiles to minimize
618 // the total amount SMRAM required for all the CPUs. The tile size can be computed
619 // by adding the // CPU save state size, any extra CPU specific context, and
620 // the size of code that must be placed at the SMI entry point to transfer
621 // control to a C function in the native SMM execution mode. This size is
622 // rounded up to the nearest power of 2 to give the tile size for a each CPU.
623 // The total amount of memory required is the maximum number of CPUs that
624 // platform supports times the tile size. The picture below shows the tiling,
625 // where m is the number of tiles that fit in 32KB.
626 //
627 // +-----------------------------+ <-- 2^n offset from Base of allocated buffer
628 // | CPU m+1 Save State |
629 // +-----------------------------+
630 // | CPU m+1 Extra Data |
631 // +-----------------------------+
632 // | Padding |
633 // +-----------------------------+
634 // | CPU 2m SMI Entry |
635 // +#############################+ <-- Base of allocated buffer + 64 KB
636 // | CPU m-1 Save State |
637 // +-----------------------------+
638 // | CPU m-1 Extra Data |
639 // +-----------------------------+
640 // | Padding |
641 // +-----------------------------+
642 // | CPU 2m-1 SMI Entry |
643 // +=============================+ <-- 2^n offset from Base of allocated buffer
644 // | . . . . . . . . . . . . |
645 // +=============================+ <-- 2^n offset from Base of allocated buffer
646 // | CPU 2 Save State |
647 // +-----------------------------+
648 // | CPU 2 Extra Data |
649 // +-----------------------------+
650 // | Padding |
651 // +-----------------------------+
652 // | CPU m+1 SMI Entry |
653 // +=============================+ <-- Base of allocated buffer + 32 KB
654 // | CPU 1 Save State |
655 // +-----------------------------+
656 // | CPU 1 Extra Data |
657 // +-----------------------------+
658 // | Padding |
659 // +-----------------------------+
660 // | CPU m SMI Entry |
661 // +#############################+ <-- Base of allocated buffer + 32 KB == CPU 0 SMBASE + 64 KB
662 // | CPU 0 Save State |
663 // +-----------------------------+
664 // | CPU 0 Extra Data |
665 // +-----------------------------+
666 // | Padding |
667 // +-----------------------------+
668 // | CPU m-1 SMI Entry |
669 // +=============================+ <-- 2^n offset from Base of allocated buffer
670 // | . . . . . . . . . . . . |
671 // +=============================+ <-- 2^n offset from Base of allocated buffer
672 // | Padding |
673 // +-----------------------------+
674 // | CPU 1 SMI Entry |
675 // +=============================+ <-- 2^n offset from Base of allocated buffer
676 // | Padding |
677 // +-----------------------------+
678 // | CPU 0 SMI Entry |
679 // +#############################+ <-- Base of allocated buffer == CPU 0 SMBASE + 32 KB
680 //
681
682 //
683 // Retrieve CPU Family
684 //
685 AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);
686 FamilyId = (RegEax >> 8) & 0xf;
687 ModelId = (RegEax >> 4) & 0xf;
688 if (FamilyId == 0x06 || FamilyId == 0x0f) {
689 ModelId = ModelId | ((RegEax >> 12) & 0xf0);
690 }
691
692 RegEdx = 0;
693 AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
694 if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
695 AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx);
696 }
697 //
698 // Determine the mode of the CPU at the time an SMI occurs
699 // Intel(R) 64 and IA-32 Architectures Software Developer's Manual
700 // Volume 3C, Section 34.4.1.1
701 //
702 mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT;
703 if ((RegEdx & BIT29) != 0) {
704 mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT;
705 }
706 if (FamilyId == 0x06) {
707 if (ModelId == 0x17 || ModelId == 0x0f || ModelId == 0x1c) {
708 mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT;
709 }
710 }
711
712 //
713 // Compute tile size of buffer required to hold the CPU SMRAM Save State Map, extra CPU
714 // specific context start starts at SMBASE + SMM_PSD_OFFSET, and the SMI entry point.
715 // This size is rounded up to nearest power of 2.
716 //
717 TileCodeSize = GetSmiHandlerSize ();
718 TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);
719 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);
720 TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);
721 TileSize = TileDataSize + TileCodeSize - 1;
722 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
723 DEBUG ((EFI_D_INFO, "SMRAM TileSize = 0x%08x (0x%08x, 0x%08x)\n", TileSize, TileCodeSize, TileDataSize));
724
725 //
726 // If the TileSize is larger than space available for the SMI Handler of
727 // CPU[i], the extra CPU specific context of CPU[i+1], and the SMRAM Save
728 // State Map of CPU[i+1], then ASSERT(). If this ASSERT() is triggered, then
729 // the SMI Handler size must be reduced or the size of the extra CPU specific
730 // context must be reduced.
731 //
732 ASSERT (TileSize <= (SMRAM_SAVE_STATE_MAP_OFFSET + sizeof (SMRAM_SAVE_STATE_MAP) - SMM_HANDLER_OFFSET));
733
734 //
735 // Allocate buffer for all of the tiles.
736 //
737 // Intel(R) 64 and IA-32 Architectures Software Developer's Manual
738 // Volume 3C, Section 34.11 SMBASE Relocation
739 // For Pentium and Intel486 processors, the SMBASE values must be
740 // aligned on a 32-KByte boundary or the processor will enter shutdown
741 // state during the execution of a RSM instruction.
742 //
743 // Intel486 processors: FamilyId is 4
744 // Pentium processors : FamilyId is 5
745 //
746 BufferPages = EFI_SIZE_TO_PAGES (SIZE_32KB + TileSize * (mMaxNumberOfCpus - 1));
747 if ((FamilyId == 4) || (FamilyId == 5)) {
748 Buffer = AllocateAlignedCodePages (BufferPages, SIZE_32KB);
749 } else {
750 Buffer = AllocateAlignedCodePages (BufferPages, SIZE_4KB);
751 }
752 ASSERT (Buffer != NULL);
753 DEBUG ((EFI_D_INFO, "SMRAM SaveState Buffer (0x%08x, 0x%08x)\n", Buffer, EFI_PAGES_TO_SIZE(BufferPages)));
754
755 //
756 // Allocate buffer for pointers to array in SMM_CPU_PRIVATE_DATA.
757 //
758 gSmmCpuPrivate->ProcessorInfo = (EFI_PROCESSOR_INFORMATION *)AllocatePool (sizeof (EFI_PROCESSOR_INFORMATION) * mMaxNumberOfCpus);
759 ASSERT (gSmmCpuPrivate->ProcessorInfo != NULL);
760
761 gSmmCpuPrivate->Operation = (SMM_CPU_OPERATION *)AllocatePool (sizeof (SMM_CPU_OPERATION) * mMaxNumberOfCpus);
762 ASSERT (gSmmCpuPrivate->Operation != NULL);
763
764 gSmmCpuPrivate->CpuSaveStateSize = (UINTN *)AllocatePool (sizeof (UINTN) * mMaxNumberOfCpus);
765 ASSERT (gSmmCpuPrivate->CpuSaveStateSize != NULL);
766
767 gSmmCpuPrivate->CpuSaveState = (VOID **)AllocatePool (sizeof (VOID *) * mMaxNumberOfCpus);
768 ASSERT (gSmmCpuPrivate->CpuSaveState != NULL);
769
770 mSmmCpuPrivateData.SmmCoreEntryContext.CpuSaveStateSize = gSmmCpuPrivate->CpuSaveStateSize;
771 mSmmCpuPrivateData.SmmCoreEntryContext.CpuSaveState = gSmmCpuPrivate->CpuSaveState;
772
773 //
774 // Allocate buffer for pointers to array in CPU_HOT_PLUG_DATA.
775 //
776 mCpuHotPlugData.ApicId = (UINT64 *)AllocatePool (sizeof (UINT64) * mMaxNumberOfCpus);
777 ASSERT (mCpuHotPlugData.ApicId != NULL);
778 mCpuHotPlugData.SmBase = (UINTN *)AllocatePool (sizeof (UINTN) * mMaxNumberOfCpus);
779 ASSERT (mCpuHotPlugData.SmBase != NULL);
780 mCpuHotPlugData.ArrayLength = (UINT32)mMaxNumberOfCpus;
781
782 //
783 // Retrieve APIC ID of each enabled processor from the MP Services protocol.
784 // Also compute the SMBASE address, CPU Save State address, and CPU Save state
785 // size for each CPU in the platform
786 //
787 for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
788 mCpuHotPlugData.SmBase[Index] = (UINTN)Buffer + Index * TileSize - SMM_HANDLER_OFFSET;
789 gSmmCpuPrivate->CpuSaveStateSize[Index] = sizeof(SMRAM_SAVE_STATE_MAP);
790 gSmmCpuPrivate->CpuSaveState[Index] = (VOID *)(mCpuHotPlugData.SmBase[Index] + SMRAM_SAVE_STATE_MAP_OFFSET);
791 gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
792
793 if (Index < mNumberOfCpus) {
794 Status = MpServices->GetProcessorInfo (MpServices, Index, &gSmmCpuPrivate->ProcessorInfo[Index]);
795 ASSERT_EFI_ERROR (Status);
796 mCpuHotPlugData.ApicId[Index] = gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId;
797
798 DEBUG ((EFI_D_INFO, "CPU[%03x] APIC ID=%04x SMBASE=%08x SaveState=%08x Size=%08x\n",
799 Index,
800 (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId,
801 mCpuHotPlugData.SmBase[Index],
802 gSmmCpuPrivate->CpuSaveState[Index],
803 gSmmCpuPrivate->CpuSaveStateSize[Index]
804 ));
805 } else {
806 gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId = INVALID_APIC_ID;
807 mCpuHotPlugData.ApicId[Index] = INVALID_APIC_ID;
808 }
809 }
810
811 //
812 // Allocate SMI stacks for all processors.
813 //
814 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
815 //
816 // 2 more pages is allocated for each processor.
817 // one is guard page and the other is known good stack.
818 //
819 // +-------------------------------------------+-----+-------------------------------------------+
820 // | Known Good Stack | Guard Page | SMM Stack | ... | Known Good Stack | Guard Page | SMM Stack |
821 // +-------------------------------------------+-----+-------------------------------------------+
822 // | | | |
823 // |<-------------- Processor 0 -------------->| |<-------------- Processor n -------------->|
824 //
825 mSmmStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStackSize)) + 2);
826 Stacks = (UINT8 *) AllocatePages (gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStackSize)) + 2));
827 ASSERT (Stacks != NULL);
828 mSmmStackArrayBase = (UINTN)Stacks;
829 mSmmStackArrayEnd = mSmmStackArrayBase + gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * mSmmStackSize - 1;
830 } else {
831 mSmmStackSize = PcdGet32 (PcdCpuSmmStackSize);
832 Stacks = (UINT8 *) AllocatePages (EFI_SIZE_TO_PAGES (gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * mSmmStackSize));
833 ASSERT (Stacks != NULL);
834 }
835
836 //
837 // Set SMI stack for SMM base relocation
838 //
839 gSmmInitStack = (UINTN) (Stacks + mSmmStackSize - sizeof (UINTN));
840
841 //
842 // Initialize IDT
843 //
844 InitializeSmmIdt ();
845
846 //
847 // Relocate SMM Base addresses to the ones allocated from SMRAM
848 //
849 mRebased = (BOOLEAN *)AllocateZeroPool (sizeof (BOOLEAN) * mMaxNumberOfCpus);
850 ASSERT (mRebased != NULL);
851 SmmRelocateBases ();
852
853 //
854 // Call hook for BSP to perform extra actions in normal mode after all
855 // SMM base addresses have been relocated on all CPUs
856 //
857 SmmCpuFeaturesSmmRelocationComplete ();
858
859 DEBUG ((DEBUG_INFO, "mXdSupported - 0x%x\n", mXdSupported));
860
861 //
862 // SMM Time initialization
863 //
864 InitializeSmmTimer ();
865
866 //
867 // Initialize MP globals
868 //
869 Cr3 = InitializeMpServiceData (Stacks, mSmmStackSize);
870
871 //
872 // Fill in SMM Reserved Regions
873 //
874 gSmmCpuPrivate->SmmReservedSmramRegion[0].SmramReservedStart = 0;
875 gSmmCpuPrivate->SmmReservedSmramRegion[0].SmramReservedSize = 0;
876
877 //
878 // Install the SMM Configuration Protocol onto a new handle on the handle database.
879 // The entire SMM Configuration Protocol is allocated from SMRAM, so only a pointer
880 // to an SMRAM address will be present in the handle database
881 //
882 Status = SystemTable->BootServices->InstallMultipleProtocolInterfaces (
883 &gSmmCpuPrivate->SmmCpuHandle,
884 &gEfiSmmConfigurationProtocolGuid, &gSmmCpuPrivate->SmmConfiguration,
885 NULL
886 );
887 ASSERT_EFI_ERROR (Status);
888
889 //
890 // Install the SMM CPU Protocol into SMM protocol database
891 //
892 Status = gSmst->SmmInstallProtocolInterface (
893 &mSmmCpuHandle,
894 &gEfiSmmCpuProtocolGuid,
895 EFI_NATIVE_INTERFACE,
896 &mSmmCpu
897 );
898 ASSERT_EFI_ERROR (Status);
899
900 //
901 // Expose address of CPU Hot Plug Data structure if CPU hot plug is supported.
902 //
903 if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
904 Status = PcdSet64S (PcdCpuHotPlugDataAddress, (UINT64)(UINTN)&mCpuHotPlugData);
905 ASSERT_EFI_ERROR (Status);
906 }
907
908 //
909 // Initialize SMM CPU Services Support
910 //
911 Status = InitializeSmmCpuServices (mSmmCpuHandle);
912 ASSERT_EFI_ERROR (Status);
913
914 //
915 // register SMM Ready To Lock Protocol notification
916 //
917 Status = gSmst->SmmRegisterProtocolNotify (
918 &gEfiSmmReadyToLockProtocolGuid,
919 SmmReadyToLockEventNotify,
920 &Registration
921 );
922 ASSERT_EFI_ERROR (Status);
923
924 //
925 // Initialize SMM Profile feature
926 //
927 InitSmmProfile (Cr3);
928
929 GetAcpiS3EnableFlag ();
930 InitSmmS3ResumeState (Cr3);
931
932 DEBUG ((EFI_D_INFO, "SMM CPU Module exit from SMRAM with EFI_SUCCESS\n"));
933
934 return EFI_SUCCESS;
935 }
936
937 /**
938
939 Find out SMRAM information including SMRR base and SMRR size.
940
941 @param SmrrBase SMRR base
942 @param SmrrSize SMRR size
943
944 **/
945 VOID
946 FindSmramInfo (
947 OUT UINT32 *SmrrBase,
948 OUT UINT32 *SmrrSize
949 )
950 {
951 EFI_STATUS Status;
952 UINTN Size;
953 EFI_SMM_ACCESS2_PROTOCOL *SmmAccess;
954 EFI_SMRAM_DESCRIPTOR *CurrentSmramRange;
955 EFI_SMRAM_DESCRIPTOR *SmramRanges;
956 UINTN SmramRangeCount;
957 UINTN Index;
958 UINT64 MaxSize;
959 BOOLEAN Found;
960
961 //
962 // Get SMM Access Protocol
963 //
964 Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
965 ASSERT_EFI_ERROR (Status);
966
967 //
968 // Get SMRAM information
969 //
970 Size = 0;
971 Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
972 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
973
974 SmramRanges = (EFI_SMRAM_DESCRIPTOR *)AllocatePool (Size);
975 ASSERT (SmramRanges != NULL);
976
977 Status = SmmAccess->GetCapabilities (SmmAccess, &Size, SmramRanges);
978 ASSERT_EFI_ERROR (Status);
979
980 SmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
981
982 //
983 // Find the largest SMRAM range between 1MB and 4GB that is at least 256K - 4K in size
984 //
985 CurrentSmramRange = NULL;
986 for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < SmramRangeCount; Index++) {
987 //
988 // Skip any SMRAM region that is already allocated, needs testing, or needs ECC initialization
989 //
990 if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
991 continue;
992 }
993
994 if (SmramRanges[Index].CpuStart >= BASE_1MB) {
995 if ((SmramRanges[Index].CpuStart + SmramRanges[Index].PhysicalSize) <= BASE_4GB) {
996 if (SmramRanges[Index].PhysicalSize >= MaxSize) {
997 MaxSize = SmramRanges[Index].PhysicalSize;
998 CurrentSmramRange = &SmramRanges[Index];
999 }
1000 }
1001 }
1002 }
1003
1004 ASSERT (CurrentSmramRange != NULL);
1005
1006 *SmrrBase = (UINT32)CurrentSmramRange->CpuStart;
1007 *SmrrSize = (UINT32)CurrentSmramRange->PhysicalSize;
1008
1009 do {
1010 Found = FALSE;
1011 for (Index = 0; Index < SmramRangeCount; Index++) {
1012 if (SmramRanges[Index].CpuStart < *SmrrBase && *SmrrBase == (SmramRanges[Index].CpuStart + SmramRanges[Index].PhysicalSize)) {
1013 *SmrrBase = (UINT32)SmramRanges[Index].CpuStart;
1014 *SmrrSize = (UINT32)(*SmrrSize + SmramRanges[Index].PhysicalSize);
1015 Found = TRUE;
1016 } else if ((*SmrrBase + *SmrrSize) == SmramRanges[Index].CpuStart && SmramRanges[Index].PhysicalSize > 0) {
1017 *SmrrSize = (UINT32)(*SmrrSize + SmramRanges[Index].PhysicalSize);
1018 Found = TRUE;
1019 }
1020 }
1021 } while (Found);
1022
1023 FreePool (SmramRanges);
1024 DEBUG ((EFI_D_INFO, "SMRR Base: 0x%x, SMRR Size: 0x%x\n", *SmrrBase, *SmrrSize));
1025 }
1026
1027 /**
1028 Configure SMM Code Access Check feature on an AP.
1029 SMM Feature Control MSR will be locked after configuration.
1030
1031 @param[in,out] Buffer Pointer to private data buffer.
1032 **/
1033 VOID
1034 EFIAPI
1035 ConfigSmmCodeAccessCheckOnCurrentProcessor (
1036 IN OUT VOID *Buffer
1037 )
1038 {
1039 UINTN CpuIndex;
1040 UINT64 SmmFeatureControlMsr;
1041 UINT64 NewSmmFeatureControlMsr;
1042
1043 //
1044 // Retrieve the CPU Index from the context passed in
1045 //
1046 CpuIndex = *(UINTN *)Buffer;
1047
1048 //
1049 // Get the current SMM Feature Control MSR value
1050 //
1051 SmmFeatureControlMsr = SmmCpuFeaturesGetSmmRegister (CpuIndex, SmmRegFeatureControl);
1052
1053 //
1054 // Compute the new SMM Feature Control MSR value
1055 //
1056 NewSmmFeatureControlMsr = SmmFeatureControlMsr;
1057 if (mSmmCodeAccessCheckEnable) {
1058 NewSmmFeatureControlMsr |= SMM_CODE_CHK_EN_BIT;
1059 if (FeaturePcdGet (PcdCpuSmmFeatureControlMsrLock)) {
1060 NewSmmFeatureControlMsr |= SMM_FEATURE_CONTROL_LOCK_BIT;
1061 }
1062 }
1063
1064 //
1065 // Only set the SMM Feature Control MSR value if the new value is different than the current value
1066 //
1067 if (NewSmmFeatureControlMsr != SmmFeatureControlMsr) {
1068 SmmCpuFeaturesSetSmmRegister (CpuIndex, SmmRegFeatureControl, NewSmmFeatureControlMsr);
1069 }
1070
1071 //
1072 // Release the spin lock user to serialize the updates to the SMM Feature Control MSR
1073 //
1074 ReleaseSpinLock (mConfigSmmCodeAccessCheckLock);
1075 }
1076
1077 /**
1078 Configure SMM Code Access Check feature for all processors.
1079 SMM Feature Control MSR will be locked after configuration.
1080 **/
1081 VOID
1082 ConfigSmmCodeAccessCheck (
1083 VOID
1084 )
1085 {
1086 UINTN Index;
1087 EFI_STATUS Status;
1088
1089 //
1090 // Check to see if the Feature Control MSR is supported on this CPU
1091 //
1092 Index = gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu;
1093 if (!SmmCpuFeaturesIsSmmRegisterSupported (Index, SmmRegFeatureControl)) {
1094 mSmmCodeAccessCheckEnable = FALSE;
1095 return;
1096 }
1097
1098 //
1099 // Check to see if the CPU supports the SMM Code Access Check feature
1100 // Do not access this MSR unless the CPU supports the SmmRegFeatureControl
1101 //
1102 if ((AsmReadMsr64 (EFI_MSR_SMM_MCA_CAP) & SMM_CODE_ACCESS_CHK_BIT) == 0) {
1103 mSmmCodeAccessCheckEnable = FALSE;
1104 return;
1105 }
1106
1107 //
1108 // Initialize the lock used to serialize the MSR programming in BSP and all APs
1109 //
1110 InitializeSpinLock (mConfigSmmCodeAccessCheckLock);
1111
1112 //
1113 // Acquire Config SMM Code Access Check spin lock. The BSP will release the
1114 // spin lock when it is done executing ConfigSmmCodeAccessCheckOnCurrentProcessor().
1115 //
1116 AcquireSpinLock (mConfigSmmCodeAccessCheckLock);
1117
1118 //
1119 // Enable SMM Code Access Check feature on the BSP.
1120 //
1121 ConfigSmmCodeAccessCheckOnCurrentProcessor (&Index);
1122
1123 //
1124 // Enable SMM Code Access Check feature for the APs.
1125 //
1126 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
1127 if (Index != gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) {
1128
1129 //
1130 // Acquire Config SMM Code Access Check spin lock. The AP will release the
1131 // spin lock when it is done executing ConfigSmmCodeAccessCheckOnCurrentProcessor().
1132 //
1133 AcquireSpinLock (mConfigSmmCodeAccessCheckLock);
1134
1135 //
1136 // Call SmmStartupThisAp() to enable SMM Code Access Check on an AP.
1137 //
1138 Status = gSmst->SmmStartupThisAp (ConfigSmmCodeAccessCheckOnCurrentProcessor, Index, &Index);
1139 ASSERT_EFI_ERROR (Status);
1140
1141 //
1142 // Wait for the AP to release the Config SMM Code Access Check spin lock.
1143 //
1144 while (!AcquireSpinLockOrFail (mConfigSmmCodeAccessCheckLock)) {
1145 CpuPause ();
1146 }
1147
1148 //
1149 // Release the Config SMM Code Access Check spin lock.
1150 //
1151 ReleaseSpinLock (mConfigSmmCodeAccessCheckLock);
1152 }
1153 }
1154 }
1155
1156 /**
1157 Set code region to be read only and data region to be execute disable.
1158 **/
1159 VOID
1160 SetRegionAttributes (
1161 VOID
1162 )
1163 {
1164 SetMemMapAttributes ();
1165 }
1166
1167 /**
1168 This API provides a way to allocate memory for page table.
1169
1170 This API can be called more once to allocate memory for page tables.
1171
1172 Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
1173 allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
1174 is returned. If there is not enough memory remaining to satisfy the request, then NULL is
1175 returned.
1176
1177 @param Pages The number of 4 KB pages to allocate.
1178
1179 @return A pointer to the allocated buffer or NULL if allocation fails.
1180
1181 **/
1182 VOID *
1183 AllocatePageTableMemory (
1184 IN UINTN Pages
1185 )
1186 {
1187 VOID *Buffer;
1188
1189 Buffer = SmmCpuFeaturesAllocatePageTableMemory (Pages);
1190 if (Buffer != NULL) {
1191 return Buffer;
1192 }
1193 return AllocatePages (Pages);
1194 }
1195
1196 /**
1197 Allocate pages for code.
1198
1199 @param[in] Pages Number of pages to be allocated.
1200
1201 @return Allocated memory.
1202 **/
1203 VOID *
1204 AllocateCodePages (
1205 IN UINTN Pages
1206 )
1207 {
1208 EFI_STATUS Status;
1209 EFI_PHYSICAL_ADDRESS Memory;
1210
1211 if (Pages == 0) {
1212 return NULL;
1213 }
1214
1215 Status = gSmst->SmmAllocatePages (AllocateAnyPages, EfiRuntimeServicesCode, Pages, &Memory);
1216 if (EFI_ERROR (Status)) {
1217 return NULL;
1218 }
1219 return (VOID *) (UINTN) Memory;
1220 }
1221
1222 /**
1223 Allocate aligned pages for code.
1224
1225 @param[in] Pages Number of pages to be allocated.
1226 @param[in] Alignment The requested alignment of the allocation.
1227 Must be a power of two.
1228 If Alignment is zero, then byte alignment is used.
1229
1230 @return Allocated memory.
1231 **/
1232 VOID *
1233 AllocateAlignedCodePages (
1234 IN UINTN Pages,
1235 IN UINTN Alignment
1236 )
1237 {
1238 EFI_STATUS Status;
1239 EFI_PHYSICAL_ADDRESS Memory;
1240 UINTN AlignedMemory;
1241 UINTN AlignmentMask;
1242 UINTN UnalignedPages;
1243 UINTN RealPages;
1244
1245 //
1246 // Alignment must be a power of two or zero.
1247 //
1248 ASSERT ((Alignment & (Alignment - 1)) == 0);
1249
1250 if (Pages == 0) {
1251 return NULL;
1252 }
1253 if (Alignment > EFI_PAGE_SIZE) {
1254 //
1255 // Calculate the total number of pages since alignment is larger than page size.
1256 //
1257 AlignmentMask = Alignment - 1;
1258 RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);
1259 //
1260 // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow.
1261 //
1262 ASSERT (RealPages > Pages);
1263
1264 Status = gSmst->SmmAllocatePages (AllocateAnyPages, EfiRuntimeServicesCode, RealPages, &Memory);
1265 if (EFI_ERROR (Status)) {
1266 return NULL;
1267 }
1268 AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask;
1269 UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory);
1270 if (UnalignedPages > 0) {
1271 //
1272 // Free first unaligned page(s).
1273 //
1274 Status = gSmst->SmmFreePages (Memory, UnalignedPages);
1275 ASSERT_EFI_ERROR (Status);
1276 }
1277 Memory = (EFI_PHYSICAL_ADDRESS) (AlignedMemory + EFI_PAGES_TO_SIZE (Pages));
1278 UnalignedPages = RealPages - Pages - UnalignedPages;
1279 if (UnalignedPages > 0) {
1280 //
1281 // Free last unaligned page(s).
1282 //
1283 Status = gSmst->SmmFreePages (Memory, UnalignedPages);
1284 ASSERT_EFI_ERROR (Status);
1285 }
1286 } else {
1287 //
1288 // Do not over-allocate pages in this case.
1289 //
1290 Status = gSmst->SmmAllocatePages (AllocateAnyPages, EfiRuntimeServicesCode, Pages, &Memory);
1291 if (EFI_ERROR (Status)) {
1292 return NULL;
1293 }
1294 AlignedMemory = (UINTN) Memory;
1295 }
1296 return (VOID *) AlignedMemory;
1297 }
1298
1299 /**
1300 Perform the remaining tasks.
1301
1302 **/
1303 VOID
1304 PerformRemainingTasks (
1305 VOID
1306 )
1307 {
1308 if (mSmmReadyToLock) {
1309 //
1310 // Start SMM Profile feature
1311 //
1312 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
1313 SmmProfileStart ();
1314 }
1315 //
1316 // Create a mix of 2MB and 4KB page table. Update some memory ranges absent and execute-disable.
1317 //
1318 InitPaging ();
1319
1320 //
1321 // Mark critical region to be read-only in page table
1322 //
1323 SetRegionAttributes ();
1324
1325 //
1326 // Set page table itself to be read-only
1327 //
1328 SetPageTableAttributes ();
1329
1330 //
1331 // Configure SMM Code Access Check feature if available.
1332 //
1333 ConfigSmmCodeAccessCheck ();
1334
1335 SmmCpuFeaturesCompleteSmmReadyToLock ();
1336
1337 //
1338 // Clean SMM ready to lock flag
1339 //
1340 mSmmReadyToLock = FALSE;
1341 }
1342 }
1343
1344 /**
1345 Perform the pre tasks.
1346
1347 **/
1348 VOID
1349 PerformPreTasks (
1350 VOID
1351 )
1352 {
1353 RestoreSmmConfigurationInS3 ();
1354 }