| 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 |
| 24 | EFI_STATUS\r |
| 25 | CreateExtContextEntry (\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 |
| 37 | VOID *\r |
| 38 | EFIAPI\r |
| 39 | AllocateZeroPages (\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 |
| 59 | VOID\r |
| 60 | SetSecondLevelPagingEntryAttribute (\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 |
| 77 | EFI_STATUS\r |
| 78 | CreateContextEntry (\r |
| 79 | IN UINTN VtdIndex\r |
| 80 | )\r |
| 81 | {\r |
| 82 | UINTN Index;\r |
| 83 | VOID *Buffer;\r |
| 84 | UINTN RootPages;\r |
| 85 | UINTN ContextPages;\r |
| 86 | VTD_ROOT_ENTRY *RootEntry;\r |
| 87 | VTD_CONTEXT_ENTRY *ContextEntryTable;\r |
| 88 | VTD_CONTEXT_ENTRY *ContextEntry;\r |
| 89 | VTD_SOURCE_ID *PciSourceId;\r |
| 90 | VTD_SOURCE_ID SourceId;\r |
| 91 | UINTN MaxBusNumber;\r |
| 92 | UINTN EntryTablePages;\r |
| 93 | \r |
| 94 | MaxBusNumber = 0;\r |
| 95 | for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.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 |
| 99 | }\r |
| 100 | }\r |
| 101 | DEBUG ((DEBUG_INFO," MaxBusNumber - 0x%x\n", MaxBusNumber));\r |
| 102 | \r |
| 103 | RootPages = EFI_SIZE_TO_PAGES (sizeof (VTD_ROOT_ENTRY) * VTD_ROOT_ENTRY_NUMBER);\r |
| 104 | ContextPages = EFI_SIZE_TO_PAGES (sizeof (VTD_CONTEXT_ENTRY) * VTD_CONTEXT_ENTRY_NUMBER);\r |
| 105 | EntryTablePages = RootPages + ContextPages * (MaxBusNumber + 1);\r |
| 106 | Buffer = AllocateZeroPages (EntryTablePages);\r |
| 107 | if (Buffer == NULL) {\r |
| 108 | DEBUG ((DEBUG_INFO,"Could not Alloc Root Entry Table.. \n"));\r |
| 109 | return EFI_OUT_OF_RESOURCES;\r |
| 110 | }\r |
| 111 | mVtdUnitInformation[VtdIndex].RootEntryTable = (VTD_ROOT_ENTRY *)Buffer;\r |
| 112 | Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (RootPages);\r |
| 113 | \r |
| 114 | for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceDataNumber; Index++) {\r |
| 115 | PciSourceId = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[Index].PciSourceId;\r |
| 116 | \r |
| 117 | SourceId.Bits.Bus = PciSourceId->Bits.Bus;\r |
| 118 | SourceId.Bits.Device = PciSourceId->Bits.Device;\r |
| 119 | SourceId.Bits.Function = PciSourceId->Bits.Function;\r |
| 120 | \r |
| 121 | RootEntry = &mVtdUnitInformation[VtdIndex].RootEntryTable[SourceId.Index.RootIndex];\r |
| 122 | if (RootEntry->Bits.Present == 0) {\r |
| 123 | RootEntry->Bits.ContextTablePointerLo = (UINT32) RShiftU64 ((UINT64)(UINTN)Buffer, 12);\r |
| 124 | RootEntry->Bits.ContextTablePointerHi = (UINT32) RShiftU64 ((UINT64)(UINTN)Buffer, 32);\r |
| 125 | RootEntry->Bits.Present = 1;\r |
| 126 | Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (ContextPages);\r |
| 127 | }\r |
| 128 | \r |
| 129 | ContextEntryTable = (VTD_CONTEXT_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(RootEntry->Bits.ContextTablePointerLo, RootEntry->Bits.ContextTablePointerHi) ;\r |
| 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 |
| 147 | FlushPageTableMemory (VtdIndex, (UINTN)mVtdUnitInformation[VtdIndex].RootEntryTable, EFI_PAGES_TO_SIZE(EntryTablePages));\r |
| 148 | \r |
| 149 | return EFI_SUCCESS;\r |
| 150 | }\r |
| 151 | \r |
| 152 | /**\r |
| 153 | Create second level paging entry table.\r |
| 154 | \r |
| 155 | @param[in] VtdIndex The index of the VTd engine.\r |
| 156 | @param[in] SecondLevelPagingEntry The second level paging entry.\r |
| 157 | @param[in] MemoryBase The base of the memory.\r |
| 158 | @param[in] MemoryLimit The limit of the memory.\r |
| 159 | @param[in] IoMmuAccess The IOMMU access.\r |
| 160 | \r |
| 161 | @return The second level paging entry.\r |
| 162 | **/\r |
| 163 | VTD_SECOND_LEVEL_PAGING_ENTRY *\r |
| 164 | CreateSecondLevelPagingEntryTable (\r |
| 165 | IN UINTN VtdIndex,\r |
| 166 | IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry,\r |
| 167 | IN UINT64 MemoryBase,\r |
| 168 | IN UINT64 MemoryLimit,\r |
| 169 | IN UINT64 IoMmuAccess\r |
| 170 | )\r |
| 171 | {\r |
| 172 | UINTN Index4;\r |
| 173 | UINTN Index3;\r |
| 174 | UINTN Index2;\r |
| 175 | UINTN Lvl4Start;\r |
| 176 | UINTN Lvl4End;\r |
| 177 | UINTN Lvl3Start;\r |
| 178 | UINTN Lvl3End;\r |
| 179 | VTD_SECOND_LEVEL_PAGING_ENTRY *Lvl4PtEntry;\r |
| 180 | VTD_SECOND_LEVEL_PAGING_ENTRY *Lvl3PtEntry;\r |
| 181 | VTD_SECOND_LEVEL_PAGING_ENTRY *Lvl2PtEntry;\r |
| 182 | UINT64 BaseAddress;\r |
| 183 | UINT64 EndAddress;\r |
| 184 | \r |
| 185 | if (MemoryLimit == 0) {\r |
| 186 | return EFI_SUCCESS;\r |
| 187 | }\r |
| 188 | \r |
| 189 | BaseAddress = ALIGN_VALUE_LOW(MemoryBase, SIZE_2MB);\r |
| 190 | EndAddress = ALIGN_VALUE_UP(MemoryLimit, SIZE_2MB);\r |
| 191 | DEBUG ((DEBUG_INFO,"CreateSecondLevelPagingEntryTable: BaseAddress - 0x%016lx, EndAddress - 0x%016lx\n", BaseAddress, EndAddress));\r |
| 192 | \r |
| 193 | if (SecondLevelPagingEntry == NULL) {\r |
| 194 | SecondLevelPagingEntry = AllocateZeroPages (1);\r |
| 195 | if (SecondLevelPagingEntry == NULL) {\r |
| 196 | DEBUG ((DEBUG_ERROR,"Could not Alloc LVL4 PT. \n"));\r |
| 197 | return NULL;\r |
| 198 | }\r |
| 199 | FlushPageTableMemory (VtdIndex, (UINTN)SecondLevelPagingEntry, EFI_PAGES_TO_SIZE(1));\r |
| 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 |
| 223 | FlushPageTableMemory (VtdIndex, (UINTN)Lvl4PtEntry[Index4].Uint64, SIZE_4KB);\r |
| 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 |
| 235 | Lvl3PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(Lvl4PtEntry[Index4].Bits.AddressLo, Lvl4PtEntry[Index4].Bits.AddressHi);\r |
| 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 |
| 244 | FlushPageTableMemory (VtdIndex, (UINTN)Lvl3PtEntry[Index3].Uint64, SIZE_4KB);\r |
| 245 | SetSecondLevelPagingEntryAttribute (&Lvl3PtEntry[Index3], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r |
| 246 | }\r |
| 247 | \r |
| 248 | Lvl2PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(Lvl3PtEntry[Index3].Bits.AddressLo, Lvl3PtEntry[Index3].Bits.AddressHi);\r |
| 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 |
| 258 | FlushPageTableMemory (VtdIndex, (UINTN)Lvl2PtEntry, SIZE_4KB);\r |
| 259 | }\r |
| 260 | FlushPageTableMemory (VtdIndex, (UINTN)&Lvl3PtEntry[Lvl3Start], (UINTN)&Lvl3PtEntry[Lvl3End + 1] - (UINTN)&Lvl3PtEntry[Lvl3Start]);\r |
| 261 | }\r |
| 262 | FlushPageTableMemory (VtdIndex, (UINTN)&Lvl4PtEntry[Lvl4Start], (UINTN)&Lvl4PtEntry[Lvl4End + 1] - (UINTN)&Lvl4PtEntry[Lvl4Start]);\r |
| 263 | \r |
| 264 | Done:\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 |
| 276 | VTD_SECOND_LEVEL_PAGING_ENTRY *\r |
| 277 | CreateSecondLevelPagingEntry (\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 | \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 |
| 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 |
| 307 | EFI_STATUS\r |
| 308 | SetupTranslationTable (\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 |
| 335 | VOID\r |
| 336 | DumpDmarContextEntryTable (\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 |
| 357 | ContextEntry = (VTD_CONTEXT_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(RootEntry[Index].Bits.ContextTablePointerLo, RootEntry[Index].Bits.ContextTablePointerHi);\r |
| 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 |
| 366 | DumpSecondLevelPagingEntry ((VOID *)(UINTN)VTD_64BITS_ADDRESS(ContextEntry[Index2].Bits.SecondLevelPageTranslationPointerLo, ContextEntry[Index2].Bits.SecondLevelPageTranslationPointerHi));\r |
| 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 |
| 377 | VOID\r |
| 378 | DumpSecondLevelPagingEntry (\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 |
| 403 | Lvl3PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(Lvl4PtEntry[Index4].Bits.AddressLo, Lvl4PtEntry[Index4].Bits.AddressHi);\r |
| 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 |
| 412 | Lvl2PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(Lvl3PtEntry[Index3].Bits.AddressLo, Lvl3PtEntry[Index3].Bits.AddressHi);\r |
| 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 |
| 421 | Lvl1PtEntry = (VTD_SECOND_LEVEL_PAGING_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(Lvl2PtEntry[Index2].Bits.AddressLo, Lvl2PtEntry[Index2].Bits.AddressHi);\r |
| 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 |
| 439 | VOID\r |
| 440 | InvalidatePageEntry (\r |
| 441 | IN UINTN VtdIndex\r |
| 442 | )\r |
| 443 | {\r |
| 444 | if (mVtdUnitInformation[VtdIndex].HasDirtyContext || mVtdUnitInformation[VtdIndex].HasDirtyPages) {\r |
| 445 | InvalidateVtdIOTLBGlobal (VtdIndex);\r |
| 446 | }\r |
| 447 | mVtdUnitInformation[VtdIndex].HasDirtyContext = FALSE;\r |
| 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 |
| 471 | typedef enum {\r |
| 472 | PageNone,\r |
| 473 | Page4K,\r |
| 474 | Page2M,\r |
| 475 | Page1G,\r |
| 476 | } PAGE_ATTRIBUTE;\r |
| 477 | \r |
| 478 | typedef struct {\r |
| 479 | PAGE_ATTRIBUTE Attribute;\r |
| 480 | UINT64 Length;\r |
| 481 | UINT64 AddressMask;\r |
| 482 | } PAGE_ATTRIBUTE_TABLE;\r |
| 483 | \r |
| 484 | PAGE_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 |
| 497 | UINTN\r |
| 498 | PageAttributeToLength (\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 |
| 514 | @param[in] VtdIndex The index used to identify a VTd engine.\r |
| 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 |
| 521 | VOID *\r |
| 522 | GetSecondLevelPageTableEntry (\r |
| 523 | IN UINTN VtdIndex,\r |
| 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 |
| 552 | FlushPageTableMemory (VtdIndex, (UINTN)L4PageTable[Index4], SIZE_4KB);\r |
| 553 | SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *)&L4PageTable[Index4], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r |
| 554 | FlushPageTableMemory (VtdIndex, (UINTN)&L4PageTable[Index4], sizeof(L4PageTable[Index4]));\r |
| 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 |
| 566 | FlushPageTableMemory (VtdIndex, (UINTN)L3PageTable[Index3], SIZE_4KB);\r |
| 567 | SetSecondLevelPagingEntryAttribute ((VTD_SECOND_LEVEL_PAGING_ENTRY *)&L3PageTable[Index3], EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r |
| 568 | FlushPageTableMemory (VtdIndex, (UINTN)&L3PageTable[Index3], sizeof(L3PageTable[Index3]));\r |
| 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 |
| 581 | FlushPageTableMemory (VtdIndex, (UINTN)&L2PageTable[Index2], sizeof(L2PageTable[Index2]));\r |
| 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 |
| 602 | @param[in] VtdIndex The index used to identify a VTd engine.\r |
| 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 |
| 607 | VOID\r |
| 608 | ConvertSecondLevelPageEntryAttribute (\r |
| 609 | IN UINTN VtdIndex,\r |
| 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 |
| 620 | FlushPageTableMemory (VtdIndex, (UINTN)PageEntry, sizeof(*PageEntry));\r |
| 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 |
| 640 | PAGE_ATTRIBUTE\r |
| 641 | NeedSplitPage (\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 |
| 665 | @param[in] VtdIndex The index used to identify a VTd engine.\r |
| 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 |
| 674 | RETURN_STATUS\r |
| 675 | SplitSecondLevelPage (\r |
| 676 | IN UINTN VtdIndex,\r |
| 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 |
| 703 | FlushPageTableMemory (VtdIndex, (UINTN)NewPageEntry, SIZE_4KB);\r |
| 704 | \r |
| 705 | PageEntry->Uint64 = (UINT64)(UINTN)NewPageEntry;\r |
| 706 | SetSecondLevelPagingEntryAttribute (PageEntry, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r |
| 707 | FlushPageTableMemory (VtdIndex, (UINTN)PageEntry, sizeof(*PageEntry));\r |
| 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 |
| 728 | FlushPageTableMemory (VtdIndex, (UINTN)NewPageEntry, SIZE_4KB);\r |
| 729 | \r |
| 730 | PageEntry->Uint64 = (UINT64)(UINTN)NewPageEntry;\r |
| 731 | SetSecondLevelPagingEntryAttribute (PageEntry, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE);\r |
| 732 | FlushPageTableMemory (VtdIndex, (UINTN)PageEntry, sizeof(*PageEntry));\r |
| 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 |
| 746 | @param[in] DomainIdentifier The domain ID of the source.\r |
| 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 |
| 762 | EFI_STATUS\r |
| 763 | SetSecondLevelPagingAttribute (\r |
| 764 | IN UINTN VtdIndex,\r |
| 765 | IN UINT16 DomainIdentifier,\r |
| 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 |
| 792 | PageEntry = GetSecondLevelPageTableEntry (VtdIndex, SecondLevelPagingEntry, BaseAddress, &PageAttribute);\r |
| 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 |
| 800 | ConvertSecondLevelPageEntryAttribute (VtdIndex, PageEntry, IoMmuAccess, &IsEntryModified);\r |
| 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 |
| 810 | Status = SplitSecondLevelPage (VtdIndex, PageEntry, PageAttribute, SplitAttribute);\r |
| 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 |
| 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 |
| 830 | @param[in] DomainIdentifier The domain ID of the source.\r |
| 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 |
| 846 | EFI_STATUS\r |
| 847 | SetPageAttribute (\r |
| 848 | IN UINTN VtdIndex,\r |
| 849 | IN UINT16 DomainIdentifier,\r |
| 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 |
| 859 | Status = SetSecondLevelPagingAttribute (VtdIndex, DomainIdentifier, SecondLevelPagingEntry, BaseAddress, Length, IoMmuAccess);\r |
| 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 |
| 883 | EFI_STATUS\r |
| 884 | SetAccessAttribute (\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 |
| 898 | UINTN PciDataIndex;\r |
| 899 | UINT16 DomainIdentifier;\r |
| 900 | \r |
| 901 | SecondLevelPagingEntry = NULL;\r |
| 902 | \r |
| 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 |
| 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 |
| 911 | PciDataIndex = GetPciDataIndex (VtdIndex, Segment, SourceId);\r |
| 912 | mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[PciDataIndex].AccessCount++;\r |
| 913 | //\r |
| 914 | // DomainId should not be 0.\r |
| 915 | //\r |
| 916 | DomainIdentifier = (UINT16)(PciDataIndex + 1);\r |
| 917 | \r |
| 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 |
| 924 | ExtContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt;\r |
| 925 | ExtContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20);\r |
| 926 | ExtContextEntry->Bits.DomainIdentifier = DomainIdentifier;\r |
| 927 | ExtContextEntry->Bits.Present = 1;\r |
| 928 | FlushPageTableMemory (VtdIndex, (UINTN)ExtContextEntry, sizeof(*ExtContextEntry));\r |
| 929 | DumpDmarExtContextEntryTable (mVtdUnitInformation[VtdIndex].ExtRootEntryTable);\r |
| 930 | mVtdUnitInformation[VtdIndex].HasDirtyContext = TRUE;\r |
| 931 | } else {\r |
| 932 | SecondLevelPagingEntry = (VOID *)(UINTN)VTD_64BITS_ADDRESS(ExtContextEntry->Bits.SecondLevelPageTranslationPointerLo, ExtContextEntry->Bits.SecondLevelPageTranslationPointerHi);\r |
| 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 |
| 935 | } else if (ContextEntry != NULL) {\r |
| 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 |
| 941 | ContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt;\r |
| 942 | ContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20);\r |
| 943 | ContextEntry->Bits.DomainIdentifier = DomainIdentifier;\r |
| 944 | ContextEntry->Bits.Present = 1;\r |
| 945 | FlushPageTableMemory (VtdIndex, (UINTN)ContextEntry, sizeof(*ContextEntry));\r |
| 946 | DumpDmarContextEntryTable (mVtdUnitInformation[VtdIndex].RootEntryTable);\r |
| 947 | mVtdUnitInformation[VtdIndex].HasDirtyContext = TRUE;\r |
| 948 | } else {\r |
| 949 | SecondLevelPagingEntry = (VOID *)(UINTN)VTD_64BITS_ADDRESS(ContextEntry->Bits.SecondLevelPageTranslationPointerLo, ContextEntry->Bits.SecondLevelPageTranslationPointerHi);\r |
| 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 |
| 960 | DomainIdentifier,\r |
| 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 |
| 972 | InvalidatePageEntry (VtdIndex);\r |
| 973 | \r |
| 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 |
| 985 | EFI_STATUS\r |
| 986 | AlwaysEnablePageAttribute (\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 |
| 1013 | ExtContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt;\r |
| 1014 | ExtContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20);\r |
| 1015 | ExtContextEntry->Bits.DomainIdentifier = ((1 << (UINT8)((UINTN)mVtdUnitInformation[VtdIndex].CapReg.Bits.ND * 2 + 4)) - 1);\r |
| 1016 | ExtContextEntry->Bits.Present = 1;\r |
| 1017 | FlushPageTableMemory (VtdIndex, (UINTN)ExtContextEntry, sizeof(*ExtContextEntry));\r |
| 1018 | } else if (ContextEntry != NULL) {\r |
| 1019 | ContextEntry->Bits.SecondLevelPageTranslationPointerLo = (UINT32) Pt;\r |
| 1020 | ContextEntry->Bits.SecondLevelPageTranslationPointerHi = (UINT32) RShiftU64(Pt, 20);\r |
| 1021 | ContextEntry->Bits.DomainIdentifier = ((1 << (UINT8)((UINTN)mVtdUnitInformation[VtdIndex].CapReg.Bits.ND * 2 + 4)) - 1);\r |
| 1022 | ContextEntry->Bits.Present = 1;\r |
| 1023 | FlushPageTableMemory (VtdIndex, (UINTN)ContextEntry, sizeof(*ContextEntry));\r |
| 1024 | }\r |
| 1025 | \r |
| 1026 | return EFI_SUCCESS;\r |
| 1027 | }\r |