2 Instance of SMM memory check library.
4 SMM memory check library library implementation. This library consumes SMM_ACCESS2_PROTOCOL
5 to get SMRAM information. In order to use this library instance, the platform should produce
6 all SMRAM range via SMM_ACCESS2_PROTOCOL, including the range for firmware (like SMM Core
7 and SMM driver) and/or specific dedicated hardware.
9 Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
10 This program and the accompanying materials
11 are licensed and made available under the terms and conditions of the BSD License
12 which accompanies this distribution. The full text of the license may be found at
13 http://opensource.org/licenses/bsd-license.php
15 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
16 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
23 #include <Library/BaseLib.h>
24 #include <Library/BaseMemoryLib.h>
25 #include <Library/DebugLib.h>
26 #include <Library/MemoryAllocationLib.h>
27 #include <Library/UefiBootServicesTableLib.h>
28 #include <Library/SmmServicesTableLib.h>
29 #include <Library/HobLib.h>
30 #include <Protocol/SmmAccess2.h>
31 #include <Protocol/SmmReadyToLock.h>
32 #include <Protocol/SmmEndOfDxe.h>
34 #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
35 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))
37 EFI_SMRAM_DESCRIPTOR
*mSmmMemLibInternalSmramRanges
;
38 UINTN mSmmMemLibInternalSmramCount
;
41 // Maximum support address used to check input buffer
43 EFI_PHYSICAL_ADDRESS mSmmMemLibInternalMaximumSupportAddress
= 0;
45 UINTN mMemoryMapEntryCount
;
46 EFI_MEMORY_DESCRIPTOR
*mMemoryMap
;
47 UINTN mDescriptorSize
;
49 VOID
*mRegistrationEndOfDxe
;
50 VOID
*mRegistrationReadyToLock
;
52 BOOLEAN mSmmReadyToLock
= FALSE
;
55 Calculate and save the maximum support address.
59 SmmMemLibInternalCalculateMaximumSupportAddress (
65 UINT8 PhysicalAddressBits
;
68 // Get physical address bits supported.
70 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
72 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
74 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
75 if (RegEax
>= 0x80000008) {
76 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
77 PhysicalAddressBits
= (UINT8
) RegEax
;
79 PhysicalAddressBits
= 36;
83 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
85 ASSERT (PhysicalAddressBits
<= 52);
86 if (PhysicalAddressBits
> 48) {
87 PhysicalAddressBits
= 48;
91 // Save the maximum support address in one global variable
93 mSmmMemLibInternalMaximumSupportAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)(LShiftU64 (1, PhysicalAddressBits
) - 1);
94 DEBUG ((EFI_D_INFO
, "mSmmMemLibInternalMaximumSupportAddress = 0x%lx\n", mSmmMemLibInternalMaximumSupportAddress
));
98 This function check if the buffer is valid per processor architecture and not overlap with SMRAM.
100 @param Buffer The buffer start address to be checked.
101 @param Length The buffer length to be checked.
103 @retval TRUE This buffer is valid per processor architecture and not overlap with SMRAM.
104 @retval FALSE This buffer is not valid per processor architecture or overlap with SMRAM.
108 SmmIsBufferOutsideSmmValid (
109 IN EFI_PHYSICAL_ADDRESS Buffer
,
117 // NOTE: (B:0->L:4G) is invalid for IA32, but (B:1->L:4G-1)/(B:4G-1->L:1) is valid.
119 if ((Length
> mSmmMemLibInternalMaximumSupportAddress
) ||
120 (Buffer
> mSmmMemLibInternalMaximumSupportAddress
) ||
121 ((Length
!= 0) && (Buffer
> (mSmmMemLibInternalMaximumSupportAddress
- (Length
- 1)))) ) {
127 "SmmIsBufferOutsideSmmValid: Overflow: Buffer (0x%lx) - Length (0x%lx), MaximumSupportAddress (0x%lx)\n",
130 mSmmMemLibInternalMaximumSupportAddress
135 for (Index
= 0; Index
< mSmmMemLibInternalSmramCount
; Index
++) {
136 if (((Buffer
>= mSmmMemLibInternalSmramRanges
[Index
].CpuStart
) && (Buffer
< mSmmMemLibInternalSmramRanges
[Index
].CpuStart
+ mSmmMemLibInternalSmramRanges
[Index
].PhysicalSize
)) ||
137 ((mSmmMemLibInternalSmramRanges
[Index
].CpuStart
>= Buffer
) && (mSmmMemLibInternalSmramRanges
[Index
].CpuStart
< Buffer
+ Length
))) {
140 "SmmIsBufferOutsideSmmValid: Overlap: Buffer (0x%lx) - Length (0x%lx), ",
146 "CpuStart (0x%lx) - PhysicalSize (0x%lx)\n",
147 mSmmMemLibInternalSmramRanges
[Index
].CpuStart
,
148 mSmmMemLibInternalSmramRanges
[Index
].PhysicalSize
155 // Check override for Valid Communication Region
157 if (mSmmReadyToLock
) {
158 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
159 BOOLEAN InValidCommunicationRegion
;
161 InValidCommunicationRegion
= FALSE
;
162 MemoryMap
= mMemoryMap
;
163 for (Index
= 0; Index
< mMemoryMapEntryCount
; Index
++) {
164 if ((Buffer
>= MemoryMap
->PhysicalStart
) &&
165 (Buffer
+ Length
<= MemoryMap
->PhysicalStart
+ LShiftU64 (MemoryMap
->NumberOfPages
, EFI_PAGE_SHIFT
))) {
166 InValidCommunicationRegion
= TRUE
;
168 MemoryMap
= NEXT_MEMORY_DESCRIPTOR(MemoryMap
, mDescriptorSize
);
171 if (!InValidCommunicationRegion
) {
174 "SmmIsBufferOutsideSmmValid: Not in ValidCommunicationRegion: Buffer (0x%lx) - Length (0x%lx), ",
185 Copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
187 This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
188 It checks if source buffer is valid per processor architecture and not overlap with SMRAM.
189 If the check passes, it copies memory and returns EFI_SUCCESS.
190 If the check fails, it return EFI_SECURITY_VIOLATION.
191 The implementation must be reentrant.
193 @param DestinationBuffer The pointer to the destination buffer of the memory copy.
194 @param SourceBuffer The pointer to the source buffer of the memory copy.
195 @param Length The number of bytes to copy from SourceBuffer to DestinationBuffer.
197 @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
198 @retval EFI_SUCCESS Memory is copied.
204 OUT VOID
*DestinationBuffer
,
205 IN CONST VOID
*SourceBuffer
,
209 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS
)(UINTN
)SourceBuffer
, Length
)) {
210 DEBUG ((EFI_D_ERROR
, "SmmCopyMemToSmram: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer
, Length
));
211 return EFI_SECURITY_VIOLATION
;
213 CopyMem (DestinationBuffer
, SourceBuffer
, Length
);
218 Copies a source buffer (SMRAM) to a destination buffer (NON-SMRAM).
220 This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
221 It checks if destination buffer is valid per processor architecture and not overlap with SMRAM.
222 If the check passes, it copies memory and returns EFI_SUCCESS.
223 If the check fails, it returns EFI_SECURITY_VIOLATION.
224 The implementation must be reentrant.
226 @param DestinationBuffer The pointer to the destination buffer of the memory copy.
227 @param SourceBuffer The pointer to the source buffer of the memory copy.
228 @param Length The number of bytes to copy from SourceBuffer to DestinationBuffer.
230 @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
231 @retval EFI_SUCCESS Memory is copied.
236 SmmCopyMemFromSmram (
237 OUT VOID
*DestinationBuffer
,
238 IN CONST VOID
*SourceBuffer
,
242 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS
)(UINTN
)DestinationBuffer
, Length
)) {
243 DEBUG ((EFI_D_ERROR
, "SmmCopyMemFromSmram: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer
, Length
));
244 return EFI_SECURITY_VIOLATION
;
246 CopyMem (DestinationBuffer
, SourceBuffer
, Length
);
251 Copies a source buffer (NON-SMRAM) to a destination buffer (NON-SMRAM).
253 This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
254 It checks if source buffer and destination buffer are valid per processor architecture and not overlap with SMRAM.
255 If the check passes, it copies memory and returns EFI_SUCCESS.
256 If the check fails, it returns EFI_SECURITY_VIOLATION.
257 The implementation must be reentrant, and it must handle the case where source buffer overlaps destination buffer.
259 @param DestinationBuffer The pointer to the destination buffer of the memory copy.
260 @param SourceBuffer The pointer to the source buffer of the memory copy.
261 @param Length The number of bytes to copy from SourceBuffer to DestinationBuffer.
263 @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
264 @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
265 @retval EFI_SUCCESS Memory is copied.
271 OUT VOID
*DestinationBuffer
,
272 IN CONST VOID
*SourceBuffer
,
276 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS
)(UINTN
)DestinationBuffer
, Length
)) {
277 DEBUG ((EFI_D_ERROR
, "SmmCopyMem: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer
, Length
));
278 return EFI_SECURITY_VIOLATION
;
280 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS
)(UINTN
)SourceBuffer
, Length
)) {
281 DEBUG ((EFI_D_ERROR
, "SmmCopyMem: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer
, Length
));
282 return EFI_SECURITY_VIOLATION
;
284 CopyMem (DestinationBuffer
, SourceBuffer
, Length
);
289 Fills a target buffer (NON-SMRAM) with a byte value.
291 This function fills a target buffer (non-SMRAM) with a byte value.
292 It checks if target buffer is valid per processor architecture and not overlap with SMRAM.
293 If the check passes, it fills memory and returns EFI_SUCCESS.
294 If the check fails, it returns EFI_SECURITY_VIOLATION.
296 @param Buffer The memory to set.
297 @param Length The number of bytes to set.
298 @param Value The value with which to fill Length bytes of Buffer.
300 @retval EFI_SECURITY_VIOLATION The Buffer is invalid per processor architecture or overlap with SMRAM.
301 @retval EFI_SUCCESS Memory is set.
312 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS
)(UINTN
)Buffer
, Length
)) {
313 DEBUG ((EFI_D_ERROR
, "SmmSetMem: Security Violation: Source (0x%x), Length (0x%x)\n", Buffer
, Length
));
314 return EFI_SECURITY_VIOLATION
;
316 SetMem (Buffer
, Length
, Value
);
321 Notification for SMM EndOfDxe protocol.
323 @param[in] Protocol Points to the protocol's unique identifier.
324 @param[in] Interface Points to the interface instance.
325 @param[in] Handle The handle on which the interface was installed.
327 @retval EFI_SUCCESS Notification runs successfully.
331 SmmLibInternalEndOfDxeNotify (
332 IN CONST EFI_GUID
*Protocol
,
340 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
341 EFI_MEMORY_DESCRIPTOR
*MemoryMapStart
;
342 EFI_MEMORY_DESCRIPTOR
*SmmMemoryMapStart
;
343 UINTN MemoryMapEntryCount
;
344 UINTN DescriptorSize
;
345 UINT32 DescriptorVersion
;
350 Status
= gBS
->GetMemoryMap (
357 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
360 Status
= gBS
->AllocatePool (EfiBootServicesData
, MemoryMapSize
, (VOID
**)&MemoryMap
);
361 ASSERT (MemoryMap
!= NULL
);
363 Status
= gBS
->GetMemoryMap (
370 if (EFI_ERROR (Status
)) {
371 gBS
->FreePool (MemoryMap
);
373 } while (Status
== EFI_BUFFER_TOO_SMALL
);
378 mDescriptorSize
= DescriptorSize
;
379 MemoryMapEntryCount
= MemoryMapSize
/DescriptorSize
;
380 MemoryMapStart
= MemoryMap
;
381 mMemoryMapEntryCount
= 0;
382 for (Index
= 0; Index
< MemoryMapEntryCount
; Index
++) {
383 switch (MemoryMap
->Type
) {
384 case EfiReservedMemoryType
:
385 case EfiRuntimeServicesCode
:
386 case EfiRuntimeServicesData
:
387 case EfiACPIMemoryNVS
:
388 mMemoryMapEntryCount
++;
391 MemoryMap
= NEXT_MEMORY_DESCRIPTOR(MemoryMap
, DescriptorSize
);
393 MemoryMap
= MemoryMapStart
;
398 mMemoryMap
= AllocatePool (mMemoryMapEntryCount
*DescriptorSize
);
399 ASSERT (mMemoryMap
!= NULL
);
400 SmmMemoryMapStart
= mMemoryMap
;
401 for (Index
= 0; Index
< MemoryMapEntryCount
; Index
++) {
402 switch (MemoryMap
->Type
) {
403 case EfiReservedMemoryType
:
404 case EfiRuntimeServicesCode
:
405 case EfiRuntimeServicesData
:
406 case EfiACPIMemoryNVS
:
407 CopyMem (mMemoryMap
, MemoryMap
, DescriptorSize
);
408 mMemoryMap
= NEXT_MEMORY_DESCRIPTOR(mMemoryMap
, DescriptorSize
);
411 MemoryMap
= NEXT_MEMORY_DESCRIPTOR(MemoryMap
, DescriptorSize
);
413 mMemoryMap
= SmmMemoryMapStart
;
414 MemoryMap
= MemoryMapStart
;
416 gBS
->FreePool (MemoryMap
);
423 Notification for SMM ReadyToLock protocol.
425 @param[in] Protocol Points to the protocol's unique identifier.
426 @param[in] Interface Points to the interface instance.
427 @param[in] Handle The handle on which the interface was installed.
429 @retval EFI_SUCCESS Notification runs successfully.
433 SmmLibInternalReadyToLockNotify (
434 IN CONST EFI_GUID
*Protocol
,
439 mSmmReadyToLock
= TRUE
;
443 The constructor function initializes the Smm Mem library
445 @param ImageHandle The firmware allocated handle for the EFI image.
446 @param SystemTable A pointer to the EFI System Table.
448 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
453 SmmMemLibConstructor (
454 IN EFI_HANDLE ImageHandle
,
455 IN EFI_SYSTEM_TABLE
*SystemTable
459 EFI_SMM_ACCESS2_PROTOCOL
*SmmAccess
;
463 // Get SMRAM information
465 Status
= gBS
->LocateProtocol (&gEfiSmmAccess2ProtocolGuid
, NULL
, (VOID
**)&SmmAccess
);
466 ASSERT_EFI_ERROR (Status
);
469 Status
= SmmAccess
->GetCapabilities (SmmAccess
, &Size
, NULL
);
470 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
472 mSmmMemLibInternalSmramRanges
= AllocatePool (Size
);
473 ASSERT (mSmmMemLibInternalSmramRanges
!= NULL
);
475 Status
= SmmAccess
->GetCapabilities (SmmAccess
, &Size
, mSmmMemLibInternalSmramRanges
);
476 ASSERT_EFI_ERROR (Status
);
478 mSmmMemLibInternalSmramCount
= Size
/ sizeof (EFI_SMRAM_DESCRIPTOR
);
481 // Calculate and save maximum support address
483 SmmMemLibInternalCalculateMaximumSupportAddress ();
486 // Register EndOfDxe to get UEFI memory map
488 Status
= gSmst
->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid
, SmmLibInternalEndOfDxeNotify
, &mRegistrationEndOfDxe
);
489 ASSERT_EFI_ERROR (Status
);
492 // Register ready to lock so that we can know when to check valid SMRAM region
494 Status
= gSmst
->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid
, SmmLibInternalReadyToLockNotify
, &mRegistrationReadyToLock
);
495 ASSERT_EFI_ERROR (Status
);
501 The destructor function frees resource used in the Smm Mem library
503 @param[in] ImageHandle The firmware allocated handle for the EFI image.
504 @param[in] SystemTable A pointer to the EFI System Table.
506 @retval EFI_SUCCESS The deconstructor always returns EFI_SUCCESS.
510 SmmMemLibDestructor (
511 IN EFI_HANDLE ImageHandle
,
512 IN EFI_SYSTEM_TABLE
*SystemTable
515 FreePool (mSmmMemLibInternalSmramRanges
);
517 gSmst
->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid
, NULL
, &mRegistrationEndOfDxe
);
518 gSmst
->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid
, NULL
, &mRegistrationReadyToLock
);