immu_dmar.c revision e03dceed3deb85ad561202c77277e701f763fa13
/*
* 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
*/
/*
* Portions Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2009, Intel Corporation.
* All rights reserved.
*/
#include <sys/sysmacros.h>
#include <sys/pci_cfgspace.h>
#include <sys/pci_impl.h>
#include <sys/bootconf.h>
#include <sys/int_fmtio.h>
#include <sys/iommulib.h>
/*
* internal global variables
*/
static char *dmar_raw; /* raw DMAR ACPI table */
/*
* global variables exported outside this file
*/
/* ######################################################################### */
/*
* helper functions to read the "raw" DMAR table
*/
static uint8_t
{
return (val);
}
static uint16_t
get_uint16(char *cp)
{
return (val);
}
static uint32_t
get_uint32(char *cp)
{
return (val);
}
static uint64_t
get_uint64(char *cp)
{
return (val);
}
static char *
{
return (str);
}
static void
{
if (list_is_empty(scope_list)) {
return;
}
}
}
static void
{
if (list_is_empty(drhd_list)) {
return;
}
}
}
static void
{
if (list_is_empty(rmrr_list)) {
return;
}
}
}
/*
* parse_scope()
* parse a scope structure in the "raw" table
*/
static scope_t *
parse_scope(char *shead)
{
char *phead;
int depth;
}
/* ok we got the device BDF */
return (scope);
}
/* setup the ioapic_drhd structure */
static void
ioapic_drhd_setup(void)
{
}
/* get ioapic source id for interrupt remapping */
static void
{
}
static ioapic_drhd_t *
{
break;
}
}
return (idt);
}
static void
ioapic_drhd_destroy(void)
{
}
}
/*
* parse_drhd()
* parse the drhd uints in dmar table
*/
static int
{
int seg;
int len;
char *shead;
"in DRHD unit in ACPI DMAR table", seg);
return (DDI_FAILURE);
}
/*
* parse each scope.
*/
return (DDI_FAILURE);
}
}
}
return (DDI_SUCCESS);
}
/*
* parse_rmrr()
* parse the rmrr units in dmar table
*/
static int
{
int seg;
int len;
char *shead;
"in RMRR unit in ACPI DMAR table", seg);
return (DDI_FAILURE);
}
/* RMRR region is [base,limit] */
"RMRR: base (%lx) > limit (%lx)",
return (DDI_SUCCESS);
}
/*
* parse each scope in RMRR
*/
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
#define TBL_OEM_ID_SZ (6)
#define TBL_OEM_TBLID_SZ (8)
/*
* parse the "raw" DMAR table and convert it
* into a useful form.
*/
static int
{
char *uhead;
int i;
char *unmstr;
/*
* do a sanity check. make sure the raw table
* has the right signature
*/
"signature != \"DMAR\"");
return (DDI_FAILURE);
}
/*
* the platform has intel iommu, create processed ACPI struct
*/
/*
* Note we explicitly show offsets for clarity
*/
/* XXX TO DO verify checksum of table */
/* create lists for DRHD and RMRR */
for (i = 0; i < IMMU_MAXSEG; i++) {
}
/*
* parse each unit. Currently only DRHD and RMRR types
* are parsed. We ignore all other types of units.
*/
switch (get_uint16(uhead)) {
case DMAR_DRHD:
goto failed;
}
break;
case DMAR_RMRR:
goto failed;
}
break;
case DMAR_ATSR:
unmstr = "ATSR";
break;
case DMAR_RHSA:
unmstr = "RHSA";
break;
default:
unmstr = "unknown unity type";
break;
}
if (unmstr) {
"skipping unsupported unit type %s", unmstr);
}
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static char *
scope_type(int devtype)
{
char *typestr;
switch (devtype) {
case DMAR_ENDPOINT:
typestr = "endpoint-device";
break;
case DMAR_SUBTREE:
typestr = "subtree-device";
break;
case DMAR_IOAPIC:
typestr = "IOAPIC";
break;
case DMAR_HPET:
typestr = "HPET";
break;
default:
typestr = "Unknown device";
break;
}
return (typestr);
}
static void
{
if (list_is_empty(scope_list))
return;
}
}
static void
{
if (list_is_empty(drhd_list))
return;
}
}
static void
{
if (list_is_empty(rmrr_list))
return;
}
}
/*
* print DMAR table
*/
static void
{
int i;
if (dmar_print == B_FALSE) {
return;
}
/* print the title */
/* print drhd list */
for (i = 0; i < IMMU_MAXSEG; i++) {
}
/* print rmrr list */
for (i = 0; i < IMMU_MAXSEG; i++) {
}
}
static void
{
struct ddi_parent_private_data *pdptr;
DEVI_SID_NODEID, &dip);
reg.regspec_bustype = 0;
/*
* update the reg properties
*
* reg property will be used for register
* set access
*
* refer to the bus_map of root nexus driver
* I/O or memory mapping:
*
* <bustype=0, addr=x, len=x>: memory
* <bustype=1, addr=x, len=x>: i/o
* <bustype>1, addr=0, len=x>: x86-compatibility i/o
*/
sizeof (struct regspec) / sizeof (int));
}
/*
* dmar_devinfos_create()
*
* create the dev_info node in the device tree,
* the info node is a nuxus child of the root
* nexus
*/
static void
{
char name[IMMU_MAXNAMELEN];
int i, unit;
for (i = 0; i < IMMU_MAXSEG; i++) {
if (list_is_empty(drhd_list))
continue;
"drhd%d,%d", i, unit);
}
}
}
static void
{
int count;
}
}
/*
* dmar_devi_destroy()
*
* destroy dev_info nodes for all drhd units
*/
static void
{
int i;
for (i = 0; i < IMMU_MAXSEG; i++) {
if (list_is_empty(drhd_list))
continue;
}
}
}
static int
{
/* rdip can be NULL */
}
}
static void
{
int i;
/* destroy lists for DRHD and RMRR */
for (i = 0; i < IMMU_MAXSEG; i++) {
}
/* free strings */
}
/*
* #########################################################################
* Functions exported by dmar.c
* This file deals with reading and processing the DMAR ACPI table
* #########################################################################
*/
/*
* immu_dmar_setup()
* Check if the system has a DMAR ACPI table. If yes, the system
* has Intel IOMMU hardware
*/
int
immu_dmar_setup(void)
{
"No DMAR ACPI table. No Intel IOMMU present\n");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* immu_dmar_parse()
* Called by immu.c to parse and convert "raw" ACPI DMAR table
*/
int
immu_dmar_parse(void)
{
/* we should already have found the "raw" table */
dmar_table = NULL;
/*
* parse DMAR ACPI table
*/
return (DDI_FAILURE);
}
/*
* create one devinfo for every drhd unit
* in the DMAR table
*/
/*
* print the dmar table if the debug option is set
*/
dmar_table = tbl;
return (DDI_SUCCESS);
}
void
immu_dmar_startup(void)
{
/* nothing to do */
}
void
immu_dmar_shutdown(void)
{
/* nothing to do */
}
void
immu_dmar_destroy(void)
{
dmar_table = NULL;
}
{
int i;
char oem_rev[IMMU_MAXNAMELEN];
/*
* Must be a minimum of 4
*/
if (nstrs < 4) {
return (B_FALSE);
}
for (i = 0; nstrs - i >= 4; i++) {
((char *)strptr == '\0' ||
((char *)strptr == '\0' ||
return (B_TRUE);
}
i += 3; /* for loops adds 1 as well, so only 3 here */
}
}
return (B_FALSE);
}
void
immu_dmar_rmrr_map(void)
{
int seg;
int e;
int count;
tbl = dmar_table;
/* called during boot, when kernel is single threaded. No lock */
/*
* for each segment, walk the rmrr list looking for an exact match
*/
rmrr)) {
/*
* try to match BDF *exactly* to a device scope.
*/
for (; scope;
immu_arg_t imarg = {0};
/* PCI endpoint devices only */
continue;
/* XXX should be optimized */
"No dip found for "
"bus=0x%x, dev=0x%x, func= 0x%x",
continue;
}
/*
* This address must be in the BIOS reserved
* map
*/
if (!address_in_memlist(bios_rsvd,
" not in BIOS reserved map",
}
/* XXX could be more efficient */
" is in physinstall map",
}
"IMMU: Mapping RMRR range "
/*
* dip may have unity domain or xlate domain
* If the former, PHYSICAL is returned else
* MAPPED is returned.
*/
ASSERT(e == DDI_DMA_MAPPED ||
e == DDI_DMA_USE_PHYSICAL);
}
}
}
}
immu_t *
{
int seg;
int tlevel;
int level;
tbl = dmar_table;
/*
* for each segment, walk the drhd list looking for an exact match
*/
drhd)) {
/*
* we are currently searching for exact matches so
* skip "include all" (catchall) and subtree matches
*/
continue;
/*
* try to match BDF *exactly* to a device scope.
*/
for (; scope;
immu_arg_t imarg = {0};
/* PCI endpoint devices only */
continue;
level = 0;
!= DDI_SUCCESS) {
/* skip - nothing else we can do */
continue;
}
/* Should have walked only 1 level i.e. rdip */
goto found;
}
}
}
}
/*
* walk the drhd list looking for subtree match
* i.e. is the device a descendant of a devscope BDF.
* We want the lowest subtree.
*/
tlevel = 0;
drhd)) {
/* looking for subtree match */
continue;
/*
* try to match the device scope
*/
for (; scope;
immu_arg_t imarg = {0};
/* PCI subtree only */
continue;
level = 0;
/* skip - nothing else we can do */
continue;
}
/* should have walked 1 level i.e. rdip */
/* look for lowest ancestor matching drhd */
}
}
}
}
goto found;
}
drhd)) {
/* Look for include all */
break;
}
}
}
/*FALLTHRU*/
/*
* No drhd (dmar unit) found for this device in the ACPI DMAR tables.
* This may happen with buggy versions of BIOSes. Just warn instead
* of panic as we don't want whole system to go down because of one
* device.
*/
"device in ACPI DMAR table.");
return (NULL);
}
}
char *
immu_dmar_unit_name(void *dmar_unit)
{
}
immu_dmar_unit_dip(void *dmar_unit)
{
}
void *
{
} else {
}
}
void
{
}
{
return (dmar_table->tbl_intrmap);
}
/* for a given ioapicid, find the source id and immu */
{
"IOAPIC (id = %d)", ioapicid);
/*NOTREACHED*/
}
return (idt->ioapic_sid);
}
/* for a given ioapicid, find the source id and immu */
immu_t *
{
if (idt) {
}
return (NULL);
}