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