Re-implement the DMAR I/O MMU code in terms of PCI RIDs
Under the hood the VT-d spec is really implemented in terms of PCI RIDs instead of bus/slot/function, even though the spec makes pains to convert back to bus/slot/function in examples. However working with bus/slot/function is not correct when PCI ARI is in use, so convert to using RIDs in most cases. bus/slot/function will only be used when reporting errors to a user. Reviewed by: kib MFC after: 2 months Sponsored by: Sandvine Inc.
This commit is contained in:
parent
5605a99e36
commit
6749935455
@ -93,7 +93,7 @@ dmar_bus_dma_is_dev_disabled(int domain, int bus, int slot, int func)
|
||||
* bounce mapping.
|
||||
*/
|
||||
static device_t
|
||||
dmar_get_requester(device_t dev, int *bus, int *slot, int *func)
|
||||
dmar_get_requester(device_t dev, uint16_t *rid)
|
||||
{
|
||||
devclass_t pci_class;
|
||||
device_t pci, pcib, requester;
|
||||
@ -102,9 +102,7 @@ dmar_get_requester(device_t dev, int *bus, int *slot, int *func)
|
||||
pci_class = devclass_find("pci");
|
||||
requester = dev;
|
||||
|
||||
*bus = pci_get_bus(dev);
|
||||
*slot = pci_get_slot(dev);
|
||||
*func = pci_get_function(dev);
|
||||
*rid = pci_get_rid(dev);
|
||||
|
||||
/*
|
||||
* Walk the bridge hierarchy from the target device to the
|
||||
@ -161,8 +159,7 @@ dmar_get_requester(device_t dev, int *bus, int *slot, int *func)
|
||||
* same page tables for taken and
|
||||
* non-taken transactions.
|
||||
*/
|
||||
*bus = pci_get_bus(dev);
|
||||
*slot = *func = 0;
|
||||
*rid = PCI_RID(pci_get_bus(dev), 0, 0);
|
||||
} else {
|
||||
/*
|
||||
* Neither the device nor the bridge
|
||||
@ -171,9 +168,7 @@ dmar_get_requester(device_t dev, int *bus, int *slot, int *func)
|
||||
* 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);
|
||||
*rid = pci_get_rid(pcib);
|
||||
}
|
||||
}
|
||||
/*
|
||||
@ -193,9 +188,9 @@ 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;
|
||||
uint16_t rid;
|
||||
|
||||
requester = dmar_get_requester(dev, &bus, &slot, &func);
|
||||
requester = dmar_get_requester(dev, &rid);
|
||||
|
||||
/*
|
||||
* If the user requested the IOMMU disabled for the device, we
|
||||
@ -204,9 +199,10 @@ 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(pci_get_domain(dev), bus,
|
||||
slot, func);
|
||||
ctx = dmar_get_ctx(dmar, requester, bus, slot, func, disabled, rmrr);
|
||||
disabled = dmar_bus_dma_is_dev_disabled(pci_get_domain(requester),
|
||||
pci_get_bus(requester), pci_get_slot(requester),
|
||||
pci_get_function(requester));
|
||||
ctx = dmar_get_ctx(dmar, requester, rid, disabled, rmrr);
|
||||
if (ctx == NULL)
|
||||
return (NULL);
|
||||
if (disabled) {
|
||||
|
@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <x86/iommu/intel_reg.h>
|
||||
#include <x86/iommu/busdma_dmar.h>
|
||||
#include <x86/iommu/intel_dmar.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
|
||||
static MALLOC_DEFINE(M_DMAR_CTX, "dmar_ctx", "Intel DMAR Context");
|
||||
@ -105,14 +106,14 @@ dmar_map_ctx_entry(struct dmar_ctx *ctx, struct sf_buf **sfp)
|
||||
{
|
||||
dmar_ctx_entry_t *ctxp;
|
||||
|
||||
ctxp = dmar_map_pgtbl(ctx->dmar->ctx_obj, 1 + ctx->bus,
|
||||
ctxp = dmar_map_pgtbl(ctx->dmar->ctx_obj, 1 + PCI_RID2BUS(ctx->rid),
|
||||
DMAR_PGF_NOALLOC | DMAR_PGF_WAITOK, sfp);
|
||||
ctxp += ((ctx->slot & 0x1f) << 3) + (ctx->func & 0x7);
|
||||
ctxp += ctx->rid & 0xff;
|
||||
return (ctxp);
|
||||
}
|
||||
|
||||
static void
|
||||
ctx_tag_init(struct dmar_ctx *ctx)
|
||||
ctx_tag_init(struct dmar_ctx *ctx, device_t dev)
|
||||
{
|
||||
bus_addr_t maxaddr;
|
||||
|
||||
@ -126,6 +127,7 @@ ctx_tag_init(struct dmar_ctx *ctx)
|
||||
ctx->ctx_tag.common.nsegments = BUS_SPACE_UNRESTRICTED;
|
||||
ctx->ctx_tag.common.maxsegsz = maxaddr;
|
||||
ctx->ctx_tag.ctx = ctx;
|
||||
ctx->ctx_tag.owner = dev;
|
||||
/* XXXKIB initialize tag further */
|
||||
}
|
||||
|
||||
@ -138,7 +140,10 @@ ctx_id_entry_init(struct dmar_ctx *ctx, dmar_ctx_entry_t *ctxp)
|
||||
unit = ctx->dmar;
|
||||
KASSERT(ctxp->ctx1 == 0 && ctxp->ctx2 == 0,
|
||||
("dmar%d: initialized ctx entry %d:%d:%d 0x%jx 0x%jx",
|
||||
unit->unit, ctx->bus, ctx->slot, ctx->func, ctxp->ctx1,
|
||||
unit->unit, pci_get_bus(ctx->ctx_tag.owner),
|
||||
pci_get_slot(ctx->ctx_tag.owner),
|
||||
pci_get_function(ctx->ctx_tag.owner),
|
||||
ctxp->ctx1,
|
||||
ctxp->ctx2));
|
||||
ctxp->ctx2 = DMAR_CTX2_DID(ctx->domain);
|
||||
ctxp->ctx2 |= ctx->awlvl;
|
||||
@ -227,7 +232,7 @@ ctx_init_rmrr(struct dmar_ctx *ctx, device_t dev)
|
||||
}
|
||||
|
||||
static struct dmar_ctx *
|
||||
dmar_get_ctx_alloc(struct dmar_unit *dmar, int bus, int slot, int func)
|
||||
dmar_get_ctx_alloc(struct dmar_unit *dmar, uint16_t rid)
|
||||
{
|
||||
struct dmar_ctx *ctx;
|
||||
|
||||
@ -237,9 +242,7 @@ dmar_get_ctx_alloc(struct dmar_unit *dmar, int bus, int slot, int func)
|
||||
TASK_INIT(&ctx->unload_task, 0, dmar_ctx_unload_task, ctx);
|
||||
mtx_init(&ctx->lock, "dmarctx", NULL, MTX_DEF);
|
||||
ctx->dmar = dmar;
|
||||
ctx->bus = bus;
|
||||
ctx->slot = slot;
|
||||
ctx->func = func;
|
||||
ctx->rid = rid;
|
||||
return (ctx);
|
||||
}
|
||||
|
||||
@ -262,19 +265,22 @@ 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, int bus, int slot, int func,
|
||||
bool id_mapped, bool rmrr_init)
|
||||
dmar_get_ctx(struct dmar_unit *dmar, device_t dev, uint16_t rid, bool id_mapped,
|
||||
bool rmrr_init)
|
||||
{
|
||||
struct dmar_ctx *ctx, *ctx1;
|
||||
dmar_ctx_entry_t *ctxp;
|
||||
struct sf_buf *sf;
|
||||
int error, mgaw;
|
||||
int bus, slot, func, 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);
|
||||
ctx = dmar_find_ctx_locked(dmar, bus, slot, func);
|
||||
ctx = dmar_find_ctx_locked(dmar, rid);
|
||||
error = 0;
|
||||
if (ctx == NULL) {
|
||||
/*
|
||||
@ -283,7 +289,7 @@ dmar_get_ctx(struct dmar_unit *dmar, device_t dev, int bus, int slot, int func,
|
||||
*/
|
||||
DMAR_UNLOCK(dmar);
|
||||
dmar_ensure_ctx_page(dmar, bus);
|
||||
ctx1 = dmar_get_ctx_alloc(dmar, bus, slot, func);
|
||||
ctx1 = dmar_get_ctx_alloc(dmar, rid);
|
||||
|
||||
if (id_mapped) {
|
||||
/*
|
||||
@ -351,7 +357,7 @@ dmar_get_ctx(struct dmar_unit *dmar, device_t dev, int bus, int slot, int func,
|
||||
* Recheck the contexts, other thread might have
|
||||
* already allocated needed one.
|
||||
*/
|
||||
ctx = dmar_find_ctx_locked(dmar, bus, slot, func);
|
||||
ctx = dmar_find_ctx_locked(dmar, rid);
|
||||
if (ctx == NULL) {
|
||||
ctx = ctx1;
|
||||
ctx->ctx_tag.owner = dev;
|
||||
@ -363,7 +369,7 @@ dmar_get_ctx(struct dmar_unit *dmar, device_t dev, int bus, int slot, int func,
|
||||
TD_PINNED_ASSERT;
|
||||
return (NULL);
|
||||
}
|
||||
ctx_tag_init(ctx);
|
||||
ctx_tag_init(ctx, dev);
|
||||
|
||||
/*
|
||||
* This is the first activated context for the
|
||||
@ -524,14 +530,14 @@ dmar_free_ctx(struct dmar_ctx *ctx)
|
||||
}
|
||||
|
||||
struct dmar_ctx *
|
||||
dmar_find_ctx_locked(struct dmar_unit *dmar, int bus, int slot, int func)
|
||||
dmar_find_ctx_locked(struct dmar_unit *dmar, uint16_t rid)
|
||||
{
|
||||
struct dmar_ctx *ctx;
|
||||
|
||||
DMAR_ASSERT_LOCKED(dmar);
|
||||
|
||||
LIST_FOREACH(ctx, &dmar->contexts, link) {
|
||||
if (ctx->bus == bus && ctx->slot == slot && ctx->func == func)
|
||||
if (ctx->rid == rid)
|
||||
return (ctx);
|
||||
}
|
||||
return (NULL);
|
||||
|
@ -74,9 +74,7 @@ RB_PROTOTYPE(dmar_gas_entries_tree, dmar_map_entry, rb_entry,
|
||||
#define DMAR_MAP_ENTRY_TM 0x8000 /* Transient */
|
||||
|
||||
struct dmar_ctx {
|
||||
int bus; /* pci bus/slot/func */
|
||||
int slot;
|
||||
int func;
|
||||
uint16_t rid; /* pci RID */
|
||||
int domain; /* DID */
|
||||
int mgaw; /* Real max address width */
|
||||
int agaw; /* Adjusted guest address width */
|
||||
@ -269,12 +267,11 @@ 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,
|
||||
int bus, int slot, int func, bool id_mapped, bool rmrr_init);
|
||||
struct dmar_ctx *dmar_get_ctx(struct dmar_unit *dmar, device_t dev,
|
||||
uint16_t rid, 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,
|
||||
int slot, int func);
|
||||
struct dmar_ctx *dmar_find_ctx_locked(struct dmar_unit *dmar, uint16_t rid);
|
||||
void dmar_ctx_unload_entry(struct dmar_map_entry *entry, bool free);
|
||||
void dmar_ctx_unload(struct dmar_ctx *ctx,
|
||||
struct dmar_map_entries_tailq *entries, bool cansleep);
|
||||
|
@ -1005,7 +1005,9 @@ dmar_print_ctx(struct dmar_ctx *ctx, bool show_mappings)
|
||||
db_printf(
|
||||
" @%p pci%d:%d:%d dom %d mgaw %d agaw %d pglvl %d end %jx\n"
|
||||
" refs %d flags %x pgobj %p map_ents %u loads %lu unloads %lu\n",
|
||||
ctx, ctx->bus, ctx->slot, ctx->func, ctx->domain, ctx->mgaw,
|
||||
ctx, pci_get_bus(ctx->ctx_tag.owner),
|
||||
pci_get_slot(ctx->ctx_tag.owner),
|
||||
pci_get_function(ctx->ctx_tag.owner), ctx->domain, ctx->mgaw,
|
||||
ctx->agaw, ctx->pglvl, (uintmax_t)ctx->end, ctx->refs,
|
||||
ctx->flags, ctx->pgtbl_obj, ctx->entries_cnt, ctx->loads,
|
||||
ctx->unloads);
|
||||
@ -1078,8 +1080,10 @@ DB_FUNC(dmar_ctx, db_dmar_print_ctx, db_show_table, CS_OWN, NULL)
|
||||
for (i = 0; i < dmar_devcnt; i++) {
|
||||
unit = device_get_softc(dmar_devs[i]);
|
||||
LIST_FOREACH(ctx, &unit->contexts, link) {
|
||||
if (domain == unit->segment && bus == ctx->bus &&
|
||||
device == ctx->slot && function == ctx->func) {
|
||||
if (domain == unit->segment &&
|
||||
bus == pci_get_bus(ctx->ctx_tag.owner) &&
|
||||
device == pci_get_slot(ctx->ctx_tag.owner) &&
|
||||
function == pci_get_function(ctx->ctx_tag.owner)) {
|
||||
dmar_print_ctx(ctx, show_mappings);
|
||||
goto out;
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
#include <contrib/dev/acpica/include/accommon.h>
|
||||
#include <dev/acpica/acpivar.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_kern.h>
|
||||
@ -203,19 +205,28 @@ dmar_fault_task(void *arg, int pending __unused)
|
||||
DMAR_FAULT_UNLOCK(unit);
|
||||
|
||||
sid = DMAR_FRCD2_SID(fault_rec[1]);
|
||||
bus = (sid >> 8) & 0xf;
|
||||
slot = (sid >> 3) & 0x1f;
|
||||
func = sid & 0x7;
|
||||
printf("DMAR%d: ", unit->unit);
|
||||
DMAR_LOCK(unit);
|
||||
ctx = dmar_find_ctx_locked(unit, bus, slot, func);
|
||||
ctx = dmar_find_ctx_locked(unit, sid);
|
||||
if (ctx == NULL) {
|
||||
printf("<unknown dev>:");
|
||||
|
||||
/*
|
||||
* Note that the slot and function will not be correct
|
||||
* if ARI is in use, but without a ctx entry we have
|
||||
* no way of knowing whether ARI is in use or not.
|
||||
*/
|
||||
bus = PCI_RID2BUS(sid);
|
||||
slot = PCI_RID2SLOT(sid);
|
||||
func = PCI_RID2FUNC(sid);
|
||||
} else {
|
||||
ctx->flags |= DMAR_CTX_FAULTED;
|
||||
ctx->last_fault_rec[0] = fault_rec[0];
|
||||
ctx->last_fault_rec[1] = fault_rec[1];
|
||||
device_print_prettyname(ctx->ctx_tag.owner);
|
||||
bus = pci_get_bus(ctx->ctx_tag.owner);
|
||||
slot = pci_get_slot(ctx->ctx_tag.owner);
|
||||
func = pci_get_function(ctx->ctx_tag.owner);
|
||||
}
|
||||
DMAR_UNLOCK(unit);
|
||||
printf(
|
||||
|
@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/systm.h>
|
||||
#include <sys/taskqueue.h>
|
||||
#include <sys/tree.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_kern.h>
|
||||
@ -129,8 +130,10 @@ ctx_set_agaw(struct dmar_ctx *ctx, int mgaw)
|
||||
}
|
||||
device_printf(ctx->dmar->dev,
|
||||
"context request mgaw %d for pci%d:%d:%d:%d, "
|
||||
"no agaw found, sagaw %x\n", mgaw, ctx->dmar->segment, ctx->bus,
|
||||
ctx->slot, ctx->func, sagaw);
|
||||
"no agaw found, sagaw %x\n", mgaw, ctx->dmar->segment,
|
||||
pci_get_bus(ctx->ctx_tag.owner),
|
||||
pci_get_slot(ctx->ctx_tag.owner),
|
||||
pci_get_function(ctx->ctx_tag.owner), sagaw);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user