MFC r263306:
Add some support for the PCI(e)-PCI bridges to the Intel VT-d driver.
This commit is contained in:
parent
879dfba7f2
commit
5a582ae617
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user