/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Copyright (c) 2009, Intel Corporation.
* All rights reserved.
*/
#include <sys/sysmacros.h>
#include <sys/smp_impldefs.h>
typedef struct intrmap_private {
/* interrupt remapping table entry */
typedef struct intrmap_rte {
(p))
typedef enum {
typedef enum {
} intrmap_sq_t;
/*
* S field of the Interrupt Remapping Table Address Register
* the size of the interrupt remapping table is 1 << (immu_intrmap_irta_s + 1)
*/
/*
* If true, arrange to suppress broadcast EOI by setting edge-triggered mode
* even for level-triggered interrupts in the interrupt-remapping engine.
* If false, broadcast EOI can still be suppressed if the CPU supports the
* APIC_SVR_SUPPRESS_BROADCAST_EOI bit. In both cases, the IOAPIC is still
* programmed with the correct trigger mode, and pcplusmp must send an EOI
* to the IOAPIC by writing to the IOAPIC's EOI register to make up for the
* missing broadcast EOI.
*/
static int intrmap_suppress_brdcst_eoi = 0;
/*
* whether verify the source id of interrupt request
*/
static int intrmap_enable_sid_verify = 0;
/* fault types for DVMA remapping */
static char *immu_dvma_faults[] = {
"Reserved",
"The present field in root-entry is Clear",
"The present field in context-entry is Clear",
"Hardware detected invalid programming of a context-entry",
"The DMA request attempted to access an address beyond max support",
"The Write field in a page-table entry is Clear when DMA write",
"The Read field in a page-table entry is Clear when DMA read",
"Access the next level page table resulted in error",
"Access the root-entry table resulted in error",
"Access the context-entry table resulted in error",
"Reserved field not initialized to zero in a present root-entry",
"Reserved field not initialized to zero in a present context-entry",
"Reserved field not initialized to zero in a present page-table entry",
"DMA blocked due to the Translation Type field in context-entry",
"Incorrect fault event reason number",
};
/* fault types for interrupt remapping */
static char *immu_intrmap_faults[] = {
"reserved field set in IRTE",
"interrupt_index exceed the intr-remap table size",
"present field in IRTE is clear",
"hardware access intr-remap table address resulted in error",
"reserved field set in IRTE, include various conditional",
"hardware blocked an interrupt request in Compatibility format",
"remappable interrupt request blocked due to verification failure"
};
#define INTRMAP_MAX_FAULTS \
(sizeof (immu_intrmap_faults) / (sizeof (char *))) - 1
/* Function prototypes */
static int immu_intrmap_init(int apic_mode);
static void immu_intrmap_switchon(int suppress_brdcst_eoi);
static void immu_intrmap_free(void **intrmap_privatep);
};
/*
* helper functions
*/
static uint_t
{
uint_t i;
post = 0;
if (!bitset_in_set(b, i))
return (i);
}
for (i = 0; i < post; i++) {
if (!bitset_in_set(b, i))
return (i);
}
return (INTRMAP_IDX_FULL); /* no free index */
}
/*
* helper function to find 'count' contigous free
* interrupt remapping table entries
*/
static uint_t
{
uint_t i, j;
if (post == INTRMAP_IDX_FULL) {
return (INTRMAP_IDX_FULL);
}
return (INTRMAP_IDX_FULL);
for (j = 0; j < count; j++) {
if (bitset_in_set(b, (i + j))) {
i = i + j;
break;
}
if (j == count - 1)
return (i);
}
}
for (j = 0; j < count; j++) {
if (bitset_in_set(b, (i + j))) {
i = i + j;
break;
}
if (j == count - 1)
return (i);
}
}
return (INTRMAP_IDX_FULL); /* no free index */
}
/* alloc one interrupt remapping table entry */
static int
{
for (;;) {
if (idx != INTRMAP_IDX_FULL) {
break;
}
/* no free intr entry, use compatible format intr */
if (intrmap_apic_mode != LOCAL_X2APIC) {
break;
}
/*
* x2apic mode not allowed compatible
* interrupt
*/
}
return (idx);
}
/* alloc 'cnt' contigous interrupt remapping table entries */
static int
{
for (; ; ) {
if (idx != INTRMAP_IDX_FULL) {
}
for (i = 0; i < cnt; i++) {
}
break;
}
if (intrmap_apic_mode != LOCAL_X2APIC) {
break;
}
/* x2apic mode not allowed comapitible interrupt */
}
return (idx);
}
/* init interrupt remapping table */
static int
{
0U,
0xffffffffffffffffULL,
0xffffffffU,
MMU_PAGESIZE, /* page aligned */
0x1,
0x1,
0xffffffffU,
0xffffffffffffffffULL,
1,
4,
0
};
};
/*
* Using interrupt remapping implies using the queue
* invalidation interface. According to Intel,
* hardware that supports interrupt remapping should
* also support QI.
*/
if (intrmap_apic_mode == LOCAL_X2APIC) {
return (DDI_FAILURE);
}
}
if (intrmap_irta_s > INTRMAP_MAX_IRTA_SIZE) {
}
NULL,
return (DDI_FAILURE);
}
size,
NULL,
&(intrmap->intrmap_vaddr),
&size,
return (DDI_FAILURE);
}
intrmap->intrmap_free = 0;
return (DDI_SUCCESS);
}
static immu_t *
{
if (!DDI_INTR_IS_MSI_OR_MSIX(type)) {
} else {
}
return (immu);
}
static int
{
return (DDI_WALK_CONTINUE);
}
return (DDI_WALK_CONTINUE);
}
static dev_info_t *
{
return (NULL);
}
return (top_pcibridge);
}
/* function to get interrupt request source id */
static uint32_t
{
if (!intrmap_enable_sid_verify) {
return (0);
}
if (!DDI_INTR_IS_MSI_OR_MSIX(type)) {
/* for interrupt through I/O APIC */
sq = SQ_VERIFY_ALL;
} else {
/* device behind pcie to pci bridge */
sq = SQ_VERIFY_ALL;
} else {
/* pcie device or device behind pci to pci bridge */
sq = SQ_VERIFY_ALL;
}
}
}
static void
{
if (intrmap_apic_mode == LOCAL_X2APIC) {
}
}
/* ####################################################################### */
/*
* immu_intr_handler()
* the fault event handler for a single immu unit
*/
int
{
int max_fault_index;
/* read the fault status */
/* check if we have a pending fault for this immu unit */
if ((status & IMMU_FAULT_STS_PPF) == 0) {
return (DDI_INTR_UNCLAIMED);
}
/*
* handle all primary pending faults
*/
while (1) {
/* read the higher 64bits */
/* check if this fault register has pending fault */
if (!IMMU_FRR_GET_F(val)) {
break;
}
/* get the fault reason, fault type and sid */
/* read the first 64bits */
/* clear the fault */
/* report the fault info */
if (fault_reason < 0x20) {
/* immu-remapping fault */
"generated a fault event when translating DMA %s\n"
"the reason is:\n\t %s",
DVMA_MAX_FAULTS)]);
} else if (fault_reason < 0x27) {
/* intr-remapping fault */
"generated a fault event when translating "
"interrupt request\n"
"the reason is:\n\t %s",
idx,
} else {
}
index++;
if (index > max_fault_index)
index = 0;
}
/* Clear the fault */
if (!found_fault) {
"Fault register set but no fault present");
}
return (DDI_INTR_CLAIMED);
}
/* ######################################################################### */
/*
* Interrupt remap entry points
*/
/* initialize interrupt remapping */
static int
{
if (immu_intrmap_enable == B_FALSE) {
return (DDI_SUCCESS);
}
error = DDI_SUCCESS;
}
}
}
/*
* if all IOMMU units disable intr remapping,
* return FAILURE
*/
return (error);
}
/* enable interrupt remapping */
static void
{
}
}
}
/* alloc remapping entry for the interrupt */
static void
{
if (intrmap_private_tbl[0] == INTRMAP_DISABLE ||
intrmap_private_tbl[0] != NULL) {
return;
}
intrmap_private_tbl[0] =
} else {
goto intrmap_disable;
}
if (count == 1) {
} else {
}
if (idx == INTRMAP_IDX_FULL) {
goto intrmap_disable;
}
if (count == 1) {
} else {
}
return;
}
for (i = 1; i < count; i++) {
intrmap_private_tbl[i] =
}
} else {
}
return;
}
/* remapping the interrupt */
static void
int count)
{
if (intrmap_private == INTRMAP_DISABLE)
return;
if (!DDI_INTR_IS_MSI_OR_MSIX(type)) {
rh = 0;
/*
* Mark the IRTE's TM as Edge to suppress broadcast EOI.
*/
if (intrmap_suppress_brdcst_eoi) {
}
} else {
dlm = 0;
}
if (intrmap_apic_mode == LOCAL_APIC)
if (count == 1) {
/* set interrupt remapping table entry */
} else {
for (i = 0; i < count; i++) {
/* set interrupt remapping table entry */
vector++;
idx++;
}
}
}
/* free the remapping entry */
static void
{
*intrmap_privatep = NULL;
return;
}
}
*intrmap_privatep = NULL;
}
/* record the ioapic rdt entry */
static void
{
(pol << INTRMAP_IOAPIC_POL_SHIFT) |
(1 << INTRMAP_IOAPIC_FORMAT_SHIFT);
} else {
}
}
/* record the msi interrupt structure */
/*ARGSUSED*/
static void
{
(1 << INTRMAP_MSI_FORMAT_SHIFT) |
(1 << INTRMAP_MSI_SHV_SHIFT) |
} else {
}
}
/* ######################################################################### */
/*
* Functions exported by immu_intr.c
*/
void
{
/*
* Check if ACPI DMAR tables say that
* interrupt remapping is supported
*/
if (immu_dmar_intrmap_supported() == B_FALSE) {
return;
}
/*
* Check if interrupt remapping is disabled.
*/
if (immu_intrmap_enable == B_FALSE) {
return;
}
"intrmapglobal", B_TRUE);
}
}
void
{
/* do nothing */
}
}
/*
* Register a Intel IOMMU unit (i.e. DMAR unit's)
* interrupt handler
*/
void
{
if (psm_get_localapicid)
msi_addr = (MSI_ADDR_HDR |
if (intrmap_apic_mode == LOCAL_X2APIC) {
} else {
uaddr = 0;
}
/* Dont need to hold immu_intr_lock since we are in boot */
if (psm_xlate_vector_by_irq != NULL)
msi_data = ((MSI_DATA_DELIVERY_FIXED <<
(void) immu_intr_handler(immu);
}