VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c

Last change on this file was 105670, checked in by vboxsync, 6 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 19.0 KB
Line 
1/** @file
2 The CPU specific programming for PiSmmCpuDxeSmm module.
3
4 Copyright (c) 2010 - 2023, Intel Corporation. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7**/
8
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>
22#include <PiSmm.h>
23#include <Register/Amd/SmramSaveStateMap.h>
24#include <Guid/SmmBaseHob.h>
25
26//
27// EFER register LMA bit
28//
29#define LMA BIT10
30
31//
32// Indicate SmBase for each Processors has been relocated or not. If TRUE,
33// means no need to do the relocation in SmmCpuFeaturesInitializeProcessor().
34//
35BOOLEAN mSmmCpuFeaturesSmmRelocated;
36
37/**
38 The constructor function
39
40 @param[in] ImageHandle The firmware allocated handle for the EFI image.
41 @param[in] SystemTable A pointer to the EFI System Table.
42
43 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
44
45**/
46EFI_STATUS
47EFIAPI
48SmmCpuFeaturesLibConstructor (
49 IN EFI_HANDLE ImageHandle,
50 IN EFI_SYSTEM_TABLE *SystemTable
51 )
52{
53 //
54 // If gSmmBaseHobGuid found, means SmBase info has been relocated and recorded
55 // in the SmBase array.
56 //
57 mSmmCpuFeaturesSmmRelocated = (BOOLEAN)(GetFirstGuidHob (&gSmmBaseHobGuid) != NULL);
58
59 //
60 // No need to program SMRRs on our virtual platform.
61 //
62 return EFI_SUCCESS;
63}
64
65/**
66 Called during the very first SMI into System Management Mode to initialize
67 CPU features, including SMBASE, for the currently executing CPU. Since this
68 is the first SMI, the SMRAM Save State Map is at the default address of
69 SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET. The currently executing
70 CPU is specified by CpuIndex and CpuIndex can be used to access information
71 about the currently executing CPU in the ProcessorInfo array and the
72 HotPlugCpuData data structure.
73
74 @param[in] CpuIndex The index of the CPU to initialize. The value
75 must be between 0 and the NumberOfCpus field in
76 the System Management System Table (SMST).
77 @param[in] IsMonarch TRUE if the CpuIndex is the index of the CPU that
78 was elected as monarch during System Management
79 Mode initialization.
80 FALSE if the CpuIndex is not the index of the CPU
81 that was elected as monarch during System
82 Management Mode initialization.
83 @param[in] ProcessorInfo Pointer to an array of EFI_PROCESSOR_INFORMATION
84 structures. ProcessorInfo[CpuIndex] contains the
85 information for the currently executing CPU.
86 @param[in] CpuHotPlugData Pointer to the CPU_HOT_PLUG_DATA structure that
87 contains the ApidId and SmBase arrays.
88**/
89VOID
90EFIAPI
91SmmCpuFeaturesInitializeProcessor (
92 IN UINTN CpuIndex,
93 IN BOOLEAN IsMonarch,
94 IN EFI_PROCESSOR_INFORMATION *ProcessorInfo,
95 IN CPU_HOT_PLUG_DATA *CpuHotPlugData
96 )
97{
98 AMD_SMRAM_SAVE_STATE_MAP *CpuState;
99
100 //
101 // No need to configure SMBASE if SmBase relocation has been done.
102 //
103 if (!mSmmCpuFeaturesSmmRelocated) {
104 //
105 // Configure SMBASE.
106 //
107 CpuState = (AMD_SMRAM_SAVE_STATE_MAP *)(UINTN)(
108 SMM_DEFAULT_SMBASE +
109 SMRAM_SAVE_STATE_MAP_OFFSET
110 );
111 if ((CpuState->x86.SMMRevId & 0xFFFF) == 0) {
112 CpuState->x86.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex];
113 } else {
114 CpuState->x64.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex];
115 }
116 }
117
118 //
119 // No need to program SMRRs on our virtual platform.
120 //
121}
122
123/**
124 This function updates the SMRAM save state on the currently executing CPU
125 to resume execution at a specific address after an RSM instruction. This
126 function must evaluate the SMRAM save state to determine the execution mode
127 the RSM instruction resumes and update the resume execution address with
128 either NewInstructionPointer32 or NewInstructionPoint. The auto HALT restart
129 flag in the SMRAM save state must always be cleared. This function returns
130 the value of the instruction pointer from the SMRAM save state that was
131 replaced. If this function returns 0, then the SMRAM save state was not
132 modified.
133
134 This function is called during the very first SMI on each CPU after
135 SmmCpuFeaturesInitializeProcessor() to set a flag in normal execution mode
136 to signal that the SMBASE of each CPU has been updated before the default
137 SMBASE address is used for the first SMI to the next CPU.
138
139 @param[in] CpuIndex The index of the CPU to hook. The value
140 must be between 0 and the NumberOfCpus
141 field in the System Management System
142 Table (SMST).
143 @param[in] CpuState Pointer to SMRAM Save State Map for the
144 currently executing CPU.
145 @param[in] NewInstructionPointer32 Instruction pointer to use if resuming to
146 32-bit execution mode from 64-bit SMM.
147 @param[in] NewInstructionPointer Instruction pointer to use if resuming to
148 same execution mode as SMM.
149
150 @retval 0 This function did modify the SMRAM save state.
151 @retval > 0 The original instruction pointer value from the SMRAM save state
152 before it was replaced.
153**/
154UINT64
155EFIAPI
156SmmCpuFeaturesHookReturnFromSmm (
157 IN UINTN CpuIndex,
158 IN SMRAM_SAVE_STATE_MAP *CpuState,
159 IN UINT64 NewInstructionPointer32,
160 IN UINT64 NewInstructionPointer
161 )
162{
163 UINT64 OriginalInstructionPointer;
164 AMD_SMRAM_SAVE_STATE_MAP *CpuSaveState;
165
166 CpuSaveState = (AMD_SMRAM_SAVE_STATE_MAP *)CpuState;
167 if ((CpuSaveState->x86.SMMRevId & 0xFFFF) == 0) {
168 OriginalInstructionPointer = (UINT64)CpuSaveState->x86._EIP;
169 CpuSaveState->x86._EIP = (UINT32)NewInstructionPointer;
170 //
171 // Clear the auto HALT restart flag so the RSM instruction returns
172 // program control to the instruction following the HLT instruction.
173 //
174 if ((CpuSaveState->x86.AutoHALTRestart & BIT0) != 0) {
175 CpuSaveState->x86.AutoHALTRestart &= ~BIT0;
176 }
177 } else {
178 OriginalInstructionPointer = CpuSaveState->x64._RIP;
179 if ((CpuSaveState->x64.EFER & LMA) == 0) {
180 CpuSaveState->x64._RIP = (UINT32)NewInstructionPointer32;
181 } else {
182 CpuSaveState->x64._RIP = (UINT32)NewInstructionPointer;
183 }
184
185 //
186 // Clear the auto HALT restart flag so the RSM instruction returns
187 // program control to the instruction following the HLT instruction.
188 //
189 if ((CpuSaveState->x64.AutoHALTRestart & BIT0) != 0) {
190 CpuSaveState->x64.AutoHALTRestart &= ~BIT0;
191 }
192 }
193
194 return OriginalInstructionPointer;
195}
196
197STATIC CPU_HOT_EJECT_DATA *mCpuHotEjectData = NULL;
198
199/**
200 Initialize mCpuHotEjectData if PcdCpuMaxLogicalProcessorNumber > 1.
201
202 Also setup the corresponding PcdCpuHotEjectDataAddress.
203**/
204STATIC
205VOID
206InitCpuHotEjectData (
207 VOID
208 )
209{
210 UINTN Size;
211 UINT32 Idx;
212 UINT32 MaxNumberOfCpus;
213 RETURN_STATUS PcdStatus;
214
215 MaxNumberOfCpus = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
216 if (MaxNumberOfCpus == 1) {
217 return;
218 }
219
220 //
221 // We allocate CPU_HOT_EJECT_DATA and CPU_HOT_EJECT_DATA->QemuSelectorMap[]
222 // in a single allocation, and explicitly align the QemuSelectorMap[] (which
223 // is a UINT64 array) at its natural boundary.
224 // Accordingly, allocate:
225 // sizeof(*mCpuHotEjectData) + (MaxNumberOfCpus * sizeof(UINT64))
226 // and, add sizeof(UINT64) - 1 to use as padding if needed.
227 //
228
229 if (RETURN_ERROR (SafeUintnMult (MaxNumberOfCpus, sizeof (UINT64), &Size)) ||
230 RETURN_ERROR (SafeUintnAdd (Size, sizeof (*mCpuHotEjectData), &Size)) ||
231 RETURN_ERROR (SafeUintnAdd (Size, sizeof (UINT64) - 1, &Size)))
232 {
233 DEBUG ((DEBUG_ERROR, "%a: invalid CPU_HOT_EJECT_DATA\n", __func__));
234 goto Fatal;
235 }
236
237 mCpuHotEjectData = AllocatePool (Size);
238 if (mCpuHotEjectData == NULL) {
239 ASSERT (mCpuHotEjectData != NULL);
240 goto Fatal;
241 }
242
243 mCpuHotEjectData->Handler = NULL;
244 mCpuHotEjectData->ArrayLength = MaxNumberOfCpus;
245
246 mCpuHotEjectData->QemuSelectorMap = ALIGN_POINTER (
247 mCpuHotEjectData + 1,
248 sizeof (UINT64)
249 );
250 //
251 // We use mCpuHotEjectData->QemuSelectorMap to map
252 // ProcessorNum -> QemuSelector. Initialize to invalid values.
253 //
254 for (Idx = 0; Idx < mCpuHotEjectData->ArrayLength; Idx++) {
255 mCpuHotEjectData->QemuSelectorMap[Idx] = CPU_EJECT_QEMU_SELECTOR_INVALID;
256 }
257
258 //
259 // Expose address of CPU Hot eject Data structure
260 //
261 PcdStatus = PcdSet64S (
262 PcdCpuHotEjectDataAddress,
263 (UINTN)(VOID *)mCpuHotEjectData
264 );
265 ASSERT_RETURN_ERROR (PcdStatus);
266
267 return;
268
269Fatal:
270 CpuDeadLoop ();
271}
272
273/**
274 Hook point in normal execution mode that allows the one CPU that was elected
275 as monarch during System Management Mode initialization to perform additional
276 initialization actions immediately after all of the CPUs have processed their
277 first SMI and called SmmCpuFeaturesInitializeProcessor() relocating SMBASE
278 into a buffer in SMRAM and called SmmCpuFeaturesHookReturnFromSmm().
279**/
280VOID
281EFIAPI
282SmmCpuFeaturesSmmRelocationComplete (
283 VOID
284 )
285{
286 EFI_STATUS Status;
287 UINTN MapPagesBase;
288 UINTN MapPagesCount;
289
290 InitCpuHotEjectData ();
291
292 if (!MemEncryptSevIsEnabled ()) {
293 return;
294 }
295
296 //
297 // Now that SMBASE relocation is complete, re-encrypt the original SMRAM save
298 // state map's container pages, and release the pages to DXE. (The pages were
299 // allocated in PlatformPei.)
300 //
301 Status = MemEncryptSevLocateInitialSmramSaveStateMapPages (
302 &MapPagesBase,
303 &MapPagesCount
304 );
305 ASSERT_EFI_ERROR (Status);
306
307 Status = MemEncryptSevSetPageEncMask (
308 0, // Cr3BaseAddress -- use current CR3
309 MapPagesBase, // BaseAddress
310 MapPagesCount // NumPages
311 );
312 if (EFI_ERROR (Status)) {
313 DEBUG ((
314 DEBUG_ERROR,
315 "%a: MemEncryptSevSetPageEncMask(): %r\n",
316 __func__,
317 Status
318 ));
319 ASSERT (FALSE);
320 CpuDeadLoop ();
321 }
322
323 ZeroMem ((VOID *)MapPagesBase, EFI_PAGES_TO_SIZE (MapPagesCount));
324
325 if (PcdGetBool (PcdQ35SmramAtDefaultSmbase)) {
326 //
327 // The initial SMRAM Save State Map has been covered as part of a larger
328 // reserved memory allocation in PlatformPei's InitializeRamRegions(). That
329 // allocation is supposed to survive into OS runtime; we must not release
330 // any part of it. Only re-assert the containment here.
331 //
332 ASSERT (SMM_DEFAULT_SMBASE <= MapPagesBase);
333 ASSERT (
334 (MapPagesBase + EFI_PAGES_TO_SIZE (MapPagesCount) <=
335 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE)
336 );
337 } else {
338 Status = gBS->FreePages (MapPagesBase, MapPagesCount);
339 ASSERT_EFI_ERROR (Status);
340 }
341}
342
343/**
344 Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is
345 returned, then a custom SMI handler is not provided by this library,
346 and the default SMI handler must be used.
347
348 @retval 0 Use the default SMI handler.
349 @retval > 0 Use the SMI handler installed by
350 SmmCpuFeaturesInstallSmiHandler(). The caller is required to
351 allocate enough SMRAM for each CPU to support the size of the
352 custom SMI handler.
353**/
354UINTN
355EFIAPI
356SmmCpuFeaturesGetSmiHandlerSize (
357 VOID
358 )
359{
360 return 0;
361}
362
363/**
364 Install a custom SMI handler for the CPU specified by CpuIndex. This
365 function is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size
366 is greater than zero and is called by the CPU that was elected as monarch
367 during System Management Mode initialization.
368
369 @param[in] CpuIndex The index of the CPU to install the custom SMI handler.
370 The value must be between 0 and the NumberOfCpus field
371 in the System Management System Table (SMST).
372 @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex.
373 @param[in] SmiStack The stack to use when an SMI is processed by the
374 the CPU specified by CpuIndex.
375 @param[in] StackSize The size, in bytes, if the stack used when an SMI is
376 processed by the CPU specified by CpuIndex.
377 @param[in] GdtBase The base address of the GDT to use when an SMI is
378 processed by the CPU specified by CpuIndex.
379 @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is
380 processed by the CPU specified by CpuIndex.
381 @param[in] IdtBase The base address of the IDT to use when an SMI is
382 processed by the CPU specified by CpuIndex.
383 @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is
384 processed by the CPU specified by CpuIndex.
385 @param[in] Cr3 The base address of the page tables to use when an SMI
386 is processed by the CPU specified by CpuIndex.
387**/
388VOID
389EFIAPI
390SmmCpuFeaturesInstallSmiHandler (
391 IN UINTN CpuIndex,
392 IN UINT32 SmBase,
393 IN VOID *SmiStack,
394 IN UINTN StackSize,
395 IN UINTN GdtBase,
396 IN UINTN GdtSize,
397 IN UINTN IdtBase,
398 IN UINTN IdtSize,
399 IN UINT32 Cr3
400 )
401{
402}
403
404/**
405 Determines if MTRR registers must be configured to set SMRAM cache-ability
406 when executing in System Management Mode.
407
408 @retval TRUE MTRR registers must be configured to set SMRAM cache-ability.
409 @retval FALSE MTRR registers do not need to be configured to set SMRAM
410 cache-ability.
411**/
412BOOLEAN
413EFIAPI
414SmmCpuFeaturesNeedConfigureMtrrs (
415 VOID
416 )
417{
418 return FALSE;
419}
420
421/**
422 Disable SMRR register if SMRR is supported and
423 SmmCpuFeaturesNeedConfigureMtrrs() returns TRUE.
424**/
425VOID
426EFIAPI
427SmmCpuFeaturesDisableSmrr (
428 VOID
429 )
430{
431 //
432 // No SMRR support, nothing to do
433 //
434}
435
436/**
437 Enable SMRR register if SMRR is supported and
438 SmmCpuFeaturesNeedConfigureMtrrs() returns TRUE.
439**/
440VOID
441EFIAPI
442SmmCpuFeaturesReenableSmrr (
443 VOID
444 )
445{
446 //
447 // No SMRR support, nothing to do
448 //
449}
450
451/**
452 Processor specific hook point each time a CPU enters System Management Mode.
453
454 @param[in] CpuIndex The index of the CPU that has entered SMM. The value
455 must be between 0 and the NumberOfCpus field in the
456 System Management System Table (SMST).
457**/
458VOID
459EFIAPI
460SmmCpuFeaturesRendezvousEntry (
461 IN UINTN CpuIndex
462 )
463{
464 //
465 // No SMRR support, nothing to do
466 //
467}
468
469/**
470 Processor specific hook point each time a CPU exits System Management Mode.
471
472 @param[in] CpuIndex The index of the CPU that is exiting SMM. The value
473 must be between 0 and the NumberOfCpus field in the
474 System Management System Table (SMST).
475**/
476VOID
477EFIAPI
478SmmCpuFeaturesRendezvousExit (
479 IN UINTN CpuIndex
480 )
481{
482 //
483 // We only call the Handler if CPU hot-eject is enabled
484 // (PcdCpuMaxLogicalProcessorNumber > 1), and hot-eject is needed
485 // in this SMI exit (otherwise mCpuHotEjectData->Handler is not armed.)
486 //
487
488 if (mCpuHotEjectData != NULL) {
489 CPU_HOT_EJECT_HANDLER Handler;
490
491 //
492 // As the comment above mentions, mCpuHotEjectData->Handler might be
493 // written to on the BSP as part of handling of the CPU-ejection.
494 //
495 // We know that any initial assignment to mCpuHotEjectData->Handler
496 // (on the BSP, in the CpuHotplugMmi() context) is ordered-before the
497 // load below, since it is guaranteed to happen before the
498 // control-dependency of the BSP's SMI exit signal -- by way of a store
499 // to AllCpusInSync (on the BSP, in BspHandler()) and the corresponding
500 // AllCpusInSync loop (on the APs, in SmiRendezvous()) which depends on
501 // that store.
502 //
503 // This guarantees that these pieces of code can never execute
504 // simultaneously. In addition, we ensure that the following load is
505 // ordered-after the AllCpusInSync loop by using a MemoryFence() with
506 // acquire semantics.
507 //
508 MemoryFence ();
509
510 Handler = mCpuHotEjectData->Handler;
511
512 if (Handler != NULL) {
513 Handler (CpuIndex);
514 }
515 }
516}
517
518/**
519 Check to see if an SMM register is supported by a specified CPU.
520
521 @param[in] CpuIndex The index of the CPU to check for SMM register support.
522 The value must be between 0 and the NumberOfCpus field
523 in the System Management System Table (SMST).
524 @param[in] RegName Identifies the SMM register to check for support.
525
526 @retval TRUE The SMM register specified by RegName is supported by the CPU
527 specified by CpuIndex.
528 @retval FALSE The SMM register specified by RegName is not supported by the
529 CPU specified by CpuIndex.
530**/
531BOOLEAN
532EFIAPI
533SmmCpuFeaturesIsSmmRegisterSupported (
534 IN UINTN CpuIndex,
535 IN SMM_REG_NAME RegName
536 )
537{
538 ASSERT (RegName == SmmRegFeatureControl);
539 return FALSE;
540}
541
542/**
543 Returns the current value of the SMM register for the specified CPU.
544 If the SMM register is not supported, then 0 is returned.
545
546 @param[in] CpuIndex The index of the CPU to read the SMM register. The
547 value must be between 0 and the NumberOfCpus field in
548 the System Management System Table (SMST).
549 @param[in] RegName Identifies the SMM register to read.
550
551 @return The value of the SMM register specified by RegName from the CPU
552 specified by CpuIndex.
553**/
554UINT64
555EFIAPI
556SmmCpuFeaturesGetSmmRegister (
557 IN UINTN CpuIndex,
558 IN SMM_REG_NAME RegName
559 )
560{
561 //
562 // This is called for SmmRegSmmDelayed, SmmRegSmmBlocked, SmmRegSmmEnable.
563 // The last of these should actually be SmmRegSmmDisable, so we can just
564 // return FALSE.
565 //
566 return 0;
567}
568
569/**
570 Sets the value of an SMM register on a specified CPU.
571 If the SMM register is not supported, then no action is performed.
572
573 @param[in] CpuIndex The index of the CPU to write the SMM register. The
574 value must be between 0 and the NumberOfCpus field in
575 the System Management System Table (SMST).
576 @param[in] RegName Identifies the SMM register to write.
577 registers are read-only.
578 @param[in] Value The value to write to the SMM register.
579**/
580VOID
581EFIAPI
582SmmCpuFeaturesSetSmmRegister (
583 IN UINTN CpuIndex,
584 IN SMM_REG_NAME RegName,
585 IN UINT64 Value
586 )
587{
588 ASSERT (FALSE);
589}
590
591/**
592 This function is hook point called after the gEfiSmmReadyToLockProtocolGuid
593 notification is completely processed.
594**/
595VOID
596EFIAPI
597SmmCpuFeaturesCompleteSmmReadyToLock (
598 VOID
599 )
600{
601}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette