2 The CPU specific programming for PiSmmCpuDxeSmm module.
4 Copyright (c) 2010 - 2023, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <IndustryStandard/Q35MchIch9.h>
10 #include <Library/BaseLib.h>
11 #include <Library/BaseMemoryLib.h>
12 #include <Library/DebugLib.h>
13 #include <Library/MemEncryptSevLib.h>
14 #include <Library/MemoryAllocationLib.h>
15 #include <Library/PcdLib.h>
16 #include <Library/SafeIntLib.h>
17 #include <Library/SmmCpuFeaturesLib.h>
18 #include <Library/SmmServicesTableLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/HobLib.h>
21 #include <Pcd/CpuHotEjectData.h>
23 #include <Register/Intel/SmramSaveStateMap.h>
24 #include <Register/QemuSmramSaveStateMap.h>
25 #include <Guid/SmmBaseHob.h>
28 // EFER register LMA bit
33 The constructor function
35 @param[in] ImageHandle The firmware allocated handle for the EFI image.
36 @param[in] SystemTable A pointer to the EFI System Table.
38 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
43 SmmCpuFeaturesLibConstructor (
44 IN EFI_HANDLE ImageHandle
,
45 IN EFI_SYSTEM_TABLE
*SystemTable
49 // If gSmmBaseHobGuid found, means SmBase info has been relocated and recorded
50 // in the SmBase array. ASSERT it's not supported in OVMF.
52 ASSERT (GetFirstGuidHob (&gSmmBaseHobGuid
) == NULL
);
55 // No need to program SMRRs on our virtual platform.
61 Called during the very first SMI into System Management Mode to initialize
62 CPU features, including SMBASE, for the currently executing CPU. Since this
63 is the first SMI, the SMRAM Save State Map is at the default address of
64 SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET. The currently executing
65 CPU is specified by CpuIndex and CpuIndex can be used to access information
66 about the currently executing CPU in the ProcessorInfo array and the
67 HotPlugCpuData data structure.
69 @param[in] CpuIndex The index of the CPU to initialize. The value
70 must be between 0 and the NumberOfCpus field in
71 the System Management System Table (SMST).
72 @param[in] IsMonarch TRUE if the CpuIndex is the index of the CPU that
73 was elected as monarch during System Management
75 FALSE if the CpuIndex is not the index of the CPU
76 that was elected as monarch during System
77 Management Mode initialization.
78 @param[in] ProcessorInfo Pointer to an array of EFI_PROCESSOR_INFORMATION
79 structures. ProcessorInfo[CpuIndex] contains the
80 information for the currently executing CPU.
81 @param[in] CpuHotPlugData Pointer to the CPU_HOT_PLUG_DATA structure that
82 contains the ApidId and SmBase arrays.
86 SmmCpuFeaturesInitializeProcessor (
89 IN EFI_PROCESSOR_INFORMATION
*ProcessorInfo
,
90 IN CPU_HOT_PLUG_DATA
*CpuHotPlugData
93 QEMU_SMRAM_SAVE_STATE_MAP
*CpuState
;
98 CpuState
= (QEMU_SMRAM_SAVE_STATE_MAP
*)(UINTN
)(
100 SMRAM_SAVE_STATE_MAP_OFFSET
102 if ((CpuState
->x86
.SMMRevId
& 0xFFFF) == 0) {
103 CpuState
->x86
.SMBASE
= (UINT32
)CpuHotPlugData
->SmBase
[CpuIndex
];
105 CpuState
->x64
.SMBASE
= (UINT32
)CpuHotPlugData
->SmBase
[CpuIndex
];
109 // No need to program SMRRs on our virtual platform.
114 This function updates the SMRAM save state on the currently executing CPU
115 to resume execution at a specific address after an RSM instruction. This
116 function must evaluate the SMRAM save state to determine the execution mode
117 the RSM instruction resumes and update the resume execution address with
118 either NewInstructionPointer32 or NewInstructionPoint. The auto HALT restart
119 flag in the SMRAM save state must always be cleared. This function returns
120 the value of the instruction pointer from the SMRAM save state that was
121 replaced. If this function returns 0, then the SMRAM save state was not
124 This function is called during the very first SMI on each CPU after
125 SmmCpuFeaturesInitializeProcessor() to set a flag in normal execution mode
126 to signal that the SMBASE of each CPU has been updated before the default
127 SMBASE address is used for the first SMI to the next CPU.
129 @param[in] CpuIndex The index of the CPU to hook. The value
130 must be between 0 and the NumberOfCpus
131 field in the System Management System
133 @param[in] CpuState Pointer to SMRAM Save State Map for the
134 currently executing CPU.
135 @param[in] NewInstructionPointer32 Instruction pointer to use if resuming to
136 32-bit execution mode from 64-bit SMM.
137 @param[in] NewInstructionPointer Instruction pointer to use if resuming to
138 same execution mode as SMM.
140 @retval 0 This function did modify the SMRAM save state.
141 @retval > 0 The original instruction pointer value from the SMRAM save state
142 before it was replaced.
146 SmmCpuFeaturesHookReturnFromSmm (
148 IN SMRAM_SAVE_STATE_MAP
*CpuState
,
149 IN UINT64 NewInstructionPointer32
,
150 IN UINT64 NewInstructionPointer
153 UINT64 OriginalInstructionPointer
;
154 QEMU_SMRAM_SAVE_STATE_MAP
*CpuSaveState
;
156 CpuSaveState
= (QEMU_SMRAM_SAVE_STATE_MAP
*)CpuState
;
157 if ((CpuSaveState
->x86
.SMMRevId
& 0xFFFF) == 0) {
158 OriginalInstructionPointer
= (UINT64
)CpuSaveState
->x86
._EIP
;
159 CpuSaveState
->x86
._EIP
= (UINT32
)NewInstructionPointer
;
161 // Clear the auto HALT restart flag so the RSM instruction returns
162 // program control to the instruction following the HLT instruction.
164 if ((CpuSaveState
->x86
.AutoHALTRestart
& BIT0
) != 0) {
165 CpuSaveState
->x86
.AutoHALTRestart
&= ~BIT0
;
168 OriginalInstructionPointer
= CpuSaveState
->x64
._RIP
;
169 if ((CpuSaveState
->x64
.IA32_EFER
& LMA
) == 0) {
170 CpuSaveState
->x64
._RIP
= (UINT32
)NewInstructionPointer32
;
172 CpuSaveState
->x64
._RIP
= (UINT32
)NewInstructionPointer
;
176 // Clear the auto HALT restart flag so the RSM instruction returns
177 // program control to the instruction following the HLT instruction.
179 if ((CpuSaveState
->x64
.AutoHALTRestart
& BIT0
) != 0) {
180 CpuSaveState
->x64
.AutoHALTRestart
&= ~BIT0
;
184 return OriginalInstructionPointer
;
187 STATIC CPU_HOT_EJECT_DATA
*mCpuHotEjectData
= NULL
;
190 Initialize mCpuHotEjectData if PcdCpuMaxLogicalProcessorNumber > 1.
192 Also setup the corresponding PcdCpuHotEjectDataAddress.
196 InitCpuHotEjectData (
202 UINT32 MaxNumberOfCpus
;
203 RETURN_STATUS PcdStatus
;
205 MaxNumberOfCpus
= PcdGet32 (PcdCpuMaxLogicalProcessorNumber
);
206 if (MaxNumberOfCpus
== 1) {
211 // We allocate CPU_HOT_EJECT_DATA and CPU_HOT_EJECT_DATA->QemuSelectorMap[]
212 // in a single allocation, and explicitly align the QemuSelectorMap[] (which
213 // is a UINT64 array) at its natural boundary.
214 // Accordingly, allocate:
215 // sizeof(*mCpuHotEjectData) + (MaxNumberOfCpus * sizeof(UINT64))
216 // and, add sizeof(UINT64) - 1 to use as padding if needed.
219 if (RETURN_ERROR (SafeUintnMult (MaxNumberOfCpus
, sizeof (UINT64
), &Size
)) ||
220 RETURN_ERROR (SafeUintnAdd (Size
, sizeof (*mCpuHotEjectData
), &Size
)) ||
221 RETURN_ERROR (SafeUintnAdd (Size
, sizeof (UINT64
) - 1, &Size
)))
223 DEBUG ((DEBUG_ERROR
, "%a: invalid CPU_HOT_EJECT_DATA\n", __FUNCTION__
));
227 mCpuHotEjectData
= AllocatePool (Size
);
228 if (mCpuHotEjectData
== NULL
) {
229 ASSERT (mCpuHotEjectData
!= NULL
);
233 mCpuHotEjectData
->Handler
= NULL
;
234 mCpuHotEjectData
->ArrayLength
= MaxNumberOfCpus
;
236 mCpuHotEjectData
->QemuSelectorMap
= ALIGN_POINTER (
237 mCpuHotEjectData
+ 1,
241 // We use mCpuHotEjectData->QemuSelectorMap to map
242 // ProcessorNum -> QemuSelector. Initialize to invalid values.
244 for (Idx
= 0; Idx
< mCpuHotEjectData
->ArrayLength
; Idx
++) {
245 mCpuHotEjectData
->QemuSelectorMap
[Idx
] = CPU_EJECT_QEMU_SELECTOR_INVALID
;
249 // Expose address of CPU Hot eject Data structure
251 PcdStatus
= PcdSet64S (
252 PcdCpuHotEjectDataAddress
,
253 (UINTN
)(VOID
*)mCpuHotEjectData
255 ASSERT_RETURN_ERROR (PcdStatus
);
264 Hook point in normal execution mode that allows the one CPU that was elected
265 as monarch during System Management Mode initialization to perform additional
266 initialization actions immediately after all of the CPUs have processed their
267 first SMI and called SmmCpuFeaturesInitializeProcessor() relocating SMBASE
268 into a buffer in SMRAM and called SmmCpuFeaturesHookReturnFromSmm().
272 SmmCpuFeaturesSmmRelocationComplete (
280 InitCpuHotEjectData ();
282 if (!MemEncryptSevIsEnabled ()) {
287 // Now that SMBASE relocation is complete, re-encrypt the original SMRAM save
288 // state map's container pages, and release the pages to DXE. (The pages were
289 // allocated in PlatformPei.)
291 Status
= MemEncryptSevLocateInitialSmramSaveStateMapPages (
295 ASSERT_EFI_ERROR (Status
);
297 Status
= MemEncryptSevSetPageEncMask (
298 0, // Cr3BaseAddress -- use current CR3
299 MapPagesBase
, // BaseAddress
300 MapPagesCount
// NumPages
302 if (EFI_ERROR (Status
)) {
305 "%a: MemEncryptSevSetPageEncMask(): %r\n",
313 ZeroMem ((VOID
*)MapPagesBase
, EFI_PAGES_TO_SIZE (MapPagesCount
));
315 if (PcdGetBool (PcdQ35SmramAtDefaultSmbase
)) {
317 // The initial SMRAM Save State Map has been covered as part of a larger
318 // reserved memory allocation in PlatformPei's InitializeRamRegions(). That
319 // allocation is supposed to survive into OS runtime; we must not release
320 // any part of it. Only re-assert the containment here.
322 ASSERT (SMM_DEFAULT_SMBASE
<= MapPagesBase
);
324 (MapPagesBase
+ EFI_PAGES_TO_SIZE (MapPagesCount
) <=
325 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
)
328 Status
= gBS
->FreePages (MapPagesBase
, MapPagesCount
);
329 ASSERT_EFI_ERROR (Status
);
334 Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is
335 returned, then a custom SMI handler is not provided by this library,
336 and the default SMI handler must be used.
338 @retval 0 Use the default SMI handler.
339 @retval > 0 Use the SMI handler installed by
340 SmmCpuFeaturesInstallSmiHandler(). The caller is required to
341 allocate enough SMRAM for each CPU to support the size of the
346 SmmCpuFeaturesGetSmiHandlerSize (
354 Install a custom SMI handler for the CPU specified by CpuIndex. This
355 function is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size
356 is greater than zero and is called by the CPU that was elected as monarch
357 during System Management Mode initialization.
359 @param[in] CpuIndex The index of the CPU to install the custom SMI handler.
360 The value must be between 0 and the NumberOfCpus field
361 in the System Management System Table (SMST).
362 @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex.
363 @param[in] SmiStack The stack to use when an SMI is processed by the
364 the CPU specified by CpuIndex.
365 @param[in] StackSize The size, in bytes, if the stack used when an SMI is
366 processed by the CPU specified by CpuIndex.
367 @param[in] GdtBase The base address of the GDT to use when an SMI is
368 processed by the CPU specified by CpuIndex.
369 @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is
370 processed by the CPU specified by CpuIndex.
371 @param[in] IdtBase The base address of the IDT to use when an SMI is
372 processed by the CPU specified by CpuIndex.
373 @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is
374 processed by the CPU specified by CpuIndex.
375 @param[in] Cr3 The base address of the page tables to use when an SMI
376 is processed by the CPU specified by CpuIndex.
380 SmmCpuFeaturesInstallSmiHandler (
395 Determines if MTRR registers must be configured to set SMRAM cache-ability
396 when executing in System Management Mode.
398 @retval TRUE MTRR registers must be configured to set SMRAM cache-ability.
399 @retval FALSE MTRR registers do not need to be configured to set SMRAM
404 SmmCpuFeaturesNeedConfigureMtrrs (
412 Disable SMRR register if SMRR is supported and
413 SmmCpuFeaturesNeedConfigureMtrrs() returns TRUE.
417 SmmCpuFeaturesDisableSmrr (
422 // No SMRR support, nothing to do
427 Enable SMRR register if SMRR is supported and
428 SmmCpuFeaturesNeedConfigureMtrrs() returns TRUE.
432 SmmCpuFeaturesReenableSmrr (
437 // No SMRR support, nothing to do
442 Processor specific hook point each time a CPU enters System Management Mode.
444 @param[in] CpuIndex The index of the CPU that has entered SMM. The value
445 must be between 0 and the NumberOfCpus field in the
446 System Management System Table (SMST).
450 SmmCpuFeaturesRendezvousEntry (
455 // No SMRR support, nothing to do
460 Processor specific hook point each time a CPU exits System Management Mode.
462 @param[in] CpuIndex The index of the CPU that is exiting SMM. The value
463 must be between 0 and the NumberOfCpus field in the
464 System Management System Table (SMST).
468 SmmCpuFeaturesRendezvousExit (
473 // We only call the Handler if CPU hot-eject is enabled
474 // (PcdCpuMaxLogicalProcessorNumber > 1), and hot-eject is needed
475 // in this SMI exit (otherwise mCpuHotEjectData->Handler is not armed.)
478 if (mCpuHotEjectData
!= NULL
) {
479 CPU_HOT_EJECT_HANDLER Handler
;
482 // As the comment above mentions, mCpuHotEjectData->Handler might be
483 // written to on the BSP as part of handling of the CPU-ejection.
485 // We know that any initial assignment to mCpuHotEjectData->Handler
486 // (on the BSP, in the CpuHotplugMmi() context) is ordered-before the
487 // load below, since it is guaranteed to happen before the
488 // control-dependency of the BSP's SMI exit signal -- by way of a store
489 // to AllCpusInSync (on the BSP, in BspHandler()) and the corresponding
490 // AllCpusInSync loop (on the APs, in SmiRendezvous()) which depends on
493 // This guarantees that these pieces of code can never execute
494 // simultaneously. In addition, we ensure that the following load is
495 // ordered-after the AllCpusInSync loop by using a MemoryFence() with
496 // acquire semantics.
500 Handler
= mCpuHotEjectData
->Handler
;
502 if (Handler
!= NULL
) {
509 Check to see if an SMM register is supported by a specified CPU.
511 @param[in] CpuIndex The index of the CPU to check for SMM register support.
512 The value must be between 0 and the NumberOfCpus field
513 in the System Management System Table (SMST).
514 @param[in] RegName Identifies the SMM register to check for support.
516 @retval TRUE The SMM register specified by RegName is supported by the CPU
517 specified by CpuIndex.
518 @retval FALSE The SMM register specified by RegName is not supported by the
519 CPU specified by CpuIndex.
523 SmmCpuFeaturesIsSmmRegisterSupported (
525 IN SMM_REG_NAME RegName
528 ASSERT (RegName
== SmmRegFeatureControl
);
533 Returns the current value of the SMM register for the specified CPU.
534 If the SMM register is not supported, then 0 is returned.
536 @param[in] CpuIndex The index of the CPU to read the SMM register. The
537 value must be between 0 and the NumberOfCpus field in
538 the System Management System Table (SMST).
539 @param[in] RegName Identifies the SMM register to read.
541 @return The value of the SMM register specified by RegName from the CPU
542 specified by CpuIndex.
546 SmmCpuFeaturesGetSmmRegister (
548 IN SMM_REG_NAME RegName
552 // This is called for SmmRegSmmDelayed, SmmRegSmmBlocked, SmmRegSmmEnable.
553 // The last of these should actually be SmmRegSmmDisable, so we can just
560 Sets the value of an SMM register on a specified CPU.
561 If the SMM register is not supported, then no action is performed.
563 @param[in] CpuIndex The index of the CPU to write the SMM register. The
564 value must be between 0 and the NumberOfCpus field in
565 the System Management System Table (SMST).
566 @param[in] RegName Identifies the SMM register to write.
567 registers are read-only.
568 @param[in] Value The value to write to the SMM register.
572 SmmCpuFeaturesSetSmmRegister (
574 IN SMM_REG_NAME RegName
,
582 /// Macro used to simplify the lookup table entries of type
583 /// CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
585 #define SMM_CPU_OFFSET(Field) OFFSET_OF (QEMU_SMRAM_SAVE_STATE_MAP, Field)
588 /// Macro used to simplify the lookup table entries of type
589 /// CPU_SMM_SAVE_STATE_REGISTER_RANGE
591 #define SMM_REGISTER_RANGE(Start, End) { Start, End, End - Start + 1 }
594 /// Structure used to describe a range of registers
597 EFI_SMM_SAVE_STATE_REGISTER Start
;
598 EFI_SMM_SAVE_STATE_REGISTER End
;
600 } CPU_SMM_SAVE_STATE_REGISTER_RANGE
;
603 /// Structure used to build a lookup table to retrieve the widths and offsets
604 /// associated with each supported EFI_SMM_SAVE_STATE_REGISTER value
607 #define SMM_SAVE_STATE_REGISTER_FIRST_INDEX 1
616 } CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
;
619 /// Table used by GetRegisterIndex() to convert an EFI_SMM_SAVE_STATE_REGISTER
620 /// value to an index into a table of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
622 STATIC CONST CPU_SMM_SAVE_STATE_REGISTER_RANGE mSmmCpuRegisterRanges
[] = {
624 EFI_SMM_SAVE_STATE_REGISTER_GDTBASE
,
625 EFI_SMM_SAVE_STATE_REGISTER_LDTINFO
628 EFI_SMM_SAVE_STATE_REGISTER_ES
,
629 EFI_SMM_SAVE_STATE_REGISTER_RIP
632 EFI_SMM_SAVE_STATE_REGISTER_RFLAGS
,
633 EFI_SMM_SAVE_STATE_REGISTER_CR4
635 { (EFI_SMM_SAVE_STATE_REGISTER
)0, (EFI_SMM_SAVE_STATE_REGISTER
)0,0 }
639 /// Lookup table used to retrieve the widths and offsets associated with each
640 /// supported EFI_SMM_SAVE_STATE_REGISTER value
642 STATIC CONST CPU_SMM_SAVE_STATE_LOOKUP_ENTRY mSmmCpuWidthOffset
[] = {
653 // CPU Save State registers defined in PI SMM CPU Protocol.
659 SMM_CPU_OFFSET (x64
._GDTRBase
), // Offset64Lo
660 SMM_CPU_OFFSET (x64
._GDTRBase
) + 4, // Offset64Hi
662 }, // EFI_SMM_SAVE_STATE_REGISTER_GDTBASE = 4
668 SMM_CPU_OFFSET (x64
._IDTRBase
), // Offset64Lo
669 SMM_CPU_OFFSET (x64
._IDTRBase
) + 4, // Offset64Hi
671 }, // EFI_SMM_SAVE_STATE_REGISTER_IDTBASE = 5
677 SMM_CPU_OFFSET (x64
._LDTRBase
), // Offset64Lo
678 SMM_CPU_OFFSET (x64
._LDTRBase
) + 4, // Offset64Hi
680 }, // EFI_SMM_SAVE_STATE_REGISTER_LDTBASE = 6
686 SMM_CPU_OFFSET (x64
._GDTRLimit
), // Offset64Lo
687 SMM_CPU_OFFSET (x64
._GDTRLimit
) + 4, // Offset64Hi
689 }, // EFI_SMM_SAVE_STATE_REGISTER_GDTLIMIT = 7
695 SMM_CPU_OFFSET (x64
._IDTRLimit
), // Offset64Lo
696 SMM_CPU_OFFSET (x64
._IDTRLimit
) + 4, // Offset64Hi
698 }, // EFI_SMM_SAVE_STATE_REGISTER_IDTLIMIT = 8
704 SMM_CPU_OFFSET (x64
._LDTRLimit
), // Offset64Lo
705 SMM_CPU_OFFSET (x64
._LDTRLimit
) + 4, // Offset64Hi
707 }, // EFI_SMM_SAVE_STATE_REGISTER_LDTLIMIT = 9
716 }, // EFI_SMM_SAVE_STATE_REGISTER_LDTINFO = 10
721 SMM_CPU_OFFSET (x86
._ES
), // Offset32
722 SMM_CPU_OFFSET (x64
._ES
), // Offset64Lo
725 }, // EFI_SMM_SAVE_STATE_REGISTER_ES = 20
730 SMM_CPU_OFFSET (x86
._CS
), // Offset32
731 SMM_CPU_OFFSET (x64
._CS
), // Offset64Lo
734 }, // EFI_SMM_SAVE_STATE_REGISTER_CS = 21
739 SMM_CPU_OFFSET (x86
._SS
), // Offset32
740 SMM_CPU_OFFSET (x64
._SS
), // Offset64Lo
743 }, // EFI_SMM_SAVE_STATE_REGISTER_SS = 22
748 SMM_CPU_OFFSET (x86
._DS
), // Offset32
749 SMM_CPU_OFFSET (x64
._DS
), // Offset64Lo
752 }, // EFI_SMM_SAVE_STATE_REGISTER_DS = 23
757 SMM_CPU_OFFSET (x86
._FS
), // Offset32
758 SMM_CPU_OFFSET (x64
._FS
), // Offset64Lo
761 }, // EFI_SMM_SAVE_STATE_REGISTER_FS = 24
766 SMM_CPU_OFFSET (x86
._GS
), // Offset32
767 SMM_CPU_OFFSET (x64
._GS
), // Offset64Lo
770 }, // EFI_SMM_SAVE_STATE_REGISTER_GS = 25
776 SMM_CPU_OFFSET (x64
._LDTR
), // Offset64Lo
779 }, // EFI_SMM_SAVE_STATE_REGISTER_LDTR_SEL = 26
784 SMM_CPU_OFFSET (x86
._TR
), // Offset32
785 SMM_CPU_OFFSET (x64
._TR
), // Offset64Lo
788 }, // EFI_SMM_SAVE_STATE_REGISTER_TR_SEL = 27
793 SMM_CPU_OFFSET (x86
._DR7
), // Offset32
794 SMM_CPU_OFFSET (x64
._DR7
), // Offset64Lo
795 SMM_CPU_OFFSET (x64
._DR7
) + 4, // Offset64Hi
797 }, // EFI_SMM_SAVE_STATE_REGISTER_DR7 = 28
802 SMM_CPU_OFFSET (x86
._DR6
), // Offset32
803 SMM_CPU_OFFSET (x64
._DR6
), // Offset64Lo
804 SMM_CPU_OFFSET (x64
._DR6
) + 4, // Offset64Hi
806 }, // EFI_SMM_SAVE_STATE_REGISTER_DR6 = 29
812 SMM_CPU_OFFSET (x64
._R8
), // Offset64Lo
813 SMM_CPU_OFFSET (x64
._R8
) + 4, // Offset64Hi
815 }, // EFI_SMM_SAVE_STATE_REGISTER_R8 = 30
821 SMM_CPU_OFFSET (x64
._R9
), // Offset64Lo
822 SMM_CPU_OFFSET (x64
._R9
) + 4, // Offset64Hi
824 }, // EFI_SMM_SAVE_STATE_REGISTER_R9 = 31
830 SMM_CPU_OFFSET (x64
._R10
), // Offset64Lo
831 SMM_CPU_OFFSET (x64
._R10
) + 4, // Offset64Hi
833 }, // EFI_SMM_SAVE_STATE_REGISTER_R10 = 32
839 SMM_CPU_OFFSET (x64
._R11
), // Offset64Lo
840 SMM_CPU_OFFSET (x64
._R11
) + 4, // Offset64Hi
842 }, // EFI_SMM_SAVE_STATE_REGISTER_R11 = 33
848 SMM_CPU_OFFSET (x64
._R12
), // Offset64Lo
849 SMM_CPU_OFFSET (x64
._R12
) + 4, // Offset64Hi
851 }, // EFI_SMM_SAVE_STATE_REGISTER_R12 = 34
857 SMM_CPU_OFFSET (x64
._R13
), // Offset64Lo
858 SMM_CPU_OFFSET (x64
._R13
) + 4, // Offset64Hi
860 }, // EFI_SMM_SAVE_STATE_REGISTER_R13 = 35
866 SMM_CPU_OFFSET (x64
._R14
), // Offset64Lo
867 SMM_CPU_OFFSET (x64
._R14
) + 4, // Offset64Hi
869 }, // EFI_SMM_SAVE_STATE_REGISTER_R14 = 36
875 SMM_CPU_OFFSET (x64
._R15
), // Offset64Lo
876 SMM_CPU_OFFSET (x64
._R15
) + 4, // Offset64Hi
878 }, // EFI_SMM_SAVE_STATE_REGISTER_R15 = 37
883 SMM_CPU_OFFSET (x86
._EAX
), // Offset32
884 SMM_CPU_OFFSET (x64
._RAX
), // Offset64Lo
885 SMM_CPU_OFFSET (x64
._RAX
) + 4, // Offset64Hi
887 }, // EFI_SMM_SAVE_STATE_REGISTER_RAX = 38
892 SMM_CPU_OFFSET (x86
._EBX
), // Offset32
893 SMM_CPU_OFFSET (x64
._RBX
), // Offset64Lo
894 SMM_CPU_OFFSET (x64
._RBX
) + 4, // Offset64Hi
896 }, // EFI_SMM_SAVE_STATE_REGISTER_RBX = 39
901 SMM_CPU_OFFSET (x86
._ECX
), // Offset32
902 SMM_CPU_OFFSET (x64
._RCX
), // Offset64Lo
903 SMM_CPU_OFFSET (x64
._RCX
) + 4, // Offset64Hi
905 }, // EFI_SMM_SAVE_STATE_REGISTER_RCX = 40
910 SMM_CPU_OFFSET (x86
._EDX
), // Offset32
911 SMM_CPU_OFFSET (x64
._RDX
), // Offset64Lo
912 SMM_CPU_OFFSET (x64
._RDX
) + 4, // Offset64Hi
914 }, // EFI_SMM_SAVE_STATE_REGISTER_RDX = 41
919 SMM_CPU_OFFSET (x86
._ESP
), // Offset32
920 SMM_CPU_OFFSET (x64
._RSP
), // Offset64Lo
921 SMM_CPU_OFFSET (x64
._RSP
) + 4, // Offset64Hi
923 }, // EFI_SMM_SAVE_STATE_REGISTER_RSP = 42
928 SMM_CPU_OFFSET (x86
._EBP
), // Offset32
929 SMM_CPU_OFFSET (x64
._RBP
), // Offset64Lo
930 SMM_CPU_OFFSET (x64
._RBP
) + 4, // Offset64Hi
932 }, // EFI_SMM_SAVE_STATE_REGISTER_RBP = 43
937 SMM_CPU_OFFSET (x86
._ESI
), // Offset32
938 SMM_CPU_OFFSET (x64
._RSI
), // Offset64Lo
939 SMM_CPU_OFFSET (x64
._RSI
) + 4, // Offset64Hi
941 }, // EFI_SMM_SAVE_STATE_REGISTER_RSI = 44
946 SMM_CPU_OFFSET (x86
._EDI
), // Offset32
947 SMM_CPU_OFFSET (x64
._RDI
), // Offset64Lo
948 SMM_CPU_OFFSET (x64
._RDI
) + 4, // Offset64Hi
950 }, // EFI_SMM_SAVE_STATE_REGISTER_RDI = 45
955 SMM_CPU_OFFSET (x86
._EIP
), // Offset32
956 SMM_CPU_OFFSET (x64
._RIP
), // Offset64Lo
957 SMM_CPU_OFFSET (x64
._RIP
) + 4, // Offset64Hi
959 }, // EFI_SMM_SAVE_STATE_REGISTER_RIP = 46
964 SMM_CPU_OFFSET (x86
._EFLAGS
), // Offset32
965 SMM_CPU_OFFSET (x64
._RFLAGS
), // Offset64Lo
966 SMM_CPU_OFFSET (x64
._RFLAGS
) + 4, // Offset64Hi
968 }, // EFI_SMM_SAVE_STATE_REGISTER_RFLAGS = 51
973 SMM_CPU_OFFSET (x86
._CR0
), // Offset32
974 SMM_CPU_OFFSET (x64
._CR0
), // Offset64Lo
975 SMM_CPU_OFFSET (x64
._CR0
) + 4, // Offset64Hi
977 }, // EFI_SMM_SAVE_STATE_REGISTER_CR0 = 52
982 SMM_CPU_OFFSET (x86
._CR3
), // Offset32
983 SMM_CPU_OFFSET (x64
._CR3
), // Offset64Lo
984 SMM_CPU_OFFSET (x64
._CR3
) + 4, // Offset64Hi
986 }, // EFI_SMM_SAVE_STATE_REGISTER_CR3 = 53
992 SMM_CPU_OFFSET (x64
._CR4
), // Offset64Lo
993 SMM_CPU_OFFSET (x64
._CR4
) + 4, // Offset64Hi
995 }, // EFI_SMM_SAVE_STATE_REGISTER_CR4 = 54
999 // No support for I/O restart
1003 Read information from the CPU save state.
1005 @param Register Specifies the CPU register to read form the save state.
1007 @retval 0 Register is not valid
1008 @retval >0 Index into mSmmCpuWidthOffset[] associated with Register
1014 IN EFI_SMM_SAVE_STATE_REGISTER Register
1020 for (Index
= 0, Offset
= SMM_SAVE_STATE_REGISTER_FIRST_INDEX
;
1021 mSmmCpuRegisterRanges
[Index
].Length
!= 0;
1024 if ((Register
>= mSmmCpuRegisterRanges
[Index
].Start
) &&
1025 (Register
<= mSmmCpuRegisterRanges
[Index
].End
))
1027 return Register
- mSmmCpuRegisterRanges
[Index
].Start
+ Offset
;
1030 Offset
+= mSmmCpuRegisterRanges
[Index
].Length
;
1037 Read a CPU Save State register on the target processor.
1039 This function abstracts the differences that whether the CPU Save State
1040 register is in the IA32 CPU Save State Map or X64 CPU Save State Map.
1042 This function supports reading a CPU Save State register in SMBase relocation
1045 @param[in] CpuIndex Specifies the zero-based index of the CPU save
1047 @param[in] RegisterIndex Index into mSmmCpuWidthOffset[] look up table.
1048 @param[in] Width The number of bytes to read from the CPU save
1050 @param[out] Buffer Upon return, this holds the CPU register value
1051 read from the save state.
1053 @retval EFI_SUCCESS The register was read from Save State.
1054 @retval EFI_NOT_FOUND The register is not defined for the Save State
1056 @retval EFI_INVALID_PARAMTER This or Buffer is NULL.
1061 ReadSaveStateRegisterByIndex (
1063 IN UINTN RegisterIndex
,
1068 QEMU_SMRAM_SAVE_STATE_MAP
*CpuSaveState
;
1070 CpuSaveState
= (QEMU_SMRAM_SAVE_STATE_MAP
*)gSmst
->CpuSaveState
[CpuIndex
];
1072 if ((CpuSaveState
->x86
.SMMRevId
& 0xFFFF) == 0) {
1074 // If 32-bit mode width is zero, then the specified register can not be
1077 if (mSmmCpuWidthOffset
[RegisterIndex
].Width32
== 0) {
1078 return EFI_NOT_FOUND
;
1082 // If Width is bigger than the 32-bit mode width, then the specified
1083 // register can not be accessed
1085 if (Width
> mSmmCpuWidthOffset
[RegisterIndex
].Width32
) {
1086 return EFI_INVALID_PARAMETER
;
1090 // Write return buffer
1092 ASSERT (CpuSaveState
!= NULL
);
1095 (UINT8
*)CpuSaveState
+ mSmmCpuWidthOffset
[RegisterIndex
].Offset32
,
1100 // If 64-bit mode width is zero, then the specified register can not be
1103 if (mSmmCpuWidthOffset
[RegisterIndex
].Width64
== 0) {
1104 return EFI_NOT_FOUND
;
1108 // If Width is bigger than the 64-bit mode width, then the specified
1109 // register can not be accessed
1111 if (Width
> mSmmCpuWidthOffset
[RegisterIndex
].Width64
) {
1112 return EFI_INVALID_PARAMETER
;
1116 // Write lower 32-bits of return buffer
1120 (UINT8
*)CpuSaveState
+ mSmmCpuWidthOffset
[RegisterIndex
].Offset64Lo
,
1125 // Write upper 32-bits of return buffer
1128 (UINT8
*)Buffer
+ 4,
1129 (UINT8
*)CpuSaveState
+ mSmmCpuWidthOffset
[RegisterIndex
].Offset64Hi
,
1139 Read an SMM Save State register on the target processor. If this function
1140 returns EFI_UNSUPPORTED, then the caller is responsible for reading the
1141 SMM Save Sate register.
1143 @param[in] CpuIndex The index of the CPU to read the SMM Save State. The
1144 value must be between 0 and the NumberOfCpus field in
1145 the System Management System Table (SMST).
1146 @param[in] Register The SMM Save State register to read.
1147 @param[in] Width The number of bytes to read from the CPU save state.
1148 @param[out] Buffer Upon return, this holds the CPU register value read
1149 from the save state.
1151 @retval EFI_SUCCESS The register was read from Save State.
1152 @retval EFI_INVALID_PARAMTER Buffer is NULL.
1153 @retval EFI_UNSUPPORTED This function does not support reading
1158 SmmCpuFeaturesReadSaveStateRegister (
1160 IN EFI_SMM_SAVE_STATE_REGISTER Register
,
1165 UINTN RegisterIndex
;
1166 QEMU_SMRAM_SAVE_STATE_MAP
*CpuSaveState
;
1169 // Check for special EFI_SMM_SAVE_STATE_REGISTER_LMA
1171 if (Register
== EFI_SMM_SAVE_STATE_REGISTER_LMA
) {
1173 // Only byte access is supported for this register
1176 return EFI_INVALID_PARAMETER
;
1179 CpuSaveState
= (QEMU_SMRAM_SAVE_STATE_MAP
*)gSmst
->CpuSaveState
[CpuIndex
];
1184 if ((CpuSaveState
->x86
.SMMRevId
& 0xFFFF) == 0) {
1185 *(UINT8
*)Buffer
= 32;
1187 *(UINT8
*)Buffer
= 64;
1194 // Check for special EFI_SMM_SAVE_STATE_REGISTER_IO
1196 if (Register
== EFI_SMM_SAVE_STATE_REGISTER_IO
) {
1197 return EFI_NOT_FOUND
;
1201 // Convert Register to a register lookup table index. Let
1202 // PiSmmCpuDxeSmm implement other special registers (currently
1203 // there is only EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID).
1205 RegisterIndex
= GetRegisterIndex (Register
);
1206 if (RegisterIndex
== 0) {
1207 return (Register
< EFI_SMM_SAVE_STATE_REGISTER_IO
?
1212 return ReadSaveStateRegisterByIndex (CpuIndex
, RegisterIndex
, Width
, Buffer
);
1216 Writes an SMM Save State register on the target processor. If this function
1217 returns EFI_UNSUPPORTED, then the caller is responsible for writing the
1218 SMM Save Sate register.
1220 @param[in] CpuIndex The index of the CPU to write the SMM Save State. The
1221 value must be between 0 and the NumberOfCpus field in
1222 the System Management System Table (SMST).
1223 @param[in] Register The SMM Save State register to write.
1224 @param[in] Width The number of bytes to write to the CPU save state.
1225 @param[in] Buffer Upon entry, this holds the new CPU register value.
1227 @retval EFI_SUCCESS The register was written to Save State.
1228 @retval EFI_INVALID_PARAMTER Buffer is NULL.
1229 @retval EFI_UNSUPPORTED This function does not support writing
1234 SmmCpuFeaturesWriteSaveStateRegister (
1236 IN EFI_SMM_SAVE_STATE_REGISTER Register
,
1238 IN CONST VOID
*Buffer
1241 UINTN RegisterIndex
;
1242 QEMU_SMRAM_SAVE_STATE_MAP
*CpuSaveState
;
1245 // Writes to EFI_SMM_SAVE_STATE_REGISTER_LMA are ignored
1247 if (Register
== EFI_SMM_SAVE_STATE_REGISTER_LMA
) {
1252 // Writes to EFI_SMM_SAVE_STATE_REGISTER_IO are not supported
1254 if (Register
== EFI_SMM_SAVE_STATE_REGISTER_IO
) {
1255 return EFI_NOT_FOUND
;
1259 // Convert Register to a register lookup table index. Let
1260 // PiSmmCpuDxeSmm implement other special registers (currently
1261 // there is only EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID).
1263 RegisterIndex
= GetRegisterIndex (Register
);
1264 if (RegisterIndex
== 0) {
1265 return (Register
< EFI_SMM_SAVE_STATE_REGISTER_IO
?
1270 CpuSaveState
= (QEMU_SMRAM_SAVE_STATE_MAP
*)gSmst
->CpuSaveState
[CpuIndex
];
1273 // Do not write non-writable SaveState, because it will cause exception.
1275 if (!mSmmCpuWidthOffset
[RegisterIndex
].Writeable
) {
1276 return EFI_UNSUPPORTED
;
1282 if ((CpuSaveState
->x86
.SMMRevId
& 0xFFFF) == 0) {
1284 // If 32-bit mode width is zero, then the specified register can not be
1287 if (mSmmCpuWidthOffset
[RegisterIndex
].Width32
== 0) {
1288 return EFI_NOT_FOUND
;
1292 // If Width is bigger than the 32-bit mode width, then the specified
1293 // register can not be accessed
1295 if (Width
> mSmmCpuWidthOffset
[RegisterIndex
].Width32
) {
1296 return EFI_INVALID_PARAMETER
;
1300 // Write SMM State register
1302 ASSERT (CpuSaveState
!= NULL
);
1304 (UINT8
*)CpuSaveState
+ mSmmCpuWidthOffset
[RegisterIndex
].Offset32
,
1310 // If 64-bit mode width is zero, then the specified register can not be
1313 if (mSmmCpuWidthOffset
[RegisterIndex
].Width64
== 0) {
1314 return EFI_NOT_FOUND
;
1318 // If Width is bigger than the 64-bit mode width, then the specified
1319 // register can not be accessed
1321 if (Width
> mSmmCpuWidthOffset
[RegisterIndex
].Width64
) {
1322 return EFI_INVALID_PARAMETER
;
1326 // Write lower 32-bits of SMM State register
1329 (UINT8
*)CpuSaveState
+ mSmmCpuWidthOffset
[RegisterIndex
].Offset64Lo
,
1335 // Write upper 32-bits of SMM State register
1338 (UINT8
*)CpuSaveState
+ mSmmCpuWidthOffset
[RegisterIndex
].Offset64Hi
,
1339 (UINT8
*)Buffer
+ 4,
1349 This function is hook point called after the gEfiSmmReadyToLockProtocolGuid
1350 notification is completely processed.
1354 SmmCpuFeaturesCompleteSmmReadyToLock (