]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c
when SMM Communication Protocol.Communicate() is called from within SMM, SMRAM is...
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / PiSmmIpl.c
1 /** @file
2 SMM IPL that produces SMM related runtime protocols and load the SMM Core into SMRAM
3
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available
6 under the terms and conditions of the BSD License which accompanies this
7 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 <PiDxe.h>
16
17 #include <Protocol/SmmBase2.h>
18 #include <Protocol/SmmCommunication.h>
19 #include <Protocol/SmmAccess2.h>
20 #include <Protocol/SmmConfiguration.h>
21 #include <Protocol/SmmControl2.h>
22 #include <Protocol/DxeSmmReadyToLock.h>
23 #include <Protocol/Cpu.h>
24
25 #include <Guid/EventGroup.h>
26 #include <Guid/EventLegacyBios.h>
27 #include <Guid/LoadModuleAtFixedAddress.h>
28
29 #include <Library/BaseLib.h>
30 #include <Library/BaseMemoryLib.h>
31 #include <Library/PeCoffLib.h>
32 #include <Library/CacheMaintenanceLib.h>
33 #include <Library/MemoryAllocationLib.h>
34 #include <Library/DebugLib.h>
35 #include <Library/UefiBootServicesTableLib.h>
36 #include <Library/DxeServicesTableLib.h>
37 #include <Library/DxeServicesLib.h>
38 #include <Library/UefiLib.h>
39 #include <Library/UefiRuntimeLib.h>
40 #include <Library/PcdLib.h>
41
42 #include "PiSmmCorePrivateData.h"
43
44 //
45 // Function prototypes from produced protocols
46 //
47
48 /**
49 Indicate whether the driver is currently executing in the SMM Initialization phase.
50
51 @param This The EFI_SMM_BASE2_PROTOCOL instance.
52 @param InSmram Pointer to a Boolean which, on return, indicates that the driver is currently executing
53 inside of SMRAM (TRUE) or outside of SMRAM (FALSE).
54
55 @retval EFI_INVALID_PARAMETER InSmram was NULL.
56 @retval EFI_SUCCESS The call returned successfully.
57
58 **/
59 EFI_STATUS
60 EFIAPI
61 SmmBase2InSmram (
62 IN CONST EFI_SMM_BASE2_PROTOCOL *This,
63 OUT BOOLEAN *InSmram
64 );
65
66 /**
67 Retrieves the location of the System Management System Table (SMST).
68
69 @param This The EFI_SMM_BASE2_PROTOCOL instance.
70 @param Smst On return, points to a pointer to the System Management Service Table (SMST).
71
72 @retval EFI_INVALID_PARAMETER Smst or This was invalid.
73 @retval EFI_SUCCESS The memory was returned to the system.
74 @retval EFI_UNSUPPORTED Not in SMM.
75
76 **/
77 EFI_STATUS
78 EFIAPI
79 SmmBase2GetSmstLocation (
80 IN CONST EFI_SMM_BASE2_PROTOCOL *This,
81 OUT EFI_SMM_SYSTEM_TABLE2 **Smst
82 );
83
84 /**
85 Communicates with a registered handler.
86
87 This function provides a service to send and receive messages from a registered
88 UEFI service. This function is part of the SMM Communication Protocol that may
89 be called in physical mode prior to SetVirtualAddressMap() and in virtual mode
90 after SetVirtualAddressMap().
91
92 @param[in] This The EFI_SMM_COMMUNICATION_PROTOCOL instance.
93 @param[in, out] CommBuffer A pointer to the buffer to convey into SMRAM.
94 @param[in, out] CommSize The size of the data buffer being passed in.On exit, the size of data
95 being returned. Zero if the handler does not wish to reply with any data.
96
97 @retval EFI_SUCCESS The message was successfully posted.
98 @retval EFI_INVALID_PARAMETER The CommBuffer was NULL.
99 **/
100 EFI_STATUS
101 EFIAPI
102 SmmCommunicationCommunicate (
103 IN CONST EFI_SMM_COMMUNICATION_PROTOCOL *This,
104 IN OUT VOID *CommBuffer,
105 IN OUT UINTN *CommSize
106 );
107
108 /**
109 Event notification that is fired every time a gEfiSmmConfigurationProtocol installs.
110
111 @param Event The Event that is being processed, not used.
112 @param Context Event Context, not used.
113
114 **/
115 VOID
116 EFIAPI
117 SmmIplSmmConfigurationEventNotify (
118 IN EFI_EVENT Event,
119 IN VOID *Context
120 );
121
122 /**
123 Event notification that is fired every time a DxeSmmReadyToLock protocol is added
124 or if gEfiEventReadyToBootGuid is signalled.
125
126 @param Event The Event that is being processed, not used.
127 @param Context Event Context, not used.
128
129 **/
130 VOID
131 EFIAPI
132 SmmIplReadyToLockEventNotify (
133 IN EFI_EVENT Event,
134 IN VOID *Context
135 );
136
137 /**
138 Event notification that is fired when DxeDispatch Event Group is signaled.
139
140 @param Event The Event that is being processed, not used.
141 @param Context Event Context, not used.
142
143 **/
144 VOID
145 EFIAPI
146 SmmIplGuidedEventNotify (
147 IN EFI_EVENT Event,
148 IN VOID *Context
149 );
150
151 /**
152 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
153
154 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
155 It convers pointer to new virtual address.
156
157 @param Event Event whose notification function is being invoked.
158 @param Context Pointer to the notification function's context.
159
160 **/
161 VOID
162 EFIAPI
163 SmmIplSetVirtualAddressNotify (
164 IN EFI_EVENT Event,
165 IN VOID *Context
166 );
167
168 //
169 // Data structure used to declare a table of protocol notifications and event
170 // notifications required by the SMM IPL
171 //
172 typedef struct {
173 BOOLEAN Protocol;
174 BOOLEAN CloseOnLock;
175 EFI_GUID *Guid;
176 EFI_EVENT_NOTIFY NotifyFunction;
177 VOID *NotifyContext;
178 EFI_EVENT Event;
179 } SMM_IPL_EVENT_NOTIFICATION;
180
181 //
182 // Handle to install the SMM Base2 Protocol and the SMM Communication Protocol
183 //
184 EFI_HANDLE mSmmIplHandle = NULL;
185
186 //
187 // SMM Base 2 Protocol instance
188 //
189 EFI_SMM_BASE2_PROTOCOL mSmmBase2 = {
190 SmmBase2InSmram,
191 SmmBase2GetSmstLocation
192 };
193
194 //
195 // SMM Communication Protocol instance
196 //
197 EFI_SMM_COMMUNICATION_PROTOCOL mSmmCommunication = {
198 SmmCommunicationCommunicate
199 };
200
201 //
202 // SMM Core Private Data structure that contains the data shared between
203 // the SMM IPL and the SMM Core.
204 //
205 SMM_CORE_PRIVATE_DATA mSmmCorePrivateData = {
206 SMM_CORE_PRIVATE_DATA_SIGNATURE, // Signature
207 NULL, // SmmIplImageHandle
208 0, // SmramRangeCount
209 NULL, // SmramRanges
210 NULL, // SmmEntryPoint
211 FALSE, // SmmEntryPointRegistered
212 FALSE, // InSmm
213 NULL, // Smst
214 NULL, // CommunicationBuffer
215 0, // BufferSize
216 EFI_SUCCESS // ReturnStatus
217 };
218
219 //
220 // Global pointer used to access mSmmCorePrivateData from outside and inside SMM
221 //
222 SMM_CORE_PRIVATE_DATA *gSmmCorePrivate = &mSmmCorePrivateData;
223
224 //
225 // SMM IPL global variables
226 //
227 EFI_SMM_CONTROL2_PROTOCOL *mSmmControl2;
228 EFI_SMM_ACCESS2_PROTOCOL *mSmmAccess;
229 EFI_SMRAM_DESCRIPTOR *mCurrentSmramRange;
230 BOOLEAN mSmmLocked = FALSE;
231 EFI_PHYSICAL_ADDRESS mSmramCacheBase;
232 UINT64 mSmramCacheSize;
233
234 //
235 // Table of Protocol notification and GUIDed Event notifications that the SMM IPL requires
236 //
237 SMM_IPL_EVENT_NOTIFICATION mSmmIplEvents[] = {
238 //
239 // Declare protocol notification on the SMM Configuration protocol. When this notification is etablished,
240 // the associated event is immediately signalled, so the notification function will be executed and the
241 // SMM Configuration Protocol will be found if it is already in the handle database.
242 //
243 { TRUE, FALSE, &gEfiSmmConfigurationProtocolGuid, SmmIplSmmConfigurationEventNotify, &gEfiSmmConfigurationProtocolGuid, NULL },
244 //
245 // Declare protocl notification on DxeSmmReadyToLock protocols. When this notification is etablished,
246 // the associated event is immediately signalled, so the notification function will be executed and the
247 // DXE SMM Ready To Lock Protocol will be found if it is already in the handle database.
248 //
249 { TRUE, TRUE, &gEfiDxeSmmReadyToLockProtocolGuid, SmmIplReadyToLockEventNotify, &gEfiDxeSmmReadyToLockProtocolGuid, NULL },
250 //
251 // Declare event notification on the DXE Dispatch Event Group. This event is signaled by the DXE Core
252 // each time the DXE Core dispatcher has completed its work. When this event is signalled, the SMM Core
253 // if notified, so the SMM Core can dispatch SMM drivers.
254 //
255 { FALSE, TRUE, &gEfiEventDxeDispatchGuid, SmmIplGuidedEventNotify, &gEfiEventDxeDispatchGuid, NULL },
256 //
257 // Declare event notification on Ready To Boot Event Group. This is an extra event notification that is
258 // used to make sure SMRAM is locked before any boot options are processed.
259 //
260 { FALSE, TRUE, &gEfiEventReadyToBootGuid, SmmIplReadyToLockEventNotify, &gEfiEventReadyToBootGuid, NULL },
261 //
262 // Declare event notification on Legacy Boot Event Group. This is used to inform the SMM Core that the platform
263 // is performing a legacy boot operation, and that the UEFI environment is no longer available and the SMM Core
264 // must guarantee that it does not access any UEFI related structures outside of SMRAM.
265 //
266 { FALSE, FALSE, &gEfiEventLegacyBootGuid, SmmIplGuidedEventNotify, &gEfiEventLegacyBootGuid, NULL },
267 //
268 // Declare event notification on SetVirtualAddressMap() Event Group. This is used to convert gSmmCorePrivate
269 // and mSmmControl2 from physical addresses to virtual addresses.
270 //
271 { FALSE, FALSE, &gEfiEventVirtualAddressChangeGuid, SmmIplSetVirtualAddressNotify, NULL, NULL },
272 //
273 // Terminate the table of event notifications
274 //
275 { FALSE, FALSE, NULL, NULL, NULL, NULL }
276 };
277
278 /**
279 Find the maximum SMRAM cache range that covers the range specified by SmramRange.
280
281 This function searches and joins all adjacent ranges of SmramRange into a range to be cached.
282
283 @param SmramRange The SMRAM range to search from.
284 @param SmramCacheBase The returned cache range base.
285 @param SmramCacheSize The returned cache range size.
286
287 **/
288 VOID
289 GetSmramCacheRange (
290 IN EFI_SMRAM_DESCRIPTOR *SmramRange,
291 OUT EFI_PHYSICAL_ADDRESS *SmramCacheBase,
292 OUT UINT64 *SmramCacheSize
293 )
294 {
295 UINTN Index;
296 EFI_PHYSICAL_ADDRESS RangeCpuStart;
297 UINT64 RangePhysicalSize;
298 BOOLEAN FoundAjacentRange;
299
300 *SmramCacheBase = SmramRange->CpuStart;
301 *SmramCacheSize = SmramRange->PhysicalSize;
302
303 do {
304 FoundAjacentRange = FALSE;
305 for (Index = 0; Index < gSmmCorePrivate->SmramRangeCount; Index++) {
306 RangeCpuStart = gSmmCorePrivate->SmramRanges[Index].CpuStart;
307 RangePhysicalSize = gSmmCorePrivate->SmramRanges[Index].PhysicalSize;
308 if (RangeCpuStart < *SmramCacheBase && *SmramCacheBase == (RangeCpuStart + RangePhysicalSize)) {
309 *SmramCacheBase = RangeCpuStart;
310 *SmramCacheSize += RangePhysicalSize;
311 FoundAjacentRange = TRUE;
312 } else if ((*SmramCacheBase + *SmramCacheSize) == RangeCpuStart && RangePhysicalSize > 0) {
313 *SmramCacheSize += RangePhysicalSize;
314 FoundAjacentRange = TRUE;
315 }
316 }
317 } while (FoundAjacentRange);
318
319 }
320
321 /**
322 Indicate whether the driver is currently executing in the SMM Initialization phase.
323
324 @param This The EFI_SMM_BASE2_PROTOCOL instance.
325 @param InSmram Pointer to a Boolean which, on return, indicates that the driver is currently executing
326 inside of SMRAM (TRUE) or outside of SMRAM (FALSE).
327
328 @retval EFI_INVALID_PARAMETER InSmram was NULL.
329 @retval EFI_SUCCESS The call returned successfully.
330
331 **/
332 EFI_STATUS
333 EFIAPI
334 SmmBase2InSmram (
335 IN CONST EFI_SMM_BASE2_PROTOCOL *This,
336 OUT BOOLEAN *InSmram
337 )
338 {
339 if (InSmram == NULL) {
340 return EFI_INVALID_PARAMETER;
341 }
342
343 *InSmram = gSmmCorePrivate->InSmm;
344
345 return EFI_SUCCESS;
346 }
347
348 /**
349 Retrieves the location of the System Management System Table (SMST).
350
351 @param This The EFI_SMM_BASE2_PROTOCOL instance.
352 @param Smst On return, points to a pointer to the System Management Service Table (SMST).
353
354 @retval EFI_INVALID_PARAMETER Smst or This was invalid.
355 @retval EFI_SUCCESS The memory was returned to the system.
356 @retval EFI_UNSUPPORTED Not in SMM.
357
358 **/
359 EFI_STATUS
360 EFIAPI
361 SmmBase2GetSmstLocation (
362 IN CONST EFI_SMM_BASE2_PROTOCOL *This,
363 OUT EFI_SMM_SYSTEM_TABLE2 **Smst
364 )
365 {
366 if ((This == NULL) ||(Smst == NULL)) {
367 return EFI_INVALID_PARAMETER;
368 }
369
370 if (!gSmmCorePrivate->InSmm) {
371 return EFI_UNSUPPORTED;
372 }
373
374 *Smst = gSmmCorePrivate->Smst;
375
376 return EFI_SUCCESS;
377 }
378
379 /**
380 Communicates with a registered handler.
381
382 This function provides a service to send and receive messages from a registered
383 UEFI service. This function is part of the SMM Communication Protocol that may
384 be called in physical mode prior to SetVirtualAddressMap() and in virtual mode
385 after SetVirtualAddressMap().
386
387 @param[in] This The EFI_SMM_COMMUNICATION_PROTOCOL instance.
388 @param[in, out] CommBuffer A pointer to the buffer to convey into SMRAM.
389 @param[in, out] CommSize The size of the data buffer being passed in.On exit, the size of data
390 being returned. Zero if the handler does not wish to reply with any data.
391
392 @retval EFI_SUCCESS The message was successfully posted.
393 @retval EFI_INVALID_PARAMETER The CommBuffer was NULL.
394 **/
395 EFI_STATUS
396 EFIAPI
397 SmmCommunicationCommunicate (
398 IN CONST EFI_SMM_COMMUNICATION_PROTOCOL *This,
399 IN OUT VOID *CommBuffer,
400 IN OUT UINTN *CommSize
401 )
402 {
403 EFI_STATUS Status;
404 EFI_SMM_COMMUNICATE_HEADER *CommunicateHeader;
405 BOOLEAN OldInSmm;
406
407 //
408 // Check parameters
409 //
410 if ((CommBuffer == NULL) || (CommSize == NULL)) {
411 return EFI_INVALID_PARAMETER;
412 }
413
414 //
415 // CommSize must hold HeaderGuid and MessageLength
416 //
417 if (*CommSize < OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)) {
418 return EFI_INVALID_PARAMETER;
419 }
420
421 //
422 // If not already in SMM, then generate a Software SMI
423 //
424 if (!gSmmCorePrivate->InSmm && gSmmCorePrivate->SmmEntryPointRegistered) {
425 //
426 // Put arguments for Software SMI in gSmmCorePrivate
427 //
428 gSmmCorePrivate->CommunicationBuffer = CommBuffer;
429 gSmmCorePrivate->BufferSize = *CommSize;
430
431 //
432 // Generate Software SMI
433 //
434 Status = mSmmControl2->Trigger (mSmmControl2, NULL, NULL, FALSE, 0);
435 if (EFI_ERROR (Status)) {
436 return EFI_UNSUPPORTED;
437 }
438
439 //
440 // Return status from software SMI
441 //
442 *CommSize = gSmmCorePrivate->BufferSize;
443 return gSmmCorePrivate->ReturnStatus;
444 }
445
446 //
447 // If we are in SMM, then the execution mode must be physical, which means that
448 // OS established virtual addresses can not be used. If SetVirtualAddressMap()
449 // has been called, then a direct invocation of the Software SMI is not
450 // not allowed so return EFI_INVALID_PARAMETER.
451 //
452 if (EfiGoneVirtual()) {
453 return EFI_INVALID_PARAMETER;
454 }
455
456 //
457 // If we are not in SMM, don't allow call SmiManage() directly when SMRAM is closed or locked.
458 //
459 if ((!gSmmCorePrivate->InSmm) && (!mSmmAccess->OpenState || mSmmAccess->LockState)) {
460 return EFI_INVALID_PARAMETER;
461 }
462
463 //
464 // Save current InSmm state and set InSmm state to TRUE
465 //
466 OldInSmm = gSmmCorePrivate->InSmm;
467 gSmmCorePrivate->InSmm = TRUE;
468
469 //
470 // Already in SMM and before SetVirtualAddressMap(), so call SmiManage() directly.
471 //
472 CommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *)CommBuffer;
473 *CommSize -= OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data);
474 Status = gSmmCorePrivate->Smst->SmiManage (
475 &CommunicateHeader->HeaderGuid,
476 NULL,
477 CommunicateHeader->Data,
478 CommSize
479 );
480
481 //
482 // Update CommunicationBuffer, BufferSize and ReturnStatus
483 // Communicate service finished, reset the pointer to CommBuffer to NULL
484 //
485 *CommSize += OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data);
486
487 //
488 // Restore original InSmm state
489 //
490 gSmmCorePrivate->InSmm = OldInSmm;
491
492 return (Status == EFI_WARN_INTERRUPT_SOURCE_QUIESCED) ? EFI_SUCCESS : EFI_NOT_FOUND;
493 }
494
495 /**
496 Event notification that is fired when DxeDispatch Event Group is signaled.
497
498 @param Event The Event that is being processed, not used.
499 @param Context Event Context, not used.
500
501 **/
502 VOID
503 EFIAPI
504 SmmIplGuidedEventNotify (
505 IN EFI_EVENT Event,
506 IN VOID *Context
507 )
508 {
509 EFI_SMM_COMMUNICATE_HEADER CommunicateHeader;
510 UINTN Size;
511
512 //
513 // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure
514 //
515 CopyGuid (&CommunicateHeader.HeaderGuid, (EFI_GUID *)Context);
516 CommunicateHeader.MessageLength = 1;
517 CommunicateHeader.Data[0] = 0;
518
519 //
520 // Generate the Software SMI and return the result
521 //
522 Size = sizeof (CommunicateHeader);
523 SmmCommunicationCommunicate (&mSmmCommunication, &CommunicateHeader, &Size);
524 }
525
526 /**
527 Event notification that is fired every time a gEfiSmmConfigurationProtocol installs.
528
529 @param Event The Event that is being processed, not used.
530 @param Context Event Context, not used.
531
532 **/
533 VOID
534 EFIAPI
535 SmmIplSmmConfigurationEventNotify (
536 IN EFI_EVENT Event,
537 IN VOID *Context
538 )
539 {
540 EFI_STATUS Status;
541 EFI_SMM_CONFIGURATION_PROTOCOL *SmmConfiguration;
542
543 //
544 // Make sure this notification is for this handler
545 //
546 Status = gBS->LocateProtocol (Context, NULL, (VOID **)&SmmConfiguration);
547 if (EFI_ERROR (Status)) {
548 return;
549 }
550
551 //
552 // Register the SMM Entry Point provided by the SMM Core with the SMM COnfiguration protocol
553 //
554 Status = SmmConfiguration->RegisterSmmEntry (SmmConfiguration, gSmmCorePrivate->SmmEntryPoint);
555 ASSERT_EFI_ERROR (Status);
556
557 //
558 // Set flag to indicate that the SM< Entry Point has been registered which
559 // means that SMIs are now fully operational.
560 //
561 gSmmCorePrivate->SmmEntryPointRegistered = TRUE;
562
563 //
564 // Print debug message showing SMM Core entry point address.
565 //
566 DEBUG ((DEBUG_INFO, "SMM IPL registered SMM Entry Point address %p\n", (VOID *)(UINTN)gSmmCorePrivate->SmmEntryPoint));
567
568 //
569 // Attempt to reset SMRAM cacheability to UC
570 // Assume CPU AP is available at this time
571 //
572 Status = gDS->SetMemorySpaceAttributes(
573 mSmramCacheBase,
574 mSmramCacheSize,
575 EFI_MEMORY_UC
576 );
577 if (EFI_ERROR (Status)) {
578 DEBUG ((DEBUG_WARN, "SMM IPL failed to reset SMRAM window to EFI_MEMORY_UC\n"));
579 }
580
581 //
582 // Close all SMRAM ranges to protect SMRAM
583 //
584 Status = mSmmAccess->Close (mSmmAccess);
585 ASSERT_EFI_ERROR (Status);
586
587 //
588 // Print debug message that the SMRAM window is now closed.
589 //
590 DEBUG ((DEBUG_INFO, "SMM IPL closed SMRAM window\n"));
591 }
592
593 /**
594 Event notification that is fired every time a DxeSmmReadyToLock protocol is added
595 or if gEfiEventReadyToBootGuid is signalled.
596
597 @param Event The Event that is being processed, not used.
598 @param Context Event Context, not used.
599
600 **/
601 VOID
602 EFIAPI
603 SmmIplReadyToLockEventNotify (
604 IN EFI_EVENT Event,
605 IN VOID *Context
606 )
607 {
608 EFI_STATUS Status;
609 VOID *Interface;
610 UINTN Index;
611
612 //
613 // See if we are already locked
614 //
615 if (mSmmLocked) {
616 return;
617 }
618
619 //
620 // Make sure this notification is for this handler
621 //
622 if (CompareGuid ((EFI_GUID *)Context, &gEfiDxeSmmReadyToLockProtocolGuid)) {
623 Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface);
624 if (EFI_ERROR (Status)) {
625 return;
626 }
627 } else {
628 //
629 // If SMM is not locked yet and we got here from gEfiEventReadyToBootGuid being
630 // signalled, then gEfiDxeSmmReadyToLockProtocolGuid was not installed as expected.
631 // Print a warning on debug builds.
632 //
633 DEBUG ((DEBUG_WARN, "SMM IPL! DXE SMM Ready To Lock Protocol not installed before Ready To Boot signal\n"));
634 }
635
636 //
637 // Lock the SMRAM (Note: Locking SMRAM may not be supported on all platforms)
638 //
639 mSmmAccess->Lock (mSmmAccess);
640
641 //
642 // Close protocol and event notification events that do not apply after the
643 // DXE SMM Ready To Lock Protocol has been installed or the Ready To Boot
644 // event has been signalled.
645 //
646 for (Index = 0; mSmmIplEvents[Index].NotifyFunction != NULL; Index++) {
647 if (mSmmIplEvents[Index].CloseOnLock) {
648 gBS->CloseEvent (mSmmIplEvents[Index].Event);
649 }
650 }
651
652 //
653 // Inform SMM Core that the DxeSmmReadyToLock protocol was installed
654 //
655 SmmIplGuidedEventNotify (Event, (VOID *)&gEfiDxeSmmReadyToLockProtocolGuid);
656
657 //
658 // Print debug message that the SMRAM window is now locked.
659 //
660 DEBUG ((DEBUG_INFO, "SMM IPL locked SMRAM window\n"));
661
662 //
663 // Set flag so this operation will not be performed again
664 //
665 mSmmLocked = TRUE;
666 }
667
668 /**
669 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
670
671 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
672 It convers pointer to new virtual address.
673
674 @param Event Event whose notification function is being invoked.
675 @param Context Pointer to the notification function's context.
676
677 **/
678 VOID
679 EFIAPI
680 SmmIplSetVirtualAddressNotify (
681 IN EFI_EVENT Event,
682 IN VOID *Context
683 )
684 {
685 EfiConvertPointer (0x0, (VOID **)&mSmmControl2);
686 }
687
688 /**
689 Get the fixed loadding address from image header assigned by build tool. This function only be called
690 when Loading module at Fixed address feature enabled.
691
692 @param ImageContext Pointer to the image context structure that describes the PE/COFF
693 image that needs to be examined by this function.
694 @retval EFI_SUCCESS An fixed loading address is assigned to this image by build tools .
695 @retval EFI_NOT_FOUND The image has no assigned fixed loadding address.
696 **/
697 EFI_STATUS
698 GetPeCoffImageFixLoadingAssignedAddress(
699 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
700 )
701 {
702 UINTN SectionHeaderOffset;
703 EFI_STATUS Status;
704 EFI_IMAGE_SECTION_HEADER SectionHeader;
705 EFI_IMAGE_OPTIONAL_HEADER_UNION *ImgHdr;
706 EFI_PHYSICAL_ADDRESS FixLoaddingAddress;
707 UINT16 Index;
708 UINTN Size;
709 UINT16 NumberOfSections;
710 EFI_PHYSICAL_ADDRESS SmramBase;
711 UINT64 SmmCodeSize;
712 UINT64 ValueInSectionHeader;
713 //
714 // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber
715 //
716 SmmCodeSize = EFI_PAGES_TO_SIZE (PcdGet32(PcdLoadFixAddressSmmCodePageNumber));
717
718 FixLoaddingAddress = 0;
719 Status = EFI_NOT_FOUND;
720 SmramBase = mCurrentSmramRange->CpuStart;
721 //
722 // Get PeHeader pointer
723 //
724 ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8* )ImageContext->Handle + ImageContext->PeCoffHeaderOffset);
725 SectionHeaderOffset = (UINTN)(
726 ImageContext->PeCoffHeaderOffset +
727 sizeof (UINT32) +
728 sizeof (EFI_IMAGE_FILE_HEADER) +
729 ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader
730 );
731 NumberOfSections = ImgHdr->Pe32.FileHeader.NumberOfSections;
732
733 //
734 // Get base address from the first section header that doesn't point to code section.
735 //
736 for (Index = 0; Index < NumberOfSections; Index++) {
737 //
738 // Read section header from file
739 //
740 Size = sizeof (EFI_IMAGE_SECTION_HEADER);
741 Status = ImageContext->ImageRead (
742 ImageContext->Handle,
743 SectionHeaderOffset,
744 &Size,
745 &SectionHeader
746 );
747 if (EFI_ERROR (Status)) {
748 return Status;
749 }
750
751 Status = EFI_NOT_FOUND;
752
753 if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) {
754 //
755 // Build tool saves the offset to SMRAM base as image base in PointerToRelocations & PointerToLineNumbers fields in the
756 // first section header that doesn't point to code section in image header. And there is an assumption that when the
757 // feature is enabled, if a module is assigned a loading address by tools, PointerToRelocations & PointerToLineNumbers
758 // fields should NOT be Zero, or else, these 2 fileds should be set to Zero
759 //
760 ValueInSectionHeader = ReadUnaligned64((UINT64*)&SectionHeader.PointerToRelocations);
761 if (ValueInSectionHeader != 0) {
762 //
763 // Found first section header that doesn't point to code section in which uild tool saves the
764 // offset to SMRAM base as image base in PointerToRelocations & PointerToLineNumbers fields
765 //
766 FixLoaddingAddress = (EFI_PHYSICAL_ADDRESS)(SmramBase + (INT64)ValueInSectionHeader);
767
768 if (SmramBase + SmmCodeSize > FixLoaddingAddress && SmramBase <= FixLoaddingAddress) {
769 //
770 // The assigned address is valid. Return the specified loadding address
771 //
772 ImageContext->ImageAddress = FixLoaddingAddress;
773 Status = EFI_SUCCESS;
774 }
775 }
776 break;
777 }
778 SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
779 }
780 DEBUG ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address %x, Status = %r \n", FixLoaddingAddress, Status));
781 return Status;
782 }
783 /**
784 Load the SMM Core image into SMRAM and executes the SMM Core from SMRAM.
785
786 @param[in] SmramRange Descriptor for the range of SMRAM to reload the
787 currently executing image.
788 @param[in] Context Context to pass into SMM Core
789
790 @return EFI_STATUS
791
792 **/
793 EFI_STATUS
794 ExecuteSmmCoreFromSmram (
795 IN EFI_SMRAM_DESCRIPTOR *SmramRange,
796 IN VOID *Context
797 )
798 {
799 EFI_STATUS Status;
800 VOID *SourceBuffer;
801 UINTN SourceSize;
802 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
803 UINTN PageCount;
804 EFI_PHYSICAL_ADDRESS DestinationBuffer;
805 EFI_IMAGE_ENTRY_POINT EntryPoint;
806
807 //
808 // Search all Firmware Volumes for a PE/COFF image in a file of type SMM_CORE
809 //
810 Status = GetSectionFromAnyFvByFileType (
811 EFI_FV_FILETYPE_SMM_CORE,
812 0,
813 EFI_SECTION_PE32,
814 0,
815 &SourceBuffer,
816 &SourceSize
817 );
818 if (EFI_ERROR (Status)) {
819 return Status;
820 }
821
822 //
823 // Initilize ImageContext
824 //
825 ImageContext.Handle = SourceBuffer;
826 ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
827
828 //
829 // Get information about the image being loaded
830 //
831 Status = PeCoffLoaderGetImageInfo (&ImageContext);
832 if (EFI_ERROR (Status)) {
833 return Status;
834 }
835 //
836 // if Loading module at Fixed Address feature is enabled, the SMM core driver will be loaded to
837 // the address assigned by build tool.
838 //
839 if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) {
840 //
841 // Get the fixed loading address assigned by Build tool
842 //
843 Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext);
844 if (!EFI_ERROR (Status)) {
845 //
846 // Since the memory range to load SMM CORE will be cut out in SMM core, so no need to allocate and free this range
847 //
848 PageCount = 0;
849 } else {
850 DEBUG ((EFI_D_INFO, "LOADING MODULE FIXED ERROR: Loading module at fixed address at address failed\n"));
851 //
852 // Allocate memory for the image being loaded from the EFI_SRAM_DESCRIPTOR
853 // specified by SmramRange
854 //
855 PageCount = (UINTN)EFI_SIZE_TO_PAGES(ImageContext.ImageSize + ImageContext.SectionAlignment);
856
857 ASSERT ((SmramRange->PhysicalSize & EFI_PAGE_MASK) == 0);
858 ASSERT (SmramRange->PhysicalSize > EFI_PAGES_TO_SIZE (PageCount));
859
860 SmramRange->PhysicalSize -= EFI_PAGES_TO_SIZE (PageCount);
861 DestinationBuffer = SmramRange->CpuStart + SmramRange->PhysicalSize;
862
863 //
864 // Align buffer on section boundry
865 //
866 ImageContext.ImageAddress = DestinationBuffer;
867 }
868 } else {
869 //
870 // Allocate memory for the image being loaded from the EFI_SRAM_DESCRIPTOR
871 // specified by SmramRange
872 //
873 PageCount = (UINTN)EFI_SIZE_TO_PAGES(ImageContext.ImageSize + ImageContext.SectionAlignment);
874
875 ASSERT ((SmramRange->PhysicalSize & EFI_PAGE_MASK) == 0);
876 ASSERT (SmramRange->PhysicalSize > EFI_PAGES_TO_SIZE (PageCount));
877
878 SmramRange->PhysicalSize -= EFI_PAGES_TO_SIZE (PageCount);
879 DestinationBuffer = SmramRange->CpuStart + SmramRange->PhysicalSize;
880
881 //
882 // Align buffer on section boundry
883 //
884 ImageContext.ImageAddress = DestinationBuffer;
885 }
886
887 ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
888 ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1);
889
890 //
891 // Print debug message showing SMM Core load address.
892 //
893 DEBUG ((DEBUG_INFO, "SMM IPL loading SMM Core at SMRAM address %p\n", (VOID *)(UINTN)ImageContext.ImageAddress));
894
895 //
896 // Load the image to our new buffer
897 //
898 Status = PeCoffLoaderLoadImage (&ImageContext);
899 if (!EFI_ERROR (Status)) {
900 //
901 // Relocate the image in our new buffer
902 //
903 Status = PeCoffLoaderRelocateImage (&ImageContext);
904 if (!EFI_ERROR (Status)) {
905 //
906 // Flush the instruction cache so the image data are written before we execute it
907 //
908 InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
909
910 //
911 // Print debug message showing SMM Core entry point address.
912 //
913 DEBUG ((DEBUG_INFO, "SMM IPL calling SMM Core at SMRAM address %p\n", (VOID *)(UINTN)ImageContext.EntryPoint));
914
915 //
916 // Execute image
917 //
918 EntryPoint = (EFI_IMAGE_ENTRY_POINT)(UINTN)ImageContext.EntryPoint;
919 Status = EntryPoint ((EFI_HANDLE)Context, gST);
920 }
921 }
922
923 //
924 // If the load operation, relocate operation, or the image execution return an
925 // error, then free memory allocated from the EFI_SRAM_DESCRIPTOR specified by
926 // SmramRange
927 //
928 if (EFI_ERROR (Status)) {
929 SmramRange->PhysicalSize += EFI_PAGES_TO_SIZE (PageCount);
930 }
931
932 //
933 // Always free memory allocted by GetFileBufferByFilePath ()
934 //
935 FreePool (SourceBuffer);
936
937 return Status;
938 }
939
940 /**
941 The Entry Point for SMM IPL
942
943 Load SMM Core into SMRAM, register SMM Core entry point for SMIs, install
944 SMM Base 2 Protocol and SMM Communication Protocol, and register for the
945 critical events required to coordinate between DXE and SMM environments.
946
947 @param ImageHandle The firmware allocated handle for the EFI image.
948 @param SystemTable A pointer to the EFI System Table.
949
950 @retval EFI_SUCCESS The entry point is executed successfully.
951 @retval Other Some error occurred when executing this entry point.
952
953 **/
954 EFI_STATUS
955 EFIAPI
956 SmmIplEntry (
957 IN EFI_HANDLE ImageHandle,
958 IN EFI_SYSTEM_TABLE *SystemTable
959 )
960 {
961 EFI_STATUS Status;
962 EFI_SMM_CONFIGURATION_PROTOCOL *SmmConfiguration;
963 UINTN Size;
964 UINTN Index;
965 EFI_SMM_RESERVED_SMRAM_REGION *SmramResRegion;
966 UINT64 MaxSize;
967 VOID *Registration;
968 UINT64 SmmCodeSize;
969 EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE *LMFAConfigurationTable;
970 EFI_CPU_ARCH_PROTOCOL *CpuArch;
971
972 //
973 // Fill in the image handle of the SMM IPL so the SMM Core can use this as the
974 // ParentImageHandle field of the Load Image Protocol for all SMM Drivers loaded
975 // by the SMM Core
976 //
977 mSmmCorePrivateData.SmmIplImageHandle = ImageHandle;
978
979 //
980 // Get SMM Access Protocol
981 //
982 Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&mSmmAccess);
983 ASSERT_EFI_ERROR (Status);
984
985 //
986 // Get SMM Control2 Protocol
987 //
988 Status = gBS->LocateProtocol (&gEfiSmmControl2ProtocolGuid, NULL, (VOID **)&mSmmControl2);
989 ASSERT_EFI_ERROR (Status);
990
991 //
992 // Get SMM Configuration Protocol if it is present
993 //
994 SmmConfiguration = NULL;
995 Status = gBS->LocateProtocol (&gEfiSmmConfigurationProtocolGuid, NULL, (VOID **) &SmmConfiguration);
996
997 //
998 // Get SMRAM information
999 //
1000 Size = 0;
1001 Status = mSmmAccess->GetCapabilities (mSmmAccess, &Size, NULL);
1002 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
1003
1004 gSmmCorePrivate->SmramRanges = (EFI_SMRAM_DESCRIPTOR *)AllocatePool (Size);
1005 ASSERT (gSmmCorePrivate->SmramRanges != NULL);
1006
1007 Status = mSmmAccess->GetCapabilities (mSmmAccess, &Size, gSmmCorePrivate->SmramRanges);
1008 ASSERT_EFI_ERROR (Status);
1009
1010 gSmmCorePrivate->SmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
1011
1012 //
1013 // Open all SMRAM ranges
1014 //
1015 Status = mSmmAccess->Open (mSmmAccess);
1016 ASSERT_EFI_ERROR (Status);
1017
1018 //
1019 // Print debug message that the SMRAM window is now open.
1020 //
1021 DEBUG ((DEBUG_INFO, "SMM IPL opened SMRAM window\n"));
1022
1023 //
1024 // Subtract SMRAM any reserved SMRAM regions.
1025 //
1026 if (SmmConfiguration != NULL) {
1027 SmramResRegion = SmmConfiguration->SmramReservedRegions;
1028 while (SmramResRegion->SmramReservedSize != 0) {
1029 for (Index = 0; Index < gSmmCorePrivate->SmramRangeCount; Index ++) {
1030 if ((SmramResRegion->SmramReservedStart >= gSmmCorePrivate->SmramRanges[Index].CpuStart) && \
1031 ((SmramResRegion->SmramReservedStart + SmramResRegion->SmramReservedSize) <= \
1032 (gSmmCorePrivate->SmramRanges[Index].CpuStart + gSmmCorePrivate->SmramRanges[Index].PhysicalSize))) {
1033 //
1034 // This range has reserved area, calculate the left free size
1035 //
1036 gSmmCorePrivate->SmramRanges[Index].PhysicalSize = SmramResRegion->SmramReservedStart - gSmmCorePrivate->SmramRanges[Index].CpuStart;
1037 }
1038 }
1039 SmramResRegion++;
1040 }
1041 }
1042
1043 //
1044 // Find the largest SMRAM range between 1MB and 4GB that is at least 256KB - 4K in size
1045 //
1046 mCurrentSmramRange = NULL;
1047 for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < gSmmCorePrivate->SmramRangeCount; Index++) {
1048 if (gSmmCorePrivate->SmramRanges[Index].CpuStart >= BASE_1MB) {
1049 if ((gSmmCorePrivate->SmramRanges[Index].CpuStart + gSmmCorePrivate->SmramRanges[Index].PhysicalSize) <= BASE_4GB) {
1050 if (gSmmCorePrivate->SmramRanges[Index].PhysicalSize >= MaxSize) {
1051 MaxSize = gSmmCorePrivate->SmramRanges[Index].PhysicalSize;
1052 mCurrentSmramRange = &gSmmCorePrivate->SmramRanges[Index];
1053 }
1054 }
1055 }
1056 }
1057
1058 if (mCurrentSmramRange != NULL) {
1059 //
1060 // Print debug message showing SMRAM window that will be used by SMM IPL and SMM Core
1061 //
1062 DEBUG ((DEBUG_INFO, "SMM IPL found SMRAM window %p - %p\n",
1063 (VOID *)(UINTN)mCurrentSmramRange->CpuStart,
1064 (VOID *)(UINTN)(mCurrentSmramRange->CpuStart + mCurrentSmramRange->PhysicalSize - 1)
1065 ));
1066
1067 GetSmramCacheRange (mCurrentSmramRange, &mSmramCacheBase, &mSmramCacheSize);
1068 //
1069 // If CPU AP is present, attempt to set SMRAM cacheability to WB
1070 // Note that it is expected that cacheability of SMRAM has been set to WB if CPU AP
1071 // is not available here.
1072 //
1073 CpuArch = NULL;
1074 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&CpuArch);
1075 if (!EFI_ERROR (Status)) {
1076 Status = gDS->SetMemorySpaceAttributes(
1077 mSmramCacheBase,
1078 mSmramCacheSize,
1079 EFI_MEMORY_WB
1080 );
1081 if (EFI_ERROR (Status)) {
1082 DEBUG ((DEBUG_WARN, "SMM IPL failed to set SMRAM window to EFI_MEMORY_WB\n"));
1083 }
1084 }
1085 //
1086 // if Loading module at Fixed Address feature is enabled, save the SMRAM base to Load
1087 // Modules At Fixed Address Configuration Table.
1088 //
1089 if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) {
1090 //
1091 // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber
1092 //
1093 SmmCodeSize = LShiftU64 (PcdGet32(PcdLoadFixAddressSmmCodePageNumber), EFI_PAGE_SHIFT);
1094 //
1095 // The SMRAM available memory is assumed to be larger than SmmCodeSize
1096 //
1097 ASSERT (mCurrentSmramRange->PhysicalSize > SmmCodeSize);
1098 //
1099 // Retrieve Load modules At fixed address configuration table and save the SMRAM base.
1100 //
1101 Status = EfiGetSystemConfigurationTable (
1102 &gLoadFixedAddressConfigurationTableGuid,
1103 (VOID **) &LMFAConfigurationTable
1104 );
1105 if (!EFI_ERROR (Status) && LMFAConfigurationTable != NULL) {
1106 LMFAConfigurationTable->SmramBase = mCurrentSmramRange->CpuStart;
1107 //
1108 // Print the SMRAM base
1109 //
1110 DEBUG ((EFI_D_INFO, "LOADING MODULE FIXED INFO: TSEG BASE is %x. \n", LMFAConfigurationTable->SmramBase));
1111 }
1112 }
1113 //
1114 // Load SMM Core into SMRAM and execute it from SMRAM
1115 //
1116 Status = ExecuteSmmCoreFromSmram (mCurrentSmramRange, gSmmCorePrivate);
1117 if (EFI_ERROR (Status)) {
1118 //
1119 // Print error message that the SMM Core failed to be loaded and executed.
1120 //
1121 DEBUG ((DEBUG_ERROR, "SMM IPL could not load and execute SMM Core from SMRAM\n"));
1122
1123 //
1124 // Attempt to reset SMRAM cacheability to UC
1125 //
1126 if (CpuArch != NULL) {
1127 Status = gDS->SetMemorySpaceAttributes(
1128 mSmramCacheBase,
1129 mSmramCacheSize,
1130 EFI_MEMORY_UC
1131 );
1132 if (EFI_ERROR (Status)) {
1133 DEBUG ((DEBUG_WARN, "SMM IPL failed to reset SMRAM window to EFI_MEMORY_UC\n"));
1134 }
1135 }
1136 }
1137 } else {
1138 //
1139 // Print error message that there are not enough SMRAM resources to load the SMM Core.
1140 //
1141 DEBUG ((DEBUG_ERROR, "SMM IPL could not find a large enough SMRAM region to load SMM Core\n"));
1142 }
1143
1144 //
1145 // If the SMM Core could not be loaded then close SMRAM window, free allocated
1146 // resources, and return an error so SMM IPL will be unloaded.
1147 //
1148 if (mCurrentSmramRange == NULL || EFI_ERROR (Status)) {
1149 //
1150 // Close all SMRAM ranges
1151 //
1152 Status = mSmmAccess->Close (mSmmAccess);
1153 ASSERT_EFI_ERROR (Status);
1154
1155 //
1156 // Print debug message that the SMRAM window is now closed.
1157 //
1158 DEBUG ((DEBUG_INFO, "SMM IPL closed SMRAM window\n"));
1159
1160 //
1161 // Free all allocated resources
1162 //
1163 FreePool (gSmmCorePrivate->SmramRanges);
1164
1165 return EFI_UNSUPPORTED;
1166 }
1167
1168 //
1169 // Install SMM Base2 Protocol and SMM Communication Protocol
1170 //
1171 Status = gBS->InstallMultipleProtocolInterfaces (
1172 &mSmmIplHandle,
1173 &gEfiSmmBase2ProtocolGuid, &mSmmBase2,
1174 &gEfiSmmCommunicationProtocolGuid, &mSmmCommunication,
1175 NULL
1176 );
1177 ASSERT_EFI_ERROR (Status);
1178
1179 //
1180 // Create the set of protocol and event notififcations that the SMM IPL requires
1181 //
1182 for (Index = 0; mSmmIplEvents[Index].NotifyFunction != NULL; Index++) {
1183 if (mSmmIplEvents[Index].Protocol) {
1184 mSmmIplEvents[Index].Event = EfiCreateProtocolNotifyEvent (
1185 mSmmIplEvents[Index].Guid,
1186 TPL_CALLBACK,
1187 mSmmIplEvents[Index].NotifyFunction,
1188 mSmmIplEvents[Index].NotifyContext,
1189 &Registration
1190 );
1191 } else {
1192 Status = gBS->CreateEventEx (
1193 EVT_NOTIFY_SIGNAL,
1194 TPL_CALLBACK,
1195 mSmmIplEvents[Index].NotifyFunction,
1196 mSmmIplEvents[Index].NotifyContext,
1197 mSmmIplEvents[Index].Guid,
1198 &mSmmIplEvents[Index].Event
1199 );
1200 ASSERT_EFI_ERROR (Status);
1201 }
1202 }
1203
1204 return EFI_SUCCESS;
1205 }