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