sata.c revision d3080269c400bce011f72255205cee37b5b04079
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* SATA libtopo enumerator plugin
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
#include <alloca.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <libnvpair.h>
#include <fm/topo_mod.h>
#include <libdevinfo.h>
#include <config_admin.h>
#include <smbios.h>
#include <dirent.h>
#include <libgen.h>
#include <assert.h>
#include <pthread.h>
#include "did_impl.h"
#include "did_props.h"
#include "sata.h"
#include "sfx4500_props.h"
#define MAX_ACTION_RULES 10
#define MAX_MACHNAMELEN 256
struct sata_machine_specific_properties *machprops[] = {
};
int *err);
int *err);
nvlist_t **);
/*
* The topo method is bound to the sata-port nodes because we want
* the hc scheme's lookup to find the method and execute it. If it
* were bound to the disk nodes, and if a disk were not inserted,
* the present method would never execute, and the hc scheme plugin
* would return PRESENT (because other hc-schemed FMRIs do not implement
* a presence method, and in those cases, the correct default is to
* assert presence).
*/
const topo_method_t SATA_METHODS[] = {
{ NULL }
};
/*
* Since the global data is only initialized ONCE in _topo_init
* and multiple threads that use libtopo can cause _topo_init to
* be called, the flag and lock here enable only the first to enter
* _topo_init to initialize the global data. After that, it's all
* read-only. The global data used here could have been stored in
* the node-private data area, but that's overkill, because all data
* items will not change once they are set -- they depend only on
* the machine on which this module executes.
*/
static char machname[MAX_MACHNAMELEN];
static const char *pgroupname = NULL;
const topo_modinfo_t sata_info =
static void
{
/* Only need to unregister methods for the sata port nodes */
}
static char *
{
char *rv;
}
return (rv);
}
static char *
{
char *scheme;
char *dpath;
int e;
return (NULL);
}
"Bad or missing %s in ASRU?\n", FM_FMRI_DEV_PATH);
return (NULL);
}
/*
* dup the string before the nvlist_free() to get a copy before it's
* freed
*/
return (dpath);
}
/*
* If add_fru is B_TRUE, and fru is NULL, the node's FMRI will be used as
* the FRU, otherwise the FMRI specified in the fru nvlist is used.
*
* If asru is NULL, no ASRU will be added.
*
* If label is NULL, no label will be added.
*/
static tnode_t *
{
/* Set the FRU to the node's FMRI if called didn't specify it */
if (add_fru)
err);
}
if (pfmri)
if (fmri)
if (args)
return (cnode);
}
static char *
{
int i;
while (isspace(*s) && *s != 0)
s++;
i = strlen(s) - 1;
while (i >= 0 && isspace(s[i]))
i--;
rs[i + 1] = 0;
return (rs);
}
static boolean_t
{
}
}
return (retval);
}
int
{
int i;
int mnamelen;
char *mname;
(void) pthread_mutex_lock(&global_data_mutex);
if (!global_data_initted) {
i = 0;
/* Initialize the sata_dev_props and ruleset globals */
i++;
}
}
}
}
(void) pthread_mutex_unlock(&global_data_mutex);
return (-1); /* mod errno set */
}
return (0);
}
void
{
}
static int
{
}
static int
{
int timeout = 0;
int e;
#define TIMEOUT_MAX 60
/*
* This timeout mechanism handles attachment points that are
* temporarily BUSY while the device is initialized.
*/
do {
case CFGA_OK:
return (CFGA_OK);
case CFGA_BUSY:
case CFGA_SYSTEM_BUSY:
if (timeout++ >= TIMEOUT_MAX)
else {
(void) sleep(1);
}
break;
default:
break;
}
return (e);
}
static boolean_t
{
char *p, *apsn;
int buflen;
/* Some cfgadm plugins provide serial number information (e.g. sata) */
/* The sata plugin provides the serial number after "SN: " */
p += 4 /* strlen("SN: ") */;
/*
* Terminate the string at any spaces (the serial
* number is a string with no spaces)
*/
*p = 0;
}
} else /* Unknown cfgadm plugin ap type -- assume sn matches */
return (rv);
}
/*ARGSUSED*/
static int
{
int err;
int nlist;
char *ap_path[1];
int present = 1;
char *sn;
/*
* If this FMRI is a legacy hc FMRI with a component
* looking like an attachment point, use libcfgadm to
* detect its state:
*/
}
}
/* The hc-list must have 1 entry and that entry must be a component */
== CFGA_OK) {
!= 0)
/*
* If a serial number is included in the FMRI, use it
* to verify presence
*/
list_array[0].ap_info))));
}
}
return (present);
}
/*
* Translates a device minor name into an attachment point (`sataX/Y`)
* Allocates memory and returns the attachment point name in *ap.
* (Caller is responsible for deallocating *ap.)
*/
static int
{
char *p;
int nlist;
/*
* The logical id is:
* <attachmentpoint>[::<logicaldisknode>],
* so remove the portion we're not interested in.
*/
*p = 0;
return (0);
}
return (-1);
}
static sata_dev_prop_t *
lookup_sdp_by_minor(char *minorpath)
{
int i;
if (sata_dev_props == NULL)
return (NULL);
return (&sata_dev_props[i]);
}
return (NULL);
}
/*
* Look for disk nodes of the form {physpath}/<whatever>@{portnum},0
* and return the path to that node in physpath
*/
static boolean_t
{
char matchstr[32];
char *p;
int dentlen;
return (-1);
/*
* Allocate a dirent structure large enough to hold the longest path
* in the devfs filesystem. The NUL byte is accounted-for in the
* 1 byte already allocated as part of d_name in the dirent structure.
*/
sizeof (struct dirent);
errno = 0;
/* Skip entries that we don't care about */
continue;
}
/* Set errno to 0 before next call to readdir_r */
errno = 0;
}
return (found);
}
static int
{
return (-1);
/* basename() may modify its argument, so dup the string first: */
return (-1);
}
/*
* Allocate a dirent structure large enough to hold the longest path
* in the devfs filesystem. The NUL byte is accounted-for in the
* 1 byte already allocated as part of d_name in the dirent structure.
*/
sizeof (struct dirent);
/*
* Open the directory at "/devices" + dpath + "/.." and scan for the
* minor node names (of minor nodes whose name portion is == to the
* last path element of dpath) with the largest number
*/
errno = 0;
/* Skip entries that we don't care about */
continue;
/* Update the largest minor # by pulling it out of the dirent */
}
/* Set errno to 0 before next call to readdir_r */
errno = 0;
}
if (errno != 0)
nodemax = -1;
return (nodemax);
}
static void
{
int i;
#define MAX_PNAME_LEN 128
char pname[MAX_PNAME_LEN];
/*
* Save the attachment point physical path
*/
/*
* The private properties are the core of the configuration
* mechanism for the sfx4500-disk Diagnosis Engine.
*/
}
/* Add the indicators: */
/*
* Since topo node properties can't contain nvlists,
* the indicators and their actions will go into two
* separate properties
*/
i);
err);
i);
err);
}
/* Now, the (global) indicator rules */
i);
err);
SATA_INDRULE_ACTIONS "-%d", i);
err);
}
}
/*
* minorpath is the path to the minor node for this port (/devices/...:<n>)
*/
static void
{
/* Try to match the minorpath to one of the sata_dev_prop_t's */
}
}
static void
{
int infolen;
/*
* The information string from the SATA cfgadm plugin has the
* following form:
*
* Mod: <model> FRev: <revision> SN: <serialno>
*/
return;
}
*revp = 0; /* terminate model string */
*snp = 0; /* terminate revision string */
/*
* The model string is broken into 2 fields --
* manufacturer and model string (separated by one space)
* If there is no space, then there's just a model number
* with no manufacturer.
*/
} else {
*modlp = 0;
modlp += 1;
}
}
static void
{
char *ldev, *p;
/*
* The physical path to the drive can be derived by
* taking the attachment point physical path, chopping off the
* minor portion, and looking for the target node of the form
* <nodename>@<X>,0 (where <X> is the port number)
*/
/* The AP phys path MUST have a colon: */
err);
}
ldev = p + 1;
}
}
if (model) {
}
if (manuf) {
}
if (serial) {
}
if (firm) {
}
/*
* Try to get the disk capacity and store it in a property if the
* device is accessible
*/
if (physpath_found) {
int fd;
char capstr[32];
/*
* On x86, for disk target drivers, `:q,raw' means "access the
* character device that corresponds to the whole disk".
*/
}
}
if (capacity > 0) {
capacity);
err);
}
}
#endif
}
static boolean_t
is_sata_controller(char *dpath)
{
int *sataprops;
/*
* SATA controllers have a `sata' property on their nodes with
* a value of 1.
*/
if (devnode != DI_NODE_NIL) {
}
return (satactrlr);
}
static int
{
int i, nerrs = 0;
SATA_DISK " range [%d..%d]: %s\n",
return (-1);
}
for (i = 0; i < ndisks; i++) {
/*
* Check to see if there's a disk inserted in this port
*/
!= NULL) {
err) != 0) {
"FRU while creating " SATA_DISK
" nodes: %s\n",
topo_strerror(*err));
}
}
/*
* If this disk is one which we're decorating with
* machine-specific properties, set the FRU to
* a legacy-hc FMRI with the label as the component,
* the ASRU as the parent's ASRU (the ASRU of the
* sata port, e.g. the attachment point); otherwise,
* use the node's FMRI as its FRU (fru will be NULL
* in that case, causing node_create to use the FMRI).
*/
} else {
"node for port %d: %s\n", portnum,
topo_strerror(*err));
++nerrs;
}
if (fru)
}
}
if (nerrs) {
return (-1);
}
return (0);
}
static int
{
int i;
char *dpath;
int apbuflen;
int dpathlen;
if (min < 0)
min = 0;
return (0);
/* Get the device path from the parent node's properties: */
return (0);
if (!is_sata_controller(dpath)) {
return (0);
}
"on SATA controller at %s\n", dpath);
return (-1);
}
SATA_PORT " range [%d..%d]: %s\n",
return (-1);
}
/* Create the FRU - a legacy component FMRI */
topo_strerror(err));
return (-1);
}
/* The minor node name = "/devices" + dpath + ":" + i */
mod) != 0) {
"into an attachment point: ignoring sata-port=%d\n",
i);
continue;
}
/* Create the ASRU - a legacy component FMRI */
++nerr;
continue;
}
/*
* The ASRU is a legacy-hc component FMRI with the
* component being the attachment point
* The FRU is a legacy-hc component FMRI with the
* component being "MB".
*/
++nerr;
continue;
}
++nerr;
continue;
}
/* For now, ignore errors from private property creation */
/* Create the disk node(s) under this sata-port: */
++nerr;
}
}
if (nerr != 0) {
return (-1);
} else
return (0);
}
/*ARGSUSED*/
static int
{
return (0);
}