VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/UefiCpuPkg/Library/SmmRelocationLib/SmmRelocationLib.c@ 105670

Last change on this file since 105670 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: 16.7 KB
Line 
1/** @file
2 SMM Relocation Lib for each processor.
3
4 This Lib produces the SMM_BASE_HOB in HOB database which tells
5 the PiSmmCpuDxeSmm driver (runs at a later phase) about the new
6 SMBASE for each processor. PiSmmCpuDxeSmm driver installs the
7 SMI handler at the SMM_BASE_HOB.SmBase[Index]+0x8000 for processor
8 Index.
9
10 Copyright (c) 2024, Intel Corporation. All rights reserved.<BR>
11 SPDX-License-Identifier: BSD-2-Clause-Patent
12
13**/
14#include "InternalSmmRelocationLib.h"
15
16UINTN mMaxNumberOfCpus = 1;
17UINTN mNumberOfCpus = 1;
18
19//
20// IDT used during SMM Init
21//
22IA32_DESCRIPTOR gcSmmInitIdtr;
23
24//
25// Smbase for current CPU
26//
27UINT64 mSmBase;
28
29//
30// SmBase Rebased flag for current CPU
31//
32volatile BOOLEAN mRebased;
33
34/**
35 This function will get the SmBase for CpuIndex.
36
37 @param[in] CpuIndex The processor index.
38 @param[in] SmmRelocationStart The start address of Smm relocated memory in SMRAM.
39 @param[in] TileSize The total size required for a CPU save state, any
40 additional CPU-specific context and the size of code
41 for the SMI entry point.
42
43 @retval The value of SmBase for CpuIndex.
44
45**/
46UINTN
47GetSmBase (
48 IN UINTN CpuIndex,
49 IN EFI_PHYSICAL_ADDRESS SmmRelocationStart,
50 IN UINTN TileSize
51 )
52{
53 return (UINTN)(SmmRelocationStart) + CpuIndex * TileSize - SMM_HANDLER_OFFSET;
54}
55
56/**
57 This function will create SmBase for all CPUs.
58
59 @param[in] SmmRelocationStart The start address of Smm relocated memory in SMRAM.
60 @param[in] TileSize The total size required for a CPU save state, any
61 additional CPU-specific context and the size of code
62 for the SMI entry point.
63
64 @retval EFI_SUCCESS Create SmBase for all CPUs successfully.
65 @retval Others Failed to create SmBase for all CPUs.
66
67**/
68EFI_STATUS
69CreateSmmBaseHob (
70 IN EFI_PHYSICAL_ADDRESS SmmRelocationStart,
71 IN UINTN TileSize
72 )
73{
74 UINTN Index;
75 SMM_BASE_HOB_DATA *SmmBaseHobData;
76 UINT32 CpuCount;
77 UINT32 NumberOfProcessorsInHob;
78 UINT32 MaxCapOfProcessorsInHob;
79 UINT32 HobCount;
80
81 SmmBaseHobData = NULL;
82 CpuCount = 0;
83 NumberOfProcessorsInHob = 0;
84 MaxCapOfProcessorsInHob = 0;
85 HobCount = 0;
86
87 //
88 // Count the HOB instance maximum capacity of CPU (MaxCapOfProcessorsInHob) since the max HobLength is 0xFFF8.
89 //
90 MaxCapOfProcessorsInHob = (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE) - sizeof (SMM_BASE_HOB_DATA)) / sizeof (UINT64) + 1;
91 DEBUG ((DEBUG_INFO, "CreateSmmBaseHob - MaxCapOfProcessorsInHob: %d\n", MaxCapOfProcessorsInHob));
92
93 //
94 // Create Guided SMM Base HOB Instances.
95 //
96 while (CpuCount != mMaxNumberOfCpus) {
97 NumberOfProcessorsInHob = MIN ((UINT32)mMaxNumberOfCpus - CpuCount, MaxCapOfProcessorsInHob);
98
99 SmmBaseHobData = BuildGuidHob (
100 &gSmmBaseHobGuid,
101 sizeof (SMM_BASE_HOB_DATA) + sizeof (UINT64) * NumberOfProcessorsInHob
102 );
103 if (SmmBaseHobData == NULL) {
104 return EFI_OUT_OF_RESOURCES;
105 }
106
107 SmmBaseHobData->ProcessorIndex = CpuCount;
108 SmmBaseHobData->NumberOfProcessors = NumberOfProcessorsInHob;
109
110 DEBUG ((DEBUG_INFO, "CreateSmmBaseHob - SmmBaseHobData[%d]->ProcessorIndex: %d\n", HobCount, SmmBaseHobData->ProcessorIndex));
111 DEBUG ((DEBUG_INFO, "CreateSmmBaseHob - SmmBaseHobData[%d]->NumberOfProcessors: %d\n", HobCount, SmmBaseHobData->NumberOfProcessors));
112 for (Index = 0; Index < SmmBaseHobData->NumberOfProcessors; Index++) {
113 //
114 // Calculate the new SMBASE address
115 //
116 SmmBaseHobData->SmBase[Index] = GetSmBase (Index + CpuCount, SmmRelocationStart, TileSize);
117 DEBUG ((DEBUG_INFO, "CreateSmmBaseHob - SmmBaseHobData[%d]->SmBase[%d]: 0x%08x\n", HobCount, Index, SmmBaseHobData->SmBase[Index]));
118 }
119
120 CpuCount += NumberOfProcessorsInHob;
121 HobCount++;
122 SmmBaseHobData = NULL;
123 }
124
125 return EFI_SUCCESS;
126}
127
128/**
129 C function for SMI handler. To change all processor's SMMBase Register.
130
131**/
132VOID
133EFIAPI
134SmmInitHandler (
135 VOID
136 )
137{
138 //
139 // Update SMM IDT entries' code segment and load IDT
140 //
141 AsmWriteIdtr (&gcSmmInitIdtr);
142
143 //
144 // Configure SmBase.
145 //
146 ConfigureSmBase (mSmBase);
147
148 //
149 // Hook return after RSM to set SMM re-based flag
150 // SMM re-based flag can't be set before RSM, because SMM save state context might be override
151 // by next AP flow before it take effect.
152 //
153 SemaphoreHook (&mRebased);
154}
155
156/**
157 Relocate SmmBases for each processor.
158 Execute on first boot and all S3 resumes
159
160 @param[in] MpServices2 Pointer to this instance of the MpServices.
161 @param[in] SmmRelocationStart The start address of Smm relocated memory in SMRAM.
162 @param[in] TileSize The total size required for a CPU save state, any
163 additional CPU-specific context and the size of code
164 for the SMI entry point.
165
166**/
167VOID
168SmmRelocateBases (
169 IN EDKII_PEI_MP_SERVICES2_PPI *MpServices2,
170 IN EFI_PHYSICAL_ADDRESS SmmRelocationStart,
171 IN UINTN TileSize
172 )
173{
174 EFI_STATUS Status;
175 UINT8 BakBuf[BACK_BUF_SIZE];
176 SMRAM_SAVE_STATE_MAP BakBuf2;
177 SMRAM_SAVE_STATE_MAP *CpuStatePtr;
178 UINT8 *U8Ptr;
179 UINTN Index;
180 UINTN BspIndex;
181 UINT32 BspApicId;
182 EFI_PROCESSOR_INFORMATION ProcessorInfo;
183
184 //
185 // Make sure the reserved size is large enough for procedure SmmInitTemplate.
186 //
187 ASSERT (sizeof (BakBuf) >= gcSmmInitSize);
188
189 //
190 // Patch ASM code template with current CR0, CR3, and CR4 values
191 //
192 PatchInstructionX86 (gPatchSmmInitCr0, AsmReadCr0 (), 4);
193 PatchInstructionX86 (gPatchSmmInitCr3, AsmReadCr3 (), 4);
194 PatchInstructionX86 (gPatchSmmInitCr4, AsmReadCr4 () & (~CR4_CET_ENABLE), 4);
195
196 U8Ptr = (UINT8 *)(UINTN)(SMM_DEFAULT_SMBASE + SMM_HANDLER_OFFSET);
197 CpuStatePtr = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET);
198
199 //
200 // Backup original contents at address 0x38000
201 //
202 CopyMem (BakBuf, U8Ptr, sizeof (BakBuf));
203 CopyMem (&BakBuf2, CpuStatePtr, sizeof (BakBuf2));
204
205 //
206 // Load image for relocation
207 //
208 CopyMem (U8Ptr, gcSmmInitTemplate, gcSmmInitSize);
209
210 //
211 // Retrieve the local APIC ID of current processor
212 //
213 BspApicId = GetApicId ();
214
215 //
216 // Relocate SM bases for all APs
217 // This is APs' 1st SMI - rebase will be done here, and APs' default SMI handler will be overridden by gcSmmInitTemplate
218 //
219 BspIndex = (UINTN)-1;
220 for (Index = 0; Index < mNumberOfCpus; Index++) {
221 Status = MpServices2->GetProcessorInfo (MpServices2, Index | CPU_V2_EXTENDED_TOPOLOGY, &ProcessorInfo);
222 ASSERT_EFI_ERROR (Status);
223
224 if (BspApicId != (UINT32)ProcessorInfo.ProcessorId) {
225 mRebased = FALSE;
226 mSmBase = GetSmBase (Index, SmmRelocationStart, TileSize);
227 SendSmiIpi ((UINT32)ProcessorInfo.ProcessorId);
228 //
229 // Wait for this AP to finish its 1st SMI
230 //
231 while (!mRebased) {
232 }
233 } else {
234 //
235 // BSP will be Relocated later
236 //
237 BspIndex = Index;
238 }
239 }
240
241 //
242 // Relocate BSP's SMM base
243 //
244 ASSERT (BspIndex != (UINTN)-1);
245 mRebased = FALSE;
246 mSmBase = GetSmBase (BspIndex, SmmRelocationStart, TileSize);
247 SendSmiIpi (BspApicId);
248
249 //
250 // Wait for the BSP to finish its 1st SMI
251 //
252 while (!mRebased) {
253 }
254
255 //
256 // Restore contents at address 0x38000
257 //
258 CopyMem (CpuStatePtr, &BakBuf2, sizeof (BakBuf2));
259 CopyMem (U8Ptr, BakBuf, sizeof (BakBuf));
260}
261
262/**
263 Initialize IDT to setup exception handlers in SMM.
264
265**/
266VOID
267InitSmmIdt (
268 VOID
269 )
270{
271 EFI_STATUS Status;
272 BOOLEAN InterruptState;
273 IA32_DESCRIPTOR PeiIdtr;
274 CONST EFI_PEI_SERVICES **PeiServices;
275
276 //
277 // There are 32 (not 255) entries in it since only processor
278 // generated exceptions will be handled.
279 //
280 gcSmmInitIdtr.Limit = (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 32) - 1;
281
282 //
283 // Allocate for IDT.
284 // sizeof (UINTN) is for the PEI Services Table pointer.
285 //
286 gcSmmInitIdtr.Base = (UINTN)AllocateZeroPool (gcSmmInitIdtr.Limit + 1 + sizeof (UINTN));
287 ASSERT (gcSmmInitIdtr.Base != 0);
288 gcSmmInitIdtr.Base += sizeof (UINTN);
289
290 //
291 // Disable Interrupt, save InterruptState and save PEI IDT table
292 //
293 InterruptState = SaveAndDisableInterrupts ();
294 AsmReadIdtr (&PeiIdtr);
295
296 //
297 // Save the PEI Services Table pointer
298 // The PEI Services Table pointer will be stored in the sizeof (UINTN) bytes
299 // immediately preceding the IDT in memory.
300 //
301 PeiServices = (CONST EFI_PEI_SERVICES **)(*(UINTN *)(PeiIdtr.Base - sizeof (UINTN)));
302 (*(UINTN *)(gcSmmInitIdtr.Base - sizeof (UINTN))) = (UINTN)PeiServices;
303
304 //
305 // Load SMM temporary IDT table
306 //
307 AsmWriteIdtr (&gcSmmInitIdtr);
308
309 //
310 // Setup SMM default exception handlers, SMM IDT table
311 // will be updated and saved in gcSmmInitIdtr
312 //
313 Status = InitializeCpuExceptionHandlers (NULL);
314 ASSERT_EFI_ERROR (Status);
315
316 //
317 // Restore PEI IDT table and CPU InterruptState
318 //
319 AsmWriteIdtr ((IA32_DESCRIPTOR *)&PeiIdtr);
320 SetInterruptState (InterruptState);
321}
322
323/**
324 This routine will split SmramReserve HOB to reserve SmmRelocationSize for Smm relocated memory.
325
326 @param[in] SmmRelocationSize SmmRelocationSize for all processors.
327 @param[in,out] SmmRelocationStart Return the start address of Smm relocated memory in SMRAM.
328
329 @retval EFI_SUCCESS The gEfiSmmSmramMemoryGuid is split successfully.
330 @retval EFI_DEVICE_ERROR Failed to build new HOB for gEfiSmmSmramMemoryGuid.
331 @retval EFI_NOT_FOUND The gEfiSmmSmramMemoryGuid is not found.
332
333**/
334EFI_STATUS
335SplitSmramHobForSmmRelocation (
336 IN UINT64 SmmRelocationSize,
337 IN OUT EFI_PHYSICAL_ADDRESS *SmmRelocationStart
338 )
339{
340 EFI_HOB_GUID_TYPE *GuidHob;
341 EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *Block;
342 EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *NewBlock;
343 UINTN NewBlockSize;
344
345 ASSERT (SmmRelocationStart != NULL);
346
347 //
348 // Retrieve the GUID HOB data that contains the set of SMRAM descriptors
349 //
350 GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);
351 if (GuidHob == NULL) {
352 return EFI_NOT_FOUND;
353 }
354
355 Block = (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *)GET_GUID_HOB_DATA (GuidHob);
356
357 //
358 // Allocate one extra EFI_SMRAM_DESCRIPTOR to describe smram carved out for all SMBASE
359 //
360 NewBlockSize = sizeof (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK) + (Block->NumberOfSmmReservedRegions * sizeof (EFI_SMRAM_DESCRIPTOR));
361
362 NewBlock = (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *)BuildGuidHob (
363 &gEfiSmmSmramMemoryGuid,
364 NewBlockSize
365 );
366 ASSERT (NewBlock != NULL);
367 if (NewBlock == NULL) {
368 return EFI_DEVICE_ERROR;
369 }
370
371 //
372 // Copy old EFI_SMRAM_HOB_DESCRIPTOR_BLOCK to new allocated region
373 //
374 CopyMem ((VOID *)NewBlock, Block, NewBlockSize - sizeof (EFI_SMRAM_DESCRIPTOR));
375
376 //
377 // Increase the number of SMRAM descriptors by 1 to make room for the ALLOCATED descriptor of size EFI_PAGE_SIZE
378 //
379 NewBlock->NumberOfSmmReservedRegions = (UINT32)(Block->NumberOfSmmReservedRegions + 1);
380
381 ASSERT (Block->NumberOfSmmReservedRegions >= 1);
382 //
383 // Copy last entry to the end - we assume TSEG is last entry.
384 //
385 CopyMem (&NewBlock->Descriptor[Block->NumberOfSmmReservedRegions], &NewBlock->Descriptor[Block->NumberOfSmmReservedRegions - 1], sizeof (EFI_SMRAM_DESCRIPTOR));
386
387 //
388 // Update the entry in the array with a size of SmmRelocationSize and put into the ALLOCATED state
389 //
390 NewBlock->Descriptor[Block->NumberOfSmmReservedRegions - 1].PhysicalSize = SmmRelocationSize;
391 NewBlock->Descriptor[Block->NumberOfSmmReservedRegions - 1].RegionState |= EFI_ALLOCATED;
392
393 //
394 // Return the start address of Smm relocated memory in SMRAM.
395 //
396 *SmmRelocationStart = NewBlock->Descriptor[Block->NumberOfSmmReservedRegions - 1].CpuStart;
397
398 //
399 // Reduce the size of the last SMRAM descriptor by SmmRelocationSize
400 //
401 NewBlock->Descriptor[Block->NumberOfSmmReservedRegions].PhysicalStart += SmmRelocationSize;
402 NewBlock->Descriptor[Block->NumberOfSmmReservedRegions].CpuStart += SmmRelocationSize;
403 NewBlock->Descriptor[Block->NumberOfSmmReservedRegions].PhysicalSize -= SmmRelocationSize;
404
405 //
406 // Last step, we can scrub old one
407 //
408 ZeroMem (&GuidHob->Name, sizeof (GuidHob->Name));
409
410 return EFI_SUCCESS;
411}
412
413/**
414 CPU SmmBase Relocation Init.
415
416 This function is to relocate CPU SmmBase.
417
418 @param[in] MpServices2 Pointer to this instance of the MpServices.
419
420 @retval EFI_SUCCESS CPU SmmBase Relocated successfully.
421 @retval Others CPU SmmBase Relocation failed.
422
423**/
424EFI_STATUS
425EFIAPI
426SmmRelocationInit (
427 IN EDKII_PEI_MP_SERVICES2_PPI *MpServices2
428 )
429{
430 EFI_STATUS Status;
431 UINTN NumberOfEnabledCpus;
432 UINTN TileSize;
433 UINT64 SmmRelocationSize;
434 EFI_PHYSICAL_ADDRESS SmmRelocationStart;
435 UINTN SmmStackSize;
436 UINT8 *SmmStacks;
437
438 SmmRelocationStart = 0;
439 SmmStacks = NULL;
440
441 DEBUG ((DEBUG_INFO, "SmmRelocationInit Start \n"));
442 if (MpServices2 == NULL) {
443 return EFI_INVALID_PARAMETER;
444 }
445
446 //
447 // Get the number of processors
448 //
449 Status = MpServices2->GetNumberOfProcessors (
450 MpServices2,
451 &mNumberOfCpus,
452 &NumberOfEnabledCpus
453 );
454 if (EFI_ERROR (Status)) {
455 goto ON_EXIT;
456 }
457
458 if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
459 mMaxNumberOfCpus = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
460 } else {
461 mMaxNumberOfCpus = mNumberOfCpus;
462 }
463
464 ASSERT (mNumberOfCpus <= mMaxNumberOfCpus);
465
466 //
467 // Calculate SmmRelocationSize for all of the tiles.
468 //
469 // The CPU save state and code for the SMI entry point are tiled within an SMRAM
470 // allocated buffer. The minimum size of this buffer for a uniprocessor system
471 // is 32 KB, because the entry point is SMBASE + 32KB, and CPU save state area
472 // just below SMBASE + 64KB. If more than one CPU is present in the platform,
473 // then the SMI entry point and the CPU save state areas can be tiles to minimize
474 // the total amount SMRAM required for all the CPUs. The tile size can be computed
475 // by adding the CPU save state size, any extra CPU specific context, and
476 // the size of code that must be placed at the SMI entry point to transfer
477 // control to a C function in the native SMM execution mode. This size is
478 // rounded up to the nearest power of 2 to give the tile size for a each CPU.
479 // The total amount of memory required is the maximum number of CPUs that
480 // platform supports times the tile size.
481 //
482 TileSize = SIZE_8KB;
483 SmmRelocationSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (SIZE_32KB + TileSize * (mMaxNumberOfCpus - 1)));
484
485 //
486 // Split SmramReserve HOB to reserve SmmRelocationSize for Smm relocated memory
487 //
488 Status = SplitSmramHobForSmmRelocation (
489 SmmRelocationSize,
490 &SmmRelocationStart
491 );
492 if (EFI_ERROR (Status)) {
493 goto ON_EXIT;
494 }
495
496 ASSERT (SmmRelocationStart != 0);
497 DEBUG ((DEBUG_INFO, "SmmRelocationInit - SmmRelocationSize: 0x%08x\n", SmmRelocationSize));
498 DEBUG ((DEBUG_INFO, "SmmRelocationInit - SmmRelocationStart: 0x%08x\n", SmmRelocationStart));
499
500 //
501 // Fix up the address of the global variable or function referred in
502 // SmmInit assembly files to be the absolute address
503 //
504 SmmInitFixupAddress ();
505
506 //
507 // Patch SMI stack for SMM base relocation
508 // Note: No need allocate stack for all CPUs since the relocation
509 // occurs serially for each CPU
510 //
511 SmmStackSize = EFI_PAGE_SIZE;
512 SmmStacks = (UINT8 *)AllocatePages (EFI_SIZE_TO_PAGES (SmmStackSize));
513 if (SmmStacks == NULL) {
514 Status = EFI_OUT_OF_RESOURCES;
515 goto ON_EXIT;
516 }
517
518 DEBUG ((DEBUG_INFO, "SmmRelocationInit - SmmStackSize: 0x%08x\n", SmmStackSize));
519 DEBUG ((DEBUG_INFO, "SmmRelocationInit - SmmStacks: 0x%08x\n", SmmStacks));
520
521 PatchInstructionX86 (
522 gPatchSmmInitStack,
523 (UINTN)(SmmStacks + SmmStackSize - sizeof (UINTN)),
524 sizeof (UINTN)
525 );
526
527 //
528 // Initialize the SMM IDT for SMM base relocation
529 //
530 InitSmmIdt ();
531
532 //
533 // Relocate SmmBases for each processor.
534 //
535 SmmRelocateBases (MpServices2, SmmRelocationStart, TileSize);
536
537 //
538 // Create the SmBase HOB for all CPUs
539 //
540 Status = CreateSmmBaseHob (SmmRelocationStart, TileSize);
541
542ON_EXIT:
543 if (SmmStacks != NULL) {
544 FreePages (SmmStacks, EFI_SIZE_TO_PAGES (SmmStackSize));
545 }
546
547 DEBUG ((DEBUG_INFO, "SmmRelocationInit Done\n"));
548 return Status;
549}
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