int vtd_map_memory_region(struct cell *cell, const struct jailhouse_memory *mem) { u32 flags = 0; // HACK for QEMU if (dmar_units == 0) return 0; if (!(mem->flags & JAILHOUSE_MEM_DMA)) return 0; if (mem->flags & JAILHOUSE_MEM_READ) flags |= VTD_PAGE_READ; if (mem->flags & JAILHOUSE_MEM_WRITE) flags |= VTD_PAGE_WRITE; return page_map_create(&cell->vtd.pg_structs, mem->phys_start, mem->size, mem->virt_start, flags, PAGE_MAP_COHERENT); }
int vtd_map_memory_region(struct cell *cell, const struct jailhouse_memory *mem) { u32 page_flags = 0; // HACK for QEMU if (dmar_units == 0) return 0; if (!(mem->access_flags & JAILHOUSE_MEM_DMA)) return 0; if (mem->access_flags & JAILHOUSE_MEM_READ) page_flags |= VTD_PAGE_READ; if (mem->access_flags & JAILHOUSE_MEM_WRITE) page_flags |= VTD_PAGE_WRITE; return page_map_create(cell->vtd.page_table, mem->phys_start, mem->size, mem->virt_start, page_flags, VTD_PAGE_READ | VTD_PAGE_WRITE, dmar_pt_levels, PAGE_MAP_COHERENT); }
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; }