• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Instance of SMM memory check library.
3 
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.
8 
9   Copyright (c) 2015 - 2016, 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
14 
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.
17 
18 **/
19 
20 
21 #include <PiSmm.h>
22 
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>
33 
34 #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
35   ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))
36 
37 EFI_SMRAM_DESCRIPTOR *mSmmMemLibInternalSmramRanges;
38 UINTN                mSmmMemLibInternalSmramCount;
39 
40 //
41 // Maximum support address used to check input buffer
42 //
43 EFI_PHYSICAL_ADDRESS  mSmmMemLibInternalMaximumSupportAddress = 0;
44 
45 UINTN                 mMemoryMapEntryCount;
46 EFI_MEMORY_DESCRIPTOR *mMemoryMap;
47 UINTN                 mDescriptorSize;
48 
49 VOID                  *mRegistrationEndOfDxe;
50 VOID                  *mRegistrationReadyToLock;
51 
52 BOOLEAN               mSmmReadyToLock = FALSE;
53 
54 /**
55   Calculate and save the maximum support address.
56 
57 **/
58 VOID
SmmMemLibInternalCalculateMaximumSupportAddress(VOID)59 SmmMemLibInternalCalculateMaximumSupportAddress (
60   VOID
61   )
62 {
63   VOID         *Hob;
64   UINT32       RegEax;
65   UINT8        PhysicalAddressBits;
66 
67   //
68   // Get physical address bits supported.
69   //
70   Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
71   if (Hob != NULL) {
72     PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
73   } else {
74     AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
75     if (RegEax >= 0x80000008) {
76       AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
77       PhysicalAddressBits = (UINT8) RegEax;
78     } else {
79       PhysicalAddressBits = 36;
80     }
81   }
82   //
83   // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
84   //
85   ASSERT (PhysicalAddressBits <= 52);
86   if (PhysicalAddressBits > 48) {
87     PhysicalAddressBits = 48;
88   }
89 
90   //
91   // Save the maximum support address in one global variable
92   //
93   mSmmMemLibInternalMaximumSupportAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, PhysicalAddressBits) - 1);
94   DEBUG ((EFI_D_INFO, "mSmmMemLibInternalMaximumSupportAddress = 0x%lx\n", mSmmMemLibInternalMaximumSupportAddress));
95 }
96 
97 /**
98   This function check if the buffer is valid per processor architecture and not overlap with SMRAM.
99 
100   @param Buffer  The buffer start address to be checked.
101   @param Length  The buffer length to be checked.
102 
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.
105 **/
106 BOOLEAN
107 EFIAPI
SmmIsBufferOutsideSmmValid(IN EFI_PHYSICAL_ADDRESS Buffer,IN UINT64 Length)108 SmmIsBufferOutsideSmmValid (
109   IN EFI_PHYSICAL_ADDRESS  Buffer,
110   IN UINT64                Length
111   )
112 {
113   UINTN  Index;
114 
115   //
116   // Check override.
117   // NOTE: (B:0->L:4G) is invalid for IA32, but (B:1->L:4G-1)/(B:4G-1->L:1) is valid.
118   //
119   if ((Length > mSmmMemLibInternalMaximumSupportAddress) ||
120       (Buffer > mSmmMemLibInternalMaximumSupportAddress) ||
121       ((Length != 0) && (Buffer > (mSmmMemLibInternalMaximumSupportAddress - (Length - 1)))) ) {
122     //
123     // Overflow happen
124     //
125     DEBUG ((
126       EFI_D_ERROR,
127       "SmmIsBufferOutsideSmmValid: Overflow: Buffer (0x%lx) - Length (0x%lx), MaximumSupportAddress (0x%lx)\n",
128       Buffer,
129       Length,
130       mSmmMemLibInternalMaximumSupportAddress
131       ));
132     return FALSE;
133   }
134 
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))) {
138       DEBUG ((
139         EFI_D_ERROR,
140         "SmmIsBufferOutsideSmmValid: Overlap: Buffer (0x%lx) - Length (0x%lx), ",
141         Buffer,
142         Length
143         ));
144       DEBUG ((
145         EFI_D_ERROR,
146         "CpuStart (0x%lx) - PhysicalSize (0x%lx)\n",
147         mSmmMemLibInternalSmramRanges[Index].CpuStart,
148         mSmmMemLibInternalSmramRanges[Index].PhysicalSize
149         ));
150       return FALSE;
151     }
152   }
153 
154   //
155   // Check override for Valid Communication Region
156   //
157   if (mSmmReadyToLock) {
158     EFI_MEMORY_DESCRIPTOR          *MemoryMap;
159     BOOLEAN                        InValidCommunicationRegion;
160 
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;
167       }
168       MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mDescriptorSize);
169     }
170 
171     if (!InValidCommunicationRegion) {
172       DEBUG ((
173         EFI_D_ERROR,
174         "SmmIsBufferOutsideSmmValid: Not in ValidCommunicationRegion: Buffer (0x%lx) - Length (0x%lx), ",
175         Buffer,
176         Length
177         ));
178       ASSERT (FALSE);
179       return FALSE;
180     }
181   }
182   return TRUE;
183 }
184 
185 /**
186   Copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
187 
188   This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
189   It checks if source buffer is valid per processor architecture and not overlap with SMRAM.
190   If the check passes, it copies memory and returns EFI_SUCCESS.
191   If the check fails, it return EFI_SECURITY_VIOLATION.
192   The implementation must be reentrant.
193 
194   @param  DestinationBuffer   The pointer to the destination buffer of the memory copy.
195   @param  SourceBuffer        The pointer to the source buffer of the memory copy.
196   @param  Length              The number of bytes to copy from SourceBuffer to DestinationBuffer.
197 
198   @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
199   @retval EFI_SUCCESS            Memory is copied.
200 
201 **/
202 EFI_STATUS
203 EFIAPI
SmmCopyMemToSmram(OUT VOID * DestinationBuffer,IN CONST VOID * SourceBuffer,IN UINTN Length)204 SmmCopyMemToSmram (
205   OUT VOID       *DestinationBuffer,
206   IN CONST VOID  *SourceBuffer,
207   IN UINTN       Length
208   )
209 {
210   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)SourceBuffer, Length)) {
211     DEBUG ((EFI_D_ERROR, "SmmCopyMemToSmram: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer, Length));
212     return EFI_SECURITY_VIOLATION;
213   }
214   CopyMem (DestinationBuffer, SourceBuffer, Length);
215   return EFI_SUCCESS;
216 }
217 
218 /**
219   Copies a source buffer (SMRAM) to a destination buffer (NON-SMRAM).
220 
221   This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
222   It checks if destination buffer is valid per processor architecture and not overlap with SMRAM.
223   If the check passes, it copies memory and returns EFI_SUCCESS.
224   If the check fails, it returns EFI_SECURITY_VIOLATION.
225   The implementation must be reentrant.
226 
227   @param  DestinationBuffer   The pointer to the destination buffer of the memory copy.
228   @param  SourceBuffer        The pointer to the source buffer of the memory copy.
229   @param  Length              The number of bytes to copy from SourceBuffer to DestinationBuffer.
230 
231   @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
232   @retval EFI_SUCCESS            Memory is copied.
233 
234 **/
235 EFI_STATUS
236 EFIAPI
SmmCopyMemFromSmram(OUT VOID * DestinationBuffer,IN CONST VOID * SourceBuffer,IN UINTN Length)237 SmmCopyMemFromSmram (
238   OUT VOID       *DestinationBuffer,
239   IN CONST VOID  *SourceBuffer,
240   IN UINTN       Length
241   )
242 {
243   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)DestinationBuffer, Length)) {
244     DEBUG ((EFI_D_ERROR, "SmmCopyMemFromSmram: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer, Length));
245     return EFI_SECURITY_VIOLATION;
246   }
247   CopyMem (DestinationBuffer, SourceBuffer, Length);
248   return EFI_SUCCESS;
249 }
250 
251 /**
252   Copies a source buffer (NON-SMRAM) to a destination buffer (NON-SMRAM).
253 
254   This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
255   It checks if source buffer and destination buffer are valid per processor architecture and not overlap with SMRAM.
256   If the check passes, it copies memory and returns EFI_SUCCESS.
257   If the check fails, it returns EFI_SECURITY_VIOLATION.
258   The implementation must be reentrant, and it must handle the case where source buffer overlaps destination buffer.
259 
260   @param  DestinationBuffer   The pointer to the destination buffer of the memory copy.
261   @param  SourceBuffer        The pointer to the source buffer of the memory copy.
262   @param  Length              The number of bytes to copy from SourceBuffer to DestinationBuffer.
263 
264   @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
265   @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
266   @retval EFI_SUCCESS            Memory is copied.
267 
268 **/
269 EFI_STATUS
270 EFIAPI
SmmCopyMem(OUT VOID * DestinationBuffer,IN CONST VOID * SourceBuffer,IN UINTN Length)271 SmmCopyMem (
272   OUT VOID       *DestinationBuffer,
273   IN CONST VOID  *SourceBuffer,
274   IN UINTN       Length
275   )
276 {
277   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)DestinationBuffer, Length)) {
278     DEBUG ((EFI_D_ERROR, "SmmCopyMem: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer, Length));
279     return EFI_SECURITY_VIOLATION;
280   }
281   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)SourceBuffer, Length)) {
282     DEBUG ((EFI_D_ERROR, "SmmCopyMem: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer, Length));
283     return EFI_SECURITY_VIOLATION;
284   }
285   CopyMem (DestinationBuffer, SourceBuffer, Length);
286   return EFI_SUCCESS;
287 }
288 
289 /**
290   Fills a target buffer (NON-SMRAM) with a byte value.
291 
292   This function fills a target buffer (non-SMRAM) with a byte value.
293   It checks if target buffer is valid per processor architecture and not overlap with SMRAM.
294   If the check passes, it fills memory and returns EFI_SUCCESS.
295   If the check fails, it returns EFI_SECURITY_VIOLATION.
296 
297   @param  Buffer    The memory to set.
298   @param  Length    The number of bytes to set.
299   @param  Value     The value with which to fill Length bytes of Buffer.
300 
301   @retval EFI_SECURITY_VIOLATION The Buffer is invalid per processor architecture or overlap with SMRAM.
302   @retval EFI_SUCCESS            Memory is set.
303 
304 **/
305 EFI_STATUS
306 EFIAPI
SmmSetMem(OUT VOID * Buffer,IN UINTN Length,IN UINT8 Value)307 SmmSetMem (
308   OUT VOID  *Buffer,
309   IN UINTN  Length,
310   IN UINT8  Value
311   )
312 {
313   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, Length)) {
314     DEBUG ((EFI_D_ERROR, "SmmSetMem: Security Violation: Source (0x%x), Length (0x%x)\n", Buffer, Length));
315     return EFI_SECURITY_VIOLATION;
316   }
317   SetMem (Buffer, Length, Value);
318   return EFI_SUCCESS;
319 }
320 
321 /**
322   Notification for SMM EndOfDxe protocol.
323 
324   @param[in] Protocol   Points to the protocol's unique identifier.
325   @param[in] Interface  Points to the interface instance.
326   @param[in] Handle     The handle on which the interface was installed.
327 
328   @retval EFI_SUCCESS   Notification runs successfully.
329 **/
330 EFI_STATUS
331 EFIAPI
SmmLibInternalEndOfDxeNotify(IN CONST EFI_GUID * Protocol,IN VOID * Interface,IN EFI_HANDLE Handle)332 SmmLibInternalEndOfDxeNotify (
333   IN CONST EFI_GUID  *Protocol,
334   IN VOID            *Interface,
335   IN EFI_HANDLE      Handle
336   )
337 {
338   EFI_STATUS            Status;
339   UINTN                 MapKey;
340   UINTN                 MemoryMapSize;
341   EFI_MEMORY_DESCRIPTOR *MemoryMap;
342   EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
343   EFI_MEMORY_DESCRIPTOR *SmmMemoryMapStart;
344   UINTN                 MemoryMapEntryCount;
345   UINTN                 DescriptorSize;
346   UINT32                DescriptorVersion;
347   UINTN                 Index;
348 
349   MemoryMapSize = 0;
350   MemoryMap = NULL;
351   Status = gBS->GetMemoryMap (
352              &MemoryMapSize,
353              MemoryMap,
354              &MapKey,
355              &DescriptorSize,
356              &DescriptorVersion
357              );
358   ASSERT (Status == EFI_BUFFER_TOO_SMALL);
359 
360   do {
361     Status = gBS->AllocatePool (EfiBootServicesData, MemoryMapSize, (VOID **)&MemoryMap);
362     ASSERT (MemoryMap != NULL);
363 
364     Status = gBS->GetMemoryMap (
365                &MemoryMapSize,
366                MemoryMap,
367                &MapKey,
368                &DescriptorSize,
369                &DescriptorVersion
370                );
371     if (EFI_ERROR (Status)) {
372       gBS->FreePool (MemoryMap);
373     }
374   } while (Status == EFI_BUFFER_TOO_SMALL);
375 
376   //
377   // Get Count
378   //
379   mDescriptorSize = DescriptorSize;
380   MemoryMapEntryCount = MemoryMapSize/DescriptorSize;
381   MemoryMapStart = MemoryMap;
382   mMemoryMapEntryCount = 0;
383   for (Index = 0; Index < MemoryMapEntryCount; Index++) {
384     switch (MemoryMap->Type) {
385     case EfiReservedMemoryType:
386     case EfiRuntimeServicesCode:
387     case EfiRuntimeServicesData:
388     case EfiACPIMemoryNVS:
389       mMemoryMapEntryCount++;
390       break;
391     }
392     MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
393   }
394   MemoryMap = MemoryMapStart;
395 
396   //
397   // Get Data
398   //
399   mMemoryMap = AllocatePool (mMemoryMapEntryCount*DescriptorSize);
400   ASSERT (mMemoryMap != NULL);
401   SmmMemoryMapStart = mMemoryMap;
402   for (Index = 0; Index < MemoryMapEntryCount; Index++) {
403     switch (MemoryMap->Type) {
404     case EfiReservedMemoryType:
405     case EfiRuntimeServicesCode:
406     case EfiRuntimeServicesData:
407     case EfiACPIMemoryNVS:
408       CopyMem (mMemoryMap, MemoryMap, DescriptorSize);
409       mMemoryMap = NEXT_MEMORY_DESCRIPTOR(mMemoryMap, DescriptorSize);
410       break;
411     }
412     MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
413   }
414   mMemoryMap = SmmMemoryMapStart;
415   MemoryMap = MemoryMapStart;
416 
417   gBS->FreePool (MemoryMap);
418 
419   return EFI_SUCCESS;
420 }
421 
422 
423 /**
424   Notification for SMM ReadyToLock protocol.
425 
426   @param[in] Protocol   Points to the protocol's unique identifier.
427   @param[in] Interface  Points to the interface instance.
428   @param[in] Handle     The handle on which the interface was installed.
429 
430   @retval EFI_SUCCESS   Notification runs successfully.
431 **/
432 EFI_STATUS
433 EFIAPI
SmmLibInternalReadyToLockNotify(IN CONST EFI_GUID * Protocol,IN VOID * Interface,IN EFI_HANDLE Handle)434 SmmLibInternalReadyToLockNotify (
435   IN CONST EFI_GUID  *Protocol,
436   IN VOID            *Interface,
437   IN EFI_HANDLE      Handle
438   )
439 {
440   mSmmReadyToLock = TRUE;
441   return EFI_SUCCESS;
442 }
443 /**
444   The constructor function initializes the Smm Mem library
445 
446   @param  ImageHandle   The firmware allocated handle for the EFI image.
447   @param  SystemTable   A pointer to the EFI System Table.
448 
449   @retval EFI_SUCCESS   The constructor always returns EFI_SUCCESS.
450 
451 **/
452 EFI_STATUS
453 EFIAPI
SmmMemLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)454 SmmMemLibConstructor (
455   IN EFI_HANDLE        ImageHandle,
456   IN EFI_SYSTEM_TABLE  *SystemTable
457   )
458 {
459   EFI_STATUS                    Status;
460   EFI_SMM_ACCESS2_PROTOCOL      *SmmAccess;
461   UINTN                         Size;
462 
463   //
464   // Get SMRAM information
465   //
466   Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
467   ASSERT_EFI_ERROR (Status);
468 
469   Size = 0;
470   Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
471   ASSERT (Status == EFI_BUFFER_TOO_SMALL);
472 
473   mSmmMemLibInternalSmramRanges = AllocatePool (Size);
474   ASSERT (mSmmMemLibInternalSmramRanges != NULL);
475 
476   Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmmMemLibInternalSmramRanges);
477   ASSERT_EFI_ERROR (Status);
478 
479   mSmmMemLibInternalSmramCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
480 
481   //
482   // Calculate and save maximum support address
483   //
484   SmmMemLibInternalCalculateMaximumSupportAddress ();
485 
486   //
487   // Register EndOfDxe to get UEFI memory map
488   //
489   Status = gSmst->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid, SmmLibInternalEndOfDxeNotify, &mRegistrationEndOfDxe);
490   ASSERT_EFI_ERROR (Status);
491 
492   //
493   // Register ready to lock so that we can know when to check valid SMRAM region
494   //
495   Status = gSmst->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid, SmmLibInternalReadyToLockNotify, &mRegistrationReadyToLock);
496   ASSERT_EFI_ERROR (Status);
497 
498   return EFI_SUCCESS;
499 }
500 
501 /**
502   The destructor function frees resource used in the Smm Mem library
503 
504   @param[in]  ImageHandle   The firmware allocated handle for the EFI image.
505   @param[in]  SystemTable   A pointer to the EFI System Table.
506 
507   @retval     EFI_SUCCESS   The deconstructor always returns EFI_SUCCESS.
508 **/
509 EFI_STATUS
510 EFIAPI
SmmMemLibDestructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)511 SmmMemLibDestructor (
512   IN EFI_HANDLE        ImageHandle,
513   IN EFI_SYSTEM_TABLE  *SystemTable
514   )
515 {
516   FreePool (mSmmMemLibInternalSmramRanges);
517 
518   gSmst->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid, NULL, &mRegistrationEndOfDxe);
519   gSmst->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid, NULL, &mRegistrationReadyToLock);
520   return EFI_SUCCESS;
521 }
522