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