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