dr.c revision 1ae0874509b6811fdde1dfd46f0d93fd09867a3f
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "mdescplugin.h"
static di_prom_handle_t ph = DI_PROM_HANDLE_NIL;
typedef struct cpu_lookup {
di_node_t di_node;
picl_nodehdl_t nodeh;
int result;
} cpu_lookup_t;
extern int add_cpu_prop(picl_nodehdl_t node, void *args);
extern md_t *mdesc_devinit(void);
/*
* This function is identical to the one in the picldevtree plugin.
* Unfortunately we can't just reuse that code.
*/
static int
add_string_list_prop(picl_nodehdl_t nodeh, char *name, char *strlist,
unsigned int nrows)
{
ptree_propinfo_t propinfo;
picl_prophdl_t proph;
picl_prophdl_t tblh;
int err;
unsigned int i;
unsigned int j;
picl_prophdl_t *proprow;
int len;
#define NCOLS_IN_STRING_TABLE 1
err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_TABLE, PICL_READ, sizeof (picl_prophdl_t), name,
NULL, NULL);
if (err != PICL_SUCCESS)
return (err);
err = ptree_create_table(&tblh);
if (err != PICL_SUCCESS)
return (err);
err = ptree_create_and_add_prop(nodeh, &propinfo, &tblh, &proph);
if (err != PICL_SUCCESS)
return (err);
proprow = alloca(sizeof (picl_prophdl_t) * nrows);
if (proprow == NULL) {
(void) ptree_destroy_prop(proph);
return (PICL_FAILURE);
}
for (j = 0; j < nrows; ++j) {
len = strlen(strlist) + 1;
err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_READ, len, name,
NULL, NULL);
if (err != PICL_SUCCESS)
break;
err = ptree_create_prop(&propinfo, strlist, &proprow[j]);
if (err != PICL_SUCCESS)
break;
strlist += len;
err = ptree_add_row_to_table(tblh, NCOLS_IN_STRING_TABLE,
&proprow[j]);
if (err != PICL_SUCCESS)
break;
}
if (err != PICL_SUCCESS) {
for (i = 0; i < j; ++i)
(void) ptree_destroy_prop(proprow[i]);
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
return (err);
}
return (PICL_SUCCESS);
}
/*
* This function is identical to the one in the picldevtree plugin.
* Unfortunately we can't just reuse that code.
*/
static void
add_devinfo_props(picl_nodehdl_t nodeh, di_node_t di_node)
{
int instance;
char *di_val;
di_prop_t di_prop;
int di_ptype;
ptree_propinfo_t propinfo;
instance = di_instance(di_node);
(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_INT, PICL_READ, sizeof (instance), PICL_PROP_INSTANCE,
NULL, NULL);
(void) ptree_create_and_add_prop(nodeh, &propinfo, &instance, NULL);
di_val = di_bus_addr(di_node);
if (di_val) {
(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1,
PICL_PROP_BUS_ADDR, NULL, NULL);
(void) ptree_create_and_add_prop(nodeh, &propinfo, di_val,
NULL);
}
di_val = di_binding_name(di_node);
if (di_val) {
(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1,
PICL_PROP_BINDING_NAME, NULL, NULL);
(void) ptree_create_and_add_prop(nodeh, &propinfo, di_val,
NULL);
}
di_val = di_driver_name(di_node);
if (di_val) {
(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1,
PICL_PROP_DRIVER_NAME, NULL, NULL);
(void) ptree_create_and_add_prop(nodeh, &propinfo, di_val,
NULL);
}
di_val = di_devfs_path(di_node);
if (di_val) {
(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1,
PICL_PROP_DEVFS_PATH, NULL, NULL);
(void) ptree_create_and_add_prop(nodeh, &propinfo, di_val,
NULL);
di_devfs_path_free(di_val);
}
for (di_prop = di_prop_next(di_node, DI_PROP_NIL);
di_prop != DI_PROP_NIL;
di_prop = di_prop_next(di_node, di_prop)) {
di_val = di_prop_name(di_prop);
di_ptype = di_prop_type(di_prop);
switch (di_ptype) {
case DI_PROP_TYPE_BOOLEAN:
(void) ptree_init_propinfo(&propinfo,
PTREE_PROPINFO_VERSION, PICL_PTYPE_VOID,
PICL_READ, (size_t)0, di_val, NULL, NULL);
(void) ptree_create_and_add_prop(nodeh, &propinfo,
NULL, NULL);
break;
case DI_PROP_TYPE_INT: {
int *idata;
int len;
len = di_prop_ints(di_prop, &idata);
if (len < 0)
/* Recieved error, so ignore prop */
break;
if (len == 1)
(void) ptree_init_propinfo(&propinfo,
PTREE_PROPINFO_VERSION, PICL_PTYPE_INT,
PICL_READ, len * sizeof (int), di_val,
NULL, NULL);
else
(void) ptree_init_propinfo(&propinfo,
PTREE_PROPINFO_VERSION,
PICL_PTYPE_BYTEARRAY, PICL_READ,
len * sizeof (int), di_val,
NULL, NULL);
(void) ptree_create_and_add_prop(nodeh, &propinfo,
idata, NULL);
}
break;
case DI_PROP_TYPE_STRING: {
char *sdata;
int len;
len = di_prop_strings(di_prop, &sdata);
if (len < 0)
break;
if (len == 1) {
(void) ptree_init_propinfo(&propinfo,
PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_READ,
strlen(sdata) + 1, di_val,
NULL, NULL);
(void) ptree_create_and_add_prop(nodeh,
&propinfo, sdata, NULL);
} else {
(void) add_string_list_prop(nodeh, di_val,
sdata, len);
}
}
break;
case DI_PROP_TYPE_BYTE: {
int len;
unsigned char *bdata;
len = di_prop_bytes(di_prop, &bdata);
if (len < 0)
break;
(void) ptree_init_propinfo(&propinfo,
PTREE_PROPINFO_VERSION, PICL_PTYPE_BYTEARRAY,
PICL_READ, len, di_val, NULL, NULL);
(void) ptree_create_and_add_prop(nodeh, &propinfo,
bdata, NULL);
}
break;
case DI_PROP_TYPE_UNKNOWN:
break;
case DI_PROP_TYPE_UNDEF_IT:
break;
default:
break;
}
}
}
/*
* Create a picl node of type cpu and fill it.
* properties are filled from both the device tree and the
* Machine description.
*/
static int
construct_cpu_node(picl_nodehdl_t plath, di_node_t dn)
{
int err;
char *nodename;
picl_nodehdl_t anodeh;
nodename = di_node_name(dn); /* PICL_PROP_NAME */
err = ptree_create_and_add_node(plath, nodename, PICL_CLASS_CPU,
&anodeh);
if (err != PICL_SUCCESS)
return (err);
add_devinfo_props(anodeh, dn);
(void) add_cpu_prop(anodeh, NULL);
return (err);
}
/*
* Given a devinfo node find its reg property.
*/
static int
get_reg_prop(di_node_t dn, int **pdata)
{
int dret = 0;
dret = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, OBP_REG, pdata);
if (dret > 0)
return (dret);
if (!ph)
return (0);
dret = di_prom_prop_lookup_ints(ph, dn, OBP_REG, pdata);
return (dret < 0? 0 : dret);
}
/*
* Given a devinfo cpu node find its cpuid property.
*/
int
get_cpuid(di_node_t di_node)
{
int len;
int *idata;
int dcpuid = -1;
len = get_reg_prop(di_node, &idata);
if (len != SUN4V_CPU_REGSIZE)
return (dcpuid);
if (len == SUN4V_CPU_REGSIZE)
dcpuid = CFGHDL_TO_CPUID(idata[0]);
return (dcpuid);
}
int
find_cpu(di_node_t node, int cpuid)
{
int dcpuid;
di_node_t cnode;
char *nodename;
for (cnode = di_child_node(node); cnode != DI_NODE_NIL;
cnode = di_sibling_node(cnode)) {
nodename = di_node_name(cnode);
if (nodename == NULL)
continue;
if (strcmp(nodename, OBP_CPU) == 0) {
dcpuid = get_cpuid(cnode);
if (dcpuid == cpuid) {
return (1);
}
}
}
return (0);
}
/*
* Callback to the ptree walk function during remove_cpus.
* As a part of the args receives a picl nodeh, searches
* the device tree for a cpu whose cpuid matches the picl cpu node.
* Sets arg struct's result to 1 if it failed to match and terminates
* the walk.
*/
static int
remove_cpu_candidate(picl_nodehdl_t nodeh, void *c_args)
{
di_node_t di_node;
cpu_lookup_t *cpu_arg;
int err;
int pcpuid;
int reg_prop[SUN4V_CPU_REGSIZE];
if (c_args == NULL)
return (PICL_INVALIDARG);
cpu_arg = c_args;
di_node = cpu_arg->di_node;
err = ptree_get_propval_by_name(nodeh, OBP_REG, reg_prop,
sizeof (reg_prop));
if (err != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
pcpuid = CFGHDL_TO_CPUID(reg_prop[0]);
if (!find_cpu(di_node, pcpuid)) {
cpu_arg->result = 1;
cpu_arg->nodeh = nodeh;
return (PICL_WALK_TERMINATE);
}
cpu_arg->result = 0;
return (PICL_WALK_CONTINUE);
}
/*
* Given the start node of the device tree.
* find all cpus in the picl tree that don't have
* device tree counterparts and remove them.
*/
static void
remove_cpus(di_node_t di_start)
{
int err;
picl_nodehdl_t plath;
cpu_lookup_t cpu_arg;
err = ptree_get_node_by_path(PLATFORM_PATH, &plath);
if (err != PICL_SUCCESS)
return;
do {
cpu_arg.di_node = di_start;
cpu_arg.nodeh = 0;
cpu_arg.result = 0;
if (ptree_walk_tree_by_class(plath,
PICL_CLASS_CPU, &cpu_arg, remove_cpu_candidate)
!= PICL_SUCCESS)
return;
if (cpu_arg.result == 1) {
err = ptree_delete_node(cpu_arg.nodeh);
if (err == PICL_SUCCESS)
ptree_destroy_node(cpu_arg.nodeh);
}
} while (cpu_arg.result);
}
/*
* Callback to the ptree walk function during add_cpus.
* As a part of the args receives a cpu di_node, compares
* each picl cpu node's cpuid to the device tree node's cpuid.
* Sets arg struct's result to 1 on a match.
*/
static int
cpu_exists(picl_nodehdl_t nodeh, void *c_args)
{
di_node_t di_node;
cpu_lookup_t *cpu_arg;
int err;
int dcpuid, pcpuid;
int reg_prop[4];
if (c_args == NULL)
return (PICL_INVALIDARG);
cpu_arg = c_args;
di_node = cpu_arg->di_node;
dcpuid = get_cpuid(di_node);
err = ptree_get_propval_by_name(nodeh, OBP_REG, reg_prop,
sizeof (reg_prop));
if (err != PICL_SUCCESS)
return (PICL_WALK_CONTINUE);
pcpuid = CFGHDL_TO_CPUID(reg_prop[0]);
if (dcpuid == pcpuid) {
cpu_arg->result = 1;
return (PICL_WALK_TERMINATE);
}
cpu_arg->result = 0;
return (PICL_WALK_CONTINUE);
}
/*
* Given the root node of the device tree.
* compare it to the picl tree and add to it cpus
* that are new.
*/
static void
add_cpus(di_node_t di_node)
{
int err;
di_node_t cnode;
picl_nodehdl_t plath;
cpu_lookup_t cpu_arg;
char *nodename;
err = ptree_get_node_by_path(PLATFORM_PATH, &plath);
if (err != PICL_SUCCESS)
return;
for (cnode = di_child_node(di_node); cnode != DI_NODE_NIL;
cnode = di_sibling_node(cnode)) {
nodename = di_node_name(cnode);
if (nodename == NULL)
continue;
if (strcmp(nodename, OBP_CPU) == 0) {
cpu_arg.di_node = cnode;
if (ptree_walk_tree_by_class(plath,
PICL_CLASS_CPU, &cpu_arg, cpu_exists)
!= PICL_SUCCESS)
return;
if (cpu_arg.result == 0)
/*
* Didn't find a matching cpu, add it.
*/
(void) construct_cpu_node(plath,
cnode);
}
}
}
/*
* Handle DR events. Only supports cpu add and remove.
*/
int
update_devices(char *dev, int op)
{
di_node_t di_root;
if ((di_root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL)
return (PICL_FAILURE);
if ((ph = di_prom_init()) == NULL)
return (PICL_FAILURE);
if (op == DEV_ADD) {
if (strcmp(dev, OBP_CPU) == 0)
add_cpus(di_root);
}
if (op == DEV_REMOVE) {
if (strcmp(dev, OBP_CPU) == 0)
remove_cpus(di_root);
}
di_fini(di_root);
di_prom_fini(ph);
return (PICL_SUCCESS);
}