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 | EFIAPI
|
---|
195 | SetSmrr (
|
---|
196 | IN VOID *ProcedureArgument
|
---|
197 | )
|
---|
198 | {
|
---|
199 | if (ProcedureArgument != NULL) {
|
---|
200 | AsmWriteMsr64 (MSR_IA32_SMRR_PHYSBASE, ((SMRR_BASE_MASK *)ProcedureArgument)->Base);
|
---|
201 | AsmWriteMsr64 (MSR_IA32_SMRR_PHYSMASK, ((SMRR_BASE_MASK *)ProcedureArgument)->Mask);
|
---|
202 | }
|
---|
203 | }
|
---|
204 |
|
---|
205 | /**
|
---|
206 | Set SMRR in S3 path.
|
---|
207 |
|
---|
208 | **/
|
---|
209 | VOID
|
---|
210 | SetSmrrOnS3 (
|
---|
211 | VOID
|
---|
212 | )
|
---|
213 | {
|
---|
214 | EFI_STATUS Status;
|
---|
215 | SMRR_BASE_MASK Arguments;
|
---|
216 | UINTN Index;
|
---|
217 | UINT32 SmmBase;
|
---|
218 | UINT32 SmmSize;
|
---|
219 |
|
---|
220 | if ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSBASE) != 0) && ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSMASK) & BIT11) != 0)) {
|
---|
221 | return;
|
---|
222 | }
|
---|
223 |
|
---|
224 | SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalStart;
|
---|
225 | SmmSize = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalSize;
|
---|
226 | if ((mSmramHob->NumberOfSmmReservedRegions > 2) || (mSmramHob->NumberOfSmmReservedRegions == 0)) {
|
---|
227 | DEBUG ((DEBUG_ERROR, "%d SMM ranges are not supported.\n", mSmramHob->NumberOfSmmReservedRegions));
|
---|
228 | return;
|
---|
229 | } else if (mSmramHob->NumberOfSmmReservedRegions == 2) {
|
---|
230 | if ((mSmramHob->Descriptor[1].PhysicalStart + mSmramHob->Descriptor[1].PhysicalSize) == SmmBase) {
|
---|
231 | SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalStart;
|
---|
232 | } else if (mSmramHob->Descriptor[1].PhysicalStart != (SmmBase + SmmSize)) {
|
---|
233 | DEBUG ((DEBUG_ERROR, "Two SMM regions are not continous.\n"));
|
---|
234 | return;
|
---|
235 | }
|
---|
236 |
|
---|
237 | SmmSize += (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalSize;
|
---|
238 | }
|
---|
239 |
|
---|
240 | if ((SmmBase == 0) || (SmmSize < SIZE_4KB)) {
|
---|
241 | DEBUG ((DEBUG_ERROR, "Invalid SMM range.\n"));
|
---|
242 | return;
|
---|
243 | }
|
---|
244 |
|
---|
245 | //
|
---|
246 | // SMRR size must be of length 2^n
|
---|
247 | // SMRR base alignment cannot be less than SMRR length
|
---|
248 | //
|
---|
249 | if ((SmmSize != GetPowerOfTwo32 (SmmSize)) || ((SmmBase & ~(SmmSize - 1)) != SmmBase)) {
|
---|
250 | DEBUG ((DEBUG_ERROR, " Invalid SMM range.\n"));
|
---|
251 | return;
|
---|
252 | }
|
---|
253 |
|
---|
254 | //
|
---|
255 | // Calculate smrr base, mask and pass them as arguments.
|
---|
256 | //
|
---|
257 | Arguments.Base = (SmmSize | MTRR_CACHE_WRITE_BACK);
|
---|
258 | Arguments.Mask = (~(SmmSize - 1) & EFI_MSR_SMRR_MASK);
|
---|
259 |
|
---|
260 | //
|
---|
261 | // Set SMRR valid bit
|
---|
262 | //
|
---|
263 | Arguments.Mask |= BIT11;
|
---|
264 |
|
---|
265 | //
|
---|
266 | // Program smrr base and mask on BSP first and then on APs
|
---|
267 | //
|
---|
268 | SetSmrr (&Arguments);
|
---|
269 | for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
|
---|
270 | if (Index != gSmst->CurrentlyExecutingCpu) {
|
---|
271 | Status = gSmst->SmmStartupThisAp (SetSmrr, Index, (VOID *)&Arguments);
|
---|
272 | if (EFI_ERROR (Status)) {
|
---|
273 | DEBUG ((DEBUG_ERROR, "Programming SMRR on AP# %d status: %r\n", Index, Status));
|
---|
274 | }
|
---|
275 | }
|
---|
276 | }
|
---|
277 | }
|
---|
278 |
|
---|
279 | /**
|
---|
280 | Software SMI callback for restoring SMRR base and mask in S3 path.
|
---|
281 |
|
---|
282 | @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
|
---|
283 | @param[in] Context Points to an optional handler context which was specified when the
|
---|
284 | handler was registered.
|
---|
285 | @param[in, out] CommBuffer A pointer to a collection of data in memory that will
|
---|
286 | be conveyed from a non-SMM environment into an SMM environment.
|
---|
287 | @param[in, out] CommBufferSize The size of the CommBuffer.
|
---|
288 |
|
---|
289 | @retval EFI_SUCCESS The interrupt was handled successfully.
|
---|
290 |
|
---|
291 | **/
|
---|
292 | EFI_STATUS
|
---|
293 | EFIAPI
|
---|
294 | BlSwSmiHandler (
|
---|
295 | IN EFI_HANDLE DispatchHandle,
|
---|
296 | IN CONST VOID *Context,
|
---|
297 | IN OUT VOID *CommBuffer,
|
---|
298 | IN OUT UINTN *CommBufferSize
|
---|
299 | )
|
---|
300 | {
|
---|
301 | SetSmrrOnS3 ();
|
---|
302 | SmmFeatureLockOnS3 ();
|
---|
303 | LockSmiGlobalEn ();
|
---|
304 |
|
---|
305 | return EFI_SUCCESS;
|
---|
306 | }
|
---|
307 |
|
---|
308 | /**
|
---|
309 | Lock SMI in this SMM ready to lock event.
|
---|
310 |
|
---|
311 | @param Protocol Points to the protocol's unique identifier
|
---|
312 | @param Interface Points to the interface instance
|
---|
313 | @param Handle The handle on which the interface was installed
|
---|
314 |
|
---|
315 | @retval EFI_SUCCESS SmmEventCallback runs successfully
|
---|
316 | @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
|
---|
317 | **/
|
---|
318 | EFI_STATUS
|
---|
319 | EFIAPI
|
---|
320 | BlSupportSmmReadyToLockCallback (
|
---|
321 | IN CONST EFI_GUID *Protocol,
|
---|
322 | IN VOID *Interface,
|
---|
323 | IN EFI_HANDLE Handle
|
---|
324 | )
|
---|
325 | {
|
---|
326 | //
|
---|
327 | // Set SMM SMI lock
|
---|
328 | //
|
---|
329 | LockSmiGlobalEn ();
|
---|
330 | return EFI_SUCCESS;
|
---|
331 | }
|
---|
332 |
|
---|
333 | /**
|
---|
334 | The driver's entry point.
|
---|
335 |
|
---|
336 | @param[in] ImageHandle The firmware allocated handle for the EFI image.
|
---|
337 | @param[in] SystemTable A pointer to the EFI System Table.
|
---|
338 |
|
---|
339 | @retval EFI_SUCCESS The entry point is executed successfully.
|
---|
340 | @retval Others Some error occurs when executing this entry point.
|
---|
341 |
|
---|
342 | **/
|
---|
343 | EFI_STATUS
|
---|
344 | EFIAPI
|
---|
345 | BlSupportSmm (
|
---|
346 | IN EFI_HANDLE ImageHandle,
|
---|
347 | IN EFI_SYSTEM_TABLE *SystemTable
|
---|
348 | )
|
---|
349 | {
|
---|
350 | EFI_STATUS Status;
|
---|
351 | EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
|
---|
352 | EFI_SMM_SW_REGISTER_CONTEXT SwContext;
|
---|
353 | EFI_HANDLE SwHandle;
|
---|
354 | EFI_HOB_GUID_TYPE *GuidHob;
|
---|
355 | VOID *SmmHob;
|
---|
356 | VOID *Registration;
|
---|
357 |
|
---|
358 | //
|
---|
359 | // Get SMM S3 communication hob and save it
|
---|
360 | //
|
---|
361 | GuidHob = GetFirstGuidHob (&gS3CommunicationGuid);
|
---|
362 | if (GuidHob != NULL) {
|
---|
363 | SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
|
---|
364 | CopyMem (&mPldS3Hob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
|
---|
365 | } else {
|
---|
366 | return EFI_NOT_FOUND;
|
---|
367 | }
|
---|
368 |
|
---|
369 | if (mPldS3Hob.PldAcpiS3Enable) {
|
---|
370 | // Other drivers will take care of S3.
|
---|
371 | return EFI_SUCCESS;
|
---|
372 | }
|
---|
373 |
|
---|
374 | //
|
---|
375 | // Get smram hob and save it
|
---|
376 | //
|
---|
377 | GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);
|
---|
378 | if (GuidHob != NULL) {
|
---|
379 | SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
|
---|
380 | mSmramHob = AllocatePool (GET_GUID_HOB_DATA_SIZE (GuidHob));
|
---|
381 | if (mSmramHob == NULL) {
|
---|
382 | return EFI_OUT_OF_RESOURCES;
|
---|
383 | }
|
---|
384 |
|
---|
385 | CopyMem (mSmramHob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
|
---|
386 | } else {
|
---|
387 | return EFI_NOT_FOUND;
|
---|
388 | }
|
---|
389 |
|
---|
390 | //
|
---|
391 | // Get SMM register hob and save it
|
---|
392 | //
|
---|
393 | GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
|
---|
394 | if (GuidHob != NULL) {
|
---|
395 | SmmHob = (VOID *)(GET_GUID_HOB_DATA (GuidHob));
|
---|
396 | mSmmRegisterHob = AllocatePool (GET_GUID_HOB_DATA_SIZE (GuidHob));
|
---|
397 | if (mSmmRegisterHob == NULL) {
|
---|
398 | return EFI_OUT_OF_RESOURCES;
|
---|
399 | }
|
---|
400 |
|
---|
401 | CopyMem (mSmmRegisterHob, SmmHob, GET_GUID_HOB_DATA_SIZE (GuidHob));
|
---|
402 | } else {
|
---|
403 | return EFI_NOT_FOUND;
|
---|
404 | }
|
---|
405 |
|
---|
406 | //
|
---|
407 | // Get the Sw dispatch protocol and register SMI handler.
|
---|
408 | //
|
---|
409 | Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID **)&SwDispatch);
|
---|
410 | if (EFI_ERROR (Status)) {
|
---|
411 | return Status;
|
---|
412 | }
|
---|
413 |
|
---|
414 | SwContext.SwSmiInputValue = (UINTN)-1;
|
---|
415 | Status = SwDispatch->Register (SwDispatch, BlSwSmiHandler, &SwContext, &SwHandle);
|
---|
416 | if (EFI_ERROR (Status)) {
|
---|
417 | DEBUG ((DEBUG_ERROR, "Registering S3 smi handler failed: %r\n", Status));
|
---|
418 | return Status;
|
---|
419 | }
|
---|
420 |
|
---|
421 | //
|
---|
422 | // Register SMM ready to lock callback
|
---|
423 | //
|
---|
424 | Status = gSmst->SmmRegisterProtocolNotify (
|
---|
425 | &gEfiSmmReadyToLockProtocolGuid,
|
---|
426 | BlSupportSmmReadyToLockCallback,
|
---|
427 | &Registration
|
---|
428 | );
|
---|
429 | ASSERT_EFI_ERROR (Status);
|
---|
430 |
|
---|
431 | SaveSmmInfoForS3 ((UINT8)SwContext.SwSmiInputValue);
|
---|
432 |
|
---|
433 | return EFI_SUCCESS;
|
---|
434 | }
|
---|