1 /** @file
2 
3   Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
4   SPDX-License-Identifier: BSD-2-Clause-Patent
5 
6 **/
7 
8 #include <Uefi.h>
9 #include <PiPei.h>
10 #include <Library/BaseLib.h>
11 #include <Library/BaseMemoryLib.h>
12 #include <Library/MemoryAllocationLib.h>
13 #include <Library/IoLib.h>
14 #include <Library/DebugLib.h>
15 #include <Library/PeiServicesLib.h>
16 #include <Library/HobLib.h>
17 #include <IndustryStandard/Vtd.h>
18 #include <Ppi/IoMmu.h>
19 #include <Ppi/VtdInfo.h>
20 #include <Ppi/MemoryDiscovered.h>
21 #include <Ppi/EndOfPeiPhase.h>
22 #include <Guid/VtdPmrInfoHob.h>
23 #include <Library/CacheMaintenanceLib.h>
24 #include "IntelVTdDmarPei.h"
25 
26 #define ALIGN_VALUE_UP(Value, Alignment)  (((Value) + (Alignment) - 1) & (~((Alignment) - 1)))
27 #define ALIGN_VALUE_LOW(Value, Alignment) ((Value) & (~((Alignment) - 1)))
28 
29 #define VTD_64BITS_ADDRESS(Lo, Hi) (LShiftU64 (Lo, 12) | LShiftU64 (Hi, 32))
30 
31 /**
32   Allocate zero pages.
33 
34   @param[in]  Pages             the number of pages.
35 
36   @return the page address.
37   @retval NULL No resource to allocate pages.
38 **/
39 VOID *
40 EFIAPI
AllocateZeroPages(IN UINTN Pages)41 AllocateZeroPages (
42   IN UINTN                      Pages
43   )
44 {
45   VOID *Addr;
46 
47   Addr = AllocatePages (Pages);
48   if (Addr == NULL) {
49     return NULL;
50   }
51   ZeroMem (Addr, EFI_PAGES_TO_SIZE (Pages));
52   return Addr;
53 }
54 
55 /**
56   Set second level paging entry attribute based upon IoMmuAccess.
57 
58   @param[in]  PtEntry           The paging entry.
59   @param[in]  IoMmuAccess       The IOMMU access.
60 **/
61 VOID
SetSecondLevelPagingEntryAttribute(IN VTD_SECOND_LEVEL_PAGING_ENTRY * PtEntry,IN UINT64 IoMmuAccess)62 SetSecondLevelPagingEntryAttribute (
63   IN VTD_SECOND_LEVEL_PAGING_ENTRY      *PtEntry,
64   IN UINT64                             IoMmuAccess
65   )
66 {
67   PtEntry->Bits.Read  = ((IoMmuAccess & EDKII_IOMMU_ACCESS_READ) != 0);
68   PtEntry->Bits.Write = ((IoMmuAccess & EDKII_IOMMU_ACCESS_WRITE) != 0);
69   DEBUG ((DEBUG_VERBOSE, "SetSecondLevelPagingEntryAttribute - 0x%x - 0x%x\n", PtEntry, IoMmuAccess));
70 }
71 
72 /**
73   Create second level paging entry table.
74 
75   @param[in]  VTdUnitInfo               The VTd engine unit information.
76   @param[in]  SecondLevelPagingEntry    The second level paging entry.
77   @param[in]  MemoryBase                The base of the memory.
78   @param[in]  MemoryLimit               The limit of the memory.
79   @param[in]  IoMmuAccess               The IOMMU access.
80 
81   @return The second level paging entry.
82 **/
83 VTD_SECOND_LEVEL_PAGING_ENTRY *
CreateSecondLevelPagingEntryTable(IN VTD_UNIT_INFO * VTdUnitInfo,IN VTD_SECOND_LEVEL_PAGING_ENTRY * SecondLevelPagingEntry,IN UINT64 MemoryBase,IN UINT64 MemoryLimit,IN UINT64 IoMmuAccess)84 CreateSecondLevelPagingEntryTable (
85   IN VTD_UNIT_INFO                      *VTdUnitInfo,
86   IN VTD_SECOND_LEVEL_PAGING_ENTRY      *SecondLevelPagingEntry,
87   IN UINT64                             MemoryBase,
88   IN UINT64                             MemoryLimit,
89   IN UINT64                             IoMmuAccess
90   )
91 {
92   UINTN                                 Index5;
93   UINTN                                 Index4;
94   UINTN                                 Index3;
95   UINTN                                 Index2;
96   UINTN                                 Lvl5Start;
97   UINTN                                 Lvl5End;
98   UINTN                                 Lvl4PagesStart;
99   UINTN                                 Lvl4PagesEnd;
100   UINTN                                 Lvl4Start;
101   UINTN                                 Lvl4End;
102   UINTN                                 Lvl3Start;
103   UINTN                                 Lvl3End;
104   VTD_SECOND_LEVEL_PAGING_ENTRY         *Lvl5PtEntry;
105   VTD_SECOND_LEVEL_PAGING_ENTRY         *Lvl4PtEntry;
106   VTD_SECOND_LEVEL_PAGING_ENTRY         *Lvl3PtEntry;
107   VTD_SECOND_LEVEL_PAGING_ENTRY         *Lvl2PtEntry;
108   UINT64                                BaseAddress;
109   UINT64                                EndAddress;
110   BOOLEAN                               Is5LevelPaging;
111 
112   if (MemoryLimit == 0) {
113     return EFI_SUCCESS;
114   }
115 
116   BaseAddress = ALIGN_VALUE_LOW (MemoryBase, SIZE_2MB);
117   EndAddress = ALIGN_VALUE_UP (MemoryLimit, SIZE_2MB);
118   DEBUG ((DEBUG_INFO, "CreateSecondLevelPagingEntryTable: BaseAddress - 0x%016lx, EndAddress - 0x%016lx\n", BaseAddress, EndAddress));
119 
120   if (SecondLevelPagingEntry == NULL) {
121     SecondLevelPagingEntry = AllocateZeroPages (1);
122     if (SecondLevelPagingEntry == NULL) {
123       DEBUG ((DEBUG_ERROR, "Could not Alloc LVL4 or LVL5 PT. \n"));
124       return NULL;
125     }
126     FlushPageTableMemory (VTdUnitInfo, (UINTN) SecondLevelPagingEntry, EFI_PAGES_TO_SIZE (1));
127   }
128 
129   DEBUG ((DEBUG_INFO, " SecondLevelPagingEntry:0x%016lx\n", SecondLevelPagingEntry));
130   //
131   // If no access is needed, just create not present entry.
132   //
133   if (IoMmuAccess == 0) {
134     DEBUG ((DEBUG_INFO, " SecondLevelPagingEntry:0x%016lx\n", (UINTN) SecondLevelPagingEntry));
135     return SecondLevelPagingEntry;
136   }
137 
138   Is5LevelPaging = VTdUnitInfo->Is5LevelPaging;
139 
140   if (Is5LevelPaging) {
141     Lvl5Start = RShiftU64 (BaseAddress, 48) & 0x1FF;
142     Lvl5End = RShiftU64 (EndAddress - 1, 48) & 0x1FF;
143     DEBUG ((DEBUG_INFO, "  Lvl5Start - 0x%x, Lvl5End - 0x%x\n", Lvl5Start, Lvl5End));
144 
145     Lvl4Start = RShiftU64 (BaseAddress, 39) & 0x1FF;
146     Lvl4End = RShiftU64 (EndAddress - 1, 39) & 0x1FF;
147 
148     Lvl4PagesStart = (Lvl5Start<<9) | Lvl4Start;
149     Lvl4PagesEnd = (Lvl5End<<9) | Lvl4End;
150     DEBUG ((DEBUG_INFO, "  Lvl4PagesStart - 0x%x, Lvl4PagesEnd - 0x%x\n", Lvl4PagesStart, Lvl4PagesEnd));
151 
152     Lvl5PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) SecondLevelPagingEntry;
153   } else {
154     Lvl5Start = RShiftU64 (BaseAddress, 48) & 0x1FF;
155     Lvl5End = Lvl5Start;
156 
157     Lvl4Start = RShiftU64 (BaseAddress, 39) & 0x1FF;
158     Lvl4End = RShiftU64 (EndAddress - 1, 39) & 0x1FF;
159     DEBUG ((DEBUG_INFO, "  Lvl4Start - 0x%x, Lvl4End - 0x%x\n", Lvl4Start, Lvl4End));
160 
161     Lvl4PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) SecondLevelPagingEntry;
162   }
163 
164   for (Index5 = Lvl5Start; Index5 <= Lvl5End; Index5++) {
165     if (Is5LevelPaging) {
166       if (Lvl5PtEntry[Index5].Uint64 == 0) {
167         Lvl5PtEntry[Index5].Uint64 = (UINT64) (UINTN) AllocateZeroPages (1);
168         if (Lvl5PtEntry[Index5].Uint64 == 0) {
169           DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL4 PAGE FAIL (0x%x)!!!!!!\n", Index5));
170           ASSERT (FALSE);
171           return NULL;
172         }
173         FlushPageTableMemory (VTdUnitInfo, (UINTN) Lvl5PtEntry[Index5].Uint64, SIZE_4KB);
174         SetSecondLevelPagingEntryAttribute (&Lvl5PtEntry[Index5], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);
175       }
176       Lvl4Start = Lvl4PagesStart & 0x1FF;
177       if (((Index5+1)<<9) > Lvl4PagesEnd) {
178         Lvl4End = SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY) - 1;;
179         Lvl4PagesStart = (Index5+1)<<9;
180       } else {
181         Lvl4End = Lvl4PagesEnd & 0x1FF;
182       }
183       DEBUG ((DEBUG_INFO, "  Lvl5(0x%x): Lvl4Start - 0x%x, Lvl4End - 0x%x\n", Index5, Lvl4Start, Lvl4End));
184       Lvl4PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(Lvl5PtEntry[Index5].Bits.AddressLo, Lvl5PtEntry[Index5].Bits.AddressHi);
185     }
186 
187     for (Index4 = Lvl4Start; Index4 <= Lvl4End; Index4++) {
188       if (Lvl4PtEntry[Index4].Uint64 == 0) {
189         Lvl4PtEntry[Index4].Uint64 = (UINT64) (UINTN) AllocateZeroPages (1);
190         if (Lvl4PtEntry[Index4].Uint64 == 0) {
191           DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL4 PAGE FAIL (0x%x)!!!!!!\n", Index4));
192           ASSERT(FALSE);
193           return NULL;
194         }
195         FlushPageTableMemory (VTdUnitInfo, (UINTN) Lvl4PtEntry[Index4].Uint64, SIZE_4KB);
196         SetSecondLevelPagingEntryAttribute (&Lvl4PtEntry[Index4], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);
197       }
198 
199       Lvl3Start = RShiftU64 (BaseAddress, 30) & 0x1FF;
200       if (ALIGN_VALUE_LOW(BaseAddress + SIZE_1GB, SIZE_1GB) <= EndAddress) {
201         Lvl3End = SIZE_4KB / sizeof (VTD_SECOND_LEVEL_PAGING_ENTRY) - 1;
202       } else {
203         Lvl3End = RShiftU64 (EndAddress - 1, 30) & 0x1FF;
204       }
205       DEBUG ((DEBUG_INFO, "  Lvl4(0x%x): Lvl3Start - 0x%x, Lvl3End - 0x%x\n", Index4, Lvl3Start, Lvl3End));
206 
207       Lvl3PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(Lvl4PtEntry[Index4].Bits.AddressLo, Lvl4PtEntry[Index4].Bits.AddressHi);
208       for (Index3 = Lvl3Start; Index3 <= Lvl3End; Index3++) {
209         if (Lvl3PtEntry[Index3].Uint64 == 0) {
210           Lvl3PtEntry[Index3].Uint64 = (UINT64) (UINTN) AllocateZeroPages (1);
211           if (Lvl3PtEntry[Index3].Uint64 == 0) {
212             DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL3 PAGE FAIL (0x%x, 0x%x)!!!!!!\n", Index4, Index3));
213             ASSERT(FALSE);
214             return NULL;
215           }
216           FlushPageTableMemory (VTdUnitInfo, (UINTN) Lvl3PtEntry[Index3].Uint64, SIZE_4KB);
217           SetSecondLevelPagingEntryAttribute (&Lvl3PtEntry[Index3], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);
218         }
219 
220         Lvl2PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(Lvl3PtEntry[Index3].Bits.AddressLo, Lvl3PtEntry[Index3].Bits.AddressHi);
221         for (Index2 = 0; Index2 < SIZE_4KB/sizeof(VTD_SECOND_LEVEL_PAGING_ENTRY); Index2++) {
222           Lvl2PtEntry[Index2].Uint64 = BaseAddress;
223           SetSecondLevelPagingEntryAttribute (&Lvl2PtEntry[Index2], IoMmuAccess);
224           Lvl2PtEntry[Index2].Bits.PageSize = 1;
225           BaseAddress += SIZE_2MB;
226           if (BaseAddress >= MemoryLimit) {
227             break;
228           }
229         }
230         FlushPageTableMemory (VTdUnitInfo, (UINTN) Lvl2PtEntry, SIZE_4KB);
231         if (BaseAddress >= MemoryLimit) {
232           break;
233         }
234       }
235       FlushPageTableMemory (VTdUnitInfo, (UINTN) &Lvl3PtEntry[Lvl3Start], (UINTN) &Lvl3PtEntry[Lvl3End + 1] - (UINTN) &Lvl3PtEntry[Lvl3Start]);
236       if (BaseAddress >= MemoryLimit) {
237         break;
238       }
239     }
240     FlushPageTableMemory (VTdUnitInfo, (UINTN) &Lvl4PtEntry[Lvl4Start], (UINTN) &Lvl4PtEntry[Lvl4End + 1] - (UINTN) &Lvl4PtEntry[Lvl4Start]);
241   }
242   FlushPageTableMemory (VTdUnitInfo, (UINTN) &Lvl5PtEntry[Lvl5Start], (UINTN) &Lvl5PtEntry[Lvl5End + 1] - (UINTN) &Lvl5PtEntry[Lvl5Start]);
243 
244   DEBUG ((DEBUG_INFO, " SecondLevelPagingEntry:0x%016lx\n", (UINTN)SecondLevelPagingEntry));
245   return SecondLevelPagingEntry;
246 }
247 
248 /**
249   Create context entry.
250 
251   @param[in]  VTdUnitInfo       The VTd engine unit information.
252 
253   @retval EFI_SUCCESS           The context entry is created.
254   @retval EFI_OUT_OF_RESOURCE   No enough resource to create context entry.
255 
256 **/
257 EFI_STATUS
CreateContextEntry(IN VTD_UNIT_INFO * VTdUnitInfo)258 CreateContextEntry (
259   IN VTD_UNIT_INFO              *VTdUnitInfo
260   )
261 {
262   UINTN                         RootPages;
263   UINTN                         ContextPages;
264   UINTN                         EntryTablePages;
265   VOID                          *Buffer;
266   UINTN                         RootIndex;
267   UINTN                         ContextIndex;
268   VTD_ROOT_ENTRY                *RootEntryBase;
269   VTD_ROOT_ENTRY                *RootEntry;
270   VTD_CONTEXT_ENTRY             *ContextEntryTable;
271   VTD_CONTEXT_ENTRY             *ContextEntry;
272   VTD_SOURCE_ID                 SourceId;
273   VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry;
274   UINT64                        Pt;
275 
276   RootPages = EFI_SIZE_TO_PAGES (sizeof (VTD_ROOT_ENTRY) * VTD_ROOT_ENTRY_NUMBER);
277   ContextPages = EFI_SIZE_TO_PAGES (sizeof (VTD_CONTEXT_ENTRY) * VTD_CONTEXT_ENTRY_NUMBER);
278   EntryTablePages = RootPages + ContextPages * (VTD_ROOT_ENTRY_NUMBER);
279   Buffer = AllocateZeroPages (EntryTablePages);
280   if (Buffer == NULL) {
281     DEBUG ((DEBUG_ERROR, "Could not Alloc Root Entry Table.. \n"));
282     return EFI_OUT_OF_RESOURCES;
283   }
284 
285   DEBUG ((DEBUG_ERROR, "RootEntryTable address - 0x%x\n", Buffer));
286   VTdUnitInfo->RootEntryTable = (UINT32) (UINTN) Buffer;
287   VTdUnitInfo->RootEntryTablePageSize = (UINT16) EntryTablePages;
288   RootEntryBase = (VTD_ROOT_ENTRY *) Buffer;
289   Buffer = (UINT8 *) Buffer + EFI_PAGES_TO_SIZE (RootPages);
290 
291   if (VTdUnitInfo->FixedSecondLevelPagingEntry == 0) {
292     DEBUG ((DEBUG_ERROR, "FixedSecondLevelPagingEntry is empty\n"));
293     ASSERT(FALSE);
294   }
295 
296   for (RootIndex = 0; RootIndex < VTD_ROOT_ENTRY_NUMBER; RootIndex++) {
297     SourceId.Index.RootIndex = (UINT8) RootIndex;
298 
299     RootEntry = &RootEntryBase[SourceId.Index.RootIndex];
300     RootEntry->Bits.ContextTablePointerLo  = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 12);
301     RootEntry->Bits.ContextTablePointerHi  = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 32);
302     RootEntry->Bits.Present = 1;
303     Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (ContextPages);
304     ContextEntryTable = (VTD_CONTEXT_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(RootEntry->Bits.ContextTablePointerLo, RootEntry->Bits.ContextTablePointerHi) ;
305 
306     for (ContextIndex = 0; ContextIndex < VTD_CONTEXT_ENTRY_NUMBER; ContextIndex++) {
307       SourceId.Index.ContextIndex = (UINT8) ContextIndex;
308       ContextEntry = &ContextEntryTable[SourceId.Index.ContextIndex];
309 
310       ContextEntry->Bits.TranslationType = 0;
311       ContextEntry->Bits.FaultProcessingDisable = 0;
312       ContextEntry->Bits.Present = 0;
313 
314       ContextEntry->Bits.AddressWidth = VTdUnitInfo->Is5LevelPaging ? 0x3 : 0x2;
315 
316       if (VTdUnitInfo->FixedSecondLevelPagingEntry != 0) {
317         SecondLevelPagingEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTdUnitInfo->FixedSecondLevelPagingEntry;
318         Pt = (UINT64)RShiftU64 ((UINT64) (UINTN) SecondLevelPagingEntry, 12);
319         ContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt;
320         ContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20);
321         ContextEntry->Bits.DomainIdentifier = ((1 << (UINT8)((UINTN)VTdUnitInfo->CapReg.Bits.ND * 2 + 4)) - 1);
322         ContextEntry->Bits.Present = 1;
323       }
324     }
325   }
326 
327   FlushPageTableMemory (VTdUnitInfo, VTdUnitInfo->RootEntryTable, EFI_PAGES_TO_SIZE(EntryTablePages));
328 
329   return EFI_SUCCESS;
330 }
331 
332 /**
333   Create extended context entry.
334 
335   @param[in]  VTdUnitInfo       The VTd engine unit information.
336 
337   @retval EFI_SUCCESS           The extended context entry is created.
338   @retval EFI_OUT_OF_RESOURCE   No enough resource to create extended context entry.
339 **/
340 EFI_STATUS
CreateExtContextEntry(IN VTD_UNIT_INFO * VTdUnitInfo)341 CreateExtContextEntry (
342   IN VTD_UNIT_INFO              *VTdUnitInfo
343   )
344 {
345   UINTN                         RootPages;
346   UINTN                         ContextPages;
347   UINTN                         EntryTablePages;
348   VOID                          *Buffer;
349   UINTN                         RootIndex;
350   UINTN                         ContextIndex;
351   VTD_EXT_ROOT_ENTRY            *ExtRootEntryBase;
352   VTD_EXT_ROOT_ENTRY            *ExtRootEntry;
353   VTD_EXT_CONTEXT_ENTRY         *ExtContextEntryTable;
354   VTD_EXT_CONTEXT_ENTRY         *ExtContextEntry;
355   VTD_SOURCE_ID                 SourceId;
356   VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry;
357   UINT64                        Pt;
358 
359   RootPages = EFI_SIZE_TO_PAGES (sizeof (VTD_EXT_ROOT_ENTRY) * VTD_ROOT_ENTRY_NUMBER);
360   ContextPages = EFI_SIZE_TO_PAGES (sizeof (VTD_EXT_CONTEXT_ENTRY) * VTD_CONTEXT_ENTRY_NUMBER);
361   EntryTablePages = RootPages + ContextPages * (VTD_ROOT_ENTRY_NUMBER);
362   Buffer = AllocateZeroPages (EntryTablePages);
363   if (Buffer == NULL) {
364     DEBUG ((DEBUG_INFO, "Could not Alloc Root Entry Table !\n"));
365     return EFI_OUT_OF_RESOURCES;
366   }
367 
368   DEBUG ((DEBUG_ERROR, "ExtRootEntryTable address - 0x%x\n", Buffer));
369   VTdUnitInfo->ExtRootEntryTable = (UINT32) (UINTN) Buffer;
370   VTdUnitInfo->ExtRootEntryTablePageSize = (UINT16) EntryTablePages;
371   ExtRootEntryBase = (VTD_EXT_ROOT_ENTRY *) Buffer;
372   Buffer = (UINT8 *) Buffer + EFI_PAGES_TO_SIZE (RootPages);
373 
374   if (VTdUnitInfo->FixedSecondLevelPagingEntry == 0) {
375     DEBUG ((DEBUG_ERROR, "FixedSecondLevelPagingEntry is empty\n"));
376     ASSERT(FALSE);
377   }
378 
379   for (RootIndex = 0; RootIndex < VTD_ROOT_ENTRY_NUMBER; RootIndex++) {
380     SourceId.Index.RootIndex = (UINT8)RootIndex;
381 
382     ExtRootEntry = &ExtRootEntryBase[SourceId.Index.RootIndex];
383     ExtRootEntry->Bits.LowerContextTablePointerLo  = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 12);
384     ExtRootEntry->Bits.LowerContextTablePointerHi  = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 32);
385     ExtRootEntry->Bits.LowerPresent = 1;
386     ExtRootEntry->Bits.UpperContextTablePointerLo  = (UINT32) RShiftU64 ((UINT64) (UINTN) Buffer, 12) + 1;
387     ExtRootEntry->Bits.UpperContextTablePointerHi  = (UINT32) RShiftU64 (RShiftU64 ((UINT64) (UINTN) Buffer, 12) + 1, 20);
388     ExtRootEntry->Bits.UpperPresent = 1;
389     Buffer = (UINT8 *) Buffer + EFI_PAGES_TO_SIZE (ContextPages);
390     ExtContextEntryTable = (VTD_EXT_CONTEXT_ENTRY *) (UINTN) VTD_64BITS_ADDRESS (ExtRootEntry->Bits.LowerContextTablePointerLo, ExtRootEntry->Bits.LowerContextTablePointerHi) ;
391 
392     for (ContextIndex = 0; ContextIndex < VTD_CONTEXT_ENTRY_NUMBER; ContextIndex++) {
393       SourceId.Index.ContextIndex = (UINT8) ContextIndex;
394       ExtContextEntry = &ExtContextEntryTable[SourceId.Index.ContextIndex];
395 
396       ExtContextEntry->Bits.TranslationType = 0;
397       ExtContextEntry->Bits.FaultProcessingDisable = 0;
398       ExtContextEntry->Bits.Present = 0;
399 
400       ExtContextEntry->Bits.AddressWidth = VTdUnitInfo->Is5LevelPaging ? 0x3 : 0x2;
401 
402       if (VTdUnitInfo->FixedSecondLevelPagingEntry != 0) {
403         SecondLevelPagingEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTdUnitInfo->FixedSecondLevelPagingEntry;
404         Pt = (UINT64)RShiftU64 ((UINT64) (UINTN) SecondLevelPagingEntry, 12);
405 
406         ExtContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt;
407         ExtContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20);
408         ExtContextEntry->Bits.DomainIdentifier = ((1 << (UINT8) ((UINTN) VTdUnitInfo->CapReg.Bits.ND * 2 + 4)) - 1);
409         ExtContextEntry->Bits.Present = 1;
410       }
411     }
412   }
413 
414   FlushPageTableMemory (VTdUnitInfo, VTdUnitInfo->ExtRootEntryTable, EFI_PAGES_TO_SIZE(EntryTablePages));
415 
416   return EFI_SUCCESS;
417 }
418 
419 #define VTD_PG_R                   BIT0
420 #define VTD_PG_W                   BIT1
421 #define VTD_PG_X                   BIT2
422 #define VTD_PG_EMT                 (BIT3 | BIT4 | BIT5)
423 #define VTD_PG_TM                  (BIT62)
424 
425 #define VTD_PG_PS                  BIT7
426 
427 #define PAGE_PROGATE_BITS          (VTD_PG_TM | VTD_PG_EMT | VTD_PG_W | VTD_PG_R)
428 
429 #define PAGING_4K_MASK  0xFFF
430 #define PAGING_2M_MASK  0x1FFFFF
431 #define PAGING_1G_MASK  0x3FFFFFFF
432 
433 #define PAGING_VTD_INDEX_MASK     0x1FF
434 
435 #define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
436 #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
437 #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
438 
439 typedef enum {
440   PageNone,
441   Page4K,
442   Page2M,
443   Page1G,
444 } PAGE_ATTRIBUTE;
445 
446 typedef struct {
447   PAGE_ATTRIBUTE   Attribute;
448   UINT64           Length;
449   UINT64           AddressMask;
450 } PAGE_ATTRIBUTE_TABLE;
451 
452 PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
453   {Page4K,  SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},
454   {Page2M,  SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},
455   {Page1G,  SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},
456 };
457 
458 /**
459   Return length according to page attributes.
460 
461   @param[in]  PageAttributes   The page attribute of the page entry.
462 
463   @return The length of page entry.
464 **/
465 UINTN
PageAttributeToLength(IN PAGE_ATTRIBUTE PageAttribute)466 PageAttributeToLength (
467   IN PAGE_ATTRIBUTE  PageAttribute
468   )
469 {
470   UINTN  Index;
471   for (Index = 0; Index < sizeof (mPageAttributeTable) / sizeof (mPageAttributeTable[0]); Index++) {
472     if (PageAttribute == mPageAttributeTable[Index].Attribute) {
473       return (UINTN) mPageAttributeTable[Index].Length;
474     }
475   }
476   return 0;
477 }
478 
479 /**
480   Return page table entry to match the address.
481 
482   @param[in]   VTdUnitInfo              The VTd engine unit information.
483   @param[in]   SecondLevelPagingEntry   The second level paging entry in VTd table for the device.
484   @param[in]   Address                  The address to be checked.
485   @param[out]  PageAttributes           The page attribute of the page entry.
486 
487   @return The page entry.
488 **/
489 VOID *
GetSecondLevelPageTableEntry(IN VTD_UNIT_INFO * VTdUnitInfo,IN VTD_SECOND_LEVEL_PAGING_ENTRY * SecondLevelPagingEntry,IN PHYSICAL_ADDRESS Address,OUT PAGE_ATTRIBUTE * PageAttribute)490 GetSecondLevelPageTableEntry (
491   IN  VTD_UNIT_INFO                 *VTdUnitInfo,
492   IN  VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry,
493   IN  PHYSICAL_ADDRESS              Address,
494   OUT PAGE_ATTRIBUTE                *PageAttribute
495   )
496 {
497   UINTN                 Index1;
498   UINTN                 Index2;
499   UINTN                 Index3;
500   UINTN                 Index4;
501   UINTN                 Index5;
502   UINT64                *L1PageTable;
503   UINT64                *L2PageTable;
504   UINT64                *L3PageTable;
505   UINT64                *L4PageTable;
506   UINT64                *L5PageTable;
507   BOOLEAN               Is5LevelPaging;
508 
509   Index5 = ((UINTN) RShiftU64 (Address, 48)) & PAGING_VTD_INDEX_MASK;
510   Index4 = ((UINTN) RShiftU64 (Address, 39)) & PAGING_VTD_INDEX_MASK;
511   Index3 = ((UINTN) Address >> 30) & PAGING_VTD_INDEX_MASK;
512   Index2 = ((UINTN) Address >> 21) & PAGING_VTD_INDEX_MASK;
513   Index1 = ((UINTN) Address >> 12) & PAGING_VTD_INDEX_MASK;
514 
515   Is5LevelPaging = VTdUnitInfo->Is5LevelPaging;
516 
517   if (Is5LevelPaging) {
518     L5PageTable = (UINT64 *) SecondLevelPagingEntry;
519     if (L5PageTable[Index5] == 0) {
520       L5PageTable[Index5] = (UINT64) (UINTN) AllocateZeroPages (1);
521       if (L5PageTable[Index5] == 0) {
522         DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL5 PAGE FAIL (0x%x)!!!!!!\n", Index4));
523         ASSERT(FALSE);
524         *PageAttribute = PageNone;
525         return NULL;
526       }
527       FlushPageTableMemory (VTdUnitInfo, (UINTN) L5PageTable[Index5], SIZE_4KB);
528       SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *) &L5PageTable[Index5], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);
529       FlushPageTableMemory (VTdUnitInfo, (UINTN) &L5PageTable[Index5], sizeof(L5PageTable[Index5]));
530     }
531     L4PageTable = (UINT64 *) (UINTN) (L5PageTable[Index5] & PAGING_4K_ADDRESS_MASK_64);
532   } else {
533     L4PageTable = (UINT64 *)SecondLevelPagingEntry;
534   }
535 
536   if (L4PageTable[Index4] == 0) {
537     L4PageTable[Index4] = (UINT64) (UINTN) AllocateZeroPages (1);
538     if (L4PageTable[Index4] == 0) {
539       DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL4 PAGE FAIL (0x%x)!!!!!!\n", Index4));
540       ASSERT(FALSE);
541       *PageAttribute = PageNone;
542       return NULL;
543     }
544     FlushPageTableMemory (VTdUnitInfo, (UINTN) L4PageTable[Index4], SIZE_4KB);
545     SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *) &L4PageTable[Index4], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);
546     FlushPageTableMemory (VTdUnitInfo, (UINTN) &L4PageTable[Index4], sizeof(L4PageTable[Index4]));
547   }
548 
549   L3PageTable = (UINT64 *) (UINTN) (L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64);
550   if (L3PageTable[Index3] == 0) {
551     L3PageTable[Index3] = (UINT64) (UINTN) AllocateZeroPages (1);
552     if (L3PageTable[Index3] == 0) {
553       DEBUG ((DEBUG_ERROR, "!!!!!! ALLOCATE LVL3 PAGE FAIL (0x%x, 0x%x)!!!!!!\n", Index4, Index3));
554       ASSERT(FALSE);
555       *PageAttribute = PageNone;
556       return NULL;
557     }
558     FlushPageTableMemory (VTdUnitInfo, (UINTN) L3PageTable[Index3], SIZE_4KB);
559     SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *) &L3PageTable[Index3], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);
560     FlushPageTableMemory (VTdUnitInfo, (UINTN) &L3PageTable[Index3], sizeof (L3PageTable[Index3]));
561   }
562   if ((L3PageTable[Index3] & VTD_PG_PS) != 0) {
563     // 1G
564     *PageAttribute = Page1G;
565     return &L3PageTable[Index3];
566   }
567 
568   L2PageTable = (UINT64 *) (UINTN) (L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);
569   if (L2PageTable[Index2] == 0) {
570     L2PageTable[Index2] = Address & PAGING_2M_ADDRESS_MASK_64;
571     SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *) &L2PageTable[Index2], 0);
572     L2PageTable[Index2] |= VTD_PG_PS;
573     FlushPageTableMemory (VTdUnitInfo, (UINTN) &L2PageTable[Index2], sizeof (L2PageTable[Index2]));
574   }
575   if ((L2PageTable[Index2] & VTD_PG_PS) != 0) {
576     // 2M
577     *PageAttribute = Page2M;
578     return &L2PageTable[Index2];
579   }
580 
581   // 4k
582   L1PageTable = (UINT64 *) (UINTN) (L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);
583   if ((L1PageTable[Index1] == 0) && (Address != 0)) {
584     *PageAttribute = PageNone;
585     return NULL;
586   }
587   *PageAttribute = Page4K;
588   return &L1PageTable[Index1];
589 }
590 
591 /**
592   Modify memory attributes of page entry.
593 
594   @param[in]   VTdUnitInfo      The VTd engine unit information.
595   @param[in]   PageEntry        The page entry.
596   @param[in]   IoMmuAccess      The IOMMU access.
597   @param[out]  IsModified       TRUE means page table modified. FALSE means page table not modified.
598 **/
599 VOID
ConvertSecondLevelPageEntryAttribute(IN VTD_UNIT_INFO * VTdUnitInfo,IN VTD_SECOND_LEVEL_PAGING_ENTRY * PageEntry,IN UINT64 IoMmuAccess,OUT BOOLEAN * IsModified)600 ConvertSecondLevelPageEntryAttribute (
601   IN VTD_UNIT_INFO                      *VTdUnitInfo,
602   IN  VTD_SECOND_LEVEL_PAGING_ENTRY     *PageEntry,
603   IN  UINT64                            IoMmuAccess,
604   OUT BOOLEAN                           *IsModified
605   )
606 {
607   UINT64  CurrentPageEntry;
608   UINT64  NewPageEntry;
609 
610   CurrentPageEntry = PageEntry->Uint64;
611   SetSecondLevelPagingEntryAttribute (PageEntry, IoMmuAccess);
612   FlushPageTableMemory (VTdUnitInfo, (UINTN) PageEntry, sizeof(*PageEntry));
613   NewPageEntry = PageEntry->Uint64;
614   if (CurrentPageEntry != NewPageEntry) {
615     *IsModified = TRUE;
616     DEBUG ((DEBUG_VERBOSE, "ConvertSecondLevelPageEntryAttribute 0x%lx", CurrentPageEntry));
617     DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));
618   } else {
619     *IsModified = FALSE;
620   }
621 }
622 
623 /**
624   This function returns if there is need to split page entry.
625 
626   @param[in]  BaseAddress      The base address to be checked.
627   @param[in]  Length           The length to be checked.
628   @param[in]  PageAttribute    The page attribute of the page entry.
629 
630   @retval SplitAttributes on if there is need to split page entry.
631 **/
632 PAGE_ATTRIBUTE
NeedSplitPage(IN PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN PAGE_ATTRIBUTE PageAttribute)633 NeedSplitPage (
634   IN  PHYSICAL_ADDRESS                  BaseAddress,
635   IN  UINT64                            Length,
636   IN  PAGE_ATTRIBUTE                    PageAttribute
637   )
638 {
639   UINT64                PageEntryLength;
640 
641   PageEntryLength = PageAttributeToLength (PageAttribute);
642 
643   if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {
644     return PageNone;
645   }
646 
647   if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {
648     return Page4K;
649   }
650 
651   return Page2M;
652 }
653 
654 /**
655   This function splits one page entry to small page entries.
656 
657   @param[in]  VTdUnitInfo       The VTd engine unit information.
658   @param[in]  PageEntry        The page entry to be splitted.
659   @param[in]  PageAttribute    The page attribute of the page entry.
660   @param[in]  SplitAttribute   How to split the page entry.
661 
662   @retval RETURN_SUCCESS            The page entry is splitted.
663   @retval RETURN_UNSUPPORTED        The page entry does not support to be splitted.
664   @retval RETURN_OUT_OF_RESOURCES   No resource to split page entry.
665 **/
666 RETURN_STATUS
SplitSecondLevelPage(IN VTD_UNIT_INFO * VTdUnitInfo,IN VTD_SECOND_LEVEL_PAGING_ENTRY * PageEntry,IN PAGE_ATTRIBUTE PageAttribute,IN PAGE_ATTRIBUTE SplitAttribute)667 SplitSecondLevelPage (
668   IN VTD_UNIT_INFO                      *VTdUnitInfo,
669   IN  VTD_SECOND_LEVEL_PAGING_ENTRY     *PageEntry,
670   IN  PAGE_ATTRIBUTE                    PageAttribute,
671   IN  PAGE_ATTRIBUTE                    SplitAttribute
672   )
673 {
674   UINT64   BaseAddress;
675   UINT64   *NewPageEntry;
676   UINTN    Index;
677 
678   ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);
679 
680   if (PageAttribute == Page2M) {
681     //
682     // Split 2M to 4K
683     //
684     ASSERT (SplitAttribute == Page4K);
685     if (SplitAttribute == Page4K) {
686       NewPageEntry = AllocateZeroPages (1);
687       DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry));
688       if (NewPageEntry == NULL) {
689         return RETURN_OUT_OF_RESOURCES;
690       }
691       BaseAddress = PageEntry->Uint64 & PAGING_2M_ADDRESS_MASK_64;
692       for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
693         NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | (PageEntry->Uint64 & PAGE_PROGATE_BITS);
694       }
695       FlushPageTableMemory (VTdUnitInfo, (UINTN)NewPageEntry, SIZE_4KB);
696 
697       PageEntry->Uint64 = (UINT64)(UINTN)NewPageEntry;
698       SetSecondLevelPagingEntryAttribute (PageEntry, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);
699       FlushPageTableMemory (VTdUnitInfo, (UINTN)PageEntry, sizeof(*PageEntry));
700       return RETURN_SUCCESS;
701     } else {
702       return RETURN_UNSUPPORTED;
703     }
704   } else if (PageAttribute == Page1G) {
705     //
706     // Split 1G to 2M
707     // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
708     //
709     ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);
710     if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {
711       NewPageEntry = AllocateZeroPages (1);
712       DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry));
713       if (NewPageEntry == NULL) {
714         return RETURN_OUT_OF_RESOURCES;
715       }
716       BaseAddress = PageEntry->Uint64 & PAGING_1G_ADDRESS_MASK_64;
717       for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
718         NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | VTD_PG_PS | (PageEntry->Uint64 & PAGE_PROGATE_BITS);
719       }
720       FlushPageTableMemory (VTdUnitInfo, (UINTN)NewPageEntry, SIZE_4KB);
721 
722       PageEntry->Uint64 = (UINT64)(UINTN)NewPageEntry;
723       SetSecondLevelPagingEntryAttribute (PageEntry, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);
724       FlushPageTableMemory (VTdUnitInfo, (UINTN)PageEntry, sizeof(*PageEntry));
725       return RETURN_SUCCESS;
726     } else {
727       return RETURN_UNSUPPORTED;
728     }
729   } else {
730     return RETURN_UNSUPPORTED;
731   }
732 }
733 
734 /**
735   Set VTd attribute for a system memory on second level page entry
736 
737   @param[in]  VTdUnitInfo             The VTd engine unit information.
738   @param[in]  SecondLevelPagingEntry  The second level paging entry in VTd table for the device.
739   @param[in]  BaseAddress             The base of device memory address to be used as the DMA memory.
740   @param[in]  Length                  The length of device memory address to be used as the DMA memory.
741   @param[in]  IoMmuAccess             The IOMMU access.
742 
743   @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range specified by BaseAddress and Length.
744   @retval EFI_INVALID_PARAMETER  BaseAddress is not IoMmu Page size aligned.
745   @retval EFI_INVALID_PARAMETER  Length is not IoMmu Page size aligned.
746   @retval EFI_INVALID_PARAMETER  Length is 0.
747   @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination of access.
748   @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported by the IOMMU.
749   @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range specified by BaseAddress and Length.
750   @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to modify the IOMMU access.
751   @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while attempting the operation.
752 **/
753 EFI_STATUS
SetSecondLevelPagingAttribute(IN VTD_UNIT_INFO * VTdUnitInfo,IN VTD_SECOND_LEVEL_PAGING_ENTRY * SecondLevelPagingEntry,IN UINT64 BaseAddress,IN UINT64 Length,IN UINT64 IoMmuAccess)754 SetSecondLevelPagingAttribute (
755   IN VTD_UNIT_INFO                 *VTdUnitInfo,
756   IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry,
757   IN UINT64                        BaseAddress,
758   IN UINT64                        Length,
759   IN UINT64                        IoMmuAccess
760   )
761 {
762   VTD_SECOND_LEVEL_PAGING_ENTRY  *PageEntry;
763   PAGE_ATTRIBUTE                 PageAttribute;
764   UINTN                          PageEntryLength;
765   PAGE_ATTRIBUTE                 SplitAttribute;
766   EFI_STATUS                     Status;
767   BOOLEAN                        IsEntryModified;
768 
769   DEBUG ((DEBUG_INFO, "SetSecondLevelPagingAttribute (0x%016lx - 0x%016lx : %x) \n", BaseAddress, Length, IoMmuAccess));
770   DEBUG ((DEBUG_INFO, "  SecondLevelPagingEntry Base - 0x%x\n", SecondLevelPagingEntry));
771 
772   if (BaseAddress != ALIGN_VALUE(BaseAddress, SIZE_4KB)) {
773     DEBUG ((DEBUG_ERROR, "SetSecondLevelPagingAttribute - Invalid Alignment\n"));
774     return EFI_UNSUPPORTED;
775   }
776   if (Length != ALIGN_VALUE(Length, SIZE_4KB)) {
777     DEBUG ((DEBUG_ERROR, "SetSecondLevelPagingAttribute - Invalid Alignment\n"));
778     return EFI_UNSUPPORTED;
779   }
780 
781   while (Length != 0) {
782     PageEntry = GetSecondLevelPageTableEntry (VTdUnitInfo, SecondLevelPagingEntry, BaseAddress, &PageAttribute);
783     if (PageEntry == NULL) {
784       DEBUG ((DEBUG_ERROR, "PageEntry - NULL\n"));
785       return RETURN_UNSUPPORTED;
786     }
787     PageEntryLength = PageAttributeToLength (PageAttribute);
788     SplitAttribute = NeedSplitPage (BaseAddress, Length, PageAttribute);
789     if (SplitAttribute == PageNone) {
790       ConvertSecondLevelPageEntryAttribute (VTdUnitInfo, PageEntry, IoMmuAccess, &IsEntryModified);
791       if (IsEntryModified) {
792         //mVtdUnitInformation[VtdIndex].HasDirtyPages = TRUE;
793       }
794       //
795       // Convert success, move to next
796       //
797       BaseAddress += PageEntryLength;
798       Length -= PageEntryLength;
799     } else {
800       Status = SplitSecondLevelPage (VTdUnitInfo, PageEntry, PageAttribute, SplitAttribute);
801       if (RETURN_ERROR (Status)) {
802         DEBUG ((DEBUG_ERROR, "SplitSecondLevelPage - %r\n", Status));
803         return RETURN_UNSUPPORTED;
804       }
805       //mVtdUnitInformation[VtdIndex].HasDirtyPages = TRUE;
806       //
807       // Just split current page
808       // Convert success in next around
809       //
810     }
811   }
812 
813   return EFI_SUCCESS;
814 }
815 
816 /**
817   Create Fixed Second Level Paging Entry.
818 
819   @param[in]  VTdUnitInfo       The VTd engine unit information.
820 
821   @retval EFI_SUCCESS           Setup translation table successfully.
822   @retval EFI_OUT_OF_RESOURCES  Setup translation table fail.
823 
824 **/
825 EFI_STATUS
CreateFixedSecondLevelPagingEntry(IN VTD_UNIT_INFO * VTdUnitInfo)826 CreateFixedSecondLevelPagingEntry (
827   IN VTD_UNIT_INFO              *VTdUnitInfo
828   )
829 {
830   EFI_STATUS Status;
831   UINT64                        IoMmuAccess;
832   UINT64                        BaseAddress;
833   UINT64                        Length;
834   VOID                          *Hob;
835   DMA_BUFFER_INFO               *DmaBufferInfo;
836 
837   VTdUnitInfo->FixedSecondLevelPagingEntry = (UINT32) (UINTN) CreateSecondLevelPagingEntryTable (VTdUnitInfo, NULL, 0, SIZE_4GB, 0);
838   if (VTdUnitInfo->FixedSecondLevelPagingEntry == 0) {
839     DEBUG ((DEBUG_ERROR, "FixedSecondLevelPagingEntry is empty\n"));
840     return EFI_OUT_OF_RESOURCES;
841   }
842 
843   Hob = GetFirstGuidHob (&mDmaBufferInfoGuid);
844   DmaBufferInfo = GET_GUID_HOB_DATA (Hob);
845   BaseAddress = DmaBufferInfo->DmaBufferBase;
846   Length = DmaBufferInfo->DmaBufferLimit - DmaBufferInfo->DmaBufferBase;
847   IoMmuAccess = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE;
848 
849   DEBUG ((DEBUG_INFO, "  BaseAddress = 0x%lx\n", BaseAddress));
850   DEBUG ((DEBUG_INFO, "  Length = 0x%lx\n", Length));
851   DEBUG ((DEBUG_INFO, "  IoMmuAccess = 0x%lx\n", IoMmuAccess));
852 
853   Status = SetSecondLevelPagingAttribute (VTdUnitInfo, (VTD_SECOND_LEVEL_PAGING_ENTRY*) (UINTN) VTdUnitInfo->FixedSecondLevelPagingEntry, BaseAddress, Length, IoMmuAccess);
854 
855   return Status;
856 }
857 /**
858   Setup VTd translation table.
859 
860   @param[in]  VTdInfo           The VTd engine context information.
861 
862   @retval EFI_SUCCESS           Setup translation table successfully.
863   @retval EFI_OUT_OF_RESOURCES  Setup translation table fail.
864 
865 **/
866 EFI_STATUS
SetupTranslationTable(IN VTD_INFO * VTdInfo)867 SetupTranslationTable (
868   IN VTD_INFO                   *VTdInfo
869   )
870 {
871   EFI_STATUS                    Status;
872   UINTN                         Index;
873   VTD_UNIT_INFO                 *VtdUnitInfo;
874 
875   for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
876     VtdUnitInfo = &VTdInfo->VtdUnitInfo[Index];
877 
878     Status = CreateFixedSecondLevelPagingEntry (VtdUnitInfo);
879     if (EFI_ERROR (Status)) {
880       DEBUG ((DEBUG_INFO, "CreateFixedSecondLevelPagingEntry failed - %r\n", Status));
881       return Status;
882     }
883 
884     if (VtdUnitInfo->ECapReg.Bits.ECS) {
885       DEBUG ((DEBUG_INFO, "CreateExtContextEntry - %d\n", Index));
886       Status = CreateExtContextEntry (VtdUnitInfo);
887     } else {
888       DEBUG ((DEBUG_INFO, "CreateContextEntry - %d\n", Index));
889       Status = CreateContextEntry (VtdUnitInfo);
890     }
891     if (EFI_ERROR (Status)) {
892       return Status;
893     }
894   }
895   return EFI_SUCCESS;
896 }
897 
898 /**
899   Find the VTd index by the Segment and SourceId.
900 
901   @param[in]  VTdInfo           The VTd engine context information.
902   @param[in]  Segment           The segment of the source.
903   @param[in]  SourceId          The SourceId of the source.
904   @param[out] ExtContextEntry   The ExtContextEntry of the source.
905   @param[out] ContextEntry      The ContextEntry of the source.
906 
907   @return The index of the VTd engine.
908   @retval (UINTN)-1  The VTd engine is not found.
909 **/
910 UINTN
FindVtdIndexBySegmentSourceId(IN VTD_INFO * VTdInfo,IN UINT16 Segment,IN VTD_SOURCE_ID SourceId,OUT VTD_EXT_CONTEXT_ENTRY ** ExtContextEntry,OUT VTD_CONTEXT_ENTRY ** ContextEntry)911 FindVtdIndexBySegmentSourceId (
912   IN  VTD_INFO                  *VTdInfo,
913   IN  UINT16                    Segment,
914   IN  VTD_SOURCE_ID             SourceId,
915   OUT VTD_EXT_CONTEXT_ENTRY     **ExtContextEntry,
916   OUT VTD_CONTEXT_ENTRY         **ContextEntry
917   )
918 {
919   UINTN                         VtdIndex;
920   VTD_ROOT_ENTRY                *RootEntryBase;
921   VTD_ROOT_ENTRY                *RootEntry;
922   VTD_CONTEXT_ENTRY             *ContextEntryTable;
923   VTD_CONTEXT_ENTRY             *ThisContextEntry;
924   VTD_EXT_ROOT_ENTRY            *ExtRootEntryBase;
925   VTD_EXT_ROOT_ENTRY            *ExtRootEntry;
926   VTD_EXT_CONTEXT_ENTRY         *ExtContextEntryTable;
927   VTD_EXT_CONTEXT_ENTRY         *ThisExtContextEntry;
928 
929   for (VtdIndex = 0; VtdIndex < VTdInfo->VTdEngineCount; VtdIndex++) {
930     if (GetPciDataIndex (&VTdInfo->VtdUnitInfo[VtdIndex], Segment, SourceId) != (UINTN)-1) {
931       DEBUG ((DEBUG_INFO, "Find VtdIndex(0x%x) for S%04x B%02x D%02x F%02x\n", VtdIndex, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
932       break;
933     }
934   }
935   if (VtdIndex >= VTdInfo->VTdEngineCount) {
936     for (VtdIndex = 0; VtdIndex < VTdInfo->VTdEngineCount; VtdIndex++) {
937       if (Segment != VTdInfo->VtdUnitInfo[VtdIndex].Segment) {
938         continue;
939       }
940       if (VTdInfo->VtdUnitInfo[VtdIndex].PciDeviceInfo.IncludeAllFlag) {
941         DEBUG ((DEBUG_INFO, "Find IncludeAllFlag VtdIndex(0x%x) for S%04x B%02x D%02x F%02x\n", VtdIndex, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
942         break;
943       }
944     }
945   }
946 
947   if (VtdIndex < VTdInfo->VTdEngineCount) {
948     ExtRootEntryBase = (VTD_EXT_ROOT_ENTRY *) (UINTN) VTdInfo->VtdUnitInfo[VtdIndex].ExtRootEntryTable;
949     if (ExtRootEntryBase != 0) {
950       ExtRootEntry = &ExtRootEntryBase[SourceId.Index.RootIndex];
951       ExtContextEntryTable = (VTD_EXT_CONTEXT_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(ExtRootEntry->Bits.LowerContextTablePointerLo, ExtRootEntry->Bits.LowerContextTablePointerHi) ;
952       ThisExtContextEntry  = &ExtContextEntryTable[SourceId.Index.ContextIndex];
953       if (ThisExtContextEntry->Bits.AddressWidth == 0) {
954         DEBUG ((DEBUG_INFO, "ExtContextEntry AddressWidth : 0x%x\n", ThisExtContextEntry->Bits.AddressWidth));
955         return (UINTN)-1;
956       }
957       *ExtContextEntry = ThisExtContextEntry;
958       *ContextEntry    = NULL;
959     } else {
960       RootEntryBase = (VTD_ROOT_ENTRY*) (UINTN) VTdInfo->VtdUnitInfo[VtdIndex].RootEntryTable;
961       RootEntry = &RootEntryBase[SourceId.Index.RootIndex];
962       ContextEntryTable = (VTD_CONTEXT_ENTRY *) (UINTN) VTD_64BITS_ADDRESS(RootEntry->Bits.ContextTablePointerLo, RootEntry->Bits.ContextTablePointerHi) ;
963       ThisContextEntry  = &ContextEntryTable[SourceId.Index.ContextIndex];
964       if (ThisContextEntry->Bits.AddressWidth == 0) {
965         DEBUG ((DEBUG_INFO, "ContextEntry AddressWidth : 0x%x\n", ThisContextEntry->Bits.AddressWidth));
966         return (UINTN)-1;
967       }
968       *ExtContextEntry = NULL;
969       *ContextEntry    = ThisContextEntry;
970     }
971 
972     return VtdIndex;
973   }
974 
975   return (UINTN)-1;
976 }
977 
978 /**
979   Always enable the VTd page attribute for the device.
980 
981   @param[in]  VTdInfo           The VTd engine context information.
982   @param[in]  Segment           The Segment used to identify a VTd engine.
983   @param[in]  SourceId          The SourceId used to identify a VTd engine and table entry.
984   @param[in]  MemoryBase        The base of the memory.
985   @param[in]  MemoryLimit       The limit of the memory.
986   @param[in]  IoMmuAccess       The IOMMU access.
987 
988   @retval EFI_SUCCESS           The VTd entry is updated to always enable all DMA access for the specific device.
989 **/
990 EFI_STATUS
EnableRmrrPageAttribute(IN VTD_INFO * VTdInfo,IN UINT16 Segment,IN VTD_SOURCE_ID SourceId,IN UINT64 MemoryBase,IN UINT64 MemoryLimit,IN UINT64 IoMmuAccess)991 EnableRmrrPageAttribute (
992   IN VTD_INFO                   *VTdInfo,
993   IN UINT16                     Segment,
994   IN VTD_SOURCE_ID              SourceId,
995   IN UINT64                     MemoryBase,
996   IN UINT64                     MemoryLimit,
997   IN UINT64                     IoMmuAccess
998   )
999 {
1000   EFI_STATUS Status;
1001   UINTN                         VtdIndex;
1002   VTD_EXT_CONTEXT_ENTRY         *ExtContextEntry;
1003   VTD_CONTEXT_ENTRY             *ContextEntry;
1004   VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry;
1005   UINT64                        Pt;
1006 
1007   DEBUG ((DEBUG_INFO, "EnableRmrrPageAttribute (S%04x B%02x D%02x F%02x)\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
1008 
1009   VtdIndex = FindVtdIndexBySegmentSourceId (VTdInfo, Segment, SourceId, &ExtContextEntry, &ContextEntry);
1010   if (VtdIndex == (UINTN)-1) {
1011     DEBUG ((DEBUG_ERROR, "EnableRmrrPageAttribute - Can not locate Pci device (S%04x B%02x D%02x F%02x) !\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
1012     return EFI_DEVICE_ERROR;
1013   }
1014 
1015   if (VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry == 0) {
1016     DEBUG ((DEBUG_INFO, "CreateSecondLevelPagingEntry - %d\n", VtdIndex));
1017     VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry = (UINT32)(UINTN)CreateSecondLevelPagingEntryTable (&VTdInfo->VtdUnitInfo[VtdIndex], NULL, 0, SIZE_4GB, 0);
1018     if (VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry == 0) {
1019       return EFI_OUT_OF_RESOURCES;
1020     }
1021 
1022     Status =SetSecondLevelPagingAttribute (&VTdInfo->VtdUnitInfo[VtdIndex], (VTD_SECOND_LEVEL_PAGING_ENTRY*)(UINTN)VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry, MemoryBase, MemoryLimit + 1 - MemoryBase, IoMmuAccess);
1023     if (EFI_ERROR (Status)) {
1024       return Status;
1025     }
1026   }
1027 
1028   SecondLevelPagingEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *) (UINTN) VTdInfo->VtdUnitInfo[VtdIndex].RmrrSecondLevelPagingEntry;
1029   Pt = (UINT64) RShiftU64 ((UINT64) (UINTN) SecondLevelPagingEntry, 12);
1030   if (ExtContextEntry != NULL) {
1031     ExtContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt;
1032     ExtContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20);
1033     ExtContextEntry->Bits.DomainIdentifier = ((1 << (UINT8) ((UINTN) VTdInfo->VtdUnitInfo[VtdIndex].CapReg.Bits.ND * 2 + 4)) - 1);
1034     ExtContextEntry->Bits.Present = 1;
1035     FlushPageTableMemory (&VTdInfo->VtdUnitInfo[VtdIndex], (UINTN) ExtContextEntry, sizeof(*ExtContextEntry));
1036   } else if (ContextEntry != NULL) {
1037     ContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt;
1038     ContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64 (Pt, 20);
1039     ContextEntry->Bits.DomainIdentifier = ((1 << (UINT8) ((UINTN) VTdInfo->VtdUnitInfo[VtdIndex].CapReg.Bits.ND * 2 + 4)) - 1);
1040     ContextEntry->Bits.Present = 1;
1041     FlushPageTableMemory (&VTdInfo->VtdUnitInfo[VtdIndex], (UINTN) ContextEntry, sizeof (*ContextEntry));
1042   }
1043 
1044   return EFI_SUCCESS;
1045 }
1046