]> git.proxmox.com Git - mirror_edk2.git/blob - UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
UefiPayloadPkg: Apply uncrustify changes
[mirror_edk2.git] / UefiPayloadPkg / BlSupportSmm / BlSupportSmm.c
1 /** @file
2 This driver is used for SMM S3 support for the bootloader that
3 doesn't support SMM.
4 The payload would save SMM rebase info to SMM communication area.
5 The bootloader is expected to rebase the SMM and trigger SMI by
6 writting 0xB2 port with given value from SMM communication area.
7 The paylaod SMM handler got chance to restore regs in S3 path.
8
9 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
10 SPDX-License-Identifier: BSD-2-Clause-Patent
11
12 **/
13
14 #include <BlSupportSmm.h>
15
16 PLD_S3_COMMUNICATION mPldS3Hob;
17 EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *mSmramHob = NULL;
18 PLD_SMM_REGISTERS *mSmmRegisterHob = NULL;
19 UINT64 mSmmFeatureControl = 0;
20
21 /**
22 Save SMM rebase and SMI handler information to SMM communication area
23
24 The function detects SMM communication region for boot loader, if it is detected, it
25 will save SMM rebase information and S3 SMI handler information to SMM communication
26 region. Bootloader should consume these information in S3 path to restore smm base,
27 and write the 0xB2 port to trigger SMI so that payload could resume S3 registers.
28
29 @param[in] BlSwSmiHandlerInput Value written to 0xB2 to trigger SMI handler.
30
31 @retval EFI_SUCCESS Save SMM info success.
32 @retval Others Failed to save SMM info.
33 **/
34 EFI_STATUS
35 SaveSmmInfoForS3 (
36 IN UINT8 BlSwSmiHandlerInput
37 )
38 {
39 EFI_STATUS Status;
40 EFI_PROCESSOR_INFORMATION ProcessorInfo;
41 EFI_MP_SERVICES_PROTOCOL *MpService;
42 CPU_SMMBASE *SmmBaseInfo;
43 PLD_TO_BL_SMM_INFO *PldSmmInfo;
44 UINTN Index;
45
46 PldSmmInfo = (PLD_TO_BL_SMM_INFO *)(UINTN)mPldS3Hob.CommBuffer.PhysicalStart;
47 PldSmmInfo->Header.Header.HobLength = (UINT16)(sizeof (PLD_TO_BL_SMM_INFO) + gSmst->NumberOfCpus * sizeof (CPU_SMMBASE));
48 for (Index = 0; Index < mSmramHob->NumberOfSmmReservedRegions; Index++) {
49 if ((mPldS3Hob.CommBuffer.PhysicalStart >= mSmramHob->Descriptor[Index].PhysicalStart) &&
50 (mPldS3Hob.CommBuffer.PhysicalStart < mSmramHob->Descriptor[Index].PhysicalStart + mSmramHob->Descriptor[Index].PhysicalSize))
51 {
52 break;
53 }
54 }
55
56 if (Index == mSmramHob->NumberOfSmmReservedRegions) {
57 return EFI_NOT_FOUND;
58 }
59
60 //
61 // Make sure the dedicated region for SMM info communication whose attribute is "allocated" (i.e., excluded from SMM memory service)
62 //
63 if ((mSmramHob->Descriptor[Index].RegionState & EFI_ALLOCATED) == 0) {
64 DEBUG ((DEBUG_ERROR, "SMM communication region not set to EFI_ALLOCATED\n"));
65 return EFI_INVALID_PARAMETER;
66 }
67
68 if (((UINTN)PldSmmInfo + PldSmmInfo->Header.Header.HobLength) > (mSmramHob->Descriptor[Index].PhysicalStart + mSmramHob->Descriptor[Index].PhysicalSize)) {
69 DEBUG ((DEBUG_ERROR, "SMM communication buffer (0x%x) is too small.\n", (UINTN)PldSmmInfo + PldSmmInfo->Header.Header.HobLength));
70 return EFI_BUFFER_TOO_SMALL;
71 }
72
73 CopyGuid (&PldSmmInfo->Header.Name, &gS3CommunicationGuid);
74 PldSmmInfo->Header.Header.HobType = EFI_HOB_TYPE_GUID_EXTENSION;
75 PldSmmInfo->S3Info.SwSmiTriggerValue = BlSwSmiHandlerInput;
76
77 //
78 // Save APIC ID and SMM base
79 //
80 Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpService);
81 if (EFI_ERROR (Status)) {
82 return Status;
83 }
84
85 PldSmmInfo->S3Info.CpuCount = (UINT32)gSmst->NumberOfCpus;
86 SmmBaseInfo = &PldSmmInfo->S3Info.SmmBase[0];
87 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
88 Status = MpService->GetProcessorInfo (MpService, Index, &ProcessorInfo);
89 if (EFI_ERROR (Status)) {
90 return Status;
91 }
92
93 SmmBaseInfo->ApicId = (UINT32)(UINTN)ProcessorInfo.ProcessorId;
94 SmmBaseInfo->SmmBase = (UINT32)(UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET;
95 DEBUG ((DEBUG_INFO, "CPU%d ID:%02X Base: %08X\n", Index, SmmBaseInfo->ApicId, SmmBaseInfo->SmmBase));
96 SmmBaseInfo++;
97 }
98
99 return EFI_SUCCESS;
100 }
101
102 /**
103 Get specified SMI register based on given register ID
104
105 @param[in] Id The register ID to get.
106
107 @retval NULL The register is not found
108 @return smi register
109
110 **/
111 PLD_GENERIC_REGISTER *
112 GetRegisterById (
113 UINT64 Id
114 )
115 {
116 UINT32 Index;
117
118 for (Index = 0; Index < mSmmRegisterHob->Count; Index++) {
119 if (mSmmRegisterHob->Registers[Index].Id == Id) {
120 return &mSmmRegisterHob->Registers[Index];
121 }
122 }
123
124 return NULL;
125 }
126
127 /**
128 Set SMM SMI Global enable lock
129
130 **/
131 VOID
132 LockSmiGlobalEn (
133 VOID
134 )
135 {
136 PLD_GENERIC_REGISTER *SmiLockReg;
137
138 DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn .....\n"));
139
140 SmiLockReg = GetRegisterById (REGISTER_ID_SMI_GBL_EN_LOCK);
141 if (SmiLockReg == NULL) {
142 DEBUG ((DEBUG_ERROR, "SMI global enable lock reg not found.\n"));
143 return;
144 }
145
146 //
147 // Set SMM SMI lock in S3 path
148 //
149 if ((SmiLockReg->Address.AccessSize == EFI_ACPI_3_0_DWORD) &&
150 (SmiLockReg->Address.Address != 0) &&
151 (SmiLockReg->Address.RegisterBitWidth == 1) &&
152 (SmiLockReg->Address.AddressSpaceId == EFI_ACPI_3_0_SYSTEM_MEMORY) &&
153 (SmiLockReg->Value == 1))
154 {
155 DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn ....is locked\n"));
156
157 MmioOr32 ((UINT32)SmiLockReg->Address.Address, 1 << SmiLockReg->Address.RegisterBitOffset);
158 } else {
159 DEBUG ((DEBUG_ERROR, "Unexpected SMM SMI lock register, need enhancement here.\n"));
160 }
161 }
162
163 /**
164 Check and set SMM feature lock bit and code check enable bit
165 in S3 path.
166
167 **/
168 VOID
169 SmmFeatureLockOnS3 (
170 VOID
171 )
172 {
173 if (mSmmFeatureControl != 0) {
174 return;
175 }
176
177 mSmmFeatureControl = AsmReadMsr64 (MSR_SMM_FEATURE_CONTROL);
178 if ((mSmmFeatureControl & 0x5) != 0x5) {
179 //
180 // Set Lock bit [BIT0] for this register and SMM code check enable bit [BIT2]
181 //
182 AsmWriteMsr64 (MSR_SMM_FEATURE_CONTROL, mSmmFeatureControl | 0x5);
183 }
184
185 mSmmFeatureControl = AsmReadMsr64 (MSR_SMM_FEATURE_CONTROL);
186 }
187
188 /**
189 Function to program SMRR base and mask.
190
191 @param[in] ProcedureArgument Pointer to SMRR_BASE_MASK structure.
192 **/
193 VOID
194 SetSmrr (
195 IN VOID *ProcedureArgument
196 )
197 {
198 if (ProcedureArgument != NULL) {
199 AsmWriteMsr64 (MSR_IA32_SMRR_PHYSBASE, ((SMRR_BASE_MASK *)ProcedureArgument)->Base);
200 AsmWriteMsr64 (MSR_IA32_SMRR_PHYSMASK, ((SMRR_BASE_MASK *)ProcedureArgument)->Mask);
201 }
202 }
203
204 /**
205 Set SMRR in S3 path.
206
207 **/
208 VOID
209 SetSmrrOnS3 (
210 VOID
211 )
212 {
213 EFI_STATUS Status;
214 SMRR_BASE_MASK Arguments;
215 UINTN Index;
216 UINT32 SmmBase;
217 UINT32 SmmSize;
218
219 if ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSBASE) != 0) && ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSMASK) & BIT11) != 0)) {
220 return;
221 }
222
223 SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalStart;
224 SmmSize = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalSize;
225 if ((mSmramHob->NumberOfSmmReservedRegions > 2) || (mSmramHob->NumberOfSmmReservedRegions == 0)) {
226 DEBUG ((DEBUG_ERROR, "%d SMM ranges are not supported.\n", mSmramHob->NumberOfSmmReservedRegions));
227 return;
228 } else if (mSmramHob->NumberOfSmmReservedRegions == 2) {
229 if ((mSmramHob->Descriptor[1].PhysicalStart + mSmramHob->Descriptor[1].PhysicalSize) == SmmBase) {
230 SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalStart;
231 } else if (mSmramHob->Descriptor[1].PhysicalStart != (SmmBase + SmmSize)) {
232 DEBUG ((DEBUG_ERROR, "Two SMM regions are not continous.\n"));
233 return;
234 }
235
236 SmmSize += (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalSize;
237 }
238
239 if ((SmmBase == 0) || (SmmSize < SIZE_4KB)) {
240 DEBUG ((DEBUG_ERROR, "Invalid SMM range.\n"));
241 return;
242 }
243
244 //
245 // SMRR size must be of length 2^n
246 // SMRR base alignment cannot be less than SMRR length
247 //
248 if ((SmmSize != GetPowerOfTwo32 (SmmSize)) || ((SmmBase & ~(SmmSize - 1)) != SmmBase)) {
249 DEBUG ((DEBUG_ERROR, " Invalid SMM range.\n"));
250 return;
251 }
252
253 //
254 // Calculate smrr base, mask and pass them as arguments.
255 //
256 Arguments.Base = (SmmSize | MTRR_CACHE_WRITE_BACK);
257 Arguments.Mask = (~(SmmSize - 1) & EFI_MSR_SMRR_MASK);
258
259 //
260 // Set SMRR valid bit
261 //
262 Arguments.Mask |= BIT11;
263
264 //
265 // Program smrr base and mask on BSP first and then on APs
266 //
267 SetSmrr (&Arguments);
268 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
269 if (Index != gSmst->CurrentlyExecutingCpu) {
270 Status = gSmst->SmmStartupThisAp (SetSmrr, Index, (VOID *)&Arguments);
271 if (EFI_ERROR (Status)) {
272 DEBUG ((DEBUG_ERROR, "Programming SMRR on AP# %d status: %r\n", Index, Status));
273 }
274 }
275 }
276 }
277
278 /**
279 Software SMI callback for restoring SMRR base and mask in S3 path.
280
281 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
282 @param[in] Context Points to an optional handler context which was specified when the
283 handler was registered.
284 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
285 be conveyed from a non-SMM environment into an SMM environment.
286 @param[in, out] CommBufferSize The size of the CommBuffer.
287
288 @retval EFI_SUCCESS The interrupt was handled successfully.
289
290 **/
291 EFI_STATUS
292 EFIAPI
293 BlSwSmiHandler (
294 IN EFI_HANDLE DispatchHandle,
295 IN CONST VOID *Context,
296 IN OUT VOID *CommBuffer,
297 IN OUT UINTN *CommBufferSize
298 )
299 {
300 SetSmrrOnS3 ();
301 SmmFeatureLockOnS3 ();
302 LockSmiGlobalEn ();
303
304 return EFI_SUCCESS;
305 }
306
307 /**
308 Lock SMI in this SMM ready to lock event.
309
310 @param Protocol Points to the protocol's unique identifier
311 @param Interface Points to the interface instance
312 @param Handle The handle on which the interface was installed
313
314 @retval EFI_SUCCESS SmmEventCallback runs successfully
315 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
316 **/
317 EFI_STATUS
318 EFIAPI
319 BlSupportSmmReadyToLockCallback (
320 IN CONST EFI_GUID *Protocol,
321 IN VOID *Interface,
322 IN EFI_HANDLE Handle
323 )
324 {
325 //
326 // Set SMM SMI lock
327 //
328 LockSmiGlobalEn ();
329 return EFI_SUCCESS;
330 }
331
332 /**
333 The driver's entry point.
334
335 @param[in] ImageHandle The firmware allocated handle for the EFI image.
336 @param[in] SystemTable A pointer to the EFI System Table.
337
338 @retval EFI_SUCCESS The entry point is executed successfully.
339 @retval Others Some error occurs when executing this entry point.
340
341 **/
342 EFI_STATUS
343 EFIAPI
344 BlSupportSmm (
345 IN EFI_HANDLE ImageHandle,
346 IN EFI_SYSTEM_TABLE *SystemTable
347 )
348 {
349 EFI_STATUS Status;
350 EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
351 EFI_SMM_SW_REGISTER_CONTEXT SwContext;
352 EFI_HANDLE SwHandle;
353 EFI_HOB_GUID_TYPE *GuidHob;
354 VOID *SmmHob;
355 VOID *Registration;
356
357 //
358 // Get SMM S3 communication hob and save it
359 //
360 GuidHob = GetFirstGuidHob (&gS3CommunicationGuid);
361 if (GuidHob != NULL) {
362 SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
363 CopyMem (&mPldS3Hob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
364 } else {
365 return EFI_NOT_FOUND;
366 }
367
368 if (mPldS3Hob.PldAcpiS3Enable) {
369 // Other drivers will take care of S3.
370 return EFI_SUCCESS;
371 }
372
373 //
374 // Get smram hob and save it
375 //
376 GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);
377 if (GuidHob != NULL) {
378 SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
379 mSmramHob = AllocatePool (GET_GUID_HOB_DATA_SIZE (GuidHob));
380 if (mSmramHob == NULL) {
381 return EFI_OUT_OF_RESOURCES;
382 }
383
384 CopyMem (mSmramHob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
385 } else {
386 return EFI_NOT_FOUND;
387 }
388
389 //
390 // Get SMM register hob and save it
391 //
392 GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
393 if (GuidHob != NULL) {
394 SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
395 mSmmRegisterHob = AllocatePool (GET_GUID_HOB_DATA_SIZE (GuidHob));
396 if (mSmmRegisterHob == NULL) {
397 return EFI_OUT_OF_RESOURCES;
398 }
399
400 CopyMem (mSmmRegisterHob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
401 } else {
402 return EFI_NOT_FOUND;
403 }
404
405 //
406 // Get the Sw dispatch protocol and register SMI handler.
407 //
408 Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID **)&SwDispatch);
409 if (EFI_ERROR (Status)) {
410 return Status;
411 }
412
413 SwContext.SwSmiInputValue = (UINTN)-1;
414 Status = SwDispatch->Register (SwDispatch, BlSwSmiHandler, &SwContext, &SwHandle);
415 if (EFI_ERROR (Status)) {
416 DEBUG ((DEBUG_ERROR, "Registering S3 smi handler failed: %r\n", Status));
417 return Status;
418 }
419
420 //
421 // Register SMM ready to lock callback
422 //
423 Status = gSmst->SmmRegisterProtocolNotify (
424 &gEfiSmmReadyToLockProtocolGuid,
425 BlSupportSmmReadyToLockCallback,
426 &Registration
427 );
428 ASSERT_EFI_ERROR (Status);
429
430 SaveSmmInfoForS3 ((UINT8)SwContext.SwSmiInputValue);
431
432 return EFI_SUCCESS;
433 }