Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/iommu/intel_fault.c
Show First 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | |||||
* For the same reason, each translation unit task is executed in its | * For the same reason, each translation unit task is executed in its | ||||
* own thread. | * own thread. | ||||
* | * | ||||
* XXXKIB It seems there is no hardware available which implements | * XXXKIB It seems there is no hardware available which implements | ||||
* advanced fault logging, so the code to handle AFL is not written. | * advanced fault logging, so the code to handle AFL is not written. | ||||
*/ | */ | ||||
static int | static int | ||||
dmar_fault_next(struct dmar_unit *unit, int faultp) | dmar_fault_next(struct iommu_unit *unit, int faultp) | ||||
{ | { | ||||
faultp += 2; | faultp += 2; | ||||
if (faultp == unit->fault_log_size) | if (faultp == unit->fault_log_size) | ||||
faultp = 0; | faultp = 0; | ||||
return (faultp); | return (faultp); | ||||
} | } | ||||
static void | static void | ||||
dmar_fault_intr_clear(struct dmar_unit *unit, uint32_t fsts) | dmar_fault_intr_clear(struct iommu_unit *unit, uint32_t fsts) | ||||
{ | { | ||||
uint32_t clear; | uint32_t clear; | ||||
clear = 0; | clear = 0; | ||||
if ((fsts & DMAR_FSTS_ITE) != 0) { | if ((fsts & DMAR_FSTS_ITE) != 0) { | ||||
printf("DMAR%d: Invalidation timed out\n", unit->unit); | printf("DMAR%d: Invalidation timed out\n", unit->unit); | ||||
clear |= DMAR_FSTS_ITE; | clear |= DMAR_FSTS_ITE; | ||||
} | } | ||||
Show All 17 Lines | dmar_fault_intr_clear(struct iommu_unit *unit, uint32_t fsts) | ||||
} | } | ||||
if (clear != 0) | if (clear != 0) | ||||
dmar_write4(unit, DMAR_FSTS_REG, clear); | dmar_write4(unit, DMAR_FSTS_REG, clear); | ||||
} | } | ||||
int | int | ||||
dmar_fault_intr(void *arg) | dmar_fault_intr(void *arg) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct iommu_unit *unit; | ||||
uint64_t fault_rec[2]; | uint64_t fault_rec[2]; | ||||
uint32_t fsts; | uint32_t fsts; | ||||
int fri, frir, faultp; | int fri, frir, faultp; | ||||
bool enqueue; | bool enqueue; | ||||
unit = arg; | unit = arg; | ||||
enqueue = false; | enqueue = false; | ||||
fsts = dmar_read4(unit, DMAR_FSTS_REG); | fsts = dmar_read4(unit, DMAR_FSTS_REG); | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | taskqueue_enqueue(unit->fault_taskqueue, | ||||
&unit->fault_task); | &unit->fault_task); | ||||
} | } | ||||
return (FILTER_HANDLED); | return (FILTER_HANDLED); | ||||
} | } | ||||
static void | static void | ||||
dmar_fault_task(void *arg, int pending __unused) | dmar_fault_task(void *arg, int pending __unused) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct iommu_unit *unit; | ||||
struct dmar_ctx *ctx; | struct iommu_device *ctx; | ||||
uint64_t fault_rec[2]; | uint64_t fault_rec[2]; | ||||
int sid, bus, slot, func, faultp; | int sid, bus, slot, func, faultp; | ||||
unit = arg; | unit = arg; | ||||
DMAR_FAULT_LOCK(unit); | DMAR_FAULT_LOCK(unit); | ||||
for (;;) { | for (;;) { | ||||
faultp = unit->fault_log_tail; | faultp = unit->fault_log_tail; | ||||
if (faultp == unit->fault_log_head) | if (faultp == unit->fault_log_head) | ||||
break; | break; | ||||
fault_rec[0] = unit->fault_log[faultp]; | fault_rec[0] = unit->fault_log[faultp]; | ||||
fault_rec[1] = unit->fault_log[faultp + 1]; | fault_rec[1] = unit->fault_log[faultp + 1]; | ||||
unit->fault_log_tail = dmar_fault_next(unit, faultp); | unit->fault_log_tail = dmar_fault_next(unit, faultp); | ||||
DMAR_FAULT_UNLOCK(unit); | DMAR_FAULT_UNLOCK(unit); | ||||
sid = DMAR_FRCD2_SID(fault_rec[1]); | sid = DMAR_FRCD2_SID(fault_rec[1]); | ||||
printf("DMAR%d: ", unit->unit); | printf("DMAR%d: ", unit->unit); | ||||
DMAR_LOCK(unit); | IOMMU_LOCK(unit); | ||||
ctx = dmar_find_ctx_locked(unit, sid); | ctx = dmar_find_ctx_locked(unit, sid); | ||||
if (ctx == NULL) { | if (ctx == NULL) { | ||||
printf("<unknown dev>:"); | printf("<unknown dev>:"); | ||||
/* | /* | ||||
* Note that the slot and function will not be correct | * Note that the slot and function will not be correct | ||||
* if ARI is in use, but without a ctx entry we have | * if ARI is in use, but without a ctx entry we have | ||||
* no way of knowing whether ARI is in use or not. | * no way of knowing whether ARI is in use or not. | ||||
*/ | */ | ||||
bus = PCI_RID2BUS(sid); | bus = PCI_RID2BUS(sid); | ||||
slot = PCI_RID2SLOT(sid); | slot = PCI_RID2SLOT(sid); | ||||
func = PCI_RID2FUNC(sid); | func = PCI_RID2FUNC(sid); | ||||
} else { | } else { | ||||
ctx->flags |= DMAR_CTX_FAULTED; | ctx->flags |= DMAR_CTX_FAULTED; | ||||
ctx->last_fault_rec[0] = fault_rec[0]; | ctx->last_fault_rec[0] = fault_rec[0]; | ||||
ctx->last_fault_rec[1] = fault_rec[1]; | ctx->last_fault_rec[1] = fault_rec[1]; | ||||
device_print_prettyname(ctx->ctx_tag.owner); | device_print_prettyname(ctx->device_tag.owner); | ||||
bus = pci_get_bus(ctx->ctx_tag.owner); | bus = pci_get_bus(ctx->device_tag.owner); | ||||
slot = pci_get_slot(ctx->ctx_tag.owner); | slot = pci_get_slot(ctx->device_tag.owner); | ||||
func = pci_get_function(ctx->ctx_tag.owner); | func = pci_get_function(ctx->device_tag.owner); | ||||
} | } | ||||
DMAR_UNLOCK(unit); | IOMMU_UNLOCK(unit); | ||||
printf( | printf( | ||||
"pci%d:%d:%d sid %x fault acc %x adt 0x%x reason 0x%x " | "pci%d:%d:%d sid %x fault acc %x adt 0x%x reason 0x%x " | ||||
"addr %jx\n", | "addr %jx\n", | ||||
bus, slot, func, sid, DMAR_FRCD2_T(fault_rec[1]), | bus, slot, func, sid, DMAR_FRCD2_T(fault_rec[1]), | ||||
DMAR_FRCD2_AT(fault_rec[1]), DMAR_FRCD2_FR(fault_rec[1]), | DMAR_FRCD2_AT(fault_rec[1]), DMAR_FRCD2_FR(fault_rec[1]), | ||||
(uintmax_t)fault_rec[0]); | (uintmax_t)fault_rec[0]); | ||||
DMAR_FAULT_LOCK(unit); | DMAR_FAULT_LOCK(unit); | ||||
} | } | ||||
DMAR_FAULT_UNLOCK(unit); | DMAR_FAULT_UNLOCK(unit); | ||||
} | } | ||||
static void | static void | ||||
dmar_clear_faults(struct dmar_unit *unit) | dmar_clear_faults(struct iommu_unit *unit) | ||||
{ | { | ||||
uint32_t frec, frir, fsts; | uint32_t frec, frir, fsts; | ||||
int i; | int i; | ||||
for (i = 0; i < DMAR_CAP_NFR(unit->hw_cap); i++) { | for (i = 0; i < DMAR_CAP_NFR(unit->hw_cap); i++) { | ||||
frir = (DMAR_CAP_FRO(unit->hw_cap) + i) * 16; | frir = (DMAR_CAP_FRO(unit->hw_cap) + i) * 16; | ||||
frec = dmar_read4(unit, frir + 12); | frec = dmar_read4(unit, frir + 12); | ||||
if ((frec & DMAR_FRCD2_F32) == 0) | if ((frec & DMAR_FRCD2_F32) == 0) | ||||
continue; | continue; | ||||
dmar_write4(unit, frir + 12, DMAR_FRCD2_F32); | dmar_write4(unit, frir + 12, DMAR_FRCD2_F32); | ||||
} | } | ||||
fsts = dmar_read4(unit, DMAR_FSTS_REG); | fsts = dmar_read4(unit, DMAR_FSTS_REG); | ||||
dmar_write4(unit, DMAR_FSTS_REG, fsts); | dmar_write4(unit, DMAR_FSTS_REG, fsts); | ||||
} | } | ||||
int | int | ||||
dmar_init_fault_log(struct dmar_unit *unit) | dmar_init_fault_log(struct iommu_unit *unit) | ||||
{ | { | ||||
mtx_init(&unit->fault_lock, "dmarflt", NULL, MTX_SPIN); | mtx_init(&unit->fault_lock, "dmarflt", NULL, MTX_SPIN); | ||||
unit->fault_log_size = 256; /* 128 fault log entries */ | unit->fault_log_size = 256; /* 128 fault log entries */ | ||||
TUNABLE_INT_FETCH("hw.dmar.fault_log_size", &unit->fault_log_size); | TUNABLE_INT_FETCH("hw.dmar.fault_log_size", &unit->fault_log_size); | ||||
if (unit->fault_log_size % 2 != 0) | if (unit->fault_log_size % 2 != 0) | ||||
panic("hw.dmar_fault_log_size must be even"); | panic("hw.dmar_fault_log_size must be even"); | ||||
unit->fault_log = malloc(sizeof(uint64_t) * unit->fault_log_size, | unit->fault_log = malloc(sizeof(uint64_t) * unit->fault_log_size, | ||||
M_DEVBUF, M_WAITOK | M_ZERO); | M_DEVBUF, M_WAITOK | M_ZERO); | ||||
TASK_INIT(&unit->fault_task, 0, dmar_fault_task, unit); | TASK_INIT(&unit->fault_task, 0, dmar_fault_task, unit); | ||||
unit->fault_taskqueue = taskqueue_create_fast("dmarff", M_WAITOK, | unit->fault_taskqueue = taskqueue_create_fast("dmarff", M_WAITOK, | ||||
taskqueue_thread_enqueue, &unit->fault_taskqueue); | taskqueue_thread_enqueue, &unit->fault_taskqueue); | ||||
taskqueue_start_threads(&unit->fault_taskqueue, 1, PI_AV, | taskqueue_start_threads(&unit->fault_taskqueue, 1, PI_AV, | ||||
"dmar%d fault taskq", unit->unit); | "dmar%d fault taskq", unit->unit); | ||||
DMAR_LOCK(unit); | IOMMU_LOCK(unit); | ||||
dmar_disable_fault_intr(unit); | dmar_disable_fault_intr(unit); | ||||
dmar_clear_faults(unit); | dmar_clear_faults(unit); | ||||
dmar_enable_fault_intr(unit); | dmar_enable_fault_intr(unit); | ||||
DMAR_UNLOCK(unit); | IOMMU_UNLOCK(unit); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
dmar_fini_fault_log(struct dmar_unit *unit) | dmar_fini_fault_log(struct iommu_unit *unit) | ||||
{ | { | ||||
if (unit->fault_taskqueue == NULL) | if (unit->fault_taskqueue == NULL) | ||||
return; | return; | ||||
DMAR_LOCK(unit); | IOMMU_LOCK(unit); | ||||
dmar_disable_fault_intr(unit); | dmar_disable_fault_intr(unit); | ||||
DMAR_UNLOCK(unit); | IOMMU_UNLOCK(unit); | ||||
taskqueue_drain(unit->fault_taskqueue, &unit->fault_task); | taskqueue_drain(unit->fault_taskqueue, &unit->fault_task); | ||||
taskqueue_free(unit->fault_taskqueue); | taskqueue_free(unit->fault_taskqueue); | ||||
unit->fault_taskqueue = NULL; | unit->fault_taskqueue = NULL; | ||||
mtx_destroy(&unit->fault_lock); | mtx_destroy(&unit->fault_lock); | ||||
free(unit->fault_log, M_DEVBUF); | free(unit->fault_log, M_DEVBUF); | ||||
unit->fault_log = NULL; | unit->fault_log = NULL; | ||||
unit->fault_log_head = unit->fault_log_tail = 0; | unit->fault_log_head = unit->fault_log_tail = 0; | ||||
} | } | ||||
void | void | ||||
dmar_enable_fault_intr(struct dmar_unit *unit) | dmar_enable_fault_intr(struct iommu_unit *unit) | ||||
{ | { | ||||
uint32_t fectl; | uint32_t fectl; | ||||
DMAR_ASSERT_LOCKED(unit); | IOMMU_ASSERT_LOCKED(unit); | ||||
fectl = dmar_read4(unit, DMAR_FECTL_REG); | fectl = dmar_read4(unit, DMAR_FECTL_REG); | ||||
fectl &= ~DMAR_FECTL_IM; | fectl &= ~DMAR_FECTL_IM; | ||||
dmar_write4(unit, DMAR_FECTL_REG, fectl); | dmar_write4(unit, DMAR_FECTL_REG, fectl); | ||||
} | } | ||||
void | void | ||||
dmar_disable_fault_intr(struct dmar_unit *unit) | dmar_disable_fault_intr(struct iommu_unit *unit) | ||||
{ | { | ||||
uint32_t fectl; | uint32_t fectl; | ||||
DMAR_ASSERT_LOCKED(unit); | IOMMU_ASSERT_LOCKED(unit); | ||||
fectl = dmar_read4(unit, DMAR_FECTL_REG); | fectl = dmar_read4(unit, DMAR_FECTL_REG); | ||||
dmar_write4(unit, DMAR_FECTL_REG, fectl | DMAR_FECTL_IM); | dmar_write4(unit, DMAR_FECTL_REG, fectl | DMAR_FECTL_IM); | ||||
} | } |