1 /** @file
2 The CPU specific programming for PiSmmCpuDxeSmm module.
3 
4 Copyright (c) 2010 - 2015, 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 <PiSmm.h>
16 #include <Library/SmmCpuFeaturesLib.h>
17 #include <Library/BaseLib.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/PcdLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/SmmServicesTableLib.h>
22 #include <Library/DebugLib.h>
23 #include <Register/QemuSmramSaveStateMap.h>
24 
25 //
26 // EFER register LMA bit
27 //
28 #define LMA BIT10
29 
30 /**
31   The constructor function
32 
33   @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
34   @param[in]  SystemTable  A pointer to the EFI System Table.
35 
36   @retval EFI_SUCCESS      The constructor always returns EFI_SUCCESS.
37 
38 **/
39 EFI_STATUS
40 EFIAPI
SmmCpuFeaturesLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)41 SmmCpuFeaturesLibConstructor (
42   IN EFI_HANDLE        ImageHandle,
43   IN EFI_SYSTEM_TABLE  *SystemTable
44   )
45 {
46   //
47   // No need to program SMRRs on our virtual platform.
48   //
49   return EFI_SUCCESS;
50 }
51 
52 /**
53   Called during the very first SMI into System Management Mode to initialize
54   CPU features, including SMBASE, for the currently executing CPU.  Since this
55   is the first SMI, the SMRAM Save State Map is at the default address of
56   SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET.  The currently executing
57   CPU is specified by CpuIndex and CpuIndex can be used to access information
58   about the currently executing CPU in the ProcessorInfo array and the
59   HotPlugCpuData data structure.
60 
61   @param[in] CpuIndex        The index of the CPU to initialize.  The value
62                              must be between 0 and the NumberOfCpus field in
63                              the System Management System Table (SMST).
64   @param[in] IsMonarch       TRUE if the CpuIndex is the index of the CPU that
65                              was elected as monarch during System Management
66                              Mode initialization.
67                              FALSE if the CpuIndex is not the index of the CPU
68                              that was elected as monarch during System
69                              Management Mode initialization.
70   @param[in] ProcessorInfo   Pointer to an array of EFI_PROCESSOR_INFORMATION
71                              structures.  ProcessorInfo[CpuIndex] contains the
72                              information for the currently executing CPU.
73   @param[in] CpuHotPlugData  Pointer to the CPU_HOT_PLUG_DATA structure that
74                              contains the ApidId and SmBase arrays.
75 **/
76 VOID
77 EFIAPI
SmmCpuFeaturesInitializeProcessor(IN UINTN CpuIndex,IN BOOLEAN IsMonarch,IN EFI_PROCESSOR_INFORMATION * ProcessorInfo,IN CPU_HOT_PLUG_DATA * CpuHotPlugData)78 SmmCpuFeaturesInitializeProcessor (
79   IN UINTN                      CpuIndex,
80   IN BOOLEAN                    IsMonarch,
81   IN EFI_PROCESSOR_INFORMATION  *ProcessorInfo,
82   IN CPU_HOT_PLUG_DATA          *CpuHotPlugData
83   )
84 {
85   QEMU_SMRAM_SAVE_STATE_MAP  *CpuState;
86 
87   //
88   // Configure SMBASE.
89   //
90   CpuState = (QEMU_SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET);
91   if ((CpuState->x86.SMMRevId & 0xFFFF) == 0) {
92     CpuState->x86.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex];
93   } else {
94     CpuState->x64.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex];
95   }
96 
97   //
98   // No need to program SMRRs on our virtual platform.
99   //
100 }
101 
102 /**
103   This function updates the SMRAM save state on the currently executing CPU
104   to resume execution at a specific address after an RSM instruction.  This
105   function must evaluate the SMRAM save state to determine the execution mode
106   the RSM instruction resumes and update the resume execution address with
107   either NewInstructionPointer32 or NewInstructionPoint.  The auto HALT restart
108   flag in the SMRAM save state must always be cleared.  This function returns
109   the value of the instruction pointer from the SMRAM save state that was
110   replaced.  If this function returns 0, then the SMRAM save state was not
111   modified.
112 
113   This function is called during the very first SMI on each CPU after
114   SmmCpuFeaturesInitializeProcessor() to set a flag in normal execution mode
115   to signal that the SMBASE of each CPU has been updated before the default
116   SMBASE address is used for the first SMI to the next CPU.
117 
118   @param[in] CpuIndex                 The index of the CPU to hook.  The value
119                                       must be between 0 and the NumberOfCpus
120                                       field in the System Management System Table
121                                       (SMST).
122   @param[in] CpuState                 Pointer to SMRAM Save State Map for the
123                                       currently executing CPU.
124   @param[in] NewInstructionPointer32  Instruction pointer to use if resuming to
125                                       32-bit execution mode from 64-bit SMM.
126   @param[in] NewInstructionPointer    Instruction pointer to use if resuming to
127                                       same execution mode as SMM.
128 
129   @retval 0    This function did modify the SMRAM save state.
130   @retval > 0  The original instruction pointer value from the SMRAM save state
131                before it was replaced.
132 **/
133 UINT64
134 EFIAPI
SmmCpuFeaturesHookReturnFromSmm(IN UINTN CpuIndex,IN SMRAM_SAVE_STATE_MAP * CpuState,IN UINT64 NewInstructionPointer32,IN UINT64 NewInstructionPointer)135 SmmCpuFeaturesHookReturnFromSmm (
136   IN UINTN                 CpuIndex,
137   IN SMRAM_SAVE_STATE_MAP  *CpuState,
138   IN UINT64                NewInstructionPointer32,
139   IN UINT64                NewInstructionPointer
140   )
141 {
142   UINT64                      OriginalInstructionPointer;
143   QEMU_SMRAM_SAVE_STATE_MAP  *CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)CpuState;
144 
145   if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
146     OriginalInstructionPointer = (UINT64)CpuSaveState->x86._EIP;
147     CpuSaveState->x86._EIP = (UINT32)NewInstructionPointer;
148     //
149     // Clear the auto HALT restart flag so the RSM instruction returns
150     // program control to the instruction following the HLT instruction.
151     //
152     if ((CpuSaveState->x86.AutoHALTRestart & BIT0) != 0) {
153       CpuSaveState->x86.AutoHALTRestart &= ~BIT0;
154     }
155   } else {
156     OriginalInstructionPointer = CpuSaveState->x64._RIP;
157     if ((CpuSaveState->x64.IA32_EFER & LMA) == 0) {
158       CpuSaveState->x64._RIP = (UINT32)NewInstructionPointer32;
159     } else {
160       CpuSaveState->x64._RIP = (UINT32)NewInstructionPointer;
161     }
162     //
163     // Clear the auto HALT restart flag so the RSM instruction returns
164     // program control to the instruction following the HLT instruction.
165     //
166     if ((CpuSaveState->x64.AutoHALTRestart & BIT0) != 0) {
167       CpuSaveState->x64.AutoHALTRestart &= ~BIT0;
168     }
169   }
170   return OriginalInstructionPointer;
171 }
172 
173 /**
174   Hook point in normal execution mode that allows the one CPU that was elected
175   as monarch during System Management Mode initialization to perform additional
176   initialization actions immediately after all of the CPUs have processed their
177   first SMI and called SmmCpuFeaturesInitializeProcessor() relocating SMBASE
178   into a buffer in SMRAM and called SmmCpuFeaturesHookReturnFromSmm().
179 **/
180 VOID
181 EFIAPI
SmmCpuFeaturesSmmRelocationComplete(VOID)182 SmmCpuFeaturesSmmRelocationComplete (
183   VOID
184   )
185 {
186 }
187 
188 /**
189   Return the size, in bytes, of a custom SMI Handler in bytes.  If 0 is
190   returned, then a custom SMI handler is not provided by this library,
191   and the default SMI handler must be used.
192 
193   @retval 0    Use the default SMI handler.
194   @retval > 0  Use the SMI handler installed by SmmCpuFeaturesInstallSmiHandler()
195                The caller is required to allocate enough SMRAM for each CPU to
196                support the size of the custom SMI handler.
197 **/
198 UINTN
199 EFIAPI
SmmCpuFeaturesGetSmiHandlerSize(VOID)200 SmmCpuFeaturesGetSmiHandlerSize (
201   VOID
202   )
203 {
204   return 0;
205 }
206 
207 /**
208   Install a custom SMI handler for the CPU specified by CpuIndex.  This function
209   is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size is greater
210   than zero and is called by the CPU that was elected as monarch during System
211   Management Mode initialization.
212 
213   @param[in] CpuIndex   The index of the CPU to install the custom SMI handler.
214                         The value must be between 0 and the NumberOfCpus field
215                         in the System Management System Table (SMST).
216   @param[in] SmBase     The SMBASE address for the CPU specified by CpuIndex.
217   @param[in] SmiStack   The stack to use when an SMI is processed by the
218                         the CPU specified by CpuIndex.
219   @param[in] StackSize  The size, in bytes, if the stack used when an SMI is
220                         processed by the CPU specified by CpuIndex.
221   @param[in] GdtBase    The base address of the GDT to use when an SMI is
222                         processed by the CPU specified by CpuIndex.
223   @param[in] GdtSize    The size, in bytes, of the GDT used when an SMI is
224                         processed by the CPU specified by CpuIndex.
225   @param[in] IdtBase    The base address of the IDT to use when an SMI is
226                         processed by the CPU specified by CpuIndex.
227   @param[in] IdtSize    The size, in bytes, of the IDT used when an SMI is
228                         processed by the CPU specified by CpuIndex.
229   @param[in] Cr3        The base address of the page tables to use when an SMI
230                         is processed by the CPU specified by CpuIndex.
231 **/
232 VOID
233 EFIAPI
SmmCpuFeaturesInstallSmiHandler(IN UINTN CpuIndex,IN UINT32 SmBase,IN VOID * SmiStack,IN UINTN StackSize,IN UINTN GdtBase,IN UINTN GdtSize,IN UINTN IdtBase,IN UINTN IdtSize,IN UINT32 Cr3)234 SmmCpuFeaturesInstallSmiHandler (
235   IN UINTN   CpuIndex,
236   IN UINT32  SmBase,
237   IN VOID    *SmiStack,
238   IN UINTN   StackSize,
239   IN UINTN   GdtBase,
240   IN UINTN   GdtSize,
241   IN UINTN   IdtBase,
242   IN UINTN   IdtSize,
243   IN UINT32  Cr3
244   )
245 {
246 }
247 
248 /**
249   Determines if MTRR registers must be configured to set SMRAM cache-ability
250   when executing in System Management Mode.
251 
252   @retval TRUE   MTRR registers must be configured to set SMRAM cache-ability.
253   @retval FALSE  MTRR registers do not need to be configured to set SMRAM
254                  cache-ability.
255 **/
256 BOOLEAN
257 EFIAPI
SmmCpuFeaturesNeedConfigureMtrrs(VOID)258 SmmCpuFeaturesNeedConfigureMtrrs (
259   VOID
260   )
261 {
262   return FALSE;
263 }
264 
265 /**
266   Disable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs()
267   returns TRUE.
268 **/
269 VOID
270 EFIAPI
SmmCpuFeaturesDisableSmrr(VOID)271 SmmCpuFeaturesDisableSmrr (
272   VOID
273   )
274 {
275   //
276   // No SMRR support, nothing to do
277   //
278 }
279 
280 /**
281   Enable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs()
282   returns TRUE.
283 **/
284 VOID
285 EFIAPI
SmmCpuFeaturesReenableSmrr(VOID)286 SmmCpuFeaturesReenableSmrr (
287   VOID
288   )
289 {
290   //
291   // No SMRR support, nothing to do
292   //
293 }
294 
295 /**
296   Processor specific hook point each time a CPU enters System Management Mode.
297 
298   @param[in] CpuIndex  The index of the CPU that has entered SMM.  The value
299                        must be between 0 and the NumberOfCpus field in the
300                        System Management System Table (SMST).
301 **/
302 VOID
303 EFIAPI
SmmCpuFeaturesRendezvousEntry(IN UINTN CpuIndex)304 SmmCpuFeaturesRendezvousEntry (
305   IN UINTN  CpuIndex
306   )
307 {
308   //
309   // No SMRR support, nothing to do
310   //
311 }
312 
313 /**
314   Processor specific hook point each time a CPU exits System Management Mode.
315 
316   @param[in] CpuIndex  The index of the CPU that is exiting SMM.  The value must
317                        be between 0 and the NumberOfCpus field in the System
318                        Management System Table (SMST).
319 **/
320 VOID
321 EFIAPI
SmmCpuFeaturesRendezvousExit(IN UINTN CpuIndex)322 SmmCpuFeaturesRendezvousExit (
323   IN UINTN  CpuIndex
324   )
325 {
326 }
327 
328 /**
329   Check to see if an SMM register is supported by a specified CPU.
330 
331   @param[in] CpuIndex  The index of the CPU to check for SMM register support.
332                        The value must be between 0 and the NumberOfCpus field
333                        in the System Management System Table (SMST).
334   @param[in] RegName   Identifies the SMM register to check for support.
335 
336   @retval TRUE   The SMM register specified by RegName is supported by the CPU
337                  specified by CpuIndex.
338   @retval FALSE  The SMM register specified by RegName is not supported by the
339                  CPU specified by CpuIndex.
340 **/
341 BOOLEAN
342 EFIAPI
SmmCpuFeaturesIsSmmRegisterSupported(IN UINTN CpuIndex,IN SMM_REG_NAME RegName)343 SmmCpuFeaturesIsSmmRegisterSupported (
344   IN UINTN         CpuIndex,
345   IN SMM_REG_NAME  RegName
346   )
347 {
348   ASSERT (RegName == SmmRegFeatureControl);
349   return FALSE;
350 }
351 
352 /**
353   Returns the current value of the SMM register for the specified CPU.
354   If the SMM register is not supported, then 0 is returned.
355 
356   @param[in] CpuIndex  The index of the CPU to read the SMM register.  The
357                        value must be between 0 and the NumberOfCpus field in
358                        the System Management System Table (SMST).
359   @param[in] RegName   Identifies the SMM register to read.
360 
361   @return  The value of the SMM register specified by RegName from the CPU
362            specified by CpuIndex.
363 **/
364 UINT64
365 EFIAPI
SmmCpuFeaturesGetSmmRegister(IN UINTN CpuIndex,IN SMM_REG_NAME RegName)366 SmmCpuFeaturesGetSmmRegister (
367   IN UINTN         CpuIndex,
368   IN SMM_REG_NAME  RegName
369   )
370 {
371   //
372   // This is called for SmmRegSmmDelayed, SmmRegSmmBlocked, SmmRegSmmEnable.
373   // The last of these should actually be SmmRegSmmDisable, so we can just
374   // return FALSE.
375   //
376   return 0;
377 }
378 
379 /**
380   Sets the value of an SMM register on a specified CPU.
381   If the SMM register is not supported, then no action is performed.
382 
383   @param[in] CpuIndex  The index of the CPU to write the SMM register.  The
384                        value must be between 0 and the NumberOfCpus field in
385                        the System Management System Table (SMST).
386   @param[in] RegName   Identifies the SMM register to write.
387                        registers are read-only.
388   @param[in] Value     The value to write to the SMM register.
389 **/
390 VOID
391 EFIAPI
SmmCpuFeaturesSetSmmRegister(IN UINTN CpuIndex,IN SMM_REG_NAME RegName,IN UINT64 Value)392 SmmCpuFeaturesSetSmmRegister (
393   IN UINTN         CpuIndex,
394   IN SMM_REG_NAME  RegName,
395   IN UINT64        Value
396   )
397 {
398   ASSERT (FALSE);
399 }
400 
401 ///
402 /// Macro used to simplify the lookup table entries of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
403 ///
404 #define SMM_CPU_OFFSET(Field) OFFSET_OF (QEMU_SMRAM_SAVE_STATE_MAP, Field)
405 
406 ///
407 /// Macro used to simplify the lookup table entries of type CPU_SMM_SAVE_STATE_REGISTER_RANGE
408 ///
409 #define SMM_REGISTER_RANGE(Start, End) { Start, End, End - Start + 1 }
410 
411 ///
412 /// Structure used to describe a range of registers
413 ///
414 typedef struct {
415   EFI_SMM_SAVE_STATE_REGISTER  Start;
416   EFI_SMM_SAVE_STATE_REGISTER  End;
417   UINTN                        Length;
418 } CPU_SMM_SAVE_STATE_REGISTER_RANGE;
419 
420 ///
421 /// Structure used to build a lookup table to retrieve the widths and offsets
422 /// associated with each supported EFI_SMM_SAVE_STATE_REGISTER value
423 ///
424 
425 #define SMM_SAVE_STATE_REGISTER_FIRST_INDEX             1
426 
427 typedef struct {
428   UINT8   Width32;
429   UINT8   Width64;
430   UINT16  Offset32;
431   UINT16  Offset64Lo;
432   UINT16  Offset64Hi;
433   BOOLEAN Writeable;
434 } CPU_SMM_SAVE_STATE_LOOKUP_ENTRY;
435 
436 ///
437 /// Table used by GetRegisterIndex() to convert an EFI_SMM_SAVE_STATE_REGISTER
438 /// value to an index into a table of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY
439 ///
440 static CONST CPU_SMM_SAVE_STATE_REGISTER_RANGE mSmmCpuRegisterRanges[] = {
441   SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_GDTBASE, EFI_SMM_SAVE_STATE_REGISTER_LDTINFO),
442   SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_ES,      EFI_SMM_SAVE_STATE_REGISTER_RIP),
443   SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_RFLAGS,  EFI_SMM_SAVE_STATE_REGISTER_CR4),
444   { (EFI_SMM_SAVE_STATE_REGISTER)0, (EFI_SMM_SAVE_STATE_REGISTER)0, 0 }
445 };
446 
447 ///
448 /// Lookup table used to retrieve the widths and offsets associated with each
449 /// supported EFI_SMM_SAVE_STATE_REGISTER value
450 ///
451 static CONST CPU_SMM_SAVE_STATE_LOOKUP_ENTRY mSmmCpuWidthOffset[] = {
452   {0, 0, 0, 0, 0, FALSE},                                                                                                     //  Reserved
453 
454   //
455   // CPU Save State registers defined in PI SMM CPU Protocol.
456   //
457   {0, 8, 0                            , SMM_CPU_OFFSET (x64._GDTRBase) , SMM_CPU_OFFSET (x64._GDTRBase)  + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_GDTBASE  = 4
458   {0, 8, 0                            , SMM_CPU_OFFSET (x64._IDTRBase) , SMM_CPU_OFFSET (x64._IDTRBase)  + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_IDTBASE  = 5
459   {0, 8, 0                            , SMM_CPU_OFFSET (x64._LDTRBase) , SMM_CPU_OFFSET (x64._LDTRBase)  + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTBASE  = 6
460   {0, 0, 0                            , SMM_CPU_OFFSET (x64._GDTRLimit), SMM_CPU_OFFSET (x64._GDTRLimit) + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_GDTLIMIT = 7
461   {0, 0, 0                            , SMM_CPU_OFFSET (x64._IDTRLimit), SMM_CPU_OFFSET (x64._IDTRLimit) + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_IDTLIMIT = 8
462   {0, 0, 0                            , SMM_CPU_OFFSET (x64._LDTRLimit), SMM_CPU_OFFSET (x64._LDTRLimit) + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTLIMIT = 9
463   {0, 0, 0                            , 0                              , 0                               + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTINFO  = 10
464 
465   {4, 4, SMM_CPU_OFFSET (x86._ES)     , SMM_CPU_OFFSET (x64._ES)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_ES       = 20
466   {4, 4, SMM_CPU_OFFSET (x86._CS)     , SMM_CPU_OFFSET (x64._CS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CS       = 21
467   {4, 4, SMM_CPU_OFFSET (x86._SS)     , SMM_CPU_OFFSET (x64._SS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_SS       = 22
468   {4, 4, SMM_CPU_OFFSET (x86._DS)     , SMM_CPU_OFFSET (x64._DS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_DS       = 23
469   {4, 4, SMM_CPU_OFFSET (x86._FS)     , SMM_CPU_OFFSET (x64._FS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_FS       = 24
470   {4, 4, SMM_CPU_OFFSET (x86._GS)     , SMM_CPU_OFFSET (x64._GS)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_GS       = 25
471   {0, 4, 0                            , SMM_CPU_OFFSET (x64._LDTR)   , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_LDTR_SEL = 26
472   {4, 4, SMM_CPU_OFFSET (x86._TR)     , SMM_CPU_OFFSET (x64._TR)     , 0                               , FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_TR_SEL   = 27
473   {4, 8, SMM_CPU_OFFSET (x86._DR7)    , SMM_CPU_OFFSET (x64._DR7)    , SMM_CPU_OFFSET (x64._DR7)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_DR7      = 28
474   {4, 8, SMM_CPU_OFFSET (x86._DR6)    , SMM_CPU_OFFSET (x64._DR6)    , SMM_CPU_OFFSET (x64._DR6)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_DR6      = 29
475   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R8)     , SMM_CPU_OFFSET (x64._R8)     + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R8       = 30
476   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R9)     , SMM_CPU_OFFSET (x64._R9)     + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R9       = 31
477   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R10)    , SMM_CPU_OFFSET (x64._R10)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R10      = 32
478   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R11)    , SMM_CPU_OFFSET (x64._R11)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R11      = 33
479   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R12)    , SMM_CPU_OFFSET (x64._R12)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R12      = 34
480   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R13)    , SMM_CPU_OFFSET (x64._R13)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R13      = 35
481   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R14)    , SMM_CPU_OFFSET (x64._R14)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R14      = 36
482   {0, 8, 0                            , SMM_CPU_OFFSET (x64._R15)    , SMM_CPU_OFFSET (x64._R15)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_R15      = 37
483   {4, 8, SMM_CPU_OFFSET (x86._EAX)    , SMM_CPU_OFFSET (x64._RAX)    , SMM_CPU_OFFSET (x64._RAX)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RAX      = 38
484   {4, 8, SMM_CPU_OFFSET (x86._EBX)    , SMM_CPU_OFFSET (x64._RBX)    , SMM_CPU_OFFSET (x64._RBX)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RBX      = 39
485   {4, 8, SMM_CPU_OFFSET (x86._ECX)    , SMM_CPU_OFFSET (x64._RCX)    , SMM_CPU_OFFSET (x64._RCX)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RCX      = 40
486   {4, 8, SMM_CPU_OFFSET (x86._EDX)    , SMM_CPU_OFFSET (x64._RDX)    , SMM_CPU_OFFSET (x64._RDX)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RDX      = 41
487   {4, 8, SMM_CPU_OFFSET (x86._ESP)    , SMM_CPU_OFFSET (x64._RSP)    , SMM_CPU_OFFSET (x64._RSP)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RSP      = 42
488   {4, 8, SMM_CPU_OFFSET (x86._EBP)    , SMM_CPU_OFFSET (x64._RBP)    , SMM_CPU_OFFSET (x64._RBP)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RBP      = 43
489   {4, 8, SMM_CPU_OFFSET (x86._ESI)    , SMM_CPU_OFFSET (x64._RSI)    , SMM_CPU_OFFSET (x64._RSI)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RSI      = 44
490   {4, 8, SMM_CPU_OFFSET (x86._EDI)    , SMM_CPU_OFFSET (x64._RDI)    , SMM_CPU_OFFSET (x64._RDI)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RDI      = 45
491   {4, 8, SMM_CPU_OFFSET (x86._EIP)    , SMM_CPU_OFFSET (x64._RIP)    , SMM_CPU_OFFSET (x64._RIP)    + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RIP      = 46
492 
493   {4, 8, SMM_CPU_OFFSET (x86._EFLAGS) , SMM_CPU_OFFSET (x64._RFLAGS) , SMM_CPU_OFFSET (x64._RFLAGS) + 4, TRUE },  //  EFI_SMM_SAVE_STATE_REGISTER_RFLAGS   = 51
494   {4, 8, SMM_CPU_OFFSET (x86._CR0)    , SMM_CPU_OFFSET (x64._CR0)    , SMM_CPU_OFFSET (x64._CR0)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CR0      = 52
495   {4, 8, SMM_CPU_OFFSET (x86._CR3)    , SMM_CPU_OFFSET (x64._CR3)    , SMM_CPU_OFFSET (x64._CR3)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CR3      = 53
496   {0, 4, 0                            , SMM_CPU_OFFSET (x64._CR4)    , SMM_CPU_OFFSET (x64._CR4)    + 4, FALSE},  //  EFI_SMM_SAVE_STATE_REGISTER_CR4      = 54
497 };
498 
499 //
500 // No support for I/O restart
501 //
502 
503 /**
504   Read information from the CPU save state.
505 
506   @param  Register  Specifies the CPU register to read form the save state.
507 
508   @retval 0   Register is not valid
509   @retval >0  Index into mSmmCpuWidthOffset[] associated with Register
510 
511 **/
512 static UINTN
GetRegisterIndex(IN EFI_SMM_SAVE_STATE_REGISTER Register)513 GetRegisterIndex (
514   IN EFI_SMM_SAVE_STATE_REGISTER  Register
515   )
516 {
517   UINTN  Index;
518   UINTN  Offset;
519 
520   for (Index = 0, Offset = SMM_SAVE_STATE_REGISTER_FIRST_INDEX; mSmmCpuRegisterRanges[Index].Length != 0; Index++) {
521     if (Register >= mSmmCpuRegisterRanges[Index].Start && Register <= mSmmCpuRegisterRanges[Index].End) {
522       return Register - mSmmCpuRegisterRanges[Index].Start + Offset;
523     }
524     Offset += mSmmCpuRegisterRanges[Index].Length;
525   }
526   return 0;
527 }
528 
529 /**
530   Read a CPU Save State register on the target processor.
531 
532   This function abstracts the differences that whether the CPU Save State register is in the
533   IA32 CPU Save State Map or X64 CPU Save State Map.
534 
535   This function supports reading a CPU Save State register in SMBase relocation handler.
536 
537   @param[in]  CpuIndex       Specifies the zero-based index of the CPU save state.
538   @param[in]  RegisterIndex  Index into mSmmCpuWidthOffset[] look up table.
539   @param[in]  Width          The number of bytes to read from the CPU save state.
540   @param[out] Buffer         Upon return, this holds the CPU register value read from the save state.
541 
542   @retval EFI_SUCCESS           The register was read from Save State.
543   @retval EFI_NOT_FOUND         The register is not defined for the Save State of Processor.
544   @retval EFI_INVALID_PARAMTER  This or Buffer is NULL.
545 
546 **/
547 static EFI_STATUS
ReadSaveStateRegisterByIndex(IN UINTN CpuIndex,IN UINTN RegisterIndex,IN UINTN Width,OUT VOID * Buffer)548 ReadSaveStateRegisterByIndex (
549   IN UINTN   CpuIndex,
550   IN UINTN   RegisterIndex,
551   IN UINTN   Width,
552   OUT VOID   *Buffer
553   )
554 {
555   QEMU_SMRAM_SAVE_STATE_MAP  *CpuSaveState;
556 
557   CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)gSmst->CpuSaveState[CpuIndex];
558 
559   if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
560     //
561     // If 32-bit mode width is zero, then the specified register can not be accessed
562     //
563     if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) {
564       return EFI_NOT_FOUND;
565     }
566 
567     //
568     // If Width is bigger than the 32-bit mode width, then the specified register can not be accessed
569     //
570     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) {
571       return EFI_INVALID_PARAMETER;
572     }
573 
574     //
575     // Write return buffer
576     //
577     ASSERT(CpuSaveState != NULL);
578     CopyMem(Buffer, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32, Width);
579   } else {
580     //
581     // If 64-bit mode width is zero, then the specified register can not be accessed
582     //
583     if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) {
584       return EFI_NOT_FOUND;
585     }
586 
587     //
588     // If Width is bigger than the 64-bit mode width, then the specified register can not be accessed
589     //
590     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) {
591       return EFI_INVALID_PARAMETER;
592     }
593 
594     //
595     // Write lower 32-bits of return buffer
596     //
597     CopyMem(Buffer, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo, MIN(4, Width));
598     if (Width >= 4) {
599       //
600       // Write upper 32-bits of return buffer
601       //
602       CopyMem((UINT8 *)Buffer + 4, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi, Width - 4);
603     }
604   }
605   return EFI_SUCCESS;
606 }
607 
608 /**
609   Read an SMM Save State register on the target processor.  If this function
610   returns EFI_UNSUPPORTED, then the caller is responsible for reading the
611   SMM Save Sate register.
612 
613   @param[in]  CpuIndex  The index of the CPU to read the SMM Save State.  The
614                         value must be between 0 and the NumberOfCpus field in
615                         the System Management System Table (SMST).
616   @param[in]  Register  The SMM Save State register to read.
617   @param[in]  Width     The number of bytes to read from the CPU save state.
618   @param[out] Buffer    Upon return, this holds the CPU register value read
619                         from the save state.
620 
621   @retval EFI_SUCCESS           The register was read from Save State.
622   @retval EFI_INVALID_PARAMTER  Buffer is NULL.
623   @retval EFI_UNSUPPORTED       This function does not support reading Register.
624 
625 **/
626 EFI_STATUS
627 EFIAPI
SmmCpuFeaturesReadSaveStateRegister(IN UINTN CpuIndex,IN EFI_SMM_SAVE_STATE_REGISTER Register,IN UINTN Width,OUT VOID * Buffer)628 SmmCpuFeaturesReadSaveStateRegister (
629   IN  UINTN                        CpuIndex,
630   IN  EFI_SMM_SAVE_STATE_REGISTER  Register,
631   IN  UINTN                        Width,
632   OUT VOID                         *Buffer
633   )
634 {
635   UINTN                       RegisterIndex;
636   QEMU_SMRAM_SAVE_STATE_MAP  *CpuSaveState;
637 
638   //
639   // Check for special EFI_SMM_SAVE_STATE_REGISTER_LMA
640   //
641   if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) {
642     //
643     // Only byte access is supported for this register
644     //
645     if (Width != 1) {
646       return EFI_INVALID_PARAMETER;
647     }
648 
649     CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)gSmst->CpuSaveState[CpuIndex];
650 
651     //
652     // Check CPU mode
653     //
654     if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
655       *(UINT8 *)Buffer = 32;
656     } else {
657       *(UINT8 *)Buffer = 64;
658     }
659 
660     return EFI_SUCCESS;
661   }
662 
663   //
664   // Check for special EFI_SMM_SAVE_STATE_REGISTER_IO
665   //
666   if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) {
667     return EFI_NOT_FOUND;
668   }
669 
670   //
671   // Convert Register to a register lookup table index.  Let
672   // PiSmmCpuDxeSmm implement other special registers (currently
673   // there is only EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID).
674   //
675   RegisterIndex = GetRegisterIndex (Register);
676   if (RegisterIndex == 0) {
677     return Register < EFI_SMM_SAVE_STATE_REGISTER_IO ? EFI_NOT_FOUND : EFI_UNSUPPORTED;
678   }
679 
680   return ReadSaveStateRegisterByIndex (CpuIndex, RegisterIndex, Width, Buffer);
681 }
682 
683 /**
684   Writes an SMM Save State register on the target processor.  If this function
685   returns EFI_UNSUPPORTED, then the caller is responsible for writing the
686   SMM Save Sate register.
687 
688   @param[in] CpuIndex  The index of the CPU to write the SMM Save State.  The
689                        value must be between 0 and the NumberOfCpus field in
690                        the System Management System Table (SMST).
691   @param[in] Register  The SMM Save State register to write.
692   @param[in] Width     The number of bytes to write to the CPU save state.
693   @param[in] Buffer    Upon entry, this holds the new CPU register value.
694 
695   @retval EFI_SUCCESS           The register was written to Save State.
696   @retval EFI_INVALID_PARAMTER  Buffer is NULL.
697   @retval EFI_UNSUPPORTED       This function does not support writing Register.
698 **/
699 EFI_STATUS
700 EFIAPI
SmmCpuFeaturesWriteSaveStateRegister(IN UINTN CpuIndex,IN EFI_SMM_SAVE_STATE_REGISTER Register,IN UINTN Width,IN CONST VOID * Buffer)701 SmmCpuFeaturesWriteSaveStateRegister (
702   IN UINTN                        CpuIndex,
703   IN EFI_SMM_SAVE_STATE_REGISTER  Register,
704   IN UINTN                        Width,
705   IN CONST VOID                   *Buffer
706   )
707 {
708   UINTN                       RegisterIndex;
709   QEMU_SMRAM_SAVE_STATE_MAP  *CpuSaveState;
710 
711   //
712   // Writes to EFI_SMM_SAVE_STATE_REGISTER_LMA are ignored
713   //
714   if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) {
715     return EFI_SUCCESS;
716   }
717 
718   //
719   // Writes to EFI_SMM_SAVE_STATE_REGISTER_IO are not supported
720   //
721   if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) {
722     return EFI_NOT_FOUND;
723   }
724 
725   //
726   // Convert Register to a register lookup table index.  Let
727   // PiSmmCpuDxeSmm implement other special registers (currently
728   // there is only EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID).
729   //
730   RegisterIndex = GetRegisterIndex (Register);
731   if (RegisterIndex == 0) {
732     return Register < EFI_SMM_SAVE_STATE_REGISTER_IO ? EFI_NOT_FOUND : EFI_UNSUPPORTED;
733   }
734 
735   CpuSaveState = (QEMU_SMRAM_SAVE_STATE_MAP *)gSmst->CpuSaveState[CpuIndex];
736 
737   //
738   // Do not write non-writable SaveState, because it will cause exception.
739   //
740   if (!mSmmCpuWidthOffset[RegisterIndex].Writeable) {
741     return EFI_UNSUPPORTED;
742   }
743 
744   //
745   // Check CPU mode
746   //
747   if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
748     //
749     // If 32-bit mode width is zero, then the specified register can not be accessed
750     //
751     if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) {
752       return EFI_NOT_FOUND;
753     }
754 
755     //
756     // If Width is bigger than the 32-bit mode width, then the specified register can not be accessed
757     //
758     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) {
759       return EFI_INVALID_PARAMETER;
760     }
761     //
762     // Write SMM State register
763     //
764     ASSERT (CpuSaveState != NULL);
765     CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32, Buffer, Width);
766   } else {
767     //
768     // If 64-bit mode width is zero, then the specified register can not be accessed
769     //
770     if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) {
771       return EFI_NOT_FOUND;
772     }
773 
774     //
775     // If Width is bigger than the 64-bit mode width, then the specified register can not be accessed
776     //
777     if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) {
778       return EFI_INVALID_PARAMETER;
779     }
780 
781     //
782     // Write lower 32-bits of SMM State register
783     //
784     CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo, Buffer, MIN (4, Width));
785     if (Width >= 4) {
786       //
787       // Write upper 32-bits of SMM State register
788       //
789       CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi, (UINT8 *)Buffer + 4, Width - 4);
790     }
791   }
792   return EFI_SUCCESS;
793 }
794 
795 /**
796   This function is hook point called after the gEfiSmmReadyToLockProtocolGuid
797   notification is completely processed.
798 **/
799 VOID
800 EFIAPI
SmmCpuFeaturesCompleteSmmReadyToLock(VOID)801 SmmCpuFeaturesCompleteSmmReadyToLock (
802   VOID
803   )
804 {
805 }
806 
807 /**
808   This API provides a method for a CPU to allocate a specific region for storing page tables.
809 
810   This API can be called more once to allocate memory for page tables.
811 
812   Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
813   allocated buffer.  The buffer returned is aligned on a 4KB boundary.  If Pages is 0, then NULL
814   is returned.  If there is not enough memory remaining to satisfy the request, then NULL is
815   returned.
816 
817   This function can also return NULL if there is no preference on where the page tables are allocated in SMRAM.
818 
819   @param  Pages                 The number of 4 KB pages to allocate.
820 
821   @return A pointer to the allocated buffer for page tables.
822   @retval NULL      Fail to allocate a specific region for storing page tables,
823                     Or there is no preference on where the page tables are allocated in SMRAM.
824 
825 **/
826 VOID *
827 EFIAPI
SmmCpuFeaturesAllocatePageTableMemory(IN UINTN Pages)828 SmmCpuFeaturesAllocatePageTableMemory (
829   IN UINTN           Pages
830   )
831 {
832   return NULL;
833 }
834 
835