1 /** @file
2 
3   Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR>
4 
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include <PiPei.h>
10 #include <Library/BaseLib.h>
11 #include <Library/BaseMemoryLib.h>
12 #include <Library/IoLib.h>
13 #include <Library/DebugLib.h>
14 #include <IndustryStandard/Vtd.h>
15 #include <Ppi/VtdInfo.h>
16 
17 #include "IntelVTdPmrPei.h"
18 
19 /**
20   Get protected low memory alignment.
21 
22   @param HostAddressWidth   The host address width.
23   @param VtdUnitBaseAddress The base address of the VTd engine.
24 
25   @return protected low memory alignment.
26 **/
27 UINT32
GetPlmrAlignment(IN UINT8 HostAddressWidth,IN UINTN VtdUnitBaseAddress)28 GetPlmrAlignment (
29   IN UINT8         HostAddressWidth,
30   IN UINTN         VtdUnitBaseAddress
31   )
32 {
33   UINT32        Data32;
34 
35   MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG, 0xFFFFFFFF);
36   Data32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG);
37   Data32 = ~Data32 + 1;
38 
39   return Data32;
40 }
41 
42 /**
43   Get protected high memory alignment.
44 
45   @param HostAddressWidth   The host address width.
46   @param VtdUnitBaseAddress The base address of the VTd engine.
47 
48   @return protected high memory alignment.
49 **/
50 UINT64
GetPhmrAlignment(IN UINT8 HostAddressWidth,IN UINTN VtdUnitBaseAddress)51 GetPhmrAlignment (
52   IN UINT8         HostAddressWidth,
53   IN UINTN         VtdUnitBaseAddress
54   )
55 {
56   UINT64        Data64;
57 
58   MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG, 0xFFFFFFFFFFFFFFFF);
59   Data64 = MmioRead64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG);
60   Data64 = ~Data64 + 1;
61   Data64 = Data64 & (LShiftU64 (1, HostAddressWidth) - 1);
62 
63   return Data64;
64 }
65 
66 /**
67   Get protected low memory alignment.
68 
69   @param VTdInfo            The VTd engine context information.
70   @param EngineMask         The mask of the VTd engine to be accessed.
71 
72   @return protected low memory alignment.
73 **/
74 UINT32
GetLowMemoryAlignment(IN VTD_INFO * VTdInfo,IN UINT64 EngineMask)75 GetLowMemoryAlignment (
76   IN VTD_INFO      *VTdInfo,
77   IN UINT64        EngineMask
78   )
79 {
80   UINTN         Index;
81   UINT32        Alignment;
82   UINT32        FinalAlignment;
83 
84   FinalAlignment = 0;
85   for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
86     if ((EngineMask & LShiftU64(1, Index)) == 0) {
87       continue;
88     }
89     Alignment = GetPlmrAlignment (VTdInfo->HostAddressWidth, (UINTN)VTdInfo->VTdEngineAddress[Index]);
90     if (FinalAlignment < Alignment) {
91       FinalAlignment = Alignment;
92     }
93   }
94   return FinalAlignment;
95 }
96 
97 /**
98   Get protected high memory alignment.
99 
100   @param VTdInfo            The VTd engine context information.
101   @param EngineMask         The mask of the VTd engine to be accessed.
102 
103   @return protected high memory alignment.
104 **/
105 UINT64
GetHighMemoryAlignment(IN VTD_INFO * VTdInfo,IN UINT64 EngineMask)106 GetHighMemoryAlignment (
107   IN VTD_INFO      *VTdInfo,
108   IN UINT64        EngineMask
109   )
110 {
111   UINTN         Index;
112   UINT64        Alignment;
113   UINT64        FinalAlignment;
114 
115   FinalAlignment = 0;
116   for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
117     if ((EngineMask & LShiftU64(1, Index)) == 0) {
118       continue;
119     }
120     Alignment = GetPhmrAlignment (VTdInfo->HostAddressWidth, (UINTN)VTdInfo->VTdEngineAddress[Index]);
121     if (FinalAlignment < Alignment) {
122       FinalAlignment = Alignment;
123     }
124   }
125   return FinalAlignment;
126 }
127 
128 /**
129   Enable PMR in the VTd engine.
130 
131   @param VtdUnitBaseAddress The base address of the VTd engine.
132 
133   @retval EFI_SUCCESS      The PMR is enabled.
134   @retval EFI_UNSUPPORTED  The PMR is not supported.
135 **/
136 EFI_STATUS
EnablePmr(IN UINTN VtdUnitBaseAddress)137 EnablePmr (
138   IN UINTN         VtdUnitBaseAddress
139   )
140 {
141   UINT32        Reg32;
142   VTD_CAP_REG   CapReg;
143 
144   DEBUG ((DEBUG_INFO, "EnablePmr - %x\n", VtdUnitBaseAddress));
145 
146   CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);
147   if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {
148     return EFI_UNSUPPORTED;
149   }
150 
151   Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
152   if (Reg32 == 0xFFFFFFFF) {
153     DEBUG ((DEBUG_ERROR, "R_PMEN_ENABLE_REG - 0x%x\n", Reg32));
154     ASSERT(FALSE);
155   }
156 
157   if ((Reg32 & BIT0) == 0) {
158     MmioWrite32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG, BIT31);
159     do {
160       Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
161     } while((Reg32 & BIT0) == 0);
162   }
163 
164   DEBUG ((DEBUG_INFO, "EnablePmr - Done\n"));
165 
166   return EFI_SUCCESS;
167 }
168 
169 /**
170   Disable PMR in the VTd engine.
171 
172   @param VtdUnitBaseAddress The base address of the VTd engine.
173 
174   @retval EFI_SUCCESS      The PMR is disabled.
175   @retval EFI_UNSUPPORTED  The PMR is not supported.
176 **/
177 EFI_STATUS
DisablePmr(IN UINTN VtdUnitBaseAddress)178 DisablePmr (
179   IN UINTN         VtdUnitBaseAddress
180   )
181 {
182   UINT32        Reg32;
183   VTD_CAP_REG   CapReg;
184 
185   CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);
186   if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {
187     return EFI_UNSUPPORTED;
188   }
189 
190   Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
191   if (Reg32 == 0xFFFFFFFF) {
192     DEBUG ((DEBUG_ERROR, "R_PMEN_ENABLE_REG - 0x%x\n", Reg32));
193     ASSERT(FALSE);
194   }
195 
196   if ((Reg32 & BIT0) != 0) {
197     MmioWrite32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG, 0x0);
198     do {
199       Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
200     } while((Reg32 & BIT0) != 0);
201   }
202 
203   return EFI_SUCCESS;
204 }
205 
206 /**
207   Set PMR region in the VTd engine.
208 
209   @param HostAddressWidth   The host address width.
210   @param VtdUnitBaseAddress The base address of the VTd engine.
211   @param LowMemoryBase      The protected low memory region base.
212   @param LowMemoryLength    The protected low memory region length.
213   @param HighMemoryBase     The protected high memory region base.
214   @param HighMemoryLength   The protected high memory region length.
215 
216   @retval EFI_SUCCESS      The PMR is set to protected region.
217   @retval EFI_UNSUPPORTED  The PMR is not supported.
218 **/
219 EFI_STATUS
SetPmrRegion(IN UINT8 HostAddressWidth,IN UINTN VtdUnitBaseAddress,IN UINT32 LowMemoryBase,IN UINT32 LowMemoryLength,IN UINT64 HighMemoryBase,IN UINT64 HighMemoryLength)220 SetPmrRegion (
221   IN UINT8         HostAddressWidth,
222   IN UINTN         VtdUnitBaseAddress,
223   IN UINT32        LowMemoryBase,
224   IN UINT32        LowMemoryLength,
225   IN UINT64        HighMemoryBase,
226   IN UINT64        HighMemoryLength
227   )
228 {
229   VTD_CAP_REG   CapReg;
230   UINT32        PlmrAlignment;
231   UINT64        PhmrAlignment;
232 
233   DEBUG ((DEBUG_INFO, "VtdUnitBaseAddress - 0x%x\n", VtdUnitBaseAddress));
234 
235   CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);
236   if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {
237     DEBUG ((DEBUG_ERROR, "PLMR/PHMR unsupported\n"));
238     return EFI_UNSUPPORTED;
239   }
240 
241   PlmrAlignment = GetPlmrAlignment (HostAddressWidth, VtdUnitBaseAddress);
242   DEBUG ((DEBUG_INFO, "PlmrAlignment - 0x%x\n", PlmrAlignment));
243   PhmrAlignment = GetPhmrAlignment (HostAddressWidth, VtdUnitBaseAddress);
244   DEBUG ((DEBUG_INFO, "PhmrAlignment - 0x%lx\n", PhmrAlignment));
245 
246   if ((LowMemoryBase    != ALIGN_VALUE(LowMemoryBase, PlmrAlignment)) ||
247       (LowMemoryLength  != ALIGN_VALUE(LowMemoryLength, PlmrAlignment)) ||
248       (HighMemoryBase   != ALIGN_VALUE(HighMemoryBase, PhmrAlignment)) ||
249       (HighMemoryLength != ALIGN_VALUE(HighMemoryLength, PhmrAlignment))) {
250     DEBUG ((DEBUG_ERROR, "PLMR/PHMR alignment issue\n"));
251     return EFI_UNSUPPORTED;
252   }
253 
254   if (LowMemoryBase == 0 && LowMemoryLength == 0) {
255     LowMemoryBase = 0xFFFFFFFF;
256   }
257   if (HighMemoryBase == 0 && HighMemoryLength == 0) {
258     HighMemoryBase = 0xFFFFFFFFFFFFFFFF;
259   }
260 
261   MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG,    LowMemoryBase);
262   MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_LIMITE_REG,  LowMemoryBase + LowMemoryLength - 1);
263   DEBUG ((DEBUG_INFO, "PLMR set done\n"));
264   MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG,   HighMemoryBase);
265   MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_LIMITE_REG, HighMemoryBase + HighMemoryLength - 1);
266   DEBUG ((DEBUG_INFO, "PHMR set done\n"));
267 
268   return EFI_SUCCESS;
269 }
270 
271 /**
272   Set DMA protected region.
273 
274   @param VTdInfo            The VTd engine context information.
275   @param EngineMask         The mask of the VTd engine to be accessed.
276   @param LowMemoryBase      The protected low memory region base.
277   @param LowMemoryLength    The protected low memory region length.
278   @param HighMemoryBase     The protected high memory region base.
279   @param HighMemoryLength   The protected high memory region length.
280 
281   @retval EFI_SUCCESS      The DMA protection is set.
282   @retval EFI_UNSUPPORTED  The DMA protection is not set.
283 **/
284 EFI_STATUS
SetDmaProtectedRange(IN VTD_INFO * VTdInfo,IN UINT64 EngineMask,IN UINT32 LowMemoryBase,IN UINT32 LowMemoryLength,IN UINT64 HighMemoryBase,IN UINT64 HighMemoryLength)285 SetDmaProtectedRange (
286   IN VTD_INFO      *VTdInfo,
287   IN UINT64        EngineMask,
288   IN UINT32        LowMemoryBase,
289   IN UINT32        LowMemoryLength,
290   IN UINT64        HighMemoryBase,
291   IN UINT64        HighMemoryLength
292   )
293 {
294   UINTN       Index;
295   EFI_STATUS  Status;
296 
297   DEBUG ((DEBUG_INFO, "SetDmaProtectedRange(0x%lx) - [0x%x, 0x%x] [0x%016lx, 0x%016lx]\n", EngineMask, LowMemoryBase, LowMemoryLength, HighMemoryBase, HighMemoryLength));
298 
299   for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
300     if ((EngineMask & LShiftU64(1, Index)) == 0) {
301       continue;
302     }
303     DisablePmr ((UINTN)VTdInfo->VTdEngineAddress[Index]);
304     Status = SetPmrRegion (
305                VTdInfo->HostAddressWidth,
306                (UINTN)VTdInfo->VTdEngineAddress[Index],
307                LowMemoryBase,
308                LowMemoryLength,
309                HighMemoryBase,
310                HighMemoryLength
311                );
312     if (EFI_ERROR(Status)) {
313       return Status;
314     }
315     Status = EnablePmr ((UINTN)VTdInfo->VTdEngineAddress[Index]);
316     if (EFI_ERROR(Status)) {
317       return Status;
318     }
319   }
320 
321   return EFI_SUCCESS;
322 }
323 
324 /**
325   Diable DMA protection.
326 
327   @param VTdInfo            The VTd engine context information.
328   @param EngineMask         The mask of the VTd engine to be accessed.
329 
330   @retval EFI_SUCCESS DMA protection is disabled.
331 **/
332 EFI_STATUS
DisableDmaProtection(IN VTD_INFO * VTdInfo,IN UINT64 EngineMask)333 DisableDmaProtection (
334   IN VTD_INFO      *VTdInfo,
335   IN UINT64        EngineMask
336   )
337 {
338   UINTN       Index;
339   EFI_STATUS  Status;
340 
341   DEBUG ((DEBUG_INFO, "DisableDmaProtection - 0x%lx\n", EngineMask));
342 
343   for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
344     DEBUG ((DEBUG_INFO, "Disabling...%d\n", Index));
345 
346     if ((EngineMask & LShiftU64(1, Index)) == 0) {
347       continue;
348     }
349     Status = DisablePmr ((UINTN)VTdInfo->VTdEngineAddress[Index]);
350     if (EFI_ERROR(Status)) {
351       return Status;
352     }
353   }
354 
355   return EFI_SUCCESS;
356 }
357 
358 /**
359   Return if the PMR is enabled.
360 
361   @param VtdUnitBaseAddress The base address of the VTd engine.
362 
363   @retval TRUE  PMR is enabled.
364   @retval FALSE PMR is disabled or unsupported.
365 **/
366 BOOLEAN
IsPmrEnabled(IN UINTN VtdUnitBaseAddress)367 IsPmrEnabled (
368   IN UINTN         VtdUnitBaseAddress
369   )
370 {
371   UINT32        Reg32;
372   VTD_CAP_REG   CapReg;
373 
374   CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);
375   if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {
376     return FALSE;
377   }
378 
379   Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
380   if ((Reg32 & BIT0) == 0) {
381     return FALSE;
382   }
383 
384   return TRUE;
385 }
386 
387 /**
388   Return the mask of the VTd engine which is enabled.
389 
390   @param VTdInfo            The VTd engine context information.
391   @param EngineMask         The mask of the VTd engine to be accessed.
392 
393   @return the mask of the VTd engine which is enabled.
394 **/
395 UINT64
GetDmaProtectionEnabledEngineMask(IN VTD_INFO * VTdInfo,IN UINT64 EngineMask)396 GetDmaProtectionEnabledEngineMask (
397   IN VTD_INFO      *VTdInfo,
398   IN UINT64        EngineMask
399   )
400 {
401   UINTN       Index;
402   BOOLEAN     Result;
403   UINT64      EnabledEngineMask;
404 
405   DEBUG ((DEBUG_INFO, "GetDmaProtectionEnabledEngineMask - 0x%lx\n", EngineMask));
406 
407   EnabledEngineMask = 0;
408   for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
409     if ((EngineMask & LShiftU64(1, Index)) == 0) {
410       continue;
411     }
412     Result = IsPmrEnabled ((UINTN)VTdInfo->VTdEngineAddress[Index]);
413     if (Result) {
414       EnabledEngineMask |= LShiftU64(1, Index);
415     }
416   }
417 
418   DEBUG ((DEBUG_INFO, "EnabledEngineMask - 0x%lx\n", EnabledEngineMask));
419   return EnabledEngineMask;
420 }
421