6880834 Intel IOMMU needs mdb support
authorVikram Hegde <Vikram.Hegde@Sun.COM>
Mon, 14 Sep 2009 21:48:21 -0700
changeset 10569 4d3eb0bc0c83
parent 10568 05d6365b5963
child 10570 c21442694040
6880834 Intel IOMMU needs mdb support
usr/src/cmd/mdb/common/modules/rootnex/intel_iommu.c
usr/src/cmd/mdb/intel/amd64/Makefile
usr/src/cmd/mdb/intel/amd64/rootnex/Makefile
usr/src/cmd/mdb/intel/ia32/Makefile
usr/src/cmd/mdb/intel/ia32/rootnex/Makefile
usr/src/pkgdefs/SUNWmdb/prototype_i386
usr/src/pkgdefs/SUNWmdbr/prototype_i386
usr/src/uts/i86pc/io/intel_iommu.c
usr/src/uts/i86pc/sys/dmar_acpi.h
usr/src/uts/i86pc/sys/intel_iommu.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/rootnex/intel_iommu.c	Mon Sep 14 21:48:21 2009 -0700
@@ -0,0 +1,883 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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/list.h>
+#include <sys/note.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 boolean_t iommu_support = B_FALSE;
+
+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
+print_device_scope_cb(uintptr_t addr, pci_dev_scope_t *devs, void *cbdata)
+{
+	_NOTE(ARGUNUSED(addr))
+
+	mdb_printf((char *)cbdata);
+	mdb_printf("BDF[%x:%x:%x],type[%x]\n",
+	    devs->pds_bus,
+	    devs->pds_dev,
+	    devs->pds_func,
+	    devs->pds_type);
+
+	return (WALK_NEXT);
+}
+
+/*
+ * print_device_scope()
+ *   a common function to print device scope of a drhd or rmrr
+ */
+static void
+print_device_scope(const char *pre, uintptr_t addr)
+{
+	mdb_pwalk("list",
+	    (mdb_walk_cb_t)print_device_scope_cb, (void *)pre, addr);
+}
+
+/*
+ * 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
+parse_hw_capa(uint64_t capa)
+{
+	char string[128];
+	size_t len;
+
+	strcpy(string, "  Hardware Capability:\t\t");
+	if (IOMMU_CAP_GET_DRD(capa))
+		strcat(string, "DRD ");
+	if (IOMMU_CAP_GET_DWD(capa))
+		strcat(string, "DWD ");
+	if (IOMMU_CAP_GET_PSI(capa))
+		strcat(string, "PSI ");
+	if (IOMMU_CAP_GET_ISOCH(capa))
+		strcat(string, "ISOCH ");
+	if (IOMMU_CAP_GET_ZLR(capa))
+		strcat(string, "ZLR ");
+	if (IOMMU_CAP_GET_CM(capa))
+		strcat(string, "CM ");
+	if (IOMMU_CAP_GET_PHMR(capa))
+		strcat(string, "PHMR ");
+	if (IOMMU_CAP_GET_PLMR(capa))
+		strcat(string, "PLMR ");
+	if (IOMMU_CAP_GET_RWBF(capa))
+		strcat(string, "RWBF ");
+	if (IOMMU_CAP_GET_AFL(capa))
+		strcat(string, "AFL ");
+
+	len = strlen(string);
+	if ((len > 1) &&
+	    (string[len - 1] == ' '))
+		string[len - 1] = 0;
+
+	strcat(string, "\n");
+	mdb_printf(string);
+}
+
+static void
+parse_hw_excapa(uint64_t excapa)
+{
+	char string[128];
+	size_t len;
+
+	strcpy(string, "  Hardware Ex-Capability:\t");
+	if (IOMMU_ECAP_GET_SC(excapa))
+		strcat(string, "SC ");
+	if (IOMMU_ECAP_GET_PT(excapa))
+		strcat(string, "PT ");
+	if (IOMMU_ECAP_GET_CH(excapa))
+		strcat(string, "CH ");
+	if (IOMMU_ECAP_GET_EIM(excapa))
+		strcat(string, "EIM ");
+	if (IOMMU_ECAP_GET_IR(excapa))
+		strcat(string, "IR ");
+	if (IOMMU_ECAP_GET_DI(excapa))
+		strcat(string, "DI ");
+	if (IOMMU_ECAP_GET_QI(excapa))
+		strcat(string, "QI ");
+	if (IOMMU_ECAP_GET_C(excapa))
+		strcat(string, "C ");
+
+	len = strlen(string);
+	if ((len > 1) &&
+	    (string[len - 1] == ' '))
+		string[len - 1] = 0;
+
+	strcat(string, "\n");
+	mdb_printf(string);
+}
+
+typedef enum {
+	ERROR_SCOPE,
+	INCLUDE_ALL_SCOPE,
+	DEV_SCOPE
+} 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
+print_iommu_state(intel_iommu_state_t *iommu, drhd_info_t *drhd)
+{
+	if ((iommu == NULL) || (drhd == NULL)) {
+		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",
+	    (iommu->iu_enabled & DMAR_ENABLE) ? "Enabled" : "Disabled");
+	mdb_printf("  Queued Invalid:\t\t%s\n",
+	    (iommu->iu_enabled & QINV_ENABLE) ? "Enabled" : "Disabled");
+	mdb_printf("  Interrupt remapping:\t\t%s\n",
+	    (iommu->iu_enabled & INTRR_ENABLE) ? "Enabled" : "Disabled");
+	mdb_printf("  Register Physical Address:\t%p\n",
+	    (uintptr_t)drhd->di_reg_base);
+	mdb_printf("  Register Virtual Address:\t%p\n",
+	    (uintptr_t)iommu->iu_reg_address);
+	parse_hw_capa(iommu->iu_capability);
+	parse_hw_excapa(iommu->iu_excapability);
+	mdb_printf("  Root Entry Table:\t\t%p\n",
+	    (uintptr_t)iommu->iu_root_entry_paddr);
+	mdb_printf("  Guest Address Width:\t\t%d\n", iommu->iu_gaw);
+	mdb_printf("  Adjust Guest Address Width:\t%d\n", iommu->iu_agaw);
+	mdb_printf("  Page Table Level:\t\t%d\n", iommu->iu_level);
+	mdb_printf("  Max Domain Supported:\t\t%d\n", iommu->iu_max_domain);
+	mdb_printf("  System Coherence:\t\t%s\n",
+	    iommu->iu_coherency ? "Yes" : "No");
+	mdb_printf("  Include All unit:\t\t%s\n",
+	    drhd->di_include_all ? "Yes" : "No");
+	mdb_printf("  Devinfo Node:\t\t\t%p\n",
+	    (intptr_t)drhd->di_dip);
+
+	if (iommu->iu_enabled & QINV_ENABLE) {
+		struct inv_queue_state qi_state;
+		if (iommu->iu_inv_queue &&
+		    mdb_vread(&qi_state, sizeof (qi_state),
+		    (intptr_t)iommu->iu_inv_queue) == sizeof (qi_state)) {
+			mdb_printf("  Qinv Table:\t\t\tpaddr:%p, "
+			    "vaddr:%p, size:%x\n",
+			    (uintptr_t)qi_state.iq_table.paddr,
+			    (uintptr_t)qi_state.iq_table.vaddr,
+			    qi_state.iq_table.size);
+			mdb_printf("  Sync Table:\t\t\tpaddr:%p, "
+			    "vaddr:%p, size:%x\n",
+			    (uintptr_t)qi_state.iq_sync.paddr,
+			    (uintptr_t)qi_state.iq_sync.vaddr,
+			    qi_state.iq_sync.size);
+		} else {
+			mdb_warn("failed to read iommu invalidation "
+			    "queue state at %p\n",
+			    (uintptr_t)iommu->iu_inv_queue);
+			return (ERROR_SCOPE);
+		}
+	}
+
+	return (drhd->di_include_all ? INCLUDE_ALL_SCOPE : DEV_SCOPE);
+}
+
+/*
+ * dcmd: iomuprt
+ */
+static int
+iomuprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	_NOTE(ARGUNUSED(argv))
+	intel_iommu_state_t iommu;
+	drhd_info_t drhd;
+
+	if (iommu_supported() == B_FALSE)
+		return (DCMD_OK);
+
+	if ((argc != 0) || !(flags & DCMD_ADDRSPEC))
+		return (DCMD_USAGE);
+
+	if (!DCMD_HDRSPEC(flags))
+		mdb_printf("\n");
+
+	if ((mdb_vread(&iommu, sizeof (iommu), addr) == sizeof (iommu)) &&
+	    (iommu.iu_drhd != NULL) &&
+	    (mdb_vread(&drhd, sizeof (drhd),
+	    (intptr_t)iommu.iu_drhd) == sizeof (drhd))) {
+		switch (print_iommu_state(&iommu, &drhd)) {
+		case DEV_SCOPE:
+			/*
+			 * Use actual address of list_t in kernel for walker
+			 */
+			print_device_scope("  Device Scope:\t\t\t",
+			    (uintptr_t)((char *)iommu.iu_drhd +
+			    offsetof(drhd_info_t, di_dev_list)));
+			break;
+		case ERROR_SCOPE:
+			return (DCMD_ERR);
+		default:
+			break;
+		}
+	} else {
+		mdb_warn("failed to read iommu state at %p\n", addr);
+		return (DCMD_ERR);
+	}
+
+	return (DCMD_OK);
+}
+
+/*
+ * print_iommu_addr()
+ * callback to print addresses of IOMMU unit software structures
+ */
+static int
+print_iommu_addr(uintptr_t addr, intel_iommu_state_t *ip, void *cbdata)
+{
+	_NOTE(ARGUNUSED(cbdata))
+	_NOTE(ARGUNUSED(ip))
+	intel_iommu_state_t iommu;
+
+	if (mdb_vread(&iommu, sizeof (iommu), addr) != sizeof (iommu)) {
+		mdb_warn("failed to read IOMMU structure at %p\n", addr);
+		return (WALK_ERR);
+	}
+
+	mdb_printf("%p\n", addr);
+
+	return (WALK_NEXT);
+}
+
+/*
+ * dcmd: iomunits
+ */
+static int
+iomunits(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	_NOTE(ARGUNUSED(addr))
+	_NOTE(ARGUNUSED(argv))
+	GElf_Sym sym;
+
+	if (iommu_supported() == B_FALSE)
+		return (DCMD_OK);
+
+	if ((flags & DCMD_ADDRSPEC) || (argc != 0)) {
+		return (DCMD_USAGE);
+	}
+
+	if (mdb_lookup_by_name("iommu_states", &sym) == -1) {
+		mdb_warn("failed to find symbol iommu_states\n");
+		return (DCMD_ERR);
+	}
+
+	addr = (uintptr_t)sym.st_value;
+	if (mdb_pwalk("list", (mdb_walk_cb_t)print_iommu_addr, NULL, addr)) {
+		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
+print_domain_state(dmar_domain_state_t *domain)
+{
+	if (domain == NULL) {
+		mdb_warn("Internal error: NULL domain pointer passed\n");
+		return;
+	}
+
+	mdb_printf("IOMMU device domain:\n");
+	mdb_printf("Domain ID:\t\t%d\n", domain->dm_domain_id);
+	mdb_printf("Bind IOMMU:\t\t%p\n", (uintptr_t)domain->dm_iommu);
+	mdb_printf("DVMA vmem:\t\t%p\n",
+	    (uintptr_t)domain->dm_dvma_map);
+	mdb_printf("Top Level Page Table:\t%p\n",
+	    (uintptr_t)domain->dm_page_table_paddr);
+	mdb_printf("Identity Mapping:\t\t%s\n",
+	    domain->dm_identity ? "YES" : "NO");
+}
+
+/*
+ * dcmd: iomudomprt
+ */
+static int
+iomudomprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	_NOTE(ARGUNUSED(argv))
+	dmar_domain_state_t domain;
+
+	if (iommu_supported() == B_FALSE)
+		return (DCMD_OK);
+
+	if ((argc != 0) || !(flags & DCMD_ADDRSPEC))
+		return (DCMD_USAGE);
+
+	if (!DCMD_HDRSPEC(flags))
+		mdb_printf("\n");
+
+	if (mdb_vread(&domain, sizeof (domain), addr) == sizeof (domain)) {
+		print_domain_state(&domain);
+	} else {
+		mdb_warn("failed to read domain at %p\n", addr);
+		return (DCMD_ERR);
+	}
+
+	return (DCMD_OK);
+}
+
+/*
+ * print_domain_addr()
+ */
+static int
+print_domain_addr(uintptr_t addr, dmar_domain_state_t *domp, void *cbdata)
+{
+	_NOTE(ARGUNUSED(domp))
+	_NOTE(ARGUNUSED(cbdata))
+	dmar_domain_state_t domain;
+
+	if (iommu_supported() == B_FALSE)
+		return (WALK_NEXT);
+
+	if (mdb_vread(&domain, sizeof (domain), addr) != sizeof (domain)) {
+		mdb_warn("failed to read domain at %p\n", addr);
+		return (WALK_ERR);
+	}
+
+	mdb_printf("%p\n", addr);
+
+	return (WALK_NEXT);
+}
+
+/*
+ * dcmd: iomudoms
+ */
+static int
+iomudoms(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	_NOTE(ARGUNUSED(addr))
+	_NOTE(ARGUNUSED(argv))
+	GElf_Sym sym;
+
+	if (iommu_supported() == B_FALSE)
+		return (DCMD_OK);
+
+	if ((flags & DCMD_ADDRSPEC) || (argc != 0)) {
+		return (DCMD_USAGE);
+	}
+
+	if (mdb_lookup_by_name("domain_states", &sym) == -1) {
+		mdb_warn("failed to find symbol domain_states\n");
+		return (DCMD_ERR);
+	}
+
+	addr = (uintptr_t)sym.st_value;
+	if (mdb_pwalk("list", (mdb_walk_cb_t)print_domain_addr, NULL, addr))
+		return (DCMD_ERR);
+	return (DCMD_OK);
+}
+
+/*
+ * print_rmrr_info()
+ */
+static void
+print_rmrr_info(rmrr_info_t *rmrr)
+{
+	mdb_printf("Reserved Memory Region Reporting:\n");
+	mdb_printf("  Segment:\t%d\n", rmrr->ri_segment);
+	mdb_printf("  BaseAddr:\t%p\n", (uintptr_t)rmrr->ri_baseaddr);
+	mdb_printf("  LimiAddr:\t%p\n", (uintptr_t)rmrr->ri_limiaddr);
+}
+
+/*
+ * print_rmrr_addr()
+ *   list walk callback for list_rmrr
+ */
+static int
+print_rmrr_addr(uintptr_t addr, rmrr_info_t *rp, void *cbdata)
+{
+	_NOTE(ARGUNUSED(rp))
+	_NOTE(ARGUNUSED(cbdata))
+	rmrr_info_t rmrr;
+
+	if (mdb_vread(&rmrr, sizeof (rmrr), addr) != sizeof (rmrr)) {
+		mdb_warn("failed to read RMRR structure at %p\n", addr);
+		return (WALK_ERR);
+	}
+
+	mdb_printf("%p\n", addr);
+
+	return (WALK_NEXT);
+}
+
+/*
+ * dcmd: iomurmrrs
+ */
+static int
+iomurmrrs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	_NOTE(ARGUNUSED(addr))
+	_NOTE(ARGUNUSED(argv))
+	GElf_Sym sym;
+
+	if (iommu_supported() == B_FALSE)
+		return (DCMD_OK);
+
+	if ((flags & DCMD_ADDRSPEC) || (argc != 0)) {
+		return (DCMD_USAGE);
+	}
+
+	if (mdb_lookup_by_name("rmrr_states", &sym) == -1) {
+		mdb_warn("failed to find symbol rmrr_states\n");
+		return (DCMD_ERR);
+	}
+
+	addr = (uintptr_t)sym.st_value;
+	if (mdb_pwalk("list", (mdb_walk_cb_t)print_rmrr_addr, NULL, addr))
+		return (DCMD_ERR);
+	return (DCMD_OK);
+}
+
+/*
+ * dcmd: iomurmrrprt: Given an RMRR address print the RMRR.
+ */
+static int
+iomurmrrprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	_NOTE(ARGUNUSED(argv))
+	uintptr_t dev_list_addr;
+	rmrr_info_t rmrr;
+
+	if (iommu_supported() == B_FALSE)
+		return (DCMD_OK);
+
+	if (!(flags & DCMD_ADDRSPEC) || (argc != 0)) {
+		return (DCMD_USAGE);
+	}
+
+	if (mdb_vread(&rmrr, sizeof (rmrr), addr) != sizeof (rmrr)) {
+		mdb_warn("failed to read RMRR structure at %p\n", addr);
+		return (DCMD_ERR);
+	}
+
+	dev_list_addr = addr + offsetof(rmrr_info_t, ri_dev_list);
+	print_rmrr_info(&rmrr);
+	print_device_scope("  DevScope:\t", dev_list_addr);
+
+	return (DCMD_OK);
+}
+
+/*
+ * iova_level_to_offset()
+ *   Given an iova and page table level, return the corresponding offset
+ */
+static int
+iova_level_to_offset(uintptr_t iova, int level)
+{
+	int start, offset;
+
+	start = (level - 1) * IOMMU_LEVEL_STRIDE + IOMMU_PAGE_SHIFT;
+	offset = (iova >> start) & IOMMU_LEVEL_OFFSET;
+
+	return (offset);
+}
+
+/*
+ * iovtp_read_table_entry()
+ */
+static int
+iovtp_read_table_entry(uint64_t ptaddr, size_t offset,
+    void *ent_buf, size_t ent_size)
+{
+	if (mdb_pread(ent_buf, ent_size, ptaddr + offset * ent_size)
+	    != ent_size) {
+		return (B_FALSE);
+	} else {
+		return (B_TRUE);
+	}
+}
+
+/*
+ * dcmd: iomuvtop
+ */
+static int
+iomuvtop(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	iommu_private_t private;
+	dmar_domain_state_t domain;
+	struct dev_info dinfo;
+	intel_iommu_state_t iommu;
+	int i, level, offset;
+	uintptr_t iova;
+	uint64_t ptaddr, ptentr;
+	int bus, devfn;
+
+	struct root_context_entry {
+		uint64_t asr;
+		uint64_t pro;
+	} rc_entry;
+
+	if (iommu_supported() == B_FALSE)
+		return (DCMD_OK);
+
+	if (!(flags & DCMD_ADDRSPEC) || (argc != 1)) {
+		return (DCMD_USAGE);
+	}
+
+	iova = (argv[0].a_type == MDB_TYPE_IMMEDIATE) ?
+	    (uintptr_t)argv[0].a_un.a_val :
+	    (uintptr_t)mdb_strtoull(argv->a_un.a_str);
+
+	/* read iommu private */
+	if ((mdb_vread(&dinfo, sizeof (dinfo), addr) != sizeof (dinfo)) ||
+	    (dinfo.devi_iommu_private == NULL) ||
+	    (mdb_vread(&private, sizeof (private),
+	    (uintptr_t)dinfo.devi_iommu_private) != sizeof (private))) {
+		mdb_warn("failed to read iommu private structure for "
+		    "devinfo node at address %p\n", addr);
+		return (DCMD_ERR);
+	}
+
+	bus = private.idp_bus;
+	devfn = private.idp_devfn;
+
+	/* read domain */
+	if (private.idp_intel_domain == NULL) {
+		mdb_printf("IOMMU domain for this device has not yet been "
+		    "allocated.\nNo mapped physical address for this vaddr\n");
+		return (DCMD_OK);
+	}
+
+	if (mdb_vread(&domain, sizeof (domain),
+	    (uintptr_t)private.idp_intel_domain)
+	    != sizeof (domain)) {
+		mdb_warn("failed to read domain structure at %p\n",
+		    (uintptr_t)private.idp_intel_domain);
+		return (DCMD_ERR);
+	}
+
+	/* read iommu */
+	if (mdb_vread(&iommu, sizeof (iommu), (uintptr_t)domain.dm_iommu)
+	    != sizeof (iommu)) {
+		mdb_warn("failed to read iommu structure at %p\n",
+		    (uintptr_t)domain.dm_iommu);
+		return (DCMD_ERR);
+	}
+
+	mdb_printf("Level\tPageTableAddress\tOffset\tPageTableEntry\n");
+
+	/* walk and print root context tabls */
+	ptaddr = iommu.iu_root_entry_paddr;
+	if (iovtp_read_table_entry(ptaddr, bus, &rc_entry, sizeof (rc_entry))
+	    == B_FALSE) {
+		mdb_warn("failed to read root table entry for bus %x "
+		    "at %p\n", bus, (uintptr_t)ptaddr);
+		return (DCMD_ERR);
+	}
+	mdb_printf("Root\t%p\t\t%x\tlow :%p\n", (uintptr_t)ptaddr,
+	    bus, (uintptr_t)rc_entry.asr);
+	mdb_printf("Root\t%p\t\t%x\thigh:%p\n", (uintptr_t)ptaddr,
+	    bus, (uintptr_t)rc_entry.pro);
+
+	ptaddr = rc_entry.asr & IOMMU_PAGE_MASK;
+	if (iovtp_read_table_entry(ptaddr, devfn, &rc_entry, sizeof (rc_entry))
+	    == B_FALSE) {
+		mdb_warn("failed to read context table entry for "
+		    "device-function %x at %p\n", devfn, (uintptr_t)ptaddr);
+		return (DCMD_ERR);
+	}
+	mdb_printf("Context\t%p\t\t%x\tlow :%p\n", (uintptr_t)ptaddr,
+	    devfn, (uintptr_t)rc_entry.asr);
+	mdb_printf("Context\t%p\t\t%x\thigh:%p\n", (uintptr_t)ptaddr,
+	    devfn, (uintptr_t)rc_entry.pro);
+
+	/* walk and print page tables */
+	ptaddr = rc_entry.asr & IOMMU_PAGE_MASK;
+
+	/*
+	 * Toppest level page table address should be the same
+	 * as that stored in domain structure
+	 */
+	if (ptaddr != domain.dm_page_table_paddr) {
+		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);
+	}
+
+	level = iommu.iu_level;
+	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);
+		}
+		offset = iova_level_to_offset(iova, i);
+		if (iovtp_read_table_entry(ptaddr, offset, &ptentr,
+		    sizeof (ptentr)) == B_FALSE) {
+			mdb_warn("failed to read page table entry "
+			    "(level %d) at %p\n", i, (uintptr_t)ptaddr);
+			return (DCMD_ERR);
+		}
+		mdb_printf("%x\t%p\t\t%x\t%p\n", i, (uintptr_t)ptaddr,
+		    offset, (uintptr_t)ptentr);
+		ptaddr = ptentr & IOMMU_PAGE_MASK;
+	}
+
+	return (DCMD_OK);
+}
+
+typedef struct bdf_cb_data {
+	int	dc_seg;
+	int	dc_bus;
+	int	dc_devfunc;
+	int	dc_match;
+} bdf_cb_data_t;
+
+/*
+ * match_bdf()
+ *   call back function that matches BDF
+ */
+static int
+match_bdf(uintptr_t addr, struct dev_info *dev, bdf_cb_data_t *cbdata)
+{
+	_NOTE(ARGUNUSED(addr))
+	/* if there is iommu private, get it */
+	if (dev->devi_iommu_private != NULL) {
+		iommu_private_t private;
+		if (mdb_vread((void*)&private, sizeof (private),
+		    (uintptr_t)dev->devi_iommu_private) != sizeof (private)) {
+			mdb_warn("failed to read iommu private at %p\n",
+			    (uintptr_t)dev->devi_iommu_private);
+			return (WALK_ERR);
+		}
+
+		if (private.idp_seg == cbdata->dc_seg &&
+		    private.idp_bus == cbdata->dc_bus &&
+		    private.idp_devfn == cbdata->dc_devfunc) {
+			if (cbdata->dc_match == 0) {
+				mdb_printf("%p\n", addr);
+				cbdata->dc_match = 1;
+			} 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
+bdf2devinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	_NOTE(ARGUNUSED(addr))
+	bdf_cb_data_t cbdata;
+	uint_t i, bdf[4];
+
+	if (iommu_supported() == B_FALSE)
+		return (DCMD_OK);
+
+	if ((flags & DCMD_ADDRSPEC) || (argc != 4)) {
+		return (DCMD_USAGE);
+	}
+
+	for (i = 0; i < 4; i++) {
+		bdf[i] = (argv[i].a_type == MDB_TYPE_IMMEDIATE) ?
+		    (int)argv[i].a_un.a_val :
+		    (int)mdb_strtoull(argv[i].a_un.a_str);
+	}
+
+	if ((bdf[0] != 0) || (bdf[1] > 255) || (bdf[2] > 31) || (bdf[3] > 7)) {
+		mdb_warn("invalid pci segment, bus, device, function"
+		    "tuple (%x, %x, %x, %x)\n", bdf[0], bdf[1], bdf[2], bdf[3]);
+		return (DCMD_USAGE);
+	}
+
+
+	cbdata.dc_seg = bdf[0];
+	cbdata.dc_bus = bdf[1];
+	cbdata.dc_devfunc = bdf[2] << 3 | bdf[3];
+	cbdata.dc_match = 0;
+
+	if (mdb_readvar(&addr, "top_devinfo") == -1) {
+		mdb_warn("failed to read 'top_devinfo'\n");
+		return (DCMD_ERR);
+	}
+
+	if (mdb_pwalk("devinfo",
+	    (mdb_walk_cb_t)match_bdf, &cbdata, addr)) {
+		mdb_warn("couldn't walk devinfo tree\n");
+		return (DCMD_ERR);
+	}
+
+	if (cbdata.dc_match == 0)
+		mdb_printf("No devinfo node found for %x:%x:%x:%x\n",
+		    bdf[0], bdf[1], bdf[2], bdf[3]);
+
+	return (DCMD_OK);
+}
+
+/*
+ * dcmd: iomudip2dom
+ */
+static int
+iomudip2dom(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	_NOTE(ARGUNUSED(argv))
+	struct dev_info dinfo;
+	iommu_private_t private;
+
+	if (iommu_supported() == B_FALSE)
+		return (DCMD_OK);
+
+	if (!(flags & DCMD_ADDRSPEC) || (argc != 0)) {
+		return (DCMD_USAGE);
+	}
+
+	/* read iommu private */
+	if ((mdb_vread(&dinfo, sizeof (dinfo), addr) != sizeof (dinfo)) ||
+	    (dinfo.devi_iommu_private == NULL) ||
+	    (mdb_vread(&private, sizeof (private),
+	    (uintptr_t)dinfo.devi_iommu_private) != sizeof (private))) {
+		mdb_warn("failed to read iommu private structure for "
+		    "devinfo node at %p\n", addr);
+		return (DCMD_ERR);
+	}
+
+	/* read domain */
+	if (private.idp_intel_domain != NULL) {
+		mdb_printf("%p\n", (uintptr_t)private.idp_intel_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",
+		iomuvtop, iomuvtop_help },
+	{ "bdf2devinfo", "[segment] [bus] [dev] [func]",
+		"given its pci segment/bus/dev/func, print the devinfo node",
+		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 = {
+	MDB_API_VERSION, dcmds, walkers
+};
+
+const mdb_modinfo_t *
+_mdb_init(void)
+{
+	GElf_Sym sym;
+
+	/* check to see if kernel supports iommu */
+	if (mdb_lookup_by_name("intel_iommu_support", &sym) != -1) {
+		if (mdb_vread(&iommu_support, sizeof (boolean_t),
+		    (uintptr_t)sym.st_value) != sizeof (boolean_t)) {
+			iommu_support = B_FALSE;
+		}
+	}
+
+	return (&modinfo);
+}
--- a/usr/src/cmd/mdb/intel/amd64/Makefile	Mon Sep 14 21:48:21 2009 -0700
+++ b/usr/src/cmd/mdb/intel/amd64/Makefile	Mon Sep 14 21:48:21 2009 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# 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
 # or http://www.opensolaris.org/os/licensing.
@@ -20,14 +19,13 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 include ../../Makefile.common
 
-MODULES = $(COMMON_MODULES_PROC) $(COMMON_MODULES_KVM) uhci
+MODULES = $(COMMON_MODULES_PROC) $(COMMON_MODULES_KVM) uhci rootnex
 
 $(CLOSED_BUILD)MODULES += \
 	$(CLOSED_COMMON_MODULES_KVM:%=$(CLOSED)/cmd/mdb/intel/amd64/%)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/amd64/rootnex/Makefile	Mon Sep 14 21:48:21 2009 -0700
@@ -0,0 +1,38 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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.
+
+MODULE = rootnex.so
+MDBTGT = kvm
+
+MODSRCS = intel_iommu.c
+
+include ../../../../Makefile.cmd
+include ../../../../Makefile.cmd.64
+include ../../Makefile.amd64
+include ../../../Makefile.module
+
+CPPFLAGS += -I$(SRC)/uts/i86pc
--- a/usr/src/cmd/mdb/intel/ia32/Makefile	Mon Sep 14 21:48:21 2009 -0700
+++ b/usr/src/cmd/mdb/intel/ia32/Makefile	Mon Sep 14 21:48:21 2009 -0700
@@ -19,15 +19,14 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 include ../../Makefile.common
 
 MODULES = $(COMMON_MODULES_PROC) $(COMMON_MODULES_PROC_32BIT) \
-    $(COMMON_MODULES_KVM) uhci
+    $(COMMON_MODULES_KVM) uhci rootnex
 
 $(CLOSED_BUILD)MODULES += \
 	$(CLOSED_COMMON_MODULES_KVM:%=$(CLOSED)/cmd/mdb/intel/ia32/%)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/ia32/rootnex/Makefile	Mon Sep 14 21:48:21 2009 -0700
@@ -0,0 +1,37 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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.
+
+MODULE = rootnex.so
+MDBTGT = kvm
+
+MODSRCS = intel_iommu.c
+
+include ../../../../Makefile.cmd
+include ../../Makefile.ia32
+include ../../../Makefile.module
+
+CPPFLAGS += -I$(SRC)/uts/i86pc
--- a/usr/src/pkgdefs/SUNWmdb/prototype_i386	Mon Sep 14 21:48:21 2009 -0700
+++ b/usr/src/pkgdefs/SUNWmdb/prototype_i386	Mon Sep 14 21:48:21 2009 -0700
@@ -81,6 +81,7 @@
 f none usr/lib/mdb/kvm/amd64/nfs.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/ptm.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/random.so 555 root sys
+f none usr/lib/mdb/kvm/amd64/rootnex.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/s1394.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/sata.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/scsi_vhci.so 555 root sys
@@ -117,6 +118,7 @@
 f none usr/lib/mdb/kvm/nfs.so 555 root sys
 f none usr/lib/mdb/kvm/ptm.so 555 root sys
 f none usr/lib/mdb/kvm/random.so 555 root sys
+f none usr/lib/mdb/kvm/rootnex.so 555 root sys
 f none usr/lib/mdb/kvm/s1394.so 555 root sys
 f none usr/lib/mdb/kvm/sata.so 555 root sys
 f none usr/lib/mdb/kvm/scsi_vhci.so 555 root sys
--- a/usr/src/pkgdefs/SUNWmdbr/prototype_i386	Mon Sep 14 21:48:21 2009 -0700
+++ b/usr/src/pkgdefs/SUNWmdbr/prototype_i386	Mon Sep 14 21:48:21 2009 -0700
@@ -50,6 +50,7 @@
 f none kernel/kmdb/amd64/nfs 555 root sys
 f none kernel/kmdb/amd64/ptm 555 root sys
 f none kernel/kmdb/amd64/random 555 root sys
+f none kernel/kmdb/amd64/rootnex 555 root sys
 f none kernel/kmdb/amd64/s1394 555 root sys
 f none kernel/kmdb/amd64/sata 555 root sys
 f none kernel/kmdb/amd64/scsi_vhci 555 root sys
@@ -85,6 +86,7 @@
 f none kernel/kmdb/nfs 555 root sys
 f none kernel/kmdb/ptm 555 root sys
 f none kernel/kmdb/random 555 root sys
+f none kernel/kmdb/rootnex 555 root sys
 f none kernel/kmdb/s1394 555 root sys
 f none kernel/kmdb/sata 555 root sys
 f none kernel/kmdb/scsi_vhci 555 root sys
--- a/usr/src/uts/i86pc/io/intel_iommu.c	Mon Sep 14 21:48:21 2009 -0700
+++ b/usr/src/uts/i86pc/io/intel_iommu.c	Mon Sep 14 21:48:21 2009 -0700
@@ -92,10 +92,14 @@
 
 /*
  * internal variables
- *   iommu_state	- the list of iommu structures
+ *   iommu_states	- the list of iommu
+ *   domain_states	- the list of domain
+ *   rmrr_states	- the list of rmrr
  *   page_num		- the count of pages for iommu page tables
  */
 static list_t iommu_states;
+static list_t domain_states;
+static list_t rmrr_states;
 static uint_t page_num;
 
 /*
@@ -2042,6 +2046,8 @@
 		    offsetof(dvma_cache_node_t, node));
 	}
 
+	list_insert_tail(&domain_states, domain);
+
 	return (DDI_SUCCESS);
 }
 
@@ -2759,6 +2765,7 @@
 		if (list_is_empty(&(dmar_info->dmari_rmrr[i])))
 			break;
 		for_each_in_list(&(dmar_info->dmari_rmrr[i]), rmrr) {
+			list_insert_tail(&rmrr_states, rmrr);
 			build_single_rmrr_identity_map(rmrr);
 		}
 	}
@@ -3030,6 +3037,10 @@
 	 */
 	list_create(&iommu_states, sizeof (intel_iommu_state_t),
 	    offsetof(intel_iommu_state_t, node));
+	list_create(&domain_states, sizeof (dmar_domain_state_t),
+	    offsetof(dmar_domain_state_t, node));
+	list_create(&rmrr_states, sizeof (rmrr_info_t),
+	    offsetof(rmrr_info_t, node4states));
 
 	root_devinfo = ddi_root_node();
 	ASSERT(root_devinfo);
--- a/usr/src/uts/i86pc/sys/dmar_acpi.h	Mon Sep 14 21:48:21 2009 -0700
+++ b/usr/src/uts/i86pc/sys/dmar_acpi.h	Mon Sep 14 21:48:21 2009 -0700
@@ -160,6 +160,7 @@
  */
 typedef struct rmrr_info {
 	list_node_t	node;
+	list_node_t	node4states;
 	uint16_t	ri_segment;
 	uint64_t	ri_baseaddr;
 	uint64_t	ri_limiaddr;
--- a/usr/src/uts/i86pc/sys/intel_iommu.h	Mon Sep 14 21:48:21 2009 -0700
+++ b/usr/src/uts/i86pc/sys/intel_iommu.h	Mon Sep 14 21:48:21 2009 -0700
@@ -145,20 +145,29 @@
 #define	IOMMU_CAP_GET_DWD(x)		(((x) >> 54) & 1)
 #define	IOMMU_CAP_GET_DRD(x)		(((x) >> 55) & 1)
 #define	IOMMU_CAP_GET_PSI(x)		(((x) >> 39) & 1)
+#define	IOMMU_CAP_GET_SPS(x)		(((x) >> 34) & 0xf)
+#define	IOMMU_CAP_GET_ISOCH(x)		(((x) >> 23) & 1)
+#define	IOMMU_CAP_GET_ZLR(x)		(((x) >> 22) & 1)
 #define	IOMMU_CAP_GET_MAMV(x)		(((x) >> 48) & 0x3f)
 #define	IOMMU_CAP_GET_CM(x)		(((x) >> 7) & 1)
+#define	IOMMU_CAP_GET_PHMR(x)		(((x) >> 6) & 1)
+#define	IOMMU_CAP_GET_PLMR(x)		(((x) >> 5) & 1)
 #define	IOMMU_CAP_GET_RWBF(x)		(((x) >> 4) & 1)
+#define	IOMMU_CAP_GET_AFL(x)		(((x) >> 3) & 1)
 #define	IOMMU_CAP_GET_FRO(x)		((((x) >> 24) & 0x3ff) * 16)
 #define	IOMMU_CAP_MGAW(x)		(((((uint64_t)x) >> 16) & 0x3f) + 1)
 #define	IOMMU_CAP_SAGAW(x)		(((x) >> 8) & 0x1f)
 #define	IOMMU_CAP_ND(x)			(1 << (((x) & 0x7) *2 + 4)) -1
 #define	IOMMU_ECAP_GET_IRO(x)		((((x) >> 8) & 0x3ff) << 4)
-#define	IOMMU_ECAP_GET_C(x)		((x) & 0x1)
 #define	IOMMU_ECAP_GET_MHMV(x)		((x >> 20) & 0xf)
+#define	IOMMU_ECAP_GET_SC(x)		((x) & 0x80)
+#define	IOMMU_ECAP_GET_PT(x)		((x) & 0x40)
+#define	IOMMU_ECAP_GET_CH(x)		((x) & 0x20)
 #define	IOMMU_ECAP_GET_EIM(x)		((x) & 0x10)
 #define	IOMMU_ECAP_GET_IR(x)		((x) & 0x8)
 #define	IOMMU_ECAP_GET_DI(x)		((x) & 0x4)
 #define	IOMMU_ECAP_GET_QI(x)		((x) & 0x2)
+#define	IOMMU_ECAP_GET_C(x)		((x) & 0x1)
 
 
 /* iotlb invalidation */
@@ -343,7 +352,7 @@
 
 typedef enum {
 	IEC_INV_GLOBAL = 0,
-	IEC_INV_INDEX,
+	IEC_INV_INDEX
 } iec_inv_g_t;
 
 /*
@@ -523,6 +532,7 @@
  * dm_identity		- does this domain identity mapped
  */
 typedef struct dmar_domain_state {
+	list_node_t		node;
 	uint_t			dm_domain_id;
 	intel_iommu_state_t	*dm_iommu;
 	vmem_t			*dm_dvma_map;