static void vtd_flush_dmar_caches(void *reg_base, u64 ctx_scope, u64 iotlb_scope) { void *iotlb_reg_base; mmio_write64(reg_base + VTD_CCMD_REG, ctx_scope | VTD_CCMD_ICC); while (mmio_read64(reg_base + VTD_CCMD_REG) & VTD_CCMD_ICC) cpu_relax(); iotlb_reg_base = vtd_iotlb_reg_base(reg_base); mmio_write64(iotlb_reg_base + VTD_IOTLB_REG, iotlb_scope | VTD_IOTLB_DW | VTD_IOTLB_DR | VTD_IOTLB_IVT); while (mmio_read64(iotlb_reg_base + VTD_IOTLB_REG) & VTD_IOTLB_IVT) cpu_relax(); }
void mmio_perform_access(void *base, struct mmio_access *mmio) { void *addr = base + mmio->address; if (mmio->is_write) switch (mmio->size) { case 1: mmio_write8(addr, mmio->value); break; case 2: mmio_write16(addr, mmio->value); break; case 4: mmio_write32(addr, mmio->value); break; #if BITS_PER_LONG == 64 case 8: mmio_write64(addr, mmio->value); break; #endif } else switch (mmio->size) { case 1: mmio->value = mmio_read8(addr); break; case 2: mmio->value = mmio_read16(addr); break; case 4: mmio->value = mmio_read32(addr); break; #if BITS_PER_LONG == 64 case 8: mmio->value = mmio_read64(addr); break; #endif } }
int vtd_init(void) { unsigned long offset, caps, sllps_caps = ~0UL; unsigned int pt_levels, num_did, n; const struct acpi_dmar_table *dmar; const struct acpi_dmar_drhd *drhd; void *reg_base = NULL; int err; dmar = (struct acpi_dmar_table *)acpi_find_table("DMAR", NULL); if (!dmar) // return -ENODEV; { printk("WARNING: No VT-d support found!\n"); return 0; } if (sizeof(struct acpi_dmar_table) + sizeof(struct acpi_dmar_drhd) > dmar->header.length) return -EIO; drhd = (struct acpi_dmar_drhd *)dmar->remap_structs; if (drhd->header.type != ACPI_DMAR_DRHD) return -EIO; offset = (void *)dmar->remap_structs - (void *)dmar; do { if (drhd->header.length < sizeof(struct acpi_dmar_drhd) || offset + drhd->header.length > dmar->header.length) return -EIO; /* TODO: support multiple segments */ if (drhd->segment != 0) return -EIO; printk("Found DMAR @%p\n", drhd->register_base_addr); reg_base = page_alloc(&remap_pool, 1); if (!reg_base) return -ENOMEM; if (dmar_units == 0) dmar_reg_base = reg_base; else if (reg_base != dmar_reg_base + dmar_units * PAGE_SIZE) return -ENOMEM; err = page_map_create(&hv_paging_structs, drhd->register_base_addr, PAGE_SIZE, (unsigned long)reg_base, PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED, PAGE_MAP_NON_COHERENT); if (err) return err; caps = mmio_read64(reg_base + VTD_CAP_REG); if (caps & VTD_CAP_SAGAW39) pt_levels = 3; else if (caps & VTD_CAP_SAGAW48) pt_levels = 4; else return -EIO; sllps_caps &= caps; if (dmar_pt_levels > 0 && dmar_pt_levels != pt_levels) return -EIO; dmar_pt_levels = pt_levels; if (caps & VTD_CAP_CM) return -EIO; /* We only support IOTLB registers withing the first page. */ if (vtd_iotlb_reg_base(reg_base) >= reg_base + PAGE_SIZE) return -EIO; if (mmio_read32(reg_base + VTD_GSTS_REG) & VTD_GSTS_TES) return -EBUSY; num_did = 1 << (4 + (caps & VTD_CAP_NUM_DID_MASK) * 2); if (num_did < dmar_num_did) dmar_num_did = num_did; dmar_units++; offset += drhd->header.length; drhd = (struct acpi_dmar_drhd *) (((void *)drhd) + drhd->header.length); err = vtd_init_fault_reporting(reg_base); if (err) return err; } while (offset < dmar->header.length && drhd->header.type == ACPI_DMAR_DRHD); vtd_init_fault_nmi(); /* * Derive vdt_paging from very similar x86_64_paging, * replicating 0..3 for 4 levels and 1..3 for 3 levels. */ memcpy(vtd_paging, &x86_64_paging[4 - dmar_pt_levels], sizeof(struct paging) * dmar_pt_levels); for (n = 0; n < dmar_pt_levels; n++) vtd_paging[n].set_next_pt = vtd_set_next_pt; if (!(sllps_caps & VTD_CAP_SLLPS1G)) vtd_paging[dmar_pt_levels - 3].page_size = 0; if (!(sllps_caps & VTD_CAP_SLLPS2M)) vtd_paging[dmar_pt_levels - 2].page_size = 0; return 0; }
int vtd_init(void) { const struct acpi_dmar_table *dmar; const struct acpi_dmar_drhd *drhd; unsigned int pt_levels, num_did; void *reg_base = NULL; unsigned long offset; unsigned long caps; int err; dmar = (struct acpi_dmar_table *)acpi_find_table("DMAR", NULL); if (!dmar) // return -ENODEV; { printk("WARNING: No VT-d support found!\n"); return 0; } if (sizeof(struct acpi_dmar_table) + sizeof(struct acpi_dmar_drhd) > dmar->header.length) return -EIO; drhd = (struct acpi_dmar_drhd *)dmar->remap_structs; if (drhd->header.type != ACPI_DMAR_DRHD) return -EIO; offset = (void *)dmar->remap_structs - (void *)dmar; do { if (drhd->header.length < sizeof(struct acpi_dmar_drhd) || offset + drhd->header.length > dmar->header.length) return -EIO; /* TODO: support multiple segments */ if (drhd->segment != 0) return -EIO; printk("Found DMAR @%p\n", drhd->register_base_addr); reg_base = page_alloc(&remap_pool, 1); if (!reg_base) return -ENOMEM; if (dmar_units == 0) dmar_reg_base = reg_base; else if (reg_base != dmar_reg_base + dmar_units * PAGE_SIZE) return -ENOMEM; err = page_map_create(hv_page_table, drhd->register_base_addr, PAGE_SIZE, (unsigned long)reg_base, PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED, PAGE_DEFAULT_FLAGS, PAGE_DIR_LEVELS, PAGE_MAP_NON_COHERENT); if (err) return err; caps = mmio_read64(reg_base + VTD_CAP_REG); if (caps & VTD_CAP_SAGAW39) pt_levels = 3; else if (caps & VTD_CAP_SAGAW48) pt_levels = 4; else return -EIO; if (dmar_pt_levels > 0 && dmar_pt_levels != pt_levels) return -EIO; dmar_pt_levels = pt_levels; if (caps & VTD_CAP_CM) return -EIO; /* We only support IOTLB registers withing the first page. */ if (vtd_iotlb_reg_base(reg_base) >= reg_base + PAGE_SIZE) return -EIO; if (mmio_read32(reg_base + VTD_GSTS_REG) & VTD_GSTS_TES) return -EBUSY; num_did = 1 << (4 + (caps & VTD_CAP_NUM_DID_MASK) * 2); if (num_did < dmar_num_did) dmar_num_did = num_did; dmar_units++; offset += drhd->header.length; drhd = (struct acpi_dmar_drhd *) (((void *)drhd) + drhd->header.length); } while (offset < dmar->header.length && drhd->header.type == ACPI_DMAR_DRHD); return 0; }
static void *vtd_iotlb_reg_base(void *reg_base) { return reg_base + ((mmio_read64(reg_base + VTD_ECAP_REG) & VTD_ECAP_IRO_MASK) >> VTD_ECAP_IRO_SHIFT) * 16; }