intel_iommu.c revision 4bab10e9701c2ee972891c467afe08ad790d5c07
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2009, Intel Corporation.
* All rights reserved.
*/
#include <sys/mdb_modapi.h>
#include <sys/dditypes.h>
#include <sys/ddi_impldefs.h>
#include <sys/intel_iommu.h>
#include <sys/iommulib.h>
#include <stddef.h>
/*
* Does Intel IOMMU works on this system?
*/
static void
iomuvtop_help(void)
{
mdb_printf("print physical mapping of IO virtual address\n\n"
"Usage:\n\n"
" address::iomuvtop <iova>\n\n"
"Where, \"address\" is the address of the devinfo node, "
"while \"iova\" is the DMA virtual address.\n");
}
static boolean_t
iommu_supported(void)
{
if (iommu_support == B_FALSE)
mdb_printf("No Intel IOMMU active on this system\n");
return (iommu_support);
}
/*
* print_device_scope_cb()
* call back for print_device_scope()
*/
static int
{
mdb_printf((char *)cbdata);
mdb_printf("BDF[%x:%x:%x],type[%x]\n",
return (WALK_NEXT);
}
/*
* print_device_scope()
* a common function to print device scope of a drhd or rmrr
*/
static void
{
mdb_pwalk("list",
}
/*
* parse_hw_capa()
* parse_hw_excapa()
*
* Given the capability and extension capability register contents,
* parse and print supported features in <output>
*
* Please refer to chapter 10.4.2/3 in "Intel virutalization technology
* for direct IO specification" for register details
*/
static void
{
char string[128];
if (IOMMU_CAP_GET_DRD(capa))
if (IOMMU_CAP_GET_DWD(capa))
if (IOMMU_CAP_GET_PSI(capa))
if (IOMMU_CAP_GET_ISOCH(capa))
if (IOMMU_CAP_GET_ZLR(capa))
if (IOMMU_CAP_GET_CM(capa))
if (IOMMU_CAP_GET_PHMR(capa))
if (IOMMU_CAP_GET_PLMR(capa))
if (IOMMU_CAP_GET_RWBF(capa))
if (IOMMU_CAP_GET_AFL(capa))
if ((len > 1) &&
}
static void
{
char string[128];
if (IOMMU_ECAP_GET_SC(excapa))
if (IOMMU_ECAP_GET_PT(excapa))
if (IOMMU_ECAP_GET_CH(excapa))
if (IOMMU_ECAP_GET_EIM(excapa))
if (IOMMU_ECAP_GET_IR(excapa))
if (IOMMU_ECAP_GET_DI(excapa))
if (IOMMU_ECAP_GET_QI(excapa))
if (IOMMU_ECAP_GET_C(excapa))
if ((len > 1) &&
}
typedef enum {
} iomu_scope_t;
/*
* print_iommu_state()
* Given an iommu_state structure, parse and print iommu information
*
* Returns:
* INCLUDE_ALL_SCOPE if include all is set
* DEV_SCOPE if not set
* ERROR_SCOPE on error.
*/
static iomu_scope_t
{
mdb_warn("Internal error - NULL iommu state pointer passed\n");
return (ERROR_SCOPE);
}
mdb_printf("Intel DMA remapping unit\n");
mdb_printf(" IOMMU Status:\t\t\t%s\n",
mdb_printf(" Queued Invalid:\t\t%s\n",
mdb_printf(" Interrupt remapping:\t\t%s\n",
mdb_printf(" Register Physical Address:\t%p\n",
mdb_printf(" Register Virtual Address:\t%p\n",
mdb_printf(" Root Entry Table:\t\t%p\n",
mdb_printf(" System Coherence:\t\t%s\n",
mdb_printf(" Include All unit:\t\t%s\n",
mdb_printf(" Devinfo Node:\t\t\t%p\n",
struct inv_queue_state qi_state;
if (iommu->iu_inv_queue &&
mdb_printf(" Qinv Table:\t\t\tpaddr:%p, "
"vaddr:%p, size:%x\n",
mdb_printf(" Sync Table:\t\t\tpaddr:%p, "
"vaddr:%p, size:%x\n",
} else {
mdb_warn("failed to read iommu invalidation "
"queue state at %p\n",
return (ERROR_SCOPE);
}
}
}
/*
* dcmd: iomuprt
*/
static int
{
if (iommu_supported() == B_FALSE)
return (DCMD_OK);
return (DCMD_USAGE);
if (!DCMD_HDRSPEC(flags))
mdb_printf("\n");
case DEV_SCOPE:
/*
* Use actual address of list_t in kernel for walker
*/
print_device_scope(" Device Scope:\t\t\t",
break;
case ERROR_SCOPE:
return (DCMD_ERR);
default:
break;
}
} else {
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*
* print_iommu_addr()
* callback to print addresses of IOMMU unit software structures
*/
static int
{
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*
* dcmd: iomunits
*/
static int
{
if (iommu_supported() == B_FALSE)
return (DCMD_OK);
return (DCMD_USAGE);
}
mdb_warn("failed to find symbol iommu_states\n");
return (DCMD_ERR);
}
mdb_warn("couldn't walk IOMMU state structures\n");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*
* print_domain_state()
* Given an device domain structure, parse and print information
*/
static void
{
mdb_warn("Internal error: NULL domain pointer passed\n");
return;
}
mdb_printf("IOMMU device domain:\n");
mdb_printf("DVMA vmem:\t\t%p\n",
mdb_printf("Top Level Page Table:\t%p\n",
mdb_printf("Identity Mapping:\t\t%s\n",
}
/*
* dcmd: iomudomprt
*/
static int
{
if (iommu_supported() == B_FALSE)
return (DCMD_OK);
return (DCMD_USAGE);
if (!DCMD_HDRSPEC(flags))
mdb_printf("\n");
} else {
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*
* print_domain_addr()
*/
static int
{
if (iommu_supported() == B_FALSE)
return (WALK_NEXT);
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*
* dcmd: iomudoms
*/
static int
{
if (iommu_supported() == B_FALSE)
return (DCMD_OK);
return (DCMD_USAGE);
}
mdb_warn("failed to find symbol domain_states\n");
return (DCMD_ERR);
}
return (DCMD_ERR);
return (DCMD_OK);
}
/*
* print_rmrr_info()
*/
static void
{
mdb_printf("Reserved Memory Region Reporting:\n");
}
/*
* print_rmrr_addr()
* list walk callback for list_rmrr
*/
static int
{
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*
* dcmd: iomurmrrs
*/
static int
{
if (iommu_supported() == B_FALSE)
return (DCMD_OK);
return (DCMD_USAGE);
}
mdb_warn("failed to find symbol rmrr_states\n");
return (DCMD_ERR);
}
return (DCMD_ERR);
return (DCMD_OK);
}
/*
* dcmd: iomurmrrprt: Given an RMRR address print the RMRR.
*/
static int
{
if (iommu_supported() == B_FALSE)
return (DCMD_OK);
return (DCMD_USAGE);
}
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*
* iova_level_to_offset()
* Given an iova and page table level, return the corresponding offset
*/
static int
{
return (offset);
}
/*
* iovtp_read_table_entry()
*/
static int
{
!= ent_size) {
return (B_FALSE);
} else {
return (B_TRUE);
}
}
/*
* dcmd: iomuvtop
*/
static int
{
struct root_context_entry {
} rc_entry;
if (iommu_supported() == B_FALSE)
return (DCMD_OK);
return (DCMD_USAGE);
}
/* read iommu private */
mdb_warn("failed to read iommu private structure for "
"devinfo node at address %p\n", addr);
return (DCMD_ERR);
}
/* read domain */
mdb_printf("IOMMU domain for this device has not yet been "
"allocated.\nNo mapped physical address for this vaddr\n");
return (DCMD_OK);
}
!= sizeof (domain)) {
mdb_warn("failed to read domain structure at %p\n",
return (DCMD_ERR);
}
/* read iommu */
!= sizeof (iommu)) {
mdb_warn("failed to read iommu structure at %p\n",
return (DCMD_ERR);
}
mdb_printf("Level\tPageTableAddress\tOffset\tPageTableEntry\n");
/* walk and print root context tabls */
== B_FALSE) {
mdb_warn("failed to read root table entry for bus %x "
return (DCMD_ERR);
}
== B_FALSE) {
mdb_warn("failed to read context table entry for "
return (DCMD_ERR);
}
/* walk and print page tables */
/*
* Toppest level page table address should be the same
* as that stored in domain structure
*/
mdb_warn("The top level page table retrieved from context"
" table doesn't match that from the domain structure."
" Aborting PA lookup.\n");
return (DCMD_ERR);
}
for (i = level; i > 0; i--) {
if (!ptaddr) {
mdb_printf("\nNULL page table entry encountered at "
" page table level %d. Aborting PA lookup.\n", i);
return (DCMD_OK);
}
mdb_warn("failed to read page table entry "
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
typedef struct bdf_cb_data {
int dc_seg;
int dc_bus;
int dc_devfunc;
int dc_match;
/*
* match_bdf()
* call back function that matches BDF
*/
static int
{
/* if there is iommu private, get it */
mdb_warn("failed to read iommu private at %p\n",
return (WALK_ERR);
}
} else {
mdb_warn("More than one devinfo node matches "
"a single pci device. Aborting devinfo "
"lookup\n");
return (WALK_ERR);
}
}
}
return (WALK_NEXT);
}
/*
* dcmd: bdf2devinfo
*/
static int
{
if (iommu_supported() == B_FALSE)
return (DCMD_OK);
return (DCMD_USAGE);
}
for (i = 0; i < 4; i++) {
}
mdb_warn("invalid pci segment, bus, device, function"
return (DCMD_USAGE);
}
mdb_warn("failed to read 'top_devinfo'\n");
return (DCMD_ERR);
}
if (mdb_pwalk("devinfo",
mdb_warn("couldn't walk devinfo tree\n");
return (DCMD_ERR);
}
mdb_printf("No devinfo node found for %x:%x:%x:%x\n",
return (DCMD_OK);
}
/*
* dcmd: iomudip2dom
*/
static int
{
if (iommu_supported() == B_FALSE)
return (DCMD_OK);
return (DCMD_USAGE);
}
/* read iommu private */
mdb_warn("failed to read iommu private structure for "
"devinfo node at %p\n", addr);
return (DCMD_ERR);
}
/* read domain */
} else {
mdb_printf("No domain dedicated for this device\n");
}
return (DCMD_OK);
}
static const mdb_dcmd_t dcmds[] = {
{ "iomunits", NULL,
"list addresses of software state structure for all IOMMUs",
iomunits },
{ "iomuprt", "?",
"given an IOMMU's state structure address, print its contents",
iomuprt},
{ "iomudoms", NULL,
"list addresses of all IOMMU domain software structures",
iomudoms },
{ "iomudomprt", "?",
"given an IOMMU's domain struct address, print its contents",
iomudomprt },
{ "iomurmrrs", NULL,
"list addresses of all Intel IOMMU RMRR software structures",
iomurmrrs },
{ "iomurmrrprt", NULL,
"given an IOMMU RMRR structure address, print its contents",
iomurmrrprt },
{ "iomuvtop", "?<iova>",
"print physical address of an IO virtual address",
{ "bdf2devinfo", "[segment] [bus] [dev] [func]",
bdf2devinfo },
{ "iomudip2dom", "?",
"given a devinfo node, print the address of its IOMMU domain",
iomudip2dom },
{ NULL }
};
static const mdb_walker_t walkers[] = {
{ NULL }
};
static const mdb_modinfo_t modinfo = {
};
const mdb_modinfo_t *
_mdb_init(void)
{
/* check to see if kernel supports iommu */
}
}
return (&modinfo);
}