├── .gitignore ├── Drivers └── HelloIommuDxe │ ├── HelloIommuDxe.c │ └── HelloIommuDxe.inf ├── HelloIommuPkg.dec ├── HelloIommuPkg.dsc ├── Include └── IndustryStandard │ └── Vtd.h ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /Drivers/HelloIommuDxe/HelloIommuDxe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // taken from edk2-platforms 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define Add2Ptr(Ptr, Value) ((VOID*)((UINT8*)(Ptr) + (Value))) 16 | #define UEFI_DOMAIN_ID 1 17 | #define V_IOTLB_REG_DR BIT48 18 | #define V_IOTLB_REG_DW BIT49 19 | 20 | // 21 | // 10.4.6 Root Table Address Register 22 | // 23 | typedef union _VTD_ROOT_TABLE_ADDRESS_REGISTER 24 | { 25 | struct 26 | { 27 | UINT64 Reserved_1 : 10; // [9:0] 28 | UINT64 TranslationTableMode : 2; // [11:10] 29 | UINT64 RootTable : 52; // [63:12] 30 | } Bits; 31 | UINT64 AsUInt64; 32 | } VTD_ROOT_TABLE_ADDRESS_REGISTER; 33 | STATIC_ASSERT(sizeof(VTD_ROOT_TABLE_ADDRESS_REGISTER) == sizeof(UINT64), "Unexpected size"); 34 | 35 | // 36 | // Collection of data structures used by hardware to perform DMA-remapping 37 | // translation. 38 | // 39 | typedef struct _DMAR_TRANSLATIONS 40 | { 41 | // 42 | // The root table is only for each hardware unit and made up of 256 entries. 43 | // 44 | VTD_ROOT_ENTRY RootTable[256]; 45 | 46 | // 47 | // The context table can be multiple but all root entries set up by this 48 | // project point to the same, single context table, hence this is not 49 | // ContextTable[256][256]. This table is made up of 256 entries. 50 | // 51 | VTD_CONTEXT_ENTRY ContextTable[256]; 52 | 53 | // 54 | // The second-level PML4 can be multiple but all context entries set up by 55 | // this projects point to the same, single PML4, This table is made up of 56 | // 512 entries. 57 | // 58 | VTD_SECOND_LEVEL_PAGING_ENTRY SlPml4[512]; 59 | 60 | // 61 | // This project only uses PML4[0], hence only one PDPT is used. PDPT is made 62 | // up of 512 entries. 63 | // 64 | VTD_SECOND_LEVEL_PAGING_ENTRY SlPdpt[1][512]; 65 | 66 | // 67 | // Have PD for each PDPT and each PD is made up of 512 entries; hence [512][512]. 68 | // 69 | VTD_SECOND_LEVEL_PAGING_ENTRY SlPd[1][512][512]; 70 | } DMAR_TRANSLATIONS; 71 | STATIC_ASSERT((sizeof(DMAR_TRANSLATIONS) % SIZE_4KB) == 0, "Unexpected size"); 72 | STATIC_ASSERT((OFFSET_OF(DMAR_TRANSLATIONS, ContextTable) % SIZE_4KB) == 0, "Unexpected size"); 73 | STATIC_ASSERT((OFFSET_OF(DMAR_TRANSLATIONS, SlPml4) % SIZE_4KB) == 0, "Unexpected size"); 74 | STATIC_ASSERT((OFFSET_OF(DMAR_TRANSLATIONS, SlPdpt) % SIZE_4KB) == 0, "Unexpected size"); 75 | STATIC_ASSERT((OFFSET_OF(DMAR_TRANSLATIONS, SlPd) % SIZE_4KB) == 0, "Unexpected size"); 76 | 77 | // 78 | // The representation of each DMA-remapping hardware unit. 79 | // 80 | typedef struct _DMAR_UNIT_INFORMATION 81 | { 82 | UINT64 RegisterBasePa; 83 | UINT64 RegisterBaseVa; 84 | VTD_CAP_REG Capability; 85 | VTD_ECAP_REG ExtendedCapability; 86 | DMAR_TRANSLATIONS* Translations; 87 | } DMAR_UNIT_INFORMATION; 88 | 89 | // 90 | // The helper structure for translating the guest physical address to the 91 | // host physical address. 92 | // 93 | typedef union _ADDRESS_TRANSLATION_HELPER 94 | { 95 | // 96 | // Indexes to locate paging-structure entries corresponds to this virtual 97 | // address. 98 | // 99 | struct 100 | { 101 | UINT64 Unused : 12; //< [11:0] 102 | UINT64 Pt : 9; //< [20:12] 103 | UINT64 Pd : 9; //< [29:21] 104 | UINT64 Pdpt : 9; //< [38:30] 105 | UINT64 Pml4 : 9; //< [47:39] 106 | } AsIndex; 107 | UINT64 AsUInt64; 108 | } ADDRESS_TRANSLATION_HELPER; 109 | 110 | /** 111 | * @brief Collects relevant information of each DMA-remapping hardware units. 112 | */ 113 | static 114 | EFI_STATUS 115 | ProcessDmarTable ( 116 | IN CONST EFI_ACPI_DMAR_HEADER* DmarTable, 117 | IN OUT DMAR_UNIT_INFORMATION* DmarUnits, 118 | IN UINT64 MaxDmarUnitCount, 119 | OUT UINT64* DetectedUnitCount 120 | ) 121 | { 122 | UINT64 endOfDmar; 123 | CONST EFI_ACPI_DMAR_STRUCTURE_HEADER* dmarHeader; 124 | UINT64 discoveredUnitCount; 125 | 126 | ZeroMem(DmarUnits, sizeof(*DmarUnits) * MaxDmarUnitCount); 127 | 128 | // 129 | // Walk through the DMAR table, find all DMA-remapping hardware unit 130 | // definition structures in it, and gather relevant information into DmarUnits. 131 | // 132 | discoveredUnitCount = 0; 133 | endOfDmar = (UINT64)Add2Ptr(DmarTable, DmarTable->Header.Length); 134 | dmarHeader = (CONST EFI_ACPI_DMAR_STRUCTURE_HEADER*)(DmarTable + 1); 135 | while ((UINT64)dmarHeader < endOfDmar) 136 | { 137 | if (dmarHeader->Type == EFI_ACPI_DMAR_TYPE_DRHD) 138 | { 139 | if (discoveredUnitCount < MaxDmarUnitCount) 140 | { 141 | CONST EFI_ACPI_DMAR_DRHD_HEADER* dmarUnit; 142 | 143 | dmarUnit = (CONST EFI_ACPI_DMAR_DRHD_HEADER*)dmarHeader; 144 | DmarUnits[discoveredUnitCount].RegisterBasePa = dmarUnit->RegisterBaseAddress; 145 | DmarUnits[discoveredUnitCount].RegisterBaseVa = dmarUnit->RegisterBaseAddress; 146 | DmarUnits[discoveredUnitCount].Capability.Uint64 = 147 | MmioRead64(DmarUnits[discoveredUnitCount].RegisterBaseVa + R_CAP_REG); 148 | DmarUnits[discoveredUnitCount].ExtendedCapability.Uint64 = 149 | MmioRead64(DmarUnits[discoveredUnitCount].RegisterBaseVa + R_ECAP_REG); 150 | } 151 | discoveredUnitCount++; 152 | } 153 | dmarHeader = (CONST EFI_ACPI_DMAR_STRUCTURE_HEADER*)Add2Ptr(dmarHeader, dmarHeader->Length); 154 | } 155 | 156 | // 157 | // Processed all structures. It is an error if nothing found, or found too many. 158 | // 159 | *DetectedUnitCount = discoveredUnitCount; 160 | 161 | for (UINT64 i = 0; i < discoveredUnitCount; ++i) 162 | { 163 | DEBUG((DEBUG_VERBOSE, "Unit %d at %p - Cap: %llx, ExCap: %llx\n", 164 | i, 165 | DmarUnits[i].RegisterBasePa, 166 | DmarUnits[i].Capability.Uint64, 167 | DmarUnits[i].ExtendedCapability.Uint64)); 168 | } 169 | if (discoveredUnitCount == 0) 170 | { 171 | DEBUG((DEBUG_ERROR, "No DMA remapping hardware unit found.\n")); 172 | return EFI_UNSUPPORTED; 173 | } 174 | if (discoveredUnitCount > MaxDmarUnitCount) 175 | { 176 | DEBUG((DEBUG_ERROR, 177 | "Too many DMA remapping hardware units found (%llu).\n", 178 | discoveredUnitCount)); 179 | return EFI_OUT_OF_RESOURCES; 180 | } 181 | return EFI_SUCCESS; 182 | } 183 | 184 | /** 185 | * @brief Splits the PDE to a new PT. 186 | */ 187 | static 188 | VTD_SECOND_LEVEL_PAGING_ENTRY* 189 | Split2MbPage ( 190 | IN OUT VTD_SECOND_LEVEL_PAGING_ENTRY* PageDirectoryEntry 191 | ) 192 | { 193 | UINT64 baseAddress; 194 | VTD_SECOND_LEVEL_PAGING_ENTRY* pageTable; 195 | BOOLEAN readable; 196 | BOOLEAN writable; 197 | 198 | ASSERT(PageDirectoryEntry->Bits.PageSize == TRUE); 199 | 200 | pageTable = AllocateRuntimePages(1); 201 | if (pageTable == NULL) 202 | { 203 | goto Exit; 204 | } 205 | ZeroMem(pageTable, SIZE_4KB); 206 | 207 | // 208 | // Those fields should inherit from the PDE. 209 | // 210 | readable = (PageDirectoryEntry->Bits.Read != FALSE); 211 | writable = (PageDirectoryEntry->Bits.Write != FALSE); 212 | 213 | // 214 | // Fill out the page table. 215 | // 216 | baseAddress = ((UINT64)PageDirectoryEntry->Bits.AddressLo << 12) | 217 | ((UINT64)PageDirectoryEntry->Bits.AddressHi << 32); 218 | for (UINT64 ptIndex = 0; ptIndex < 512; ++ptIndex) 219 | { 220 | pageTable[ptIndex].Uint64 = baseAddress; 221 | pageTable[ptIndex].Bits.Read = readable; 222 | pageTable[ptIndex].Bits.Write = writable; 223 | baseAddress += SIZE_4KB; 224 | } 225 | 226 | // 227 | // The PDE should no longer indicates 2MB large page. 228 | // 229 | PageDirectoryEntry->Uint64 = (UINT64)pageTable; 230 | PageDirectoryEntry->Bits.PageSize = FALSE; 231 | PageDirectoryEntry->Bits.Read = TRUE; 232 | PageDirectoryEntry->Bits.Write = TRUE; 233 | 234 | // 235 | // Write back changes to RAM. Also, invalidation of IOTLB would be required 236 | // if the DMA-remapping is already enabled. Not the case in this project. 237 | // 238 | WriteBackDataCacheRange(PageDirectoryEntry, sizeof(*PageDirectoryEntry)); 239 | WriteBackDataCacheRange(pageTable, SIZE_4KB); 240 | 241 | Exit: 242 | return pageTable; 243 | } 244 | 245 | /** 246 | * @brief Updates the access permissions in the translations for the given address. 247 | * 248 | * @note As the name suggests, this change is applied for all devices, ie, you 249 | * may not specify a source-id (ie, bus:device:function). This is purely 250 | * for overall simplicity of this project. 251 | */ 252 | static 253 | EFI_STATUS 254 | ChangePermissionOfPageForAllDevices ( 255 | IN OUT DMAR_TRANSLATIONS* Translations, 256 | IN UINT64 Address, 257 | IN BOOLEAN AllowReadWrite, 258 | OUT VTD_SECOND_LEVEL_PAGING_ENTRY** AllocatedPageTable 259 | ) 260 | { 261 | EFI_STATUS status; 262 | ADDRESS_TRANSLATION_HELPER helper; 263 | VTD_SECOND_LEVEL_PAGING_ENTRY* pde; 264 | VTD_SECOND_LEVEL_PAGING_ENTRY* pt; 265 | VTD_SECOND_LEVEL_PAGING_ENTRY* pte; 266 | 267 | *AllocatedPageTable = NULL; 268 | 269 | helper.AsUInt64 = Address; 270 | 271 | // 272 | // Locate the second-level PDE for the given address. If that entry indicates 273 | // the page is 2MB large page, split it into 512 PTEs so that the exactly 274 | // specified page (4KB) only is updated. 275 | // 276 | pde = &Translations->SlPd[helper.AsIndex.Pml4][helper.AsIndex.Pdpt][helper.AsIndex.Pd]; 277 | if (pde->Bits.PageSize != FALSE) 278 | { 279 | *AllocatedPageTable = Split2MbPage(pde); 280 | if (*AllocatedPageTable == NULL) 281 | { 282 | status = EFI_OUT_OF_RESOURCES; 283 | goto Exit; 284 | } 285 | } 286 | 287 | // 288 | // Then, update the single PTE that corresponds to the given address. 289 | // 290 | pt = (VTD_SECOND_LEVEL_PAGING_ENTRY*)(((UINT64)pde->Bits.AddressLo << 12) | 291 | ((UINT64)pde->Bits.AddressHi << 32)); 292 | pte = &pt[helper.AsIndex.Pt]; 293 | pte->Bits.Read = AllowReadWrite; 294 | pte->Bits.Write = AllowReadWrite; 295 | WriteBackDataCacheRange(pte, sizeof(*pte)); 296 | 297 | // 298 | // We are good. Note that any of page table updates would require invalidation 299 | // of IOTLB if DMA-remapping is already enabled. In our case, not yet. 300 | // 301 | status = EFI_SUCCESS; 302 | 303 | Exit: 304 | return status; 305 | } 306 | 307 | /** 308 | * @brief Returns the base address of the current image, or zero on error. 309 | */ 310 | static 311 | UINT64 312 | GetCurrentImageBase ( 313 | VOID 314 | ) 315 | { 316 | EFI_STATUS status; 317 | EFI_LOADED_IMAGE_PROTOCOL* loadedImageInfo; 318 | 319 | status = gBS->OpenProtocol(gImageHandle, 320 | &gEfiLoadedImageProtocolGuid, 321 | (VOID**)&loadedImageInfo, 322 | gImageHandle, 323 | NULL, 324 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); 325 | if (EFI_ERROR(status)) 326 | { 327 | DEBUG((DEBUG_ERROR, "OpenProtocol failed : %r\n", status)); 328 | return 0; 329 | } 330 | 331 | return (UINT64)loadedImageInfo->ImageBase; 332 | } 333 | 334 | /** 335 | * @brief Builds identity mapping for all PCI devices, up to 512GB. 336 | */ 337 | static 338 | VOID 339 | BuildPassthroughTranslations ( 340 | OUT DMAR_TRANSLATIONS* Translations 341 | ) 342 | { 343 | VTD_ROOT_ENTRY defaultRootValue; 344 | VTD_CONTEXT_ENTRY defaultContextValue; 345 | VTD_SECOND_LEVEL_PAGING_ENTRY* pdpt; 346 | VTD_SECOND_LEVEL_PAGING_ENTRY* pd; 347 | VTD_SECOND_LEVEL_PAGING_ENTRY* pml4e; 348 | VTD_SECOND_LEVEL_PAGING_ENTRY* pdpte; 349 | VTD_SECOND_LEVEL_PAGING_ENTRY* pde; 350 | UINT64 pml4Index; 351 | UINT64 destinationPa; 352 | 353 | ASSERT(((UINT64)Translations % SIZE_4KB) == 0); 354 | 355 | ZeroMem(Translations, sizeof(*Translations)); 356 | 357 | // 358 | // Fill out the root table. All root entries point to the same context table. 359 | // 360 | defaultRootValue.Uint128.Uint64Hi = defaultRootValue.Uint128.Uint64Lo = 0; 361 | defaultRootValue.Bits.ContextTablePointerLo = (UINT32)((UINT64)Translations->ContextTable >> 12); 362 | defaultRootValue.Bits.ContextTablePointerHi = (UINT32)((UINT64)Translations->ContextTable >> 32); 363 | defaultRootValue.Bits.Present = TRUE; 364 | for (UINT64 bus = 0; bus < ARRAY_SIZE(Translations->RootTable); bus++) 365 | { 366 | Translations->RootTable[bus] = defaultRootValue; 367 | } 368 | 369 | // 370 | // Fill out the context table. All context entries point to the same 371 | // second-level PML4. 372 | // 373 | // Note that pass-through translations can also be archived by setting 10b to 374 | // the TT: Translation Type field, instead of using the second-level page 375 | // tables. 376 | // 377 | defaultContextValue.Uint128.Uint64Hi = defaultContextValue.Uint128.Uint64Lo = 0; 378 | defaultContextValue.Bits.DomainIdentifier = UEFI_DOMAIN_ID; 379 | defaultContextValue.Bits.AddressWidth = BIT1; // 010b: 48-bit AGAW (4-level page table) 380 | defaultContextValue.Bits.SecondLevelPageTranslationPointerLo = (UINT32)((UINT64)Translations->SlPml4 >> 12); 381 | defaultContextValue.Bits.SecondLevelPageTranslationPointerHi = (UINT32)((UINT64)Translations->SlPml4 >> 32); 382 | defaultContextValue.Bits.Present = TRUE; 383 | for (UINT64 i = 0; i < ARRAY_SIZE(Translations->ContextTable); i++) 384 | { 385 | Translations->ContextTable[i] = defaultContextValue; 386 | } 387 | 388 | // 389 | // Fill out the second level page tables. All entries indicates readable and 390 | // writable, and translations are identity mapping. No second-level page table 391 | // is used to save space. All PDEs are configured for 2MB large pages. 392 | // 393 | destinationPa = 0; 394 | 395 | // 396 | // SL-PML4. Only the first entry (ie, translation up to 512GB) is initialized. 397 | // 398 | pml4Index = 0; 399 | pdpt = Translations->SlPdpt[pml4Index]; 400 | pml4e = &Translations->SlPml4[pml4Index]; 401 | pml4e->Uint64 = (UINT64)pdpt; 402 | pml4e->Bits.Read = TRUE; 403 | pml4e->Bits.Write = TRUE; 404 | 405 | for (UINT64 pdptIndex = 0; pdptIndex < 512; pdptIndex++) 406 | { 407 | // 408 | // SL-PDPT 409 | // 410 | pd = Translations->SlPd[pml4Index][pdptIndex]; 411 | pdpte = &pdpt[pdptIndex]; 412 | pdpte->Uint64 = (UINT64)pd; 413 | pdpte->Bits.Read = TRUE; 414 | pdpte->Bits.Write = TRUE; 415 | 416 | for (UINT64 pdIndex = 0; pdIndex < 512; pdIndex++) 417 | { 418 | // 419 | // SL-PD. 420 | // 421 | pde = &pd[pdIndex]; 422 | pde->Uint64 = destinationPa; 423 | pde->Bits.Read = TRUE; 424 | pde->Bits.Write = TRUE; 425 | pde->Bits.PageSize = TRUE; 426 | destinationPa += SIZE_2MB; 427 | } 428 | } 429 | 430 | // 431 | // Write-back the whole range of the translations object to RAM. This flushing 432 | // cache line is not required if the C: Page-walk Coherency bit is set. Same 433 | // as other flush in this project. All author's units did not set this bit. 434 | // 435 | WriteBackDataCacheRange(Translations, sizeof(*Translations)); 436 | } 437 | 438 | /** 439 | * @brief Enables DMA-remapping for the hardware unit using the given translation. 440 | */ 441 | static 442 | VOID 443 | EnableDmaRemapping ( 444 | IN CONST DMAR_UNIT_INFORMATION* DmarUnit, 445 | IN CONST DMAR_TRANSLATIONS* Translations 446 | ) 447 | { 448 | VTD_ROOT_TABLE_ADDRESS_REGISTER rootTableAddressReg; 449 | UINT64 iotlbRegOffset; 450 | 451 | DEBUG((DEBUG_INFO, "Working with the remapping unit at %p\n", DmarUnit->RegisterBasePa)); 452 | 453 | // 454 | // Set the Root Table Pointer. This is equivalent to setting CR3 conceptually. 455 | // After setting the "SRTP: Set Root Table Pointer" bit, software must wait 456 | // completion of it. See 10.4.5 Global Status Register. 457 | // 458 | DEBUG((DEBUG_INFO, "Setting the root table pointer to %p\n", Translations->RootTable)); 459 | rootTableAddressReg.AsUInt64 = 0; 460 | rootTableAddressReg.Bits.RootTable = (UINT64)Translations->RootTable >> 12; 461 | MmioWrite64(DmarUnit->RegisterBaseVa + R_RTADDR_REG, rootTableAddressReg.AsUInt64); 462 | MmioWrite32(DmarUnit->RegisterBaseVa + R_GCMD_REG, B_GMCD_REG_SRTP); 463 | for (; (MmioRead32(DmarUnit->RegisterBaseVa + R_GSTS_REG) & B_GSTS_REG_RTPS) == 0;) 464 | { 465 | CpuPause(); 466 | } 467 | 468 | // 469 | // Then, invalidate cache that may exists as requested by the specification. 470 | // 471 | // "After a ‘Set Root Table Pointer’ operation, software must perform global 472 | // invalidations on the context-cache, pasid-cache, and IOTLB, in that order." 473 | // See 10.4.4 Global Command Register 474 | // 475 | 476 | // 477 | // Invalidate context-cache. See 10.4.7 Context Command Register. 478 | // 479 | DEBUG((DEBUG_INFO, "Invalidating context-cache globally\n")); 480 | MmioWrite64(DmarUnit->RegisterBaseVa + R_CCMD_REG, V_CCMD_REG_CIRG_GLOBAL | B_CCMD_REG_ICC); 481 | for (; (MmioRead64(DmarUnit->RegisterBaseVa + R_CCMD_REG) & B_CCMD_REG_ICC) != 0;) 482 | { 483 | CpuPause(); 484 | } 485 | 486 | // 487 | // Invalidate IOTLB. See 10.4.8.1 IOTLB Invalidate Register. 488 | // Also drain all read and write requests. 489 | // "Hardware implementations supporting DMA draining must drain any inflight 490 | // DMA read/write requests" 491 | // 492 | DEBUG((DEBUG_INFO, "Invalidating IOTLB globally\n")); 493 | iotlbRegOffset = (UINT64)DmarUnit->ExtendedCapability.Bits.IRO * 16; 494 | MmioWrite64(DmarUnit->RegisterBaseVa + iotlbRegOffset + R_IOTLB_REG, 495 | B_IOTLB_REG_IVT | V_IOTLB_REG_IIRG_GLOBAL | V_IOTLB_REG_DR | V_IOTLB_REG_DW); 496 | for (; (MmioRead64(DmarUnit->RegisterBaseVa + iotlbRegOffset + R_IOTLB_REG) & B_IOTLB_REG_IVT) != 0;) 497 | { 498 | CpuPause(); 499 | } 500 | 501 | // 502 | // Enabling DMA-remapping. See 10.4.4 Global Command Register. 503 | // 504 | DEBUG((DEBUG_INFO, "Enabling DMA-remapping\n")); 505 | MmioWrite32(DmarUnit->RegisterBaseVa + R_GCMD_REG, B_GMCD_REG_TE); 506 | for (; (MmioRead32(DmarUnit->RegisterBaseVa + R_GSTS_REG) & B_GSTS_REG_TE) == 0;) 507 | { 508 | CpuPause(); 509 | } 510 | } 511 | 512 | /** 513 | * @brief Tests whether all hardware units are compatible with this project. 514 | */ 515 | static 516 | BOOLEAN 517 | AreAllDmaRemappingUnitsCompatible ( 518 | IN CONST DMAR_UNIT_INFORMATION* DmarUnits, 519 | IN UINT64 DmarUnitsCount 520 | ) 521 | { 522 | for (UINT64 i = 0; i < DmarUnitsCount; ++i) 523 | { 524 | // 525 | // This project does not handle 3-level page-table for simplicity. 526 | // 527 | if ((DmarUnits[i].Capability.Bits.SAGAW & BIT2) == 0) 528 | { 529 | DEBUG((DEBUG_ERROR, 530 | "Unit %lld does not support 48-bit AGAW (4-level page-table) : %016llx\n", 531 | i, 532 | DmarUnits[i].Capability.Uint64)); 533 | return FALSE; 534 | } 535 | 536 | // 537 | // This project requires 2MB large pages for simple second-level table 538 | // implementation. 539 | // 540 | if ((DmarUnits[i].Capability.Bits.SLLPS & BIT0) == 0) 541 | { 542 | DEBUG((DEBUG_ERROR, 543 | "Unit %lld does not support 2MB second level large pages : %016llx\n", 544 | i, 545 | DmarUnits[i].Capability.Uint64)); 546 | return FALSE; 547 | } 548 | 549 | // 550 | // Earlier implementation of DMA-remapping required explicit write buffer 551 | // flushing. The author have not encounter with such implementation. As 552 | // such, this project does not support it. See 6.8 Write Buffer Flushing. 553 | // 554 | if (DmarUnits[i].Capability.Bits.RWBF != FALSE) 555 | { 556 | DEBUG((DEBUG_ERROR, 557 | "Unit %lld requires explicit write buffer flushing : %016llx\n", 558 | i, 559 | DmarUnits[i].Capability.Uint64)); 560 | return FALSE; 561 | } 562 | 563 | // 564 | // If DMA-remapping is already enabled, do not attempt to mess with this. 565 | // This is the case when preboot VT-d is enabled, for example. 566 | // 567 | if ((MmioRead32(DmarUnits[i].RegisterBaseVa + R_GSTS_REG) & B_GSTS_REG_TE) != 0) 568 | { 569 | DEBUG((DEBUG_ERROR, 570 | "Unit %lld already enabled DMA remapping : %016llx\n", 571 | i, 572 | MmioRead32(DmarUnits[i].RegisterBaseVa + R_GSTS_REG))); 573 | return FALSE; 574 | } 575 | 576 | // 577 | // Looks good. Dump physical address of where translation fault logs are saved. 578 | // 579 | Print(L"Fault-recording Register at %p\n", 580 | DmarUnits[i].RegisterBaseVa + (UINT64)DmarUnits[i].Capability.Bits.FRO * 16); 581 | } 582 | return TRUE; 583 | } 584 | 585 | /** 586 | * @brief The module entry point. 587 | */ 588 | EFI_STATUS 589 | EFIAPI 590 | HelloIommuDxeInitialize ( 591 | IN EFI_HANDLE ImageHandle, 592 | IN EFI_SYSTEM_TABLE* SystemTable 593 | ) 594 | { 595 | EFI_STATUS status; 596 | EFI_ACPI_DMAR_HEADER* dmarTable; 597 | DMAR_UNIT_INFORMATION dmarUnits[8]; 598 | UINT64 dmarUnitCount; 599 | UINT64 addressToProtect; 600 | DMAR_TRANSLATIONS* translations; 601 | VTD_SECOND_LEVEL_PAGING_ENTRY* pageTable; 602 | 603 | translations = NULL; 604 | pageTable = NULL; 605 | 606 | DEBUG((DEBUG_VERBOSE, "Loading the driver...\n")); 607 | 608 | // 609 | // Locate the DMAR ACPI table. 610 | // 611 | dmarTable = (EFI_ACPI_DMAR_HEADER*)EfiLocateFirstAcpiTable( 612 | EFI_ACPI_4_0_DMA_REMAPPING_TABLE_SIGNATURE); 613 | if (dmarTable == NULL) 614 | { 615 | DEBUG((DEBUG_ERROR, 616 | "EfiLocateFirstAcpiTable failed. DMA remapping (VT-d) not supported.\n")); 617 | status = EFI_UNSUPPORTED; 618 | goto Exit; 619 | } 620 | 621 | // 622 | // Gather DMA remapping hardware units information from the DMAR table. This 623 | // enumerates all DMA-remapping hardware unit definiton structures and collects 624 | // relevant information such as the base register address and capability 625 | // register values. 626 | // 627 | status = ProcessDmarTable(dmarTable, dmarUnits, ARRAY_SIZE(dmarUnits), &dmarUnitCount); 628 | if (EFI_ERROR(status)) 629 | { 630 | DEBUG((DEBUG_ERROR, "ProcessDmarTable failed : %r\n", status)); 631 | goto Exit; 632 | } 633 | 634 | // 635 | // This project requires availability of certain features and expect certain 636 | // system state for simplicity. Verify that those are satisfied. 637 | // 638 | if (AreAllDmaRemappingUnitsCompatible(dmarUnits, dmarUnitCount) == FALSE) 639 | { 640 | DEBUG((DEBUG_ERROR, "One of more DMA remapping hardware unit is incompatible.\n")); 641 | status = EFI_UNSUPPORTED; 642 | goto Exit; 643 | } 644 | 645 | // 646 | // Allocate data structures configuring address translation, that is, the root 647 | // table, context table, second-level PML4, PDPT and PD. Then, initialize them 648 | // to set up identity mapping (passthrough translation). 649 | // 650 | translations = AllocateRuntimePages(EFI_SIZE_TO_PAGES(sizeof(*translations))); 651 | if (translations == NULL) 652 | { 653 | DEBUG((DEBUG_ERROR, 654 | "Failed to allocate %llu runtime pages.\n", 655 | EFI_SIZE_TO_PAGES(sizeof(*translations)))); 656 | status = EFI_OUT_OF_RESOURCES; 657 | goto Exit; 658 | } 659 | BuildPassthroughTranslations(translations); 660 | 661 | // 662 | // For demonstration, make the first page of this module to be non-readable, 663 | // non-writable via DMA. 664 | // 665 | addressToProtect = GetCurrentImageBase(); 666 | if (addressToProtect == 0) 667 | { 668 | DEBUG((DEBUG_ERROR, "Unable to resolve the location to protect.\n")); 669 | status = EFI_LOAD_ERROR; 670 | goto Exit; 671 | } 672 | status = ChangePermissionOfPageForAllDevices(translations, 673 | addressToProtect, 674 | FALSE, 675 | &pageTable); 676 | if (EFI_ERROR(status)) 677 | { 678 | DEBUG((DEBUG_ERROR, "ChangePermissionOfPageForAllDevices failed : %r\n", status)); 679 | goto Exit; 680 | } 681 | 682 | // 683 | // Finally, enable DMA-remapping for all hardware units. 684 | // 685 | for (UINT64 i = 0; i < dmarUnitCount; ++i) 686 | { 687 | EnableDmaRemapping(&dmarUnits[i], translations); 688 | } 689 | 690 | // 691 | // Break the signature of the DMAR table so that the operating system does 692 | // not try to (re)configure DMA-remapping. This obviously is not a production 693 | // quality approach, as the operating system may not secure the system using 694 | // DMA-remapping as it would do. There is no agreed interface between the 695 | // platform and IOMMU-aware OS loaders to hand over already enabled IOMMUs. 696 | // See "A Tour Beyond BIOS: Using IOMMU for DMA Protection in UEFI Firmware" 697 | // for other possible options. 698 | // 699 | dmarTable->Header.Signature = SIGNATURE_32('?', '?', '?', '?'); 700 | 701 | // 702 | // Anyway, we are good now. 703 | // 704 | status = EFI_SUCCESS; 705 | Print(L"Physical address %p-%p protected from DMA read and write\n", 706 | addressToProtect, 707 | addressToProtect + SIZE_4KB); 708 | 709 | Exit: 710 | if (EFI_ERROR(status)) 711 | { 712 | if (pageTable != NULL) 713 | { 714 | FreePages(pageTable, 1); 715 | } 716 | 717 | if (translations) 718 | { 719 | FreePages(translations, EFI_SIZE_TO_PAGES(sizeof(*translations))); 720 | } 721 | } 722 | return status; 723 | } 724 | -------------------------------------------------------------------------------- /Drivers/HelloIommuDxe/HelloIommuDxe.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.27 3 | BASE_NAME = HelloIommuDxe 4 | FILE_GUID = be96aeb2-12c6-4663-9c0d-9f7d7035912f 5 | MODULE_TYPE = DXE_RUNTIME_DRIVER 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = HelloIommuDxeInitialize 8 | 9 | [Sources] 10 | HelloIommuDxe.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | HelloIommuPkg/HelloIommuPkg.dec 15 | 16 | [LibraryClasses] 17 | UefiDriverEntryPoint 18 | UefiLib 19 | UefiRuntimeLib 20 | IoLib 21 | CacheMaintenanceLib 22 | 23 | [Depex] 24 | TRUE 25 | 26 | [BuildOptions.common.DXE_RUNTIME_DRIVER] 27 | # Detect use of deprecated interfaces if any. 28 | MSFT:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES 29 | 30 | # Remove DebugLib library instances (ASSERT and such) from the RELEASE binary. 31 | # https://github.com/tianocore-docs/edk2-UefiDriverWritersGuide/blob/master/31_testing_and_debugging_uefi_drivers/314_debugging_code_statements/3141_configuring_debuglib_with_edk_ii.md 32 | MSFT:RELEASE_*_*_CC_FLAGS = -D MDEPKG_NDEBUG 33 | 34 | # By default, certain meta-data in the PE header is zeroed out to increase 35 | # compression ratio. Some of those information can be helpful for a debugger, 36 | # for example, to reconstruct stack trace. Leave it for such cases. See also, 37 | # https://edk2-docs.gitbook.io/edk-ii-basetools-user-guides/genfw 38 | MSFT:*_*_X64_GENFW_FLAGS = --keepexceptiontable --keepzeropending --keepoptionalheader 39 | -------------------------------------------------------------------------------- /HelloIommuPkg.dec: -------------------------------------------------------------------------------- 1 | [Defines] 2 | DEC_SPECIFICATION = 1.27 3 | PACKAGE_NAME = HelloIommuPkg 4 | PACKAGE_GUID = 7587b33a-78f3-4ae0-ac96-dfec5d83169e 5 | PACKAGE_VERSION = 1.00 6 | 7 | [Includes] 8 | Include 9 | -------------------------------------------------------------------------------- /HelloIommuPkg.dsc: -------------------------------------------------------------------------------- 1 | [Defines] 2 | DSC_SPECIFICATION = 1.28 3 | PLATFORM_NAME = HelloIommuPkg 4 | PLATFORM_GUID = 2432c219-4d28-4328-82d0-bedbec8cb027 5 | PLATFORM_VERSION = 1.00 6 | OUTPUT_DIRECTORY = Build/HelloIommuPkg 7 | SUPPORTED_ARCHITECTURES = X64 8 | BUILD_TARGETS = DEBUG|RELEASE|NOOPT 9 | SKUID_IDENTIFIER = DEFAULT 10 | 11 | [Components] 12 | HelloIommuPkg/Drivers/HelloIommuDxe/HelloIommuDxe.inf 13 | 14 | [LibraryClasses] 15 | BaseLib|MdePkg/Library/BaseLib/BaseLib.inf 16 | BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf 17 | CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf 18 | DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf 19 | DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf 20 | MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf 21 | PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf 22 | PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf 23 | UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf 24 | UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf 25 | UefiLib|MdePkg/Library/UefiLib/UefiLib.inf 26 | UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf 27 | UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf 28 | RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf 29 | IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf 30 | !if $(TARGET) == RELEASE 31 | DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf 32 | !else 33 | !ifdef $(DEBUG_ON_SERIAL_PORT) 34 | SerialPortLib|PcAtChipsetPkg/Library/SerialIoLib/SerialIoLib.inf 35 | DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf 36 | !else 37 | DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf 38 | !endif 39 | !endif 40 | 41 | [PcdsFixedAtBuild] 42 | # Enable EDK2 debug features based on the TARGET configuration. 43 | # https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Debugging 44 | !if $(TARGET) == RELEASE 45 | # No debug code such as DEBUG() / ASSERT(). They will be removed. 46 | gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x0 47 | gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x0 48 | !else 49 | # Define DEBUG_ERROR | DEBUG_VERBOSE | DEBUG_INFO | DEBUG_WARN to enable 50 | # logging at those levels. Also, define DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED 51 | # and such. Assertion failure will call CpuDeadLoop. 52 | gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80400042 53 | gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2f 54 | !endif 55 | -------------------------------------------------------------------------------- /Include/IndustryStandard/Vtd.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | The definition for VTD register. 3 | It is defined in "Intel VT for Direct IO Architecture Specification". 4 | 5 | Copyright (c) 2017, Intel Corporation. All rights reserved.
6 | SPDX-License-Identifier: BSD-2-Clause-Patent 7 | 8 | **/ 9 | 10 | #ifndef __VTD_REG_H__ 11 | #define __VTD_REG_H__ 12 | 13 | #pragma pack(1) 14 | 15 | // 16 | // Translation Structure Formats 17 | // 18 | #define VTD_ROOT_ENTRY_NUMBER 256 19 | #define VTD_CONTEXT_ENTRY_NUMBER 256 20 | 21 | typedef union { 22 | struct { 23 | UINT32 Present:1; 24 | UINT32 Reserved_1:11; 25 | UINT32 ContextTablePointerLo:20; 26 | UINT32 ContextTablePointerHi:32; 27 | 28 | UINT64 Reserved_64; 29 | } Bits; 30 | struct { 31 | UINT64 Uint64Lo; 32 | UINT64 Uint64Hi; 33 | } Uint128; 34 | } VTD_ROOT_ENTRY; 35 | 36 | typedef union { 37 | struct { 38 | UINT32 LowerPresent:1; 39 | UINT32 Reserved_1:11; 40 | UINT32 LowerContextTablePointerLo:20; 41 | UINT32 LowerContextTablePointerHi:32; 42 | 43 | UINT32 UpperPresent:1; 44 | UINT32 Reserved_65:11; 45 | UINT32 UpperContextTablePointerLo:20; 46 | UINT32 UpperContextTablePointerHi:32; 47 | } Bits; 48 | struct { 49 | UINT64 Uint64Lo; 50 | UINT64 Uint64Hi; 51 | } Uint128; 52 | } VTD_EXT_ROOT_ENTRY; 53 | 54 | typedef union { 55 | struct { 56 | UINT32 Present:1; 57 | UINT32 FaultProcessingDisable:1; 58 | UINT32 TranslationType:2; 59 | UINT32 Reserved_4:8; 60 | UINT32 SecondLevelPageTranslationPointerLo:20; 61 | UINT32 SecondLevelPageTranslationPointerHi:32; 62 | 63 | UINT32 AddressWidth:3; 64 | UINT32 Ignored_67:4; 65 | UINT32 Reserved_71:1; 66 | UINT32 DomainIdentifier:16; 67 | UINT32 Reserved_88:8; 68 | UINT32 Reserved_96:32; 69 | } Bits; 70 | struct { 71 | UINT64 Uint64Lo; 72 | UINT64 Uint64Hi; 73 | } Uint128; 74 | } VTD_CONTEXT_ENTRY; 75 | 76 | typedef union { 77 | struct { 78 | UINT32 Present:1; 79 | UINT32 FaultProcessingDisable:1; 80 | UINT32 TranslationType:3; 81 | UINT32 ExtendedMemoryType:3; 82 | UINT32 DeferredInvalidateEnable:1; 83 | UINT32 PageRequestEnable:1; 84 | UINT32 NestedTranslationEnable:1; 85 | UINT32 PASIDEnable:1; 86 | UINT32 SecondLevelPageTranslationPointerLo:20; 87 | UINT32 SecondLevelPageTranslationPointerHi:32; 88 | 89 | UINT32 AddressWidth:3; 90 | UINT32 PageGlobalEnable:1; 91 | UINT32 NoExecuteEnable:1; 92 | UINT32 WriteProtectEnable:1; 93 | UINT32 CacheDisable:1; 94 | UINT32 ExtendedMemoryTypeEnable:1; 95 | UINT32 DomainIdentifier:16; 96 | UINT32 SupervisorModeExecuteProtection:1; 97 | UINT32 ExtendedAccessedFlagEnable:1; 98 | UINT32 ExecuteRequestsEnable:1; 99 | UINT32 SecondLevelExecuteEnable:1; 100 | UINT32 Reserved_92:4; 101 | UINT32 PageAttributeTable0:3; 102 | UINT32 Reserved_Pat0:1; 103 | UINT32 PageAttributeTable1:3; 104 | UINT32 Reserved_Pat1:1; 105 | UINT32 PageAttributeTable2:3; 106 | UINT32 Reserved_Pat2:1; 107 | UINT32 PageAttributeTable3:3; 108 | UINT32 Reserved_Pat3:1; 109 | UINT32 PageAttributeTable4:3; 110 | UINT32 Reserved_Pat4:1; 111 | UINT32 PageAttributeTable5:3; 112 | UINT32 Reserved_Pat5:1; 113 | UINT32 PageAttributeTable6:3; 114 | UINT32 Reserved_Pat6:1; 115 | UINT32 PageAttributeTable7:3; 116 | UINT32 Reserved_Pat7:1; 117 | 118 | UINT32 PASIDTableSize:4; 119 | UINT32 Reserved_132:8; 120 | UINT32 PASIDTablePointerLo:20; 121 | UINT32 PASIDTablePointerHi:32; 122 | 123 | UINT32 Reserved_192:12; 124 | UINT32 PASIDStateTablePointerLo:20; 125 | UINT32 PASIDStateTablePointerHi:32; 126 | } Bits; 127 | struct { 128 | UINT64 Uint64_1; 129 | UINT64 Uint64_2; 130 | UINT64 Uint64_3; 131 | UINT64 Uint64_4; 132 | } Uint256; 133 | } VTD_EXT_CONTEXT_ENTRY; 134 | 135 | typedef union { 136 | struct { 137 | UINT32 Present:1; 138 | UINT32 Reserved_1:2; 139 | UINT32 PageLevelCacheDisable:1; 140 | UINT32 PageLevelWriteThrough:1; 141 | UINT32 Reserved_5:6; 142 | UINT32 SupervisorRequestsEnable:1; 143 | UINT32 FirstLevelPageTranslationPointerLo:20; 144 | UINT32 FirstLevelPageTranslationPointerHi:32; 145 | } Bits; 146 | UINT64 Uint64; 147 | } VTD_PASID_ENTRY; 148 | 149 | typedef union { 150 | struct { 151 | UINT32 Reserved_0:32; 152 | UINT32 ActiveReferenceCount:16; 153 | UINT32 Reserved_48:15; 154 | UINT32 DeferredInvalidate:1; 155 | } Bits; 156 | UINT64 Uint64; 157 | } VTD_PASID_STATE_ENTRY; 158 | 159 | typedef union { 160 | struct { 161 | UINT32 Present:1; 162 | UINT32 ReadWrite:1; 163 | UINT32 UserSupervisor:1; 164 | UINT32 PageLevelWriteThrough:1; 165 | UINT32 PageLevelCacheDisable:1; 166 | UINT32 Accessed:1; 167 | UINT32 Dirty:1; 168 | UINT32 PageSize:1; // It is PageAttribute:1 for 4K page entry 169 | UINT32 Global:1; 170 | UINT32 Ignored_9:1; 171 | UINT32 ExtendedAccessed:1; 172 | UINT32 Ignored_11:1; 173 | // NOTE: There is PageAttribute:1 as bit12 for 1G page entry and 2M page entry 174 | UINT32 AddressLo:20; 175 | UINT32 AddressHi:20; 176 | UINT32 Ignored_52:11; 177 | UINT32 ExecuteDisable:1; 178 | } Bits; 179 | UINT64 Uint64; 180 | } VTD_FIRST_LEVEL_PAGING_ENTRY; 181 | 182 | typedef union { 183 | struct { 184 | UINT32 Read:1; 185 | UINT32 Write:1; 186 | UINT32 Execute:1; 187 | UINT32 ExtendedMemoryType:3; 188 | UINT32 IgnorePAT:1; 189 | UINT32 PageSize:1; 190 | UINT32 Ignored_8:3; 191 | UINT32 Snoop:1; 192 | UINT32 AddressLo:20; 193 | UINT32 AddressHi:20; 194 | UINT32 Ignored_52:10; 195 | UINT32 TransientMapping:1; 196 | UINT32 Ignored_63:1; 197 | } Bits; 198 | UINT64 Uint64; 199 | } VTD_SECOND_LEVEL_PAGING_ENTRY; 200 | 201 | // 202 | // Register Descriptions 203 | // 204 | #define R_VER_REG 0x00 205 | #define R_CAP_REG 0x08 206 | #define B_CAP_REG_RWBF BIT4 207 | #define R_ECAP_REG 0x10 208 | #define R_GCMD_REG 0x18 209 | #define B_GMCD_REG_WBF BIT27 210 | #define B_GMCD_REG_SRTP BIT30 211 | #define B_GMCD_REG_TE BIT31 212 | #define R_GSTS_REG 0x1C 213 | #define B_GSTS_REG_WBF BIT27 214 | #define B_GSTS_REG_RTPS BIT30 215 | #define B_GSTS_REG_TE BIT31 216 | #define R_RTADDR_REG 0x20 217 | #define R_CCMD_REG 0x28 218 | #define B_CCMD_REG_CIRG_MASK (BIT62|BIT61) 219 | #define V_CCMD_REG_CIRG_GLOBAL BIT61 220 | #define V_CCMD_REG_CIRG_DOMAIN BIT62 221 | #define V_CCMD_REG_CIRG_DEVICE (BIT62|BIT61) 222 | #define B_CCMD_REG_ICC BIT63 223 | #define R_FSTS_REG 0x34 224 | #define R_FECTL_REG 0x38 225 | #define R_FEDATA_REG 0x3C 226 | #define R_FEADDR_REG 0x40 227 | #define R_FEUADDR_REG 0x44 228 | #define R_AFLOG_REG 0x58 229 | 230 | #define R_IVA_REG 0x00 // + IRO 231 | #define B_IVA_REG_AM_MASK (BIT0|BIT1|BIT2|BIT3|BIT4|BIT5) 232 | #define B_IVA_REG_AM_4K 0 // 1 page 233 | #define B_IVA_REG_AM_2M 9 // 2M page 234 | #define B_IVA_REG_IH BIT6 235 | #define R_IOTLB_REG 0x08 // + IRO 236 | #define B_IOTLB_REG_IIRG_MASK (BIT61|BIT60) 237 | #define V_IOTLB_REG_IIRG_GLOBAL BIT60 238 | #define V_IOTLB_REG_IIRG_DOMAIN BIT61 239 | #define V_IOTLB_REG_IIRG_PAGE (BIT61|BIT60) 240 | #define B_IOTLB_REG_IVT BIT63 241 | 242 | #define R_FRCD_REG 0x00 // + FRO 243 | 244 | #define R_PMEN_ENABLE_REG 0x64 245 | #define R_PMEN_LOW_BASE_REG 0x68 246 | #define R_PMEN_LOW_LIMITE_REG 0x6C 247 | #define R_PMEN_HIGH_BASE_REG 0x70 248 | #define R_PMEN_HIGH_LIMITE_REG 0x78 249 | 250 | typedef union { 251 | struct { 252 | UINT8 ND:3; // Number of domains supported 253 | UINT8 AFL:1; // Advanced Fault Logging 254 | UINT8 RWBF:1; // Required Write-Buffer Flushing 255 | UINT8 PLMR:1; // Protected Low-Memory Region 256 | UINT8 PHMR:1; // Protected High-Memory Region 257 | UINT8 CM:1; // Caching Mode 258 | 259 | UINT8 SAGAW:5; // Supported Adjusted Guest Address Widths 260 | UINT8 Rsvd_13:3; 261 | 262 | UINT8 MGAW:6; // Maximum Guest Address Width 263 | UINT8 ZLR:1; // Zero Length Read 264 | UINT8 Rsvd_23:1; 265 | 266 | UINT16 FRO:10; // Fault-recording Register offset 267 | UINT16 SLLPS:4; // Second Level Large Page Support 268 | UINT16 Rsvd_38:1; 269 | UINT16 PSI:1; // Page Selective Invalidation 270 | 271 | UINT8 NFR:8; // Number of Fault-recording Registers 272 | 273 | UINT8 MAMV:6; // Maximum Address Mask Value 274 | UINT8 DWD:1; // Write Draining 275 | UINT8 DRD:1; // Read Draining 276 | 277 | UINT8 FL1GP:1; // First Level 1-GByte Page Support 278 | UINT8 Rsvd_57:2; 279 | UINT8 PI:1; // Posted Interrupts Support 280 | UINT8 Rsvd_60:4; 281 | } Bits; 282 | UINT64 Uint64; 283 | } VTD_CAP_REG; 284 | 285 | typedef union { 286 | struct { 287 | UINT8 C:1; // Page-walk Coherency 288 | UINT8 QI:1; // Queued Invalidation support 289 | UINT8 DT:1; // Device-TLB support 290 | UINT8 IR:1; // Interrupt Remapping support 291 | UINT8 EIM:1; // Extended Interrupt Mode 292 | UINT8 Rsvd_5:1; 293 | UINT8 PT:1; // Pass Through 294 | UINT8 SC:1; // Snoop Control 295 | 296 | UINT16 IRO:10; // IOTLB Register Offset 297 | UINT16 Rsvd_18:2; 298 | UINT16 MHMV:4; // Maximum Handle Mask Value 299 | 300 | UINT8 ECS:1; // Extended Context Support 301 | UINT8 MTS:1; // Memory Type Support 302 | UINT8 NEST:1; // Nested Translation Support 303 | UINT8 DIS:1; // Deferred Invalidate Support 304 | UINT8 PASID:1; // Process Address Space ID Support 305 | UINT8 PRS:1; // Page Request Support 306 | UINT8 ERS:1; // Execute Request Support 307 | UINT8 SRS:1; // Supervisor Request Support 308 | 309 | UINT32 Rsvd_32:1; 310 | UINT32 NWFS:1; // No Write Flag Support 311 | UINT32 EAFS:1; // Extended Accessed Flag Support 312 | UINT32 PSS:5; // PASID Size Supported 313 | UINT32 Rsvd_40:24; 314 | } Bits; 315 | UINT64 Uint64; 316 | } VTD_ECAP_REG; 317 | 318 | typedef union { 319 | struct { 320 | UINT32 Rsvd_0:12; 321 | UINT32 FILo:20; // FaultInfo 322 | UINT32 FIHi:32; // FaultInfo 323 | 324 | UINT32 SID:16; // Source Identifier 325 | UINT32 Rsvd_80:13; 326 | UINT32 PRIV:1; // Privilege Mode Requested 327 | UINT32 EXE:1; // Execute Permission Requested 328 | UINT32 PP:1; // PASID Present 329 | 330 | UINT32 FR:8; // Fault Reason 331 | UINT32 PV:20; // PASID Value 332 | UINT32 AT:2; // Address Type 333 | UINT32 T:1; // Type (0: Write, 1: Read) 334 | UINT32 F:1; // Fault 335 | } Bits; 336 | UINT64 Uint64[2]; 337 | } VTD_FRCD_REG; 338 | 339 | typedef union { 340 | struct { 341 | UINT8 Function:3; 342 | UINT8 Device:5; 343 | UINT8 Bus; 344 | } Bits; 345 | struct { 346 | UINT8 ContextIndex; 347 | UINT8 RootIndex; 348 | } Index; 349 | UINT16 Uint16; 350 | } VTD_SOURCE_ID; 351 | 352 | #pragma pack() 353 | 354 | #endif 355 | 356 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Satoshi Tanda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HelloIommuPkg 2 | ============== 3 | 4 | HelloIommuPkg is a sample DXE runtime driver demonstrating how to program DMA 5 | remapping, one of the Intel VT-d features, to protect the system from DMA access. 6 | 7 | This project is meant to show a simplified yet functioning code example for learning 8 | purposes and not designed for actual use in production systems. 9 | 10 | For more information about this project see my blog post: [Introductory Study of IOMMU (VT-d) and Kernel DMA Protection on Intel Processors](https://standa-note.blogspot.com/2020/05/introductory-study-of-iommu-vt-d-and.html) 11 | 12 | Building 13 | --------- 14 | 15 | 1. Set up edk2 build environment 16 | - `bb18fb80abb9d35d01be5d693086a9ed4b9d65b5` is the latest revision that confirmed to compile this project successfully . 17 | 2. Copy `HelloIommuPkg` as `edk2\HelloIommuPkg` 18 | 3. On the edk2 build command prompt, run the below command: 19 | ``` 20 | > edksetup.bat 21 | > build -t VS2019 -a X64 -b NOOPT -p HelloIommuPkg\HelloIommuPkg.dsc 22 | or 23 | > build -t CLANGPDB -a X64 -b NOOPT -p HelloIommuPkg\HelloIommuPkg.dsc 24 | ``` 25 | Or on WSL or Linux, 26 | ``` 27 | $ . edksetup.sh 28 | $ build -t GCC5 -a X64 -b NOOPT -p HelloIommuPkg/HelloIommuPkg.dsc 29 | ``` 30 | 31 | Also, pre-compiled binary files are available at the Release page. 32 | --------------------------------------------------------------------------------