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