MFC r263306:

Add some support for the PCI(e)-PCI bridges to the Intel VT-d driver.
This commit is contained in:
kib 2014-03-25 20:17:57 +00:00
parent 879dfba7f2
commit 5a582ae617
3 changed files with 122 additions and 17 deletions

View File

@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <sys/taskqueue.h>
#include <sys/tree.h>
#include <sys/uio.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
@ -69,15 +70,10 @@ __FBSDID("$FreeBSD$");
*/
static bool
dmar_bus_dma_is_dev_disabled(device_t dev)
dmar_bus_dma_is_dev_disabled(int domain, int bus, int slot, int func)
{
char str[128], *env;
int domain, bus, slot, func;
domain = pci_get_domain(dev);
bus = pci_get_bus(dev);
slot = pci_get_slot(dev);
func = pci_get_function(dev);
snprintf(str, sizeof(str), "hw.busdma.pci%d.%d.%d.%d.bounce",
domain, bus, slot, func);
env = getenv(str);
@ -87,11 +83,119 @@ dmar_bus_dma_is_dev_disabled(device_t dev)
return (true);
}
/*
* Given original device, find the requester ID that will be seen by
* the DMAR unit and used for page table lookup. PCI bridges may take
* ownership of transactions from downstream devices, so it may not be
* the same as the BSF of the target device. In those cases, all
* devices downstream of the bridge must share a single mapping
* domain, and must collectively be assigned to use either DMAR or
* bounce mapping.
*/
static device_t
dmar_get_requester(device_t dev, int *bus, int *slot, int *func)
{
devclass_t pci_class;
device_t pci, pcib, requester;
int cap_offset;
pci_class = devclass_find("pci");
requester = dev;
*bus = pci_get_bus(dev);
*slot = pci_get_slot(dev);
*func = pci_get_function(dev);
/*
* Walk the bridge hierarchy from the target device to the
* host port to find the translating bridge nearest the DMAR
* unit.
*/
for (;;) {
pci = device_get_parent(dev);
KASSERT(pci != NULL, ("NULL parent for pci%d:%d:%d:%d",
pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev),
pci_get_function(dev)));
KASSERT(device_get_devclass(pci) == pci_class,
("Non-pci parent for pci%d:%d:%d:%d",
pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev),
pci_get_function(dev)));
pcib = device_get_parent(pci);
KASSERT(pcib != NULL, ("NULL bridge for pci%d:%d:%d:%d",
pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev),
pci_get_function(dev)));
/*
* The parent of our "bridge" isn't another PCI bus,
* so pcib isn't a PCI->PCI bridge but rather a host
* port, and the requester ID won't be translated
* further.
*/
if (device_get_devclass(device_get_parent(pcib)) != pci_class)
break;
if (pci_find_cap(dev, PCIY_EXPRESS, &cap_offset) != 0) {
/*
* Device is not PCIe, it cannot be seen as a
* requester by DMAR unit.
*/
requester = pcib;
/* Check whether the bus above is PCIe. */
if (pci_find_cap(pcib, PCIY_EXPRESS,
&cap_offset) == 0) {
/*
* The current device is not PCIe, but
* the bridge above it is. This is a
* PCIe->PCI bridge. Assume that the
* requester ID will be the secondary
* bus number with slot and function
* set to zero.
*
* XXX: Doesn't handle the case where
* the bridge is PCIe->PCI-X, and the
* bridge will only take ownership of
* requests in some cases. We should
* provide context entries with the
* same page tables for taken and
* non-taken transactions.
*/
*bus = pci_get_bus(dev);
*slot = *func = 0;
} else {
/*
* Neither the device nor the bridge
* above it are PCIe. This is a
* conventional PCI->PCI bridge, which
* will use the bridge's BSF as the
* requester ID.
*/
*bus = pci_get_bus(pcib);
*slot = pci_get_slot(pcib);
*func = pci_get_function(pcib);
}
}
/*
* Do not stop the loop even if the target device is
* PCIe, because it is possible (but unlikely) to have
* a PCI->PCIe bridge somewhere in the hierarchy.
*/
dev = pcib;
}
return (requester);
}
struct dmar_ctx *
dmar_instantiate_ctx(struct dmar_unit *dmar, device_t dev, bool rmrr)
{
device_t requester;
struct dmar_ctx *ctx;
bool disabled;
int bus, slot, func;
requester = dmar_get_requester(dev, &bus, &slot, &func);
/*
* If the user requested the IOMMU disabled for the device, we
@ -100,11 +204,11 @@ dmar_instantiate_ctx(struct dmar_unit *dmar, device_t dev, bool rmrr)
* Instead provide the identity mapping for the device
* context.
*/
disabled = dmar_bus_dma_is_dev_disabled(dev);
ctx = dmar_get_ctx(dmar, dev, disabled, rmrr);
disabled = dmar_bus_dma_is_dev_disabled(pci_get_domain(dev), bus,
slot, func);
ctx = dmar_get_ctx(dmar, requester, bus, slot, func, disabled, rmrr);
if (ctx == NULL)
return (NULL);
ctx->ctx_tag.owner = dev;
if (disabled) {
/*
* Keep the first reference on context, release the

View File

@ -262,17 +262,15 @@ dmar_ctx_dtr(struct dmar_ctx *ctx, bool gas_inited, bool pgtbl_inited)
}
struct dmar_ctx *
dmar_get_ctx(struct dmar_unit *dmar, device_t dev, bool id_mapped, bool rmrr_init)
dmar_get_ctx(struct dmar_unit *dmar, device_t dev, int bus, int slot, int func,
bool id_mapped, bool rmrr_init)
{
struct dmar_ctx *ctx, *ctx1;
dmar_ctx_entry_t *ctxp;
struct sf_buf *sf;
int bus, slot, func, error, mgaw;
int error, mgaw;
bool enable;
bus = pci_get_bus(dev);
slot = pci_get_slot(dev);
func = pci_get_function(dev);
enable = false;
TD_PREP_PINNED_ASSERT;
DMAR_LOCK(dmar);
@ -356,6 +354,7 @@ dmar_get_ctx(struct dmar_unit *dmar, device_t dev, bool id_mapped, bool rmrr_ini
ctx = dmar_find_ctx_locked(dmar, bus, slot, func);
if (ctx == NULL) {
ctx = ctx1;
ctx->ctx_tag.owner = dev;
ctx->domain = alloc_unrl(dmar->domids);
if (ctx->domain == -1) {
DMAR_UNLOCK(dmar);
@ -376,9 +375,11 @@ dmar_get_ctx(struct dmar_unit *dmar, device_t dev, bool id_mapped, bool rmrr_ini
LIST_INSERT_HEAD(&dmar->contexts, ctx, link);
ctx_id_entry_init(ctx, ctxp);
device_printf(dev,
"dmar%d pci%d:%d:%d:%d domain %d mgaw %d agaw %d\n",
"dmar%d pci%d:%d:%d:%d domain %d mgaw %d "
"agaw %d %s-mapped\n",
dmar->unit, dmar->segment, bus, slot,
func, ctx->domain, ctx->mgaw, ctx->agaw);
func, ctx->domain, ctx->mgaw, ctx->agaw,
id_mapped ? "id" : "re");
} else {
dmar_ctx_dtr(ctx1, true, true);
}

View File

@ -270,7 +270,7 @@ void ctx_free_pgtbl(struct dmar_ctx *ctx);
struct dmar_ctx *dmar_instantiate_ctx(struct dmar_unit *dmar, device_t dev,
bool rmrr);
struct dmar_ctx *dmar_get_ctx(struct dmar_unit *dmar, device_t dev,
bool id_mapped, bool rmrr_init);
int bus, int slot, int func, bool id_mapped, bool rmrr_init);
void dmar_free_ctx_locked(struct dmar_unit *dmar, struct dmar_ctx *ctx);
void dmar_free_ctx(struct dmar_ctx *ctx);
struct dmar_ctx *dmar_find_ctx_locked(struct dmar_unit *dmar, int bus,