svn commit: r355440 - stable/12/sys/amd64/vmm/intel
Scott Long
scottl at FreeBSD.org
Fri Dec 6 09:50:02 UTC 2019
Author: scottl
Date: Fri Dec 6 09:50:01 2019
New Revision: 355440
URL: https://svnweb.freebsd.org/changeset/base/355440
Log:
MFC r349184. This fixing PCI passthrough via VT-d on modern chipsets with
multiple translation units.
PR: 229852
Submitted by: callum at mitchison.org
Modified:
stable/12/sys/amd64/vmm/intel/vtd.c
Directory Properties:
stable/12/ (props changed)
Modified: stable/12/sys/amd64/vmm/intel/vtd.c
==============================================================================
--- stable/12/sys/amd64/vmm/intel/vtd.c Fri Dec 6 03:48:35 2019 (r355439)
+++ stable/12/sys/amd64/vmm/intel/vtd.c Fri Dec 6 09:50:01 2019 (r355440)
@@ -51,6 +51,8 @@ __FBSDID("$FreeBSD$");
* Architecture Spec, September 2008.
*/
+#define VTD_DRHD_INCLUDE_PCI_ALL(Flags) (((Flags) >> 0) & 0x1)
+
/* Section 10.4 "Register Descriptions" */
struct vtdmap {
volatile uint32_t version;
@@ -116,10 +118,11 @@ struct domain {
static SLIST_HEAD(, domain) domhead;
#define DRHD_MAX_UNITS 8
-static int drhd_num;
-static struct vtdmap *vtdmaps[DRHD_MAX_UNITS];
-static int max_domains;
-typedef int (*drhd_ident_func_t)(void);
+static ACPI_DMAR_HARDWARE_UNIT *drhds[DRHD_MAX_UNITS];
+static int drhd_num;
+static struct vtdmap *vtdmaps[DRHD_MAX_UNITS];
+static int max_domains;
+typedef int (*drhd_ident_func_t)(void);
static uint64_t root_table[PAGE_SIZE / sizeof(uint64_t)] __aligned(4096);
static uint64_t ctx_tables[256][PAGE_SIZE / sizeof(uint64_t)] __aligned(4096);
@@ -175,6 +178,69 @@ domain_id(void)
return (id);
}
+static struct vtdmap *
+vtd_device_scope(uint16_t rid)
+{
+ int i, remaining, pathremaining;
+ char *end, *pathend;
+ struct vtdmap *vtdmap;
+ ACPI_DMAR_HARDWARE_UNIT *drhd;
+ ACPI_DMAR_DEVICE_SCOPE *device_scope;
+ ACPI_DMAR_PCI_PATH *path;
+
+ for (i = 0; i < drhd_num; i++) {
+ drhd = drhds[i];
+
+ if (VTD_DRHD_INCLUDE_PCI_ALL(drhd->Flags)) {
+ /*
+ * From Intel VT-d arch spec, version 3.0:
+ * If a DRHD structure with INCLUDE_PCI_ALL flag Set is reported
+ * for a Segment, it must be enumerated by BIOS after all other
+ * DRHD structures for the same Segment.
+ */
+ vtdmap = vtdmaps[i];
+ return(vtdmap);
+ }
+
+ end = (char *)drhd + drhd->Header.Length;
+ remaining = drhd->Header.Length - sizeof(ACPI_DMAR_HARDWARE_UNIT);
+ while (remaining > sizeof(ACPI_DMAR_DEVICE_SCOPE)) {
+ device_scope = (ACPI_DMAR_DEVICE_SCOPE *)(end - remaining);
+ remaining -= device_scope->Length;
+
+ switch (device_scope->EntryType){
+ /* 0x01 and 0x02 are PCI device entries */
+ case 0x01:
+ case 0x02:
+ break;
+ default:
+ continue;
+ }
+
+ if (PCI_RID2BUS(rid) != device_scope->Bus)
+ continue;
+
+ pathend = (char *)device_scope + device_scope->Length;
+ pathremaining = device_scope->Length - sizeof(ACPI_DMAR_DEVICE_SCOPE);
+ while (pathremaining >= sizeof(ACPI_DMAR_PCI_PATH)) {
+ path = (ACPI_DMAR_PCI_PATH *)(pathend - pathremaining);
+ pathremaining -= sizeof(ACPI_DMAR_PCI_PATH);
+
+ if (PCI_RID2SLOT(rid) != path->Device)
+ continue;
+ if (PCI_RID2FUNC(rid) != path->Function)
+ continue;
+
+ vtdmap = vtdmaps[i];
+ return (vtdmap);
+ }
+ }
+ }
+
+ /* No matching scope */
+ return (NULL);
+}
+
static void
vtd_wbflush(struct vtdmap *vtdmap)
{
@@ -240,7 +306,7 @@ vtd_translation_disable(struct vtdmap *vtdmap)
static int
vtd_init(void)
{
- int i, units, remaining;
+ int i, units, remaining, tmp;
struct vtdmap *vtdmap;
vm_paddr_t ctx_paddr;
char *end, envname[32];
@@ -291,8 +357,9 @@ vtd_init(void)
break;
drhd = (ACPI_DMAR_HARDWARE_UNIT *)hdr;
- vtdmaps[units++] = (struct vtdmap *)PHYS_TO_DMAP(drhd->Address);
- if (units >= DRHD_MAX_UNITS)
+ drhds[units] = drhd;
+ vtdmaps[units] = (struct vtdmap *)PHYS_TO_DMAP(drhd->Address);
+ if (++units >= DRHD_MAX_UNITS)
break;
remaining -= hdr->Length;
}
@@ -302,13 +369,19 @@ vtd_init(void)
skip_dmar:
drhd_num = units;
- vtdmap = vtdmaps[0];
- if (VTD_CAP_CM(vtdmap->cap) != 0)
- panic("vtd_init: invalid caching mode");
+ max_domains = 64 * 1024; /* maximum valid value */
+ for (i = 0; i < drhd_num; i++){
+ vtdmap = vtdmaps[i];
- max_domains = vtd_max_domains(vtdmap);
+ if (VTD_CAP_CM(vtdmap->cap) != 0)
+ panic("vtd_init: invalid caching mode");
+ /* take most compatible (minimum) value */
+ if ((tmp = vtd_max_domains(vtdmap)) < max_domains)
+ max_domains = tmp;
+ }
+
/*
* Set up the root-table to point to the context-entry tables
*/
@@ -373,7 +446,6 @@ vtd_add_device(void *arg, uint16_t rid)
struct vtdmap *vtdmap;
uint8_t bus;
- vtdmap = vtdmaps[0];
bus = PCI_RID2BUS(rid);
ctxp = ctx_tables[bus];
pt_paddr = vtophys(dom->ptp);
@@ -385,6 +457,10 @@ vtd_add_device(void *arg, uint16_t rid)
(uint16_t)(ctxp[idx + 1] >> 8));
}
+ if ((vtdmap = vtd_device_scope(rid)) == NULL)
+ panic("vtd_add_device: device %x is not in scope for "
+ "any DMA remapping unit", rid);
+
/*
* Order is important. The 'present' bit is set only after all fields
* of the context pointer are initialized.
@@ -568,8 +644,6 @@ vtd_create_domain(vm_paddr_t maxaddr)
if (drhd_num <= 0)
panic("vtd_create_domain: no dma remapping hardware available");
- vtdmap = vtdmaps[0];
-
/*
* Calculate AGAW.
* Section 3.4.2 "Adjusted Guest Address Width", Architecture Spec.
@@ -594,7 +668,14 @@ vtd_create_domain(vm_paddr_t maxaddr)
pt_levels = 2;
sagaw = 30;
addrwidth = 0;
- tmp = VTD_CAP_SAGAW(vtdmap->cap);
+
+ tmp = ~0;
+ for (i = 0; i < drhd_num; i++) {
+ vtdmap = vtdmaps[i];
+ /* take most compatible value */
+ tmp &= VTD_CAP_SAGAW(vtdmap->cap);
+ }
+
for (i = 0; i < 5; i++) {
if ((tmp & (1 << i)) != 0 && sagaw >= agaw)
break;
@@ -606,8 +687,8 @@ vtd_create_domain(vm_paddr_t maxaddr)
}
if (i >= 5) {
- panic("vtd_create_domain: SAGAW 0x%lx does not support AGAW %d",
- VTD_CAP_SAGAW(vtdmap->cap), agaw);
+ panic("vtd_create_domain: SAGAW 0x%x does not support AGAW %d",
+ tmp, agaw);
}
dom = malloc(sizeof(struct domain), M_VTD, M_ZERO | M_WAITOK);
@@ -634,7 +715,12 @@ vtd_create_domain(vm_paddr_t maxaddr)
* There is not any code to deal with the demotion at the moment
* so we disable superpage mappings altogether.
*/
- dom->spsmask = VTD_CAP_SPS(vtdmap->cap);
+ dom->spsmask = ~0;
+ for (i = 0; i < drhd_num; i++) {
+ vtdmap = vtdmaps[i];
+ /* take most compatible value */
+ dom->spsmask &= VTD_CAP_SPS(vtdmap->cap);
+ }
#endif
SLIST_INSERT_HEAD(&domhead, dom, next);
More information about the svn-src-stable
mailing list