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, 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 
32 EFI_SMRAM_DESCRIPTOR *mSmmMemLibInternalSmramRanges;
33 UINTN                mSmmMemLibInternalSmramCount;
34 
35 //
36 // Maximum support address used to check input buffer
37 //
38 EFI_PHYSICAL_ADDRESS  mSmmMemLibInternalMaximumSupportAddress = 0;
39 
40 /**
41   Calculate and save the maximum support address.
42 
43 **/
44 VOID
SmmMemLibInternalCalculateMaximumSupportAddress(VOID)45 SmmMemLibInternalCalculateMaximumSupportAddress (
46   VOID
47   )
48 {
49   VOID         *Hob;
50   UINT32       RegEax;
51   UINT8        PhysicalAddressBits;
52 
53   //
54   // Get physical address bits supported.
55   //
56   Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
57   if (Hob != NULL) {
58     PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
59   } else {
60     AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
61     if (RegEax >= 0x80000008) {
62       AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
63       PhysicalAddressBits = (UINT8) RegEax;
64     } else {
65       PhysicalAddressBits = 36;
66     }
67   }
68   //
69   // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
70   //
71   ASSERT (PhysicalAddressBits <= 52);
72   if (PhysicalAddressBits > 48) {
73     PhysicalAddressBits = 48;
74   }
75 
76   //
77   // Save the maximum support address in one global variable
78   //
79   mSmmMemLibInternalMaximumSupportAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, PhysicalAddressBits) - 1);
80   DEBUG ((EFI_D_INFO, "mSmmMemLibInternalMaximumSupportAddress = 0x%lx\n", mSmmMemLibInternalMaximumSupportAddress));
81 }
82 
83 /**
84   This function check if the buffer is valid per processor architecture and not overlap with SMRAM.
85 
86   @param Buffer  The buffer start address to be checked.
87   @param Length  The buffer length to be checked.
88 
89   @retval TRUE  This buffer is valid per processor architecture and not overlap with SMRAM.
90   @retval FALSE This buffer is not valid per processor architecture or overlap with SMRAM.
91 **/
92 BOOLEAN
93 EFIAPI
SmmIsBufferOutsideSmmValid(IN EFI_PHYSICAL_ADDRESS Buffer,IN UINT64 Length)94 SmmIsBufferOutsideSmmValid (
95   IN EFI_PHYSICAL_ADDRESS  Buffer,
96   IN UINT64                Length
97   )
98 {
99   UINTN  Index;
100 
101   //
102   // Check override.
103   // NOTE: (B:0->L:4G) is invalid for IA32, but (B:1->L:4G-1)/(B:4G-1->L:1) is valid.
104   //
105   if ((Length > mSmmMemLibInternalMaximumSupportAddress) ||
106       (Buffer > mSmmMemLibInternalMaximumSupportAddress) ||
107       ((Length != 0) && (Buffer > (mSmmMemLibInternalMaximumSupportAddress - (Length - 1)))) ) {
108     //
109     // Overflow happen
110     //
111     DEBUG ((
112       EFI_D_ERROR,
113       "SmmIsBufferOutsideSmmValid: Overflow: Buffer (0x%lx) - Length (0x%lx), MaximumSupportAddress (0x%lx)\n",
114       Buffer,
115       Length,
116       mSmmMemLibInternalMaximumSupportAddress
117       ));
118     return FALSE;
119   }
120 
121   for (Index = 0; Index < mSmmMemLibInternalSmramCount; Index ++) {
122     if (((Buffer >= mSmmMemLibInternalSmramRanges[Index].CpuStart) && (Buffer < mSmmMemLibInternalSmramRanges[Index].CpuStart + mSmmMemLibInternalSmramRanges[Index].PhysicalSize)) ||
123         ((mSmmMemLibInternalSmramRanges[Index].CpuStart >= Buffer) && (mSmmMemLibInternalSmramRanges[Index].CpuStart < Buffer + Length))) {
124       DEBUG ((
125         EFI_D_ERROR,
126         "SmmIsBufferOutsideSmmValid: Overlap: Buffer (0x%lx) - Length (0x%lx), ",
127         Buffer,
128         Length
129         ));
130       DEBUG ((
131         EFI_D_ERROR,
132         "CpuStart (0x%lx) - PhysicalSize (0x%lx)\n",
133         mSmmMemLibInternalSmramRanges[Index].CpuStart,
134         mSmmMemLibInternalSmramRanges[Index].PhysicalSize
135         ));
136       return FALSE;
137     }
138   }
139 
140   return TRUE;
141 }
142 
143 /**
144   Copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
145 
146   This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
147   It checks if source buffer is valid per processor architecture and not overlap with SMRAM.
148   If the check passes, it copies memory and returns EFI_SUCCESS.
149   If the check fails, it return EFI_SECURITY_VIOLATION.
150   The implementation must be reentrant.
151 
152   @param  DestinationBuffer   The pointer to the destination buffer of the memory copy.
153   @param  SourceBuffer        The pointer to the source buffer of the memory copy.
154   @param  Length              The number of bytes to copy from SourceBuffer to DestinationBuffer.
155 
156   @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
157   @retval EFI_SUCCESS            Memory is copied.
158 
159 **/
160 EFI_STATUS
161 EFIAPI
SmmCopyMemToSmram(OUT VOID * DestinationBuffer,IN CONST VOID * SourceBuffer,IN UINTN Length)162 SmmCopyMemToSmram (
163   OUT VOID       *DestinationBuffer,
164   IN CONST VOID  *SourceBuffer,
165   IN UINTN       Length
166   )
167 {
168   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)SourceBuffer, Length)) {
169     DEBUG ((EFI_D_ERROR, "SmmCopyMemToSmram: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer, Length));
170     return EFI_SECURITY_VIOLATION;
171   }
172   CopyMem (DestinationBuffer, SourceBuffer, Length);
173   return EFI_SUCCESS;
174 }
175 
176 /**
177   Copies a source buffer (SMRAM) to a destination buffer (NON-SMRAM).
178 
179   This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
180   It checks if destination buffer is valid per processor architecture and not overlap with SMRAM.
181   If the check passes, it copies memory and returns EFI_SUCCESS.
182   If the check fails, it returns EFI_SECURITY_VIOLATION.
183   The implementation must be reentrant.
184 
185   @param  DestinationBuffer   The pointer to the destination buffer of the memory copy.
186   @param  SourceBuffer        The pointer to the source buffer of the memory copy.
187   @param  Length              The number of bytes to copy from SourceBuffer to DestinationBuffer.
188 
189   @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
190   @retval EFI_SUCCESS            Memory is copied.
191 
192 **/
193 EFI_STATUS
194 EFIAPI
SmmCopyMemFromSmram(OUT VOID * DestinationBuffer,IN CONST VOID * SourceBuffer,IN UINTN Length)195 SmmCopyMemFromSmram (
196   OUT VOID       *DestinationBuffer,
197   IN CONST VOID  *SourceBuffer,
198   IN UINTN       Length
199   )
200 {
201   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)DestinationBuffer, Length)) {
202     DEBUG ((EFI_D_ERROR, "SmmCopyMemFromSmram: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer, Length));
203     return EFI_SECURITY_VIOLATION;
204   }
205   CopyMem (DestinationBuffer, SourceBuffer, Length);
206   return EFI_SUCCESS;
207 }
208 
209 /**
210   Copies a source buffer (NON-SMRAM) to a destination buffer (NON-SMRAM).
211 
212   This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
213   It checks if source buffer and destination buffer are valid per processor architecture and not overlap with SMRAM.
214   If the check passes, it copies memory and returns EFI_SUCCESS.
215   If the check fails, it returns EFI_SECURITY_VIOLATION.
216   The implementation must be reentrant, and it must handle the case where source buffer overlaps destination buffer.
217 
218   @param  DestinationBuffer   The pointer to the destination buffer of the memory copy.
219   @param  SourceBuffer        The pointer to the source buffer of the memory copy.
220   @param  Length              The number of bytes to copy from SourceBuffer to DestinationBuffer.
221 
222   @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
223   @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
224   @retval EFI_SUCCESS            Memory is copied.
225 
226 **/
227 EFI_STATUS
228 EFIAPI
SmmCopyMem(OUT VOID * DestinationBuffer,IN CONST VOID * SourceBuffer,IN UINTN Length)229 SmmCopyMem (
230   OUT VOID       *DestinationBuffer,
231   IN CONST VOID  *SourceBuffer,
232   IN UINTN       Length
233   )
234 {
235   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)DestinationBuffer, Length)) {
236     DEBUG ((EFI_D_ERROR, "SmmCopyMem: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer, Length));
237     return EFI_SECURITY_VIOLATION;
238   }
239   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)SourceBuffer, Length)) {
240     DEBUG ((EFI_D_ERROR, "SmmCopyMem: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer, Length));
241     return EFI_SECURITY_VIOLATION;
242   }
243   CopyMem (DestinationBuffer, SourceBuffer, Length);
244   return EFI_SUCCESS;
245 }
246 
247 /**
248   Fills a target buffer (NON-SMRAM) with a byte value.
249 
250   This function fills a target buffer (non-SMRAM) with a byte value.
251   It checks if target buffer is valid per processor architecture and not overlap with SMRAM.
252   If the check passes, it fills memory and returns EFI_SUCCESS.
253   If the check fails, it returns EFI_SECURITY_VIOLATION.
254 
255   @param  Buffer    The memory to set.
256   @param  Length    The number of bytes to set.
257   @param  Value     The value with which to fill Length bytes of Buffer.
258 
259   @retval EFI_SECURITY_VIOLATION The Buffer is invalid per processor architecture or overlap with SMRAM.
260   @retval EFI_SUCCESS            Memory is set.
261 
262 **/
263 EFI_STATUS
264 EFIAPI
SmmSetMem(OUT VOID * Buffer,IN UINTN Length,IN UINT8 Value)265 SmmSetMem (
266   OUT VOID  *Buffer,
267   IN UINTN  Length,
268   IN UINT8  Value
269   )
270 {
271   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, Length)) {
272     DEBUG ((EFI_D_ERROR, "SmmSetMem: Security Violation: Source (0x%x), Length (0x%x)\n", Buffer, Length));
273     return EFI_SECURITY_VIOLATION;
274   }
275   SetMem (Buffer, Length, Value);
276   return EFI_SUCCESS;
277 }
278 
279 /**
280   The constructor function initializes the Smm Mem library
281 
282   @param  ImageHandle   The firmware allocated handle for the EFI image.
283   @param  SystemTable   A pointer to the EFI System Table.
284 
285   @retval EFI_SUCCESS   The constructor always returns EFI_SUCCESS.
286 
287 **/
288 EFI_STATUS
289 EFIAPI
SmmMemLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)290 SmmMemLibConstructor (
291   IN EFI_HANDLE        ImageHandle,
292   IN EFI_SYSTEM_TABLE  *SystemTable
293   )
294 {
295   EFI_STATUS                    Status;
296   EFI_SMM_ACCESS2_PROTOCOL      *SmmAccess;
297   UINTN                         Size;
298 
299   //
300   // Get SMRAM information
301   //
302   Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
303   ASSERT_EFI_ERROR (Status);
304 
305   Size = 0;
306   Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
307   ASSERT (Status == EFI_BUFFER_TOO_SMALL);
308 
309   mSmmMemLibInternalSmramRanges = AllocatePool (Size);
310   ASSERT (mSmmMemLibInternalSmramRanges != NULL);
311 
312   Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmmMemLibInternalSmramRanges);
313   ASSERT_EFI_ERROR (Status);
314 
315   mSmmMemLibInternalSmramCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
316 
317   //
318   // Calculate and save maximum support address
319   //
320   SmmMemLibInternalCalculateMaximumSupportAddress ();
321 
322   return EFI_SUCCESS;
323 }
324 
325 /**
326   The destructor function frees resource used in the Smm Mem library
327 
328   @param[in]  ImageHandle   The firmware allocated handle for the EFI image.
329   @param[in]  SystemTable   A pointer to the EFI System Table.
330 
331   @retval     EFI_SUCCESS   The deconstructor always returns EFI_SUCCESS.
332 **/
333 EFI_STATUS
334 EFIAPI
SmmMemLibDestructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)335 SmmMemLibDestructor (
336   IN EFI_HANDLE        ImageHandle,
337   IN EFI_SYSTEM_TABLE  *SystemTable
338   )
339 {
340   FreePool (mSmmMemLibInternalSmramRanges);
341 
342   return EFI_SUCCESS;
343 }
344