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