1/** 2 * \file acpi_parse_dmar.c 3 * \brief ACPI DMA REMAPPING STRUCTURE 4 * 5 * Intel Virtualization Technology for Directed I/O Architecture Specification, 6 * Rev. 2.5 November 2017, Chapter 8 BIOS Considerations 7 * https://software.intel.com/sites/default/files/managed/c5/15/vt-directed-io-spec.pdf 8 */ 9 10/* 11 * Copyright (c) 2017 ETH Zurich. 12 * All rights reserved. 13 * 14 * This file is distributed under the terms in the attached LICENSE file. 15 * If you do not find this file, copies can be found by writing to: 16 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group. 17 */ 18 19#include <barrelfish/barrelfish.h> 20 21#include <skb/skb.h> 22#include <octopus/getset.h> 23 24#include <hw_records.h> 25 26#include "acpi_debug.h" 27#include "acpi_shared.h" 28 29 30#define SKB_SCHEMA_DMAR \ 31 "dmar(%" PRIu8 ", %" PRIu8 ")." 32 33#define SKB_SCHEMA_DMAR_HW_UNIT \ 34 "dmar_drhd(%" PRIu32 ", %" PRIu8 ", %" PRIu16 ", %" PRIu64 ")." 35 36#define SKB_SCHEMA_IOMMU \ 37 "iommu(%" PRIu32 ", %" PRIu32 ", %" PRIu8 ", %" PRIu16 ")." 38 39#define SKB_SCHEMA_DMAR_RESERVED_MEMORY \ 40 "dmar_rmem(%" PRIu16 ", %" PRIu64 ", %" PRIu64 ")." 41 42#define SKB_SCHEMA_DMAR_ATSR \ 43 "dmar_atsr(%" PRIu8 ", %" PRIu16 ")." 44 45#define SKB_SCHEMA_DMAR_RHSA \ 46 "dmar_rhsa(%" PRIu64 ", %" PRIu32 ")." 47 48#define SKB_SCHEMA_DMAR_ANDD \ 49 "dmar_andd(%" PRIu8 ", %s)." 50 51#define SKB_SCHEMA_DMAR_DEVSC \ 52 "dmar_devsc(%" PRIu32 ", %" PRIu8 ", %" PRIu8 ", "\ 53 "addr(%" PRIu16 ", %" PRIu8 ", %" PRIu8 ", %" PRIu8 "), "\ 54 "%" PRIu8 ")." 55 56 57/* 58 * The Device Scope Structure is made up of Device Scope Entries. Each Device 59 * Scope Entry may be used to indicate a PCI endpoint device, a PCI sub-hierarchy, 60 * or devices such as I/OxAPICs or HPET (High Precision Event Timer). 61 * 62 * A PCI sub-hierarchy is defined as the collection of PCI controllers that are 63 * downstream to a specific PCI-PCI bridge. To identify a PCI sub-hierarchy, 64 * the Device Scope Entry needs to identify only the parent PCI-PCI bridge of 65 * the sub-hierarchy. 66 */ 67static errval_t parse_device_scope(ACPI_DMAR_DEVICE_SCOPE *dsc, void *end, 68 uint16_t segment, enum AcpiDmarType type, 69 bool include_all_flag, uint32_t unit_idx) 70{ 71 errval_t err; 72 ACPI_DMAR_PCI_PATH *pcip; 73 74 while((void *)dsc < end) { 75 if ((dsc->Length - sizeof(ACPI_DMAR_DEVICE_SCOPE)) 76 > sizeof(ACPI_DMAR_PCI_PATH)) { 77 debug_printf(" > [dmar] [dscp] Too deep in the hierarchy, we curently " 78 "only handle path length of 1.\n"); 79 dsc = (ACPI_DMAR_DEVICE_SCOPE *) ((uint8_t *) dsc + dsc->Length); 80 continue; 81 } 82 assert((dsc->Length - sizeof(ACPI_DMAR_DEVICE_SCOPE)) == sizeof(ACPI_DMAR_PCI_PATH)); 83 pcip = (ACPI_DMAR_PCI_PATH *)((uint8_t *)dsc + sizeof(ACPI_DMAR_DEVICE_SCOPE)); 84 85 ACPI_DEBUG(SKB_SCHEMA_DMAR_DEVSC "\n", unit_idx, type, dsc->EntryType, 86 segment, dsc->Bus, pcip->Device, pcip->Function, 87 dsc->EnumerationId); 88 89 /* we put the raw entry into the SKB */ 90 err = skb_add_fact(SKB_SCHEMA_DMAR_DEVSC, unit_idx, type, dsc->EntryType, 91 segment, dsc->Bus, pcip->Device, pcip->Function, 92 dsc->EnumerationId); 93 if (err_is_fail(err)) { 94 DEBUG_ERR(err, "Failed to insert fact into the SKB" 95 SKB_SCHEMA_DMAR_DEVSC "\n", unit_idx, type, dsc->EntryType, 96 segment, dsc->Bus, pcip->Device, pcip->Function, 97 dsc->EnumerationId); 98 } 99 100 switch(dsc->EntryType) { 101 case ACPI_DMAR_SCOPE_TYPE_ENDPOINT: 102 /* 103 * 0x01: PCI Endpoint Device - The device identified by the 'Path' 104 * field is a PCI endpoint device. This type must not be used in 105 * Device Scope of DRHD structures with INCLUDE_PCI_ALL flag Set. 106 */ 107 debug_printf(" > [dmar] [dscp] PCI Endpoint Device. Enumeration ID=%u," 108 "Start Bus: %u.%u.%u Path Length: %u\n", 109 dsc->EnumerationId, dsc->Bus, pcip->Device, pcip->Function, 110 (dsc->Length - 6) >> 1); 111 assert(dsc->EnumerationId == 0); 112 assert(!(type == ACPI_DMAR_TYPE_HARDWARE_UNIT && include_all_flag)); 113 114 break; 115 case ACPI_DMAR_SCOPE_TYPE_BRIDGE: 116 /* 117 * 0x02: PCI Sub-hierarchy - The device identified by the 'Path' 118 * field is a PCI-PCI bridge. In this case, the specified bridge 119 * device and all its downstream devices are included in the scope. 120 * This type must not be in Device Scope of DRHD structures with 121 * INCLUDE_PCI_ALL flag Set. 122 */ 123 debug_printf(" > [dmar] [dscp] PCI-PCI Bridge. Enumeration ID=%u," 124 "Start Bus: %u.%u.%u, Path Length: %u\n", 125 dsc->EnumerationId, dsc->Bus, pcip->Device, pcip->Function, 126 (dsc->Length - 6) >> 1); 127 assert(dsc->EnumerationId == 0); 128 assert(!(type == ACPI_DMAR_TYPE_HARDWARE_UNIT && include_all_flag)); 129 break; 130 case ACPI_DMAR_SCOPE_TYPE_IOAPIC: 131 /* 132 * 0x03: IOAPIC - The device identified by the 'Path' field is 133 * an I/O APIC (or I/O SAPIC) device, enumerated through the 134 * ACPI MADT I/O APIC (or I/O SAPIC) structure. 135 * 136 * Enumeration ID: the IOAPIC ID as provided in ACPI MADT 137 */ 138 139 debug_printf(" > [dmar] [dscp] IOAPIC. Enumeration ID=%u," 140 "Start Bus: %u.%u.%u, Path Length: %u\n", 141 dsc->EnumerationId, dsc->Bus,pcip->Device, pcip->Function, 142 (dsc->Length - 6) >> 1); 143 144 break; 145 case ACPI_DMAR_SCOPE_TYPE_HPET: 146 /* 0x04: MSI_CAPABLE_HPET1 - The device identified by the 'Path' 147 * field is an HPET Timer Block capable of generating MSI (Message 148 * Signaled interrupts). HPET hardware is reported through ACPI 149 * HPET structure. 150 * 151 * Enumeration ID: HPET Number corresponding to the APCI HPET block 152 */ 153 debug_printf(" > [dmar] [dscp] MSI HPET Device. Enumeration ID=%u," 154 "Start Bus: %u.%u.%u, Path Length: %u\n", 155 dsc->EnumerationId, dsc->Bus, pcip->Device, pcip->Function, 156 (dsc->Length - 6) >> 1); 157 break; 158 case ACPI_DMAR_SCOPE_TYPE_NAMESPACE: 159 /* 160 * 0x05: ACPI_NAMESPACE_DEVICE - The device identified by the 161 * 'Path' field is an ACPI namespace enumerated device capable 162 * of generating DMA requests. 163 * 164 * Enumeration ID is the ACPI device number as in ANDD structure 165 */ 166 debug_printf(" > [dmar] [dscp] ACPI Namespace device. Enumeration ID=%u," 167 "Start Bus: %u.%u.%u, Path Length: %u\n", 168 dsc->EnumerationId, dsc->Bus, pcip->Device, pcip->Function, 169 (dsc->Length - 6) >> 1); 170 default: 171 return ACPI_ERR_INVALID_HANDLE; 172 } 173 174 dsc = (ACPI_DMAR_DEVICE_SCOPE *) ((uint8_t *) dsc + dsc->Length); 175 } 176 177 return SYS_ERR_OK; 178} 179 180/** 181 * @brief parses the DMA remapping hardware unit structure 182 * 183 * @param drhd pointer to the ACPI DRHD sub table 184 * @param end pointer to the end of the sub table 185 * @param idx hardware unit index 186 * 187 * @return SYS_ERR_OK on success, errval on failure 188 * 189 * Each remapping hardware unit is reported by such a structure. there is 190 * at least one structure per segment. 191 */ 192static errval_t parse_hardware_unit(ACPI_DMAR_HARDWARE_UNIT *drhd, void *end, 193 uint32_t idx) 194{ 195 errval_t err; 196 197 ACPI_DEBUG("[dmar] [drhd] " SKB_SCHEMA_DMAR_HW_UNIT "\n", 198 idx, drhd->Flags, drhd->Segment, drhd->Address); 199 200 err = skb_add_fact(SKB_SCHEMA_DMAR_HW_UNIT, idx, drhd->Flags, drhd->Segment, 201 drhd->Address); 202 if (err_is_fail(err)) { 203 DEBUG_ERR(err, "Failed to insert into SKB: " SKB_SCHEMA_DMAR_HW_UNIT "\n", 204 idx, drhd->Flags, drhd->Segment, drhd->Address); 205 } 206 207 err = skb_add_fact(SKB_SCHEMA_IOMMU, HW_PCI_IOMMU_INTEL, idx, 208 drhd->Flags & ACPI_DMAR_INCLUDE_ALL, drhd->Segment); 209 if (err_is_fail(err)) { 210 DEBUG_ERR(err, "Failed to insert into SKB: " SKB_SCHEMA_DMAR_HW_UNIT "\n", 211 idx, drhd->Flags, drhd->Segment, drhd->Address); 212 } 213 214 // Remove from memory that can be used for PCI bars 215 err = skb_add_fact("fixed_memory(%"PRIu64", %"PRIu64")", 216 drhd->Address, drhd->Address + BASE_PAGE_SIZE); 217 if (err_is_fail(err)) { 218 DEBUG_ERR(err, "Failed to insert into SKB: fixed memory region %"PRIx64" \n", 219 drhd->Address); 220 } 221 222 debug_printf("[dmar] [drhd] set fixed_memory(%"PRIx64", %"PRIx64")\n", 223 drhd->Address, drhd->Address + BASE_PAGE_SIZE); 224 /* 225 * If Set, this remapping hardware unit has under its scope all PCI 226 * compatible devices in the specified Segment, except devices reported 227 * under the scope of other remapping hardware units for the same Segment. 228 * If a DRHD structure with INCLUDE_PCI_ALL flag Set is reported for a 229 * Segment, it must be enumerated by BIOS after all other DRHD structures 230 * for the same Segment1. A DRHD structure with INCLUDE_PCI_ALL flag Set 231 * may use the 'DeviceScope' field to enumerate I/OxAPIC and HPET 232 * devices under its scope. 233 */ 234 if (drhd->Flags & ACPI_DMAR_INCLUDE_ALL) { 235 debug_printf("[dmar] [drhd] ACPI_DMAR_INCLUDE_ALL set for segment %u\n", 236 drhd->Segment); 237 } 238 239 /* 240 * The Device Scope structure contains zero or more Device Scope Entries 241 * that identify devices in the specified segment and under the scope of 242 * this remapping hardware unit. 243 */ 244 void *sub = ((uint8_t *)drhd) + sizeof(ACPI_DMAR_HARDWARE_UNIT); 245 err = parse_device_scope(sub, end, drhd->Segment, ACPI_DMAR_TYPE_HARDWARE_UNIT, 246 drhd->Flags & ACPI_DMAR_INCLUDE_ALL, idx); 247 if (err_is_fail(err)) { 248 DEBUG_ERR(err, "Failed to parse device scope: " SKB_SCHEMA_DMAR_HW_UNIT "\n", 249 idx, drhd->Flags, drhd->Segment, drhd->Address); 250 } 251 252 debug_printf("[dmar] [drhd] set " HW_PCI_IOMMU_RECORD_FORMAT "\n", 253 idx, HW_PCI_IOMMU_INTEL, drhd->Flags, drhd->Segment, drhd->Address); 254 255 return oct_mset(SET_SEQUENTIAL, HW_PCI_IOMMU_RECORD_FORMAT, idx, 256 HW_PCI_IOMMU_INTEL, drhd->Flags, drhd->Segment, 257 drhd->Address); 258} 259 260 261/** 262 * @brief parses the reserved memory region (RMRR) structures 263 * 264 * @param rmem pointer ot the ACIP RMRR sub table 265 * @param end pointer to the end of the table 266 * 267 * @return SYS_ERR_OK on success, errval on failure 268 * 269 * The RMRR regions are expected to be used for legacy usages 270 * (such as USB, UMA Graphics, etc.) requiring reserved memory. 271 * 272 * The BIOS reports each memory region through a RMRR structure and a list of 273 * devices that require access to the specified reserved memory region. 274 */ 275static errval_t parse_reserved_memory(ACPI_DMAR_RESERVED_MEMORY *rmem, void *end) 276{ 277 errval_t err; 278 279 ACPI_DEBUG("[dmar] [rmem] " SKB_SCHEMA_DMAR_RESERVED_MEMORY "\n", 280 rmem->Segment, rmem->BaseAddress, rmem->EndAddress); 281 282 debug_printf("[dmar] [rmem] [0x%lx..0x%lx]\n", rmem->BaseAddress, 283 rmem->EndAddress); 284 285 err = skb_add_fact(SKB_SCHEMA_DMAR_RESERVED_MEMORY, rmem->Segment, 286 rmem->BaseAddress, rmem->EndAddress); 287 if (err_is_fail(err)) { 288 DEBUG_ERR(err, "Failed to insert into SKB: " 289 SKB_SCHEMA_DMAR_RESERVED_MEMORY "\n", 290 rmem->Segment, rmem->BaseAddress, rmem->EndAddress); 291 } 292 293 void *sub = ((uint8_t *)rmem) + sizeof(ACPI_DMAR_RESERVED_MEMORY); 294 return parse_device_scope(sub, end, rmem->Segment, 295 ACPI_DMAR_TYPE_RESERVED_MEMORY, false, 0); 296} 297 298 299/** 300 * @brief parses the root-port address translation services (ATSR) structure 301 * 302 * @param atsr pointer to the ACPI sub table 303 * @param end end address of the table 304 * 305 * @return SYS_ERR_OK on success, errval on failure 306 * 307 * This structure is only for platforms supporting device TLBs. For each 308 * PCI segment there shall be one ATSR structure. The structure identifies 309 * which PCI Express root ports supporting ATS transactions 310 */ 311static errval_t parse_root_ats_capabilities(ACPI_DMAR_ATSR *atsr, void *end) 312{ 313 errval_t err; 314 315 ACPI_DEBUG("[dmar] [atsr] " SKB_SCHEMA_DMAR_ATSR "\n", 316 atsr->Flags, atsr->Segment); 317 318 err = skb_add_fact(SKB_SCHEMA_DMAR_ATSR, atsr->Flags, atsr->Segment); 319 if (err_is_fail(err)) { 320 DEBUG_ERR(err, "Failed to insert into the SKB: " SKB_SCHEMA_DMAR_ATSR "\n", 321 atsr->Flags, atsr->Segment); 322 } 323 324 /* 325 * Bit 0: ALL_PORTS: 326 * If Set, all PCI-Express Root Ports in the segment support ATS transactions. 327 * If Clear, only root-ports indicated by the device scope fields support 328 * ATS transactions 329 */ 330 if (atsr->Flags & ACPI_DMAR_ALL_PORTS) { 331 debug_printf("[dmar] [atsr] ACPI_DMAR_ALL_PORTS flag is set. " 332 "Omitting device scope structures\n"); 333 return SYS_ERR_OK; 334 } 335 336 /* 337 * The Device Scope structure is described in Section 8.3.1. All Device Scope 338 * Entries in this structure must have a Device Scope Entry Type of 339 * 02h 340 */ 341 void *sub = ((uint8_t *)atsr) + sizeof(ACPI_DMAR_ATSR); 342 return parse_device_scope(sub, end, atsr->Segment, ACPI_DMAR_TYPE_ROOT_ATS, 343 false, 0); 344} 345 346 347/** 348 * @brief parses the Remapping Hardware Static Affinity Structure 349 * 350 * @param rhsa pointer to the ACPI sub table 351 * 352 * @return SYS_ERR_OK on success, errval on failure 353 * 354 * On systems with NUMA nodes, it may be better peformance wise to allocate 355 * translation structures on the NUMA node close by the hardware unit. 356 * This RHSA structure provides proximity information similar to the SRAT 357 * table. 358 */ 359static errval_t parse_hardware_resource_affinity(ACPI_DMAR_RHSA *rhsa) 360{ 361 ACPI_DEBUG("[dmar] [rhsa] " SKB_SCHEMA_DMAR_RHSA "\n", 362 rhsa->BaseAddress, rhsa->ProximityDomain); 363 364 return skb_add_fact(SKB_SCHEMA_DMAR_RHSA, rhsa->BaseAddress, 365 rhsa->ProximityDomain); 366} 367 368/** 369 * @brief parses ACPI name-space device declarations 370 * 371 * @param andd pointer to the ACPI namespace table 372 * 373 * @return SYS_ERR_OK on success, errval on failure 374 * 375 * NOTE: This is not yet implemented. 376 */ 377static errval_t parse_namespace_device_declaration(ACPI_DMAR_ANDD *andd) 378{ 379 ACPI_DEBUG("[dmar] [andd] NYI! " SKB_SCHEMA_DMAR_ANDD "\n", 380 andd->DeviceNumber, andd->DeviceName); 381 382 return skb_add_fact(SKB_SCHEMA_DMAR_ANDD, andd->DeviceNumber, 383 andd->DeviceName); 384} 385 386 387/** 388 * @brief Parses the DMA Remapping Reporting Table (DMAR) 389 * 390 * @return SYS_ERR_OK on success, error value on failure 391 */ 392errval_t acpi_parse_dmar(void) 393{ 394 errval_t err; 395 396 ACPI_STATUS as; 397 ACPI_TABLE_DMAR *dmar; 398 ACPI_TABLE_HEADER *ath; 399 400 /* Get the ACPI DMAR table (the DMAR) */ 401 as = AcpiGetTable(ACPI_SIG_DMAR, 1, (ACPI_TABLE_HEADER **)&ath); 402 403 if(ACPI_FAILURE(as)) { 404 debug_printf("No DMAR found in ACPI! Cannot initialize IO MMUs.\n"); 405 406 return oct_mset(SET_SEQUENTIAL, HW_PCI_IOMMU_RECORD_FORMAT, HW_PCI_IOMMU_INTEL, 0, 407 0, 0); 408 409 return ACPI_ERR_OBJECT_NOT_FOUND; 410 } 411 else { 412 dmar = (ACPI_TABLE_DMAR*)ath; 413 } 414 415 /* 416 * Width: Number of bits that can be addressed by the platform in DMA. 417 * the field is stored as W = Width + 1. 418 * 419 * Flags: 420 * Bit 0: INTR_REMAP 421 * Bit 1: X2APIC_OPT_OUT 422 * Bit 2: DMA_CTRL_PLATFORM_OPT_IN_FLAG 423 */ 424 skb_add_fact(SKB_SCHEMA_DMAR, dmar->Width + 1, dmar->Flags); 425 426 427 debug_printf("DMAR Revision: %u, Size=%u, OEM=%s, HAW=%u, flags=%x\n", 428 dmar->Header.Revision, dmar->Header.Length, dmar->Header.OemId, 429 dmar->Width + 1, dmar->Flags); 430 431 432 void *p = (void *)dmar + sizeof(ACPI_TABLE_DMAR); 433 void *table_end = (void *)dmar + dmar->Header.Length; 434 uint32_t vtd_unit_idx = 0; 435 while(p < table_end) { 436 ACPI_DMAR_HEADER *sh = (ACPI_DMAR_HEADER *)p; 437 assert(sh->Length); 438 void *p_end = p + sh->Length; 439 440 switch (sh->Type) { 441 case ACPI_DMAR_TYPE_HARDWARE_UNIT: 442 err = parse_hardware_unit(p, p_end, vtd_unit_idx); 443 if (err_is_fail(err)) { 444 DEBUG_ERR(err, "parsing hardware unit failed. Continuing...\n"); 445 } 446 vtd_unit_idx++; 447 break; 448 case ACPI_DMAR_TYPE_RESERVED_MEMORY: 449 err = parse_reserved_memory(p, p_end); 450 if (err_is_fail(err)) { 451 DEBUG_ERR(err, "reserved memory failed. Continuing...\n"); 452 } 453 break; 454 case ACPI_DMAR_TYPE_ROOT_ATS: 455 err = parse_root_ats_capabilities(p, p_end); 456 if (err_is_fail(err)) { 457 DEBUG_ERR(err, "parsing root ats caps failed. Continuing...\n"); 458 } 459 break; 460 case ACPI_DMAR_TYPE_HARDWARE_AFFINITY: 461 err = parse_hardware_resource_affinity(p); 462 if (err_is_fail(err)) { 463 DEBUG_ERR(err, "parsing resource affinity failed. " 464 "Continuing...\n"); 465 } 466 break; 467 case ACPI_DMAR_TYPE_NAMESPACE: 468 err = parse_namespace_device_declaration(p); 469 if (err_is_fail(err)) { 470 DEBUG_ERR(err, "parsing namespace declarations failed. " 471 "Continuing...\n"); 472 } 473 break; 474 default: 475 USER_PANIC("Discovered unknown subtable %u. Consider updating" 476 "ACPI. Skipping\n", sh->Type); 477 } 478 p = p_end; 479 } 480 481 return SYS_ERR_OK; 482} 483