/*
* 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.
*/
#include <picl.h>
#include <syslog.h>
#include <strings.h>
#include <alloca.h>
#include <pthread.h>
#include <synch.h>
#include <limits.h>
#include <ctype.h>
#include <unistd.h>
#include <picltree.h>
#include <signal.h>
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#include <libnvpair.h>
#include "fru_tag.h"
#include "fru_data_impl.h"
#include "fru_data.h"
#include "picld_pluginutil.h"
#pragma init(frudata_plugin_register) /* .init section */
static void frudata_plugin_init(void);
static void frudata_plugin_fini(void);
static container_tbl_t *container_table[TABLE_SIZE];
/*
* Locking Stragtegy :
* calling thread should hold the cont_tbl_lock during the course
* of container table lookup. release the cont_tbl_lock on lookup
* failure or on the condition wait.
*
* thread holding the container object rwlock should release lock
* and signal to unblock threads blocked on the condition variable
* upon i/o completion.
*
*/
static pthread_mutex_t cont_tbl_lock = PTHREAD_MUTEX_INITIALIZER;
static int add_row_to_table(hash_obj_t *, picl_nodehdl_t,
packet_t *, container_tbl_t *);
static picld_plugin_reg_t frudata_reg_info = {
PICLD_PLUGIN_VERSION_1,
PICLD_PLUGIN_NON_CRITICAL,
"SUNW_piclfrudata",
frudata_plugin_init, /* init entry point */
frudata_plugin_fini /* cleanup entry point */
};
/* initialization function */
static void
frudata_plugin_register(void)
{
/* register plugin with daemon */
if (picld_plugin_register(&frudata_reg_info) != PICL_SUCCESS) {
syslog(LOG_ERR, "SUNW_piclfrudata plugin registration failed");
}
}
static int
map_access_err(int err)
{
switch (err) {
case ENFILE :
return (PICL_PROPEXISTS);
case EAGAIN :
return (PICL_NOSPACE);
case EPERM :
return (PICL_PERMDENIED);
case EEXIST :
return (PICL_PROPEXISTS);
default :
return (PICL_FAILURE);
}
}
/*
* unlock_container_lock() should be always called by the thread holding the
* container object lock. it will signal block thread waiting on the condition
* variable.
*/
static void
unlock_container_lock(container_tbl_t *cont_hash)
{
(void) pthread_rwlock_unlock(&cont_hash->rwlock);
(void) pthread_mutex_lock(&cont_tbl_lock);
(void) pthread_cond_signal(&cont_hash->cond_var);
(void) pthread_mutex_unlock(&cont_tbl_lock);
}
/* volatile callback read routine */
/* ARGSUSED */
static int
frudata_read_callback(ptree_rarg_t *rarg, void *buf)
{
return (PICL_SUCCESS);
}
/*
* called to get hash object for specified node and object type from
* hash table.
*/
static container_tbl_t *
lookup_container_table(picl_nodehdl_t nodehdl, int object_type)
{
int index_to_hash;
int retval = PICL_SUCCESS;
container_tbl_t *first_hash;
container_tbl_t *next_hash;
picl_nodehdl_t parenthdl = 0;
switch (object_type) {
case SECTION_NODE:
retval = ptree_get_propval_by_name(nodehdl, PICL_PROP_PARENT,
&parenthdl, sizeof (picl_nodehdl_t));
break;
case SEGMENT_NODE:
retval = ptree_get_propval_by_name(nodehdl, PICL_PROP_PARENT,
&parenthdl, sizeof (picl_nodehdl_t));
retval = ptree_get_propval_by_name(parenthdl, PICL_PROP_PARENT,
&parenthdl, sizeof (picl_nodehdl_t));
break;
case CONTAINER_NODE :
parenthdl = nodehdl;
break;
default :
return (NULL);
}
if (retval != PICL_SUCCESS) {
return (NULL);
}
index_to_hash = (parenthdl % TABLE_SIZE);
first_hash = container_table[index_to_hash];
for (next_hash = first_hash; next_hash != NULL;
next_hash = next_hash->next) {
if (parenthdl == next_hash->picl_hdl) {
return (next_hash);
}
}
return (NULL);
}
static int
lock_readwrite_lock(container_tbl_t *cont_obj, int operation)
{
/* if write operation */
if (operation == PICL_WRITE) {
return (pthread_rwlock_trywrlock(&cont_obj->rwlock));
}
/* read operation */
return (pthread_rwlock_tryrdlock(&cont_obj->rwlock));
}
/*
* lock the container table, do lookup for the container object
* in the container table. if container object found try to lock
* the container object, if lock on container object is busy wait
* on condition variable till the thread holding the container
* object lock signal it.
*/
static container_tbl_t *
lock_container_lock(picl_nodehdl_t nodehdl, int object_type, int operation)
{
container_tbl_t *cont_obj = NULL;
(void) pthread_mutex_lock(&cont_tbl_lock);
while (((cont_obj = lookup_container_table(nodehdl, object_type)) !=
NULL) && (lock_readwrite_lock(cont_obj, operation) == EBUSY)) {
pthread_cond_wait(&cont_obj->cond_var, &cont_tbl_lock);
}
(void) pthread_mutex_unlock(&cont_tbl_lock);
return (cont_obj);
}
static hash_obj_t *
lookup_node_object(picl_nodehdl_t nodehdl, int object_type,
container_tbl_t *cont_tbl)
{
int index_to_hash;
hash_obj_t *first_hash;
hash_obj_t *next_hash;
index_to_hash = (nodehdl % TABLE_SIZE);
first_hash = &cont_tbl->hash_obj[index_to_hash];
for (next_hash = first_hash->next; next_hash != NULL;
next_hash = next_hash->next) {
if ((nodehdl == next_hash->picl_hdl) &&
(object_type == next_hash->object_type)) {
return (next_hash);
}
}
return (NULL);
}
/*
* called to add newly created container hash table into container hash table.
*
*/
static void
add_tblobject_to_container_tbl(container_tbl_t *cont_tbl)
{
int cnt;
int index_to_hash;
hash_obj_t *hash_ptr;
index_to_hash = ((cont_tbl->picl_hdl) % TABLE_SIZE);
cont_tbl->next = container_table[index_to_hash];
container_table[index_to_hash] = cont_tbl;
hash_ptr = cont_tbl->hash_obj;
/* initialize the bucket of this container hash table. */
for (cnt = 0; cnt < TABLE_SIZE; cnt++) {
hash_ptr->next = NULL;
hash_ptr->prev = NULL;
hash_ptr++;
}
if (cont_tbl->next != NULL) {
cont_tbl->next->prev = cont_tbl;
}
}
static void
add_nodeobject_to_hashtable(hash_obj_t *hash_obj, container_tbl_t *cont_tbl)
{
int index_to_hash;
hash_obj_t *hash_table;
index_to_hash = ((hash_obj->picl_hdl) % TABLE_SIZE);
hash_table = &cont_tbl->hash_obj[index_to_hash];
hash_obj->next = hash_table->next;
hash_table->next = hash_obj;
if (hash_obj->next != NULL) {
hash_obj->next->prev = hash_obj;
}
}
static container_tbl_t *
alloc_container_table(picl_nodehdl_t nodehdl)
{
container_tbl_t *cont_tbl;
cont_tbl = malloc(sizeof (container_tbl_t));
if (cont_tbl == NULL) {
return (NULL);
}
cont_tbl->picl_hdl = nodehdl;
cont_tbl->hash_obj = malloc(sizeof (hash_obj_t[TABLE_SIZE]));
cont_tbl->next = NULL;
cont_tbl->prev = NULL;
if (cont_tbl->hash_obj == NULL) {
(void) free(cont_tbl);
return (NULL);
}
(void) pthread_rwlock_init(&cont_tbl->rwlock, NULL);
(void) pthread_cond_init(&cont_tbl->cond_var, NULL);
return (cont_tbl);
}
/*
* called to allocate container node object for container property and a
* container table.
*/
static hash_obj_t *
alloc_container_node_object(picl_nodehdl_t nodehdl)
{
hash_obj_t *hash_obj;
fru_access_hdl_t acc_hdl;
container_node_t *cont_node;
/* open the container (call fruaccess) */
acc_hdl = fru_open_container(nodehdl);
if (acc_hdl == (container_hdl_t)0) {
return (NULL);
}
/* allocate container node object */
cont_node = malloc(sizeof (container_node_t));
if (cont_node == NULL) {
return (NULL);
}
/* allocate container hash object */
hash_obj = malloc(sizeof (hash_obj_t));
if (hash_obj == NULL) {
(void) free(cont_node);
return (NULL);
}
cont_node->cont_hdl = acc_hdl; /* fruaccess handle */
cont_node->section_list = NULL;
hash_obj->picl_hdl = nodehdl; /* picl node handle */
hash_obj->object_type = CONTAINER_NODE;
hash_obj->u.cont_node = cont_node;
hash_obj->next = NULL;
hash_obj->prev = NULL;
return (hash_obj);
}
/*
* called to allocate node object for section node.
*/
static hash_obj_t *
alloc_section_node_object(picl_nodehdl_t nodehdl, section_t *section)
{
hash_obj_t *hash_obj;
section_node_t *sec_node;
/* allocate section node object */
sec_node = malloc(sizeof (section_node_t));
if (sec_node == NULL) {
return (NULL);
}
/* allocate section hash object */
hash_obj = malloc(sizeof (hash_obj_t));
if (hash_obj == NULL) {
(void) free(sec_node);
return (NULL);
}
sec_node->section_hdl = section->handle; /* fruaccess hdl. */
sec_node->segment_list = NULL;
sec_node->next = NULL;
sec_node->num_of_segment = -1;
hash_obj->picl_hdl = nodehdl; /* picl node handle */
hash_obj->object_type = SECTION_NODE;
hash_obj->u.sec_node = sec_node;
hash_obj->next = NULL;
hash_obj->prev = NULL;
return (hash_obj);
}
/*
* called to allocate segment node object.
*/
static hash_obj_t *
alloc_segment_node_object(picl_nodehdl_t nodehdl, segment_t *segment)
{
hash_obj_t *hash_obj;
segment_node_t *seg_node;
/* allocate segment node object */
seg_node = malloc(sizeof (segment_node_t));
if (seg_node == NULL) {
return (NULL);
}
/* allocate segment hash object */
hash_obj = malloc(sizeof (hash_obj_t));
if (hash_obj == NULL) {
free(seg_node);
return (NULL);
}
/* fruaccess handle */
seg_node->segment_hdl = segment->handle;
seg_node->packet_list = NULL;
seg_node->next = NULL;
seg_node->num_of_pkt = -1;
/* picl node handle */
hash_obj->picl_hdl = nodehdl;
hash_obj->object_type = SEGMENT_NODE;
hash_obj->u.seg_node = seg_node;
hash_obj->next = NULL;
hash_obj->prev = NULL;
return (hash_obj);
}
/*
* called to allocate node object for packet.
*/
static hash_obj_t *
alloc_packet_node_object(picl_nodehdl_t nodehdl, packet_t *packet)
{
hash_obj_t *hash_obj;
packet_node_t *pkt_node;
/* allocate packet node object */
pkt_node = malloc(sizeof (packet_node_t));
if (pkt_node == NULL) {
return (NULL);
}
/* allocate packet hash object */
hash_obj = malloc(sizeof (hash_obj_t));
if (hash_obj == NULL) {
free(pkt_node);
return (NULL);
}
/* fruaccess handle */
pkt_node->pkt_handle = packet->handle;
pkt_node->next = NULL;
hash_obj->picl_hdl = nodehdl; /* picl node handle */
hash_obj->object_type = PACKET_NODE;
hash_obj->u.pkt_node = pkt_node;
hash_obj->next = NULL;
hash_obj->prev = NULL;
return (hash_obj);
}
/* add new section hash object to the section list */
static void
add_to_section_list(hash_obj_t *container_hash, hash_obj_t *sect_hash)
{
hash_obj_t *next_hash;
sect_hash->u.sec_node->container_hdl = container_hash->picl_hdl;
if (container_hash->u.cont_node->section_list == NULL) {
container_hash->u.cont_node->section_list = sect_hash;
return;
}
for (next_hash = container_hash->u.cont_node->section_list;
next_hash->u.sec_node->next != NULL;
next_hash = next_hash->u.sec_node->next) {
;
}
next_hash->u.sec_node->next = sect_hash;
}
/* add new segment hash object to the existing list */
static void
add_to_segment_list(hash_obj_t *parent_obj, hash_obj_t *child_obj)
{
hash_obj_t *next_hash;
child_obj->u.seg_node->sec_nodehdl = parent_obj->picl_hdl;
if (parent_obj->u.sec_node->segment_list == NULL) {
parent_obj->u.sec_node->segment_list = child_obj;
return;
}
for (next_hash = parent_obj->u.sec_node->segment_list;
next_hash->u.seg_node->next != NULL;
next_hash = next_hash->u.seg_node->next) {
;
}
next_hash->u.seg_node->next = child_obj;
}
/*
* called to add packet node object to the existing packet list.
*/
static void
add_to_packet_list(hash_obj_t *parent_obj, hash_obj_t *child_obj)
{
hash_obj_t *next_hash;
if (parent_obj->u.seg_node->packet_list == NULL) {
parent_obj->u.seg_node->packet_list = child_obj;
return;
}
for (next_hash = parent_obj->u.seg_node->packet_list;
next_hash->u.pkt_node->next != NULL;
next_hash = next_hash->u.pkt_node->next) {
;
}
next_hash->u.pkt_node->next = child_obj;
}
/*
* free the packet hash list.
*/
static void
free_packet_list(hash_obj_t *hash_obj, container_tbl_t *cont_tbl)
{
hash_obj_t *next_obj;
hash_obj_t *free_obj;
/* packet hash object list */
next_obj = hash_obj->u.seg_node->packet_list;
while (next_obj != NULL) {
free_obj = next_obj;
next_obj = next_obj->u.pkt_node->next;
if (free_obj->prev == NULL) { /* first node object */
cont_tbl->hash_obj[(free_obj->picl_hdl %
TABLE_SIZE)].next = free_obj->next;
if (free_obj->next != NULL) {
free_obj->next->prev = NULL;
}
} else {
free_obj->prev->next = free_obj->next;
if (free_obj->next != NULL) {
free_obj->next->prev = free_obj->prev;
}
}
free(free_obj->u.pkt_node);
free(free_obj);
}
hash_obj->u.seg_node->packet_list = NULL;
}
/*
* free the segment hash node object.
*/
static void
free_segment_node(hash_obj_t *hash_obj, picl_nodehdl_t nodehdl,
container_tbl_t *cont_tbl)
{
hash_obj_t *prev_hash_obj;
hash_obj_t *next_obj;
/* segment hash object list */
next_obj = hash_obj->u.sec_node->segment_list;
if (next_obj == NULL) {
return;
}
/* find the segment hash from the segment list to be deleted. */
if (next_obj->picl_hdl == nodehdl) {
hash_obj->u.sec_node->segment_list =
next_obj->u.seg_node->next;
} else {
while (next_obj != NULL) {
if (next_obj->picl_hdl != nodehdl) {
prev_hash_obj = next_obj;
next_obj = next_obj->u.seg_node->next;
} else {
prev_hash_obj->u.seg_node->next =
next_obj->u.seg_node->next;
break;
}
}
if (next_obj == NULL) {
return;
}
}
if (next_obj->prev == NULL) {
cont_tbl->hash_obj[(next_obj->picl_hdl % TABLE_SIZE)].next =
next_obj->next;
if (next_obj->next != NULL)
next_obj->next->prev = NULL;
} else {
next_obj->prev->next = next_obj->next;
if (next_obj->next != NULL) {
next_obj->next->prev = next_obj->prev;
}
}
free_packet_list(next_obj, cont_tbl);
free(next_obj->u.seg_node);
free(next_obj);
}
/*
* Description : frudata_delete_segment is called when volatile property
* delete_segment under class segment is accessed.
*
* Arguments : ptree_warg_t is holds node handle of segment node and property
* handle of delete_segment property.
*/
/* ARGSUSED */
static int
frudata_delete_segment(ptree_warg_t *warg, const void *buf)
{
int retval;
int num_of_segment;
int num_of_pkt;
int pkt_cnt;
int count;
packet_t *pkt_buf;
segment_t *seg_buffer;
hash_obj_t *seg_hash;
hash_obj_t *pkt_hash;
hash_obj_t *hash_obj;
fru_segdesc_t *desc;
picl_nodehdl_t sec_nodehdl;
container_tbl_t *cont_tbl;
fru_access_hdl_t seg_acc_hdl;
fru_access_hdl_t new_sec_acc_hdl;
cont_tbl = lock_container_lock(warg->nodeh, SEGMENT_NODE, PICL_WRITE);
if (!cont_tbl) {
return (PICL_FAILURE);
}
/* segment hash */
hash_obj = lookup_node_object(warg->nodeh, SEGMENT_NODE, cont_tbl);
if (hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
/* fruaccess segment handle */
seg_acc_hdl = hash_obj->u.seg_node->segment_hdl;
/* call fruaccess to get new section handle */
if (fru_delete_segment(seg_acc_hdl, &new_sec_acc_hdl, &warg->cred)
== -1) {
unlock_container_lock(cont_tbl);
return (map_access_err(errno));
}
if (ptree_delete_node(warg->nodeh) != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
if (ptree_destroy_node(warg->nodeh) != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
/* get section node handle */
sec_nodehdl = hash_obj->u.seg_node->sec_nodehdl;
/* get section hash */
hash_obj = lookup_node_object(sec_nodehdl, SECTION_NODE, cont_tbl);
if (hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
free_segment_node(hash_obj, warg->nodeh, cont_tbl);
hash_obj->u.sec_node->num_of_segment = 0;
/* call fruaccess with new section handle */
num_of_segment = fru_get_num_segments(new_sec_acc_hdl, &warg->cred);
if (num_of_segment <= 0) {
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
seg_buffer = alloca(sizeof (segment_t) * num_of_segment);
if (seg_buffer == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
/* get all the segments */
retval = fru_get_segments(new_sec_acc_hdl, seg_buffer,
num_of_segment, &warg->cred);
if (retval == -1) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
seg_hash = hash_obj->u.sec_node->segment_list;
if (seg_hash == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
/* rebuild the segment list */
for (count = 0; count < num_of_segment; count++) {
desc = (fru_segdesc_t *)&seg_buffer[count].descriptor;
if (!(desc->field.field_perm & SEGMENT_READ)) {
seg_hash = seg_hash->u.seg_node->next;
continue;
}
if (desc->field.opaque) {
seg_hash = seg_hash->u.seg_node->next;
continue;
}
hash_obj->u.sec_node->num_of_segment++;
seg_hash->u.seg_node->segment_hdl = seg_buffer[count].handle;
num_of_pkt = fru_get_num_packets(seg_buffer[count].handle,
&warg->cred);
if (num_of_pkt <= 0) {
seg_hash = seg_hash->u.seg_node->next;
continue;
}
pkt_buf = alloca(sizeof (packet_t) * num_of_pkt);
if (pkt_buf == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
retval = fru_get_packets(seg_buffer[count].handle, pkt_buf,
num_of_pkt, &warg->cred);
if (retval == -1) {
seg_hash = seg_hash->u.seg_node->next;
continue;
}
pkt_hash = seg_hash->u.seg_node->packet_list;
if (pkt_hash == NULL) {
seg_hash = seg_hash->u.seg_node->next;
continue;
}
/* rebuild the packet list */
for (pkt_cnt = 0; pkt_cnt < num_of_pkt; pkt_cnt++) {
pkt_hash->u.pkt_node->pkt_handle =
pkt_buf[pkt_cnt].handle;
pkt_hash = pkt_hash->u.pkt_node->next;
}
seg_hash = seg_hash->u.seg_node->next;
if (seg_hash == NULL) {
break;
}
}
/* updated with new section handle */
hash_obj->u.sec_node->section_hdl = new_sec_acc_hdl;
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
/*
* Description : frudata_read_payload is called when volatile property
* payload is read.
*
* Arguments : ptree_rarg_t holds node handle of the table property.
* and property handle of the payload cell.
* p_buf contains payload data when function returns.
*
* Returns : PICL_SUCCESS on success.
* PICL_FAILURE on failure.
*/
static int
frudata_read_payload(ptree_rarg_t *rarg, void *buf)
{
int num_bytes;
hash_obj_t *hash_obj;
fru_access_hdl_t pkt_acc_hdl;
container_tbl_t *cont_tbl;
cont_tbl = lock_container_lock(rarg->nodeh, SEGMENT_NODE, PICL_READ);
if (!cont_tbl) {
return (PICL_FAILURE);
}
hash_obj = lookup_node_object(rarg->proph, PACKET_NODE, cont_tbl);
if (hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
pkt_acc_hdl = hash_obj->u.pkt_node->pkt_handle;
num_bytes = fru_get_payload(pkt_acc_hdl, buf,
hash_obj->u.pkt_node->paylen, &rarg->cred);
if (num_bytes != hash_obj->u.pkt_node->paylen) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
/*
* Description : frudata_write_payload is called when payload property cell
* is accessed.
*
* Arguments : ptree_warg_t holds node handle of the packet-table.
* and property handle of the payload cell.
* p_buf contains payload data.
*
* Returns : PICL_SUCCESS on success.
*
*/
static int
frudata_write_payload(ptree_warg_t *warg, const void *buf)
{
int retval;
hash_obj_t *hash_obj;
fru_access_hdl_t pkt_acc_hdl;
container_tbl_t *cont_tbl;
cont_tbl = lock_container_lock(warg->nodeh, SEGMENT_NODE, PICL_WRITE);
if (!cont_tbl) {
return (PICL_FAILURE);
}
hash_obj = lookup_node_object(warg->proph, PACKET_NODE, cont_tbl);
if (hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
pkt_acc_hdl = hash_obj->u.pkt_node->pkt_handle;
retval = fru_update_payload(pkt_acc_hdl, buf,
hash_obj->u.pkt_node->paylen,
&pkt_acc_hdl, &warg->cred);
if (retval == -1) {
unlock_container_lock(cont_tbl);
return (map_access_err(errno));
}
hash_obj->u.pkt_node->pkt_handle = pkt_acc_hdl;
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
/*
* callback volatile function is called when tag volatile property
* is accessed. this routine holds a read lock over the hash table
* and do a lookup over the property handle i.e property handle of
* the tag property passed in rarg parameter.
* tag value is copied into the buffer (void *buf).
*/
static int
frudata_read_tag(ptree_rarg_t *rarg, void *buf)
{
int retval;
hash_obj_t *hash_obj;
picl_prophdl_t rowproph;
container_tbl_t *cont_tbl;
cont_tbl = lock_container_lock(rarg->nodeh, SEGMENT_NODE, PICL_READ);
if (!cont_tbl) {
return (PICL_FAILURE);
}
retval = ptree_get_next_by_row(rarg->proph, &rowproph);
if (retval != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (retval);
}
hash_obj = lookup_node_object(rowproph, PACKET_NODE, cont_tbl);
if (hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
(void) memcpy(buf, &hash_obj->u.pkt_node->tag, sizeof (tag_t));
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
/*
* Description : create_packet_table() is called by fru_delete_packet_row(),
* to create a packet-table volatile property. it's called after
* deleting the packet-table. fru_delete_packet_row() calls
* frudata_read_packet_table() to add rows into the table.
*/
static int
create_packet_table(picl_nodehdl_t seghdl, picl_prophdl_t *thdl)
{
int retval;
picl_prophdl_t tblhdl;
picl_nodehdl_t prophdl;
ptree_propinfo_t prop;
retval = ptree_create_table(&tblhdl);
if (retval != PICL_SUCCESS) {
return (retval);
}
prop.version = PTREE_PROPINFO_VERSION;
prop.piclinfo.type = PICL_PTYPE_TABLE;
prop.piclinfo.accessmode = PICL_READ|PICL_WRITE;
prop.piclinfo.size = sizeof (picl_prophdl_t);
prop.read = NULL;
prop.write = NULL;
(void) strcpy(prop.piclinfo.name, PICL_PROP_PACKET_TABLE);
retval = ptree_create_and_add_prop(seghdl, &prop, &tblhdl,
&prophdl);
if (retval != PICL_SUCCESS) {
return (retval);
}
/* hold the table handle */
*thdl = tblhdl;
return (PICL_SUCCESS);
}
/*
* Description : frudata_delete_packet is called when write operation is
* performed on tag volatile property.
*
*
* Arguments : ptree_warg_t holds node handle to the segment node.
* and property handle of the tag cell in the packet table to be
* deleted.
* buf contains the tag data + plus DELETE_KEY_TAG
*
* Returns : PICL_SUCCESS on success
*
*/
static int
frudata_delete_packet(ptree_warg_t *warg, const void *buf)
{
int count = 0;
int retval;
int num_of_pkt;
uint64_t tag;
packet_t *packet;
hash_obj_t *seg_hash_obj;
hash_obj_t *pkt_hash_obj;
container_tbl_t *cont_tbl;
picl_prophdl_t tblhdl;
picl_prophdl_t rowproph;
fru_access_hdl_t new_seg_acc_hdl;
cont_tbl = lock_container_lock(warg->nodeh, SEGMENT_NODE, PICL_WRITE);
if (!cont_tbl) {
return (PICL_FAILURE);
}
/* get the payload property handle */
retval = ptree_get_next_by_row(warg->proph, &rowproph);
if (retval != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (retval);
}
/* do lookup on payload property handle */
pkt_hash_obj = lookup_node_object(rowproph, PACKET_NODE, cont_tbl);
if (pkt_hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
/* verify the tag */
tag = pkt_hash_obj->u.pkt_node->tag.raw_data;
tag &= FRUDATA_DELETE_TAG_MASK;
tag |= FRUDATA_DELETE_TAG_KEY;
if (*(uint64_t *)buf != tag) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
/* call fruaccess module */
retval = fru_delete_packet(pkt_hash_obj->u.pkt_node->pkt_handle,
&new_seg_acc_hdl, &warg->cred);
if (retval == -1) {
unlock_container_lock(cont_tbl);
return (map_access_err(errno));
}
/* delete the packet table */
retval = ptree_get_prop_by_name(warg->nodeh, PICL_PROP_PACKET_TABLE,
&tblhdl);
if (retval != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (retval);
}
retval = ptree_delete_prop(tblhdl);
if (retval != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (retval);
}
retval = ptree_destroy_prop(tblhdl);
if (retval != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (retval);
}
seg_hash_obj = lookup_node_object(warg->nodeh, SEGMENT_NODE,
cont_tbl);
if (seg_hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
/* free all packet hash object */
free_packet_list(seg_hash_obj, cont_tbl);
/* recreate the packet table */
retval = create_packet_table(warg->nodeh, &tblhdl);
if (retval != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (retval);
}
seg_hash_obj->u.seg_node->segment_hdl = new_seg_acc_hdl;
seg_hash_obj->u.seg_node->num_of_pkt = 0;
num_of_pkt = fru_get_num_packets(new_seg_acc_hdl, &warg->cred);
if (num_of_pkt == -1) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
if (num_of_pkt == 0) {
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
packet = alloca(sizeof (packet_t) * num_of_pkt);
if (packet == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
retval = fru_get_packets(new_seg_acc_hdl, packet,
num_of_pkt, &warg->cred);
if (retval == -1) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
/* rebuild the packet hash object */
for (count = 0; count < num_of_pkt; count++) {
(void) add_row_to_table(seg_hash_obj, tblhdl, packet+count,
cont_tbl);
}
seg_hash_obj->u.seg_node->num_of_pkt = num_of_pkt;
(void) ptree_update_propval_by_name(warg->nodeh, PICL_PROP_NUM_TAGS,
&num_of_pkt, sizeof (uint32_t));
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
/*
* called from frudata_delete_packet(), frudata_add_packet(),
* frudata_read_packet() callback routine to add packet into
* the packet table. it also create hash node object for each
* individual packet and add the object to the packet list.
*/
static int
add_row_to_table(hash_obj_t *seg_obj, picl_nodehdl_t tblhdl, packet_t *pkt,
container_tbl_t *cont_tbl)
{
int retval;
int paylen;
size_t tag_size;
hash_obj_t *hash_obj;
fru_tagtype_t tagtype;
picl_prophdl_t prophdl[NUM_OF_COL_IN_PKT_TABLE];
ptree_propinfo_t prop;
prop.version = PTREE_PROPINFO_VERSION;
prop.piclinfo.type = PICL_PTYPE_BYTEARRAY;
prop.piclinfo.accessmode = PICL_READ|PICL_WRITE|PICL_VOLATILE;
prop.piclinfo.size = sizeof (fru_tag_t);
prop.read = frudata_read_tag;
prop.write = frudata_delete_packet;
/* tag property node */
(void) strcpy(prop.piclinfo.name, PICL_PROP_TAG);
paylen = get_payload_length((void *)&pkt->tag);
if (paylen < 0) {
return (PICL_FAILURE);
}
retval = ptree_create_prop(&prop, NULL, &prophdl[0]);
if (retval != PICL_SUCCESS) {
return (retval);
}
/* payload property node */
prop.piclinfo.type = PICL_PTYPE_BYTEARRAY;
prop.piclinfo.size = paylen;
(void) strcpy(prop.piclinfo.name, PICL_PROP_PAYLOAD);
prop.piclinfo.accessmode = PICL_READ|PICL_WRITE|PICL_VOLATILE;
prop.read = frudata_read_payload;
prop.write = frudata_write_payload;
retval = ptree_create_prop(&prop, NULL, &prophdl[1]);
if (retval != PICL_SUCCESS) {
return (retval);
}
hash_obj = alloc_packet_node_object(prophdl[1], pkt);
if (hash_obj == NULL) {
return (PICL_FAILURE);
}
retval = ptree_add_row_to_table(tblhdl, NUM_OF_COL_IN_PKT_TABLE,
prophdl);
if (retval != PICL_SUCCESS) {
free(hash_obj);
return (retval);
}
tagtype = get_tag_type((fru_tag_t *)&pkt->tag);
if (tagtype == -1) {
return (PICL_FAILURE);
}
tag_size = get_tag_size(tagtype);
if (tag_size == (size_t)-1) {
return (PICL_FAILURE);
}
hash_obj->u.pkt_node->paylen = paylen;
hash_obj->u.pkt_node->tag.raw_data = 0;
(void) memcpy(&hash_obj->u.pkt_node->tag, &pkt->tag, tag_size);
add_nodeobject_to_hashtable(hash_obj, cont_tbl);
add_to_packet_list(seg_obj, hash_obj);
return (PICL_SUCCESS);
}
/*
* called from frudata_read_segment() callback routine. it's called after
* creating the packet table under class segment. this routine reads the
* segment data to get total number of packets in the segments and add
* the tag and payload data into the table. it calls add_row_to_table
* routine to add individual row into the packet table.
*/
static int
frudata_read_packet(picl_nodehdl_t nodeh, picl_prophdl_t *tblhdl,
container_tbl_t *cont_tbl, door_cred_t *cred)
{
int cnt;
int retval;
int num_of_pkt;
packet_t *packet;
hash_obj_t *hash_obj;
fru_access_hdl_t seg_acc_hdl;
hash_obj = lookup_node_object(nodeh, SEGMENT_NODE, cont_tbl);
if (hash_obj == NULL) {
return (PICL_FAILURE);
}
if (hash_obj->u.seg_node->num_of_pkt == -1) {
/* get the access handle */
seg_acc_hdl = hash_obj->u.seg_node->segment_hdl;
/* get total number of packets */
num_of_pkt = fru_get_num_packets(seg_acc_hdl, cred);
if (num_of_pkt < 0) {
hash_obj->u.seg_node->num_of_pkt = 0;
return (map_access_err(errno));
}
if (num_of_pkt == 0) {
hash_obj->u.seg_node->num_of_pkt = 0;
return (0);
}
/* allocate buffer */
packet = alloca(sizeof (packet_t) * num_of_pkt);
if (packet == NULL) {
hash_obj->u.seg_node->num_of_pkt = 0;
return (0);
}
/* get all the packet into the packet buffer */
retval = fru_get_packets(seg_acc_hdl, packet, num_of_pkt, cred);
if (retval == -1) {
return (0);
}
/* add payload and tag into the table. */
for (cnt = 0; cnt < num_of_pkt; cnt++) {
(void) add_row_to_table(hash_obj, *tblhdl, packet+cnt,
cont_tbl);
}
hash_obj->u.seg_node->num_of_pkt = num_of_pkt;
}
return (0);
}
/*
* Description : frudata_add_packet is called when add-packet volatile
* property is accessed.
*
* Arguments : ptree_warg_t holds node handle of the segment node and
* property handle of add-packet property.
* p_buf- contains packet data to be added.
*
* Return : PICL_SUCCESS on success.
*
*/
/* ARGSUSED */
static int
frudata_add_packet(ptree_warg_t *warg, const void *buf)
{
size_t tag_size;
int paylen;
int retval;
int num_of_pkt;
int cnt;
packet_t packet;
packet_t *pkt_buf;
hash_obj_t *hash_obj;
hash_obj_t *pkt_hash;
container_tbl_t *cont_tbl;
fru_tagtype_t tagtype;
picl_prophdl_t tblhdl;
fru_access_hdl_t seg_acc_hdl;
fru_access_hdl_t new_seg_acc_hdl;
cont_tbl = lock_container_lock(warg->nodeh, SEGMENT_NODE, PICL_WRITE);
if (!cont_tbl) {
return (PICL_FAILURE);
}
hash_obj = lookup_node_object(warg->nodeh, SEGMENT_NODE, cont_tbl);
if (hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
seg_acc_hdl = hash_obj->u.seg_node->segment_hdl;
tagtype = get_tag_type((void *)buf);
if (tagtype == -1) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
tag_size = get_tag_size(tagtype);
if (tag_size == (size_t)-1) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
paylen = get_payload_length((void *)buf);
if (paylen == -1) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
packet.tag = 0;
(void) memcpy(&packet.tag, buf, tag_size);
retval = fru_append_packet(seg_acc_hdl, &packet, (char *)buf + tag_size,
paylen, &new_seg_acc_hdl, &warg->cred);
if (retval == -1) {
unlock_container_lock(cont_tbl);
return (map_access_err(errno));
}
retval = ptree_get_propval_by_name(warg->nodeh,
PICL_PROP_PACKET_TABLE, &tblhdl, sizeof (picl_prophdl_t));
if (retval != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (retval);
}
retval = add_row_to_table(hash_obj, tblhdl, &packet, cont_tbl);
if (retval != PICL_SUCCESS) {
unlock_container_lock(cont_tbl);
return (retval);
}
num_of_pkt = fru_get_num_packets(new_seg_acc_hdl, &warg->cred);
if (num_of_pkt == -1) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
pkt_buf = alloca(sizeof (packet_t) * num_of_pkt);
if (pkt_buf == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
retval = fru_get_packets(new_seg_acc_hdl, pkt_buf,
num_of_pkt, &warg->cred);
if (retval == -1) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
pkt_hash = hash_obj->u.seg_node->packet_list;
if (pkt_hash == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
for (cnt = 0; cnt < num_of_pkt; cnt++) {
pkt_hash->u.pkt_node->pkt_handle = pkt_buf[cnt].handle;
pkt_hash = pkt_hash->u.pkt_node->next;
}
hash_obj->u.seg_node->num_of_pkt = num_of_pkt;
(void) ptree_update_propval_by_name(warg->nodeh, PICL_PROP_NUM_TAGS,
&num_of_pkt, sizeof (uint32_t));
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
static void
freeup(picl_nodehdl_t nodeh)
{
(void) ptree_delete_node(nodeh);
(void) ptree_destroy_node(nodeh);
}
/*
* called by frudata_read_segment() and fru_data_add_segment() callback routine.
* it's called to create a segment node and all it's property beneith the
* segment node in the picl tree.
*/
static int
create_segment_node(hash_obj_t *sec_obj, picl_nodehdl_t sec_node,
segment_t *segment, container_tbl_t *cont_tbl, door_cred_t *cred)
{
int retval;
char segname[SEG_NAME_LEN + 1];
uint32_t numoftags = 0;
uint32_t protection;
hash_obj_t *hash_obj;
picl_nodehdl_t nodehdl;
picl_prophdl_t prophdl;
picl_nodehdl_t tblhdl;
ptree_propinfo_t prop;
(void) strlcpy(segname, segment->name, SEG_NAME_LEN + 1);
segname[SEG_NAME_LEN] = '\0';
if (!(isprint(segname[0]) || isprint(segname[1]))) {
return (PICL_FAILURE);
}
if (ptree_create_node(segname, PICL_CLASS_SEGMENT, &nodehdl)
!= PICL_SUCCESS) {
return (PICL_FAILURE);
}
/* create property node */
prop.version = PTREE_PROPINFO_VERSION;
prop.piclinfo.accessmode = PICL_READ;
prop.read = NULL;
prop.write = NULL;
prop.piclinfo.type = PICL_PTYPE_UNSIGNED_INT;
prop.piclinfo.size = sizeof (uint32_t);
/* descriptor property */
(void) strcpy(prop.piclinfo.name, PICL_PROP_DESCRIPTOR);
if (ptree_create_and_add_prop(nodehdl, &prop, &segment->descriptor,
&prophdl) != PICL_SUCCESS) {
freeup(nodehdl);
return (PICL_FAILURE);
}
/* offset property */
(void) strcpy(prop.piclinfo.name, PICL_PROP_OFFSET);
if (ptree_create_and_add_prop(nodehdl, &prop, &segment->offset,
&prophdl) != PICL_SUCCESS) {
freeup(nodehdl);
return (PICL_FAILURE);
}
/* length property */
(void) strcpy(prop.piclinfo.name, PICL_PROP_LENGTH);
if (ptree_create_and_add_prop(nodehdl, &prop, &segment->length,
&prophdl) != PICL_SUCCESS) {
freeup(nodehdl);
return (PICL_FAILURE);
}
/* Number of Tags */
(void) strcpy(prop.piclinfo.name, PICL_PROP_NUM_TAGS);
if (ptree_create_and_add_prop(nodehdl, &prop, &numoftags, &prophdl)
!= PICL_SUCCESS) {
freeup(nodehdl);
return (PICL_FAILURE);
}
if (create_packet_table(nodehdl, &tblhdl) != PICL_SUCCESS) {
freeup(nodehdl);
return (PICL_FAILURE);
}
retval = ptree_get_propval_by_name(sec_node,
PICL_PROP_PROTECTED, &protection, sizeof (uint32_t));
if (retval != PICL_SUCCESS) {
freeup(nodehdl);
return (PICL_FAILURE);
}
if (protection == 0) { /* to be added only read/write section */
/* delete segment volatile property */
prop.piclinfo.type = PICL_PTYPE_UNSIGNED_INT;
prop.piclinfo.size = sizeof (uint32_t);
prop.piclinfo.accessmode = PICL_WRITE|PICL_VOLATILE;
prop.write = frudata_delete_segment;
prop.read = frudata_read_callback;
(void) strcpy(prop.piclinfo.name, PICL_PROP_DELETE_SEGMENT);
if (ptree_create_and_add_prop(nodehdl, &prop, NULL, &prophdl)
!= PICL_SUCCESS) {
freeup(nodehdl);
return (PICL_FAILURE);
}
/* add packet volatile property */
prop.piclinfo.type = PICL_PTYPE_BYTEARRAY;
prop.piclinfo.size = segment->length; /* segment length */
prop.piclinfo.accessmode = PICL_READ|PICL_WRITE|PICL_VOLATILE;
prop.read = frudata_read_callback;
prop.write = frudata_add_packet;
(void) strcpy(prop.piclinfo.name, PICL_PROP_ADD_PACKET);
if (ptree_create_and_add_prop(nodehdl, &prop, NULL, &prophdl)
!= PICL_SUCCESS) {
freeup(nodehdl);
return (PICL_FAILURE);
}
}
if (ptree_add_node(sec_node, nodehdl) != PICL_SUCCESS) {
freeup(nodehdl);
return (PICL_FAILURE);
}
hash_obj = alloc_segment_node_object(nodehdl, segment);
if (hash_obj == NULL) {
freeup(nodehdl);
return (PICL_FAILURE);
}
add_nodeobject_to_hashtable(hash_obj, cont_tbl);
add_to_segment_list(sec_obj, hash_obj);
retval = frudata_read_packet(nodehdl, &tblhdl, cont_tbl, cred);
if (retval != 0) {
return (PICL_SUCCESS);
}
(void) ptree_update_propval_by_name(nodehdl, PICL_PROP_NUM_TAGS,
&hash_obj->u.seg_node->num_of_pkt, sizeof (uint32_t));
return (PICL_SUCCESS);
}
/*
* Description :frudata_read_segment is called when num_segment volatile
* property is accessed.
*
* Arguments : ptree_rarg_t contains node handle of the section node.
* and property node of num_segments.
* void * will hold number of segment.
*
* Returns : PICL_SUCCESS on success.
* PICL_FAILURE on failure.
*/
static int
frudata_read_segment(ptree_rarg_t *rarg, void *buf)
{
int num_of_segment;
int cnt;
int retval;
segment_t *segment;
hash_obj_t *hash_obj;
fru_segdesc_t *desc;
fru_access_hdl_t sec_acc_hdl;
container_tbl_t *cont_tbl;
cont_tbl = lock_container_lock(rarg->nodeh, SECTION_NODE, PICL_READ);
if (!cont_tbl) {
return (PICL_FAILURE);
}
hash_obj = lookup_node_object(rarg->nodeh, SECTION_NODE, cont_tbl);
if (hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
if (hash_obj->u.sec_node->num_of_segment == -1) {
sec_acc_hdl = hash_obj->u.sec_node->section_hdl;
hash_obj->u.sec_node->num_of_segment = 0;
num_of_segment = fru_get_num_segments(sec_acc_hdl,
&rarg->cred);
if (num_of_segment < 0) {
*(int *)buf = 0;
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
if (num_of_segment == 0) {
*(int *)buf = 0;
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
segment = alloca(sizeof (segment_t) * num_of_segment);
if (segment == NULL) {
*(int *)buf = 0;
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
retval = fru_get_segments(sec_acc_hdl, segment,
num_of_segment, &rarg->cred);
if (retval == -1) {
*(int *)buf = 0;
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
for (cnt = 0; cnt < num_of_segment; cnt++) {
desc = (fru_segdesc_t *)&segment[cnt].descriptor;
if (!(desc->field.field_perm & SEGMENT_READ)) {
continue;
}
/* if opaque segment don't create segment node */
if (desc->field.opaque) {
continue;
}
(void) create_segment_node(hash_obj, rarg->nodeh,
&segment[cnt], cont_tbl, &rarg->cred);
hash_obj->u.sec_node->num_of_segment++;
}
}
/* return number of segment in the section */
*(int *)buf = hash_obj->u.sec_node->num_of_segment;
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
/*
* Description : frudata_add_segment is called when volatile property
* add_segment under class node section is accessed.
*
* Arguments : ptree_warg_t holds node handle for the section node.
* property handle for the add_segment property.
*
* Returns : PICL_SUCCESS on success.
* PICL_FAILURE on failure.
*/
static int
frudata_add_segment(ptree_warg_t *warg, const void *buf)
{
int retval;
int cnt;
int num_of_segment;
segment_t *seg_buf;
segment_t segment;
hash_obj_t *seg_hash;
hash_obj_t *hash_obj;
container_tbl_t *cont_tbl;
fru_segdef_t *seg_def;
fru_segdesc_t *desc;
fru_access_hdl_t new_sec_acc_hdl;
seg_def = (fru_segdef_t *)buf;
/* initialize segment_t */
segment.handle = 0;
(void) memcpy(segment.name, seg_def->name, SEG_NAME_LEN);
segment.descriptor = seg_def->desc.raw_data;
segment.length = seg_def->size; /* segment length */
segment.offset = seg_def->address; /* segment offset */
desc = (fru_segdesc_t *)&segment.descriptor;
if (!(desc->field.field_perm & SEGMENT_READ)) {
return (PICL_PERMDENIED);
}
cont_tbl = lock_container_lock(warg->nodeh, SECTION_NODE, PICL_WRITE);
if (!cont_tbl) {
return (PICL_FAILURE);
}
hash_obj = lookup_node_object(warg->nodeh, SECTION_NODE, cont_tbl);
if (hash_obj == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
/* call fruaccess module, get the new section handle. */
retval = fru_add_segment(hash_obj->u.sec_node->section_hdl,
&segment, &new_sec_acc_hdl, &warg->cred);
if (retval == -1) {
unlock_container_lock(cont_tbl);
return (map_access_err(errno));
}
/* call access module with new section handle */
num_of_segment = fru_get_num_segments(new_sec_acc_hdl, &warg->cred);
seg_buf = alloca(sizeof (segment_t) * num_of_segment);
if (seg_buf == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
retval = fru_get_segments(new_sec_acc_hdl, seg_buf,
num_of_segment, &warg->cred);
if (retval == -1) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
segment.offset = seg_buf[(num_of_segment -1)].offset;
segment.handle = seg_buf[(num_of_segment-1)].handle;
(void) create_segment_node(hash_obj, warg->nodeh, &segment,
cont_tbl, &warg->cred);
/* rebuild segment list */
seg_hash = hash_obj->u.sec_node->segment_list;
if (seg_hash == NULL) {
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
hash_obj->u.sec_node->num_of_segment = 0;
for (cnt = 0; cnt < num_of_segment; cnt++) {
desc = (fru_segdesc_t *)&seg_buf[cnt].descriptor;
if (!(desc->field.field_perm & SEGMENT_READ)) {
continue;
}
/* if opaque segment don't create segment node */
if (desc->field.opaque) {
continue;
}
seg_hash->u.seg_node->segment_hdl =
seg_buf[cnt].handle;
seg_hash = seg_hash->u.seg_node->next;
hash_obj->u.sec_node->num_of_segment++;
}
/* update with new section handle */
hash_obj->u.sec_node->section_hdl = new_sec_acc_hdl;
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
/*
* called from frudata_write_section() callback routine to create
* section node and all the property under class section. it also
* allocate hash node object for each section in the container and
* add the section node object in the section list.
*/
static int
create_section_node(picl_nodehdl_t nodehdl, int section_count,
section_t *section, container_tbl_t *cont_tbl)
{
char sec_name[SECNAMESIZE];
hash_obj_t *hash_obj;
hash_obj_t *cont_hash;
picl_nodehdl_t chld_node;
picl_prophdl_t prophdl;
ptree_propinfo_t prop;
(void) snprintf(sec_name, SECNAMESIZE, "section%d", section_count);
if (ptree_create_node(sec_name, PICL_CLASS_SECTION, &chld_node)
!= PICL_SUCCESS) {
return (PICL_FAILURE);
}
prop.version = PTREE_PROPINFO_VERSION;
prop.piclinfo.type = PICL_PTYPE_UNSIGNED_INT;
prop.piclinfo.accessmode = PICL_READ;
prop.piclinfo.size = sizeof (uint32_t);
prop.read = NULL;
prop.write = NULL;
/* offset */
(void) strcpy(prop.piclinfo.name, PICL_PROP_OFFSET);
if (ptree_create_and_add_prop(chld_node, &prop, &section->offset,
&prophdl) != PICL_SUCCESS) {
freeup(chld_node);
return (PICL_FAILURE);
}
/* length */
(void) strcpy(prop.piclinfo.name, PICL_PROP_LENGTH);
if (ptree_create_and_add_prop(chld_node, &prop, &section->length,
&prophdl) != PICL_SUCCESS) {
freeup(chld_node);
return (PICL_FAILURE);
}
/* protected */
(void) strcpy(prop.piclinfo.name, PICL_PROP_PROTECTED);
if (ptree_create_and_add_prop(chld_node, &prop, &section->protection,
&prophdl) != PICL_SUCCESS) {
freeup(chld_node);
return (PICL_FAILURE);
}
prop.piclinfo.accessmode = PICL_READ|PICL_VOLATILE;
prop.read = frudata_read_segment;
(void) strcpy(prop.piclinfo.name, PICL_PROP_NUM_SEGMENTS);
if (ptree_create_and_add_prop(chld_node, &prop, NULL, &prophdl)
!= PICL_SUCCESS) {
freeup(chld_node);
return (PICL_FAILURE);
}
prop.piclinfo.type = PICL_PTYPE_BYTEARRAY;
prop.piclinfo.size = sizeof (fru_segdef_t);
prop.piclinfo.accessmode = PICL_WRITE|PICL_READ|PICL_VOLATILE;
prop.write = frudata_add_segment; /* callback routine */
prop.read = frudata_read_callback;
(void) strcpy(prop.piclinfo.name, PICL_PROP_ADD_SEGMENT);
/* add-segment prop if read/write section */
if (section->protection == 0) {
if (ptree_create_and_add_prop(chld_node, &prop, NULL, &prophdl)
!= PICL_SUCCESS) {
freeup(chld_node);
return (PICL_FAILURE);
}
}
if (ptree_add_node(nodehdl, chld_node) != PICL_SUCCESS) {
freeup(chld_node);
return (PICL_FAILURE);
}
/* lookup for container handle */
cont_hash = lookup_node_object(nodehdl, CONTAINER_NODE, cont_tbl);
if (cont_hash == NULL) {
freeup(chld_node);
return (PICL_FAILURE);
}
hash_obj = alloc_section_node_object(chld_node, section);
if (hash_obj == NULL) {
freeup(chld_node);
return (PICL_FAILURE);
}
add_nodeobject_to_hashtable(hash_obj, cont_tbl);
add_to_section_list(cont_hash, hash_obj);
return (PICL_SUCCESS);
}
/*
* Description :frudata_write_section is called when volatile container
* property is accessed. it reads the section table associated
* with the specified node handle(container) in ptree_rarg_t.
* it calls search_root_node to search the node handle to open the
* device associated with the node handle. it creates section
* node and it's associated property. it also creates
* volatile property num_segments.
*
* Argument : ptree_rarg_t : contains node handle of fru container the
* container.
* property handle of the container.
*
* Return : PICL_SUCCESS on success.
*
*/
/* ARGSUSED */
static int
frudata_write_section(ptree_warg_t *warg, const void *buf)
{
int retval;
int num_of_section;
int count;
section_t *section;
hash_obj_t *hash_obj;
container_tbl_t *cont_tbl = NULL;
fru_access_hdl_t cont_acc_hdl;
(void) pthread_mutex_lock(&cont_tbl_lock);
/*
* if lookup succeed return from this function with PICL_SUCCESS
* because first write operation has already occurred on this container,
* it also means that the container has been already initialzed.
*/
cont_tbl = lookup_container_table(warg->nodeh, CONTAINER_NODE);
if (cont_tbl != NULL) { /* found the hash obj in the hash table */
(void) pthread_mutex_unlock(&cont_tbl_lock);
return (PICL_SUCCESS);
}
/*
* lookup failed that means this is first write on the
* container property. allocate a new container hash table for this
* new container and add to the cont_tbl hash table.
*/
cont_tbl = alloc_container_table(warg->nodeh);
if (cont_tbl == NULL) {
(void) pthread_mutex_unlock(&cont_tbl_lock);
return (map_access_err(errno));
}
hash_obj = alloc_container_node_object(warg->nodeh);
if (hash_obj == NULL) {
(void) pthread_mutex_unlock(&cont_tbl_lock);
free(cont_tbl->hash_obj);
free(cont_tbl);
return (map_access_err(errno));
}
/* add container table object to container table */
add_tblobject_to_container_tbl(cont_tbl);
/* add the hash object to container hash table. */
add_nodeobject_to_hashtable(hash_obj, cont_tbl);
while (pthread_rwlock_trywrlock(&cont_tbl->rwlock) == EBUSY) {
pthread_cond_wait(&cont_tbl->cond_var, &cont_tbl_lock);
}
(void) pthread_mutex_unlock(&cont_tbl_lock);
/* fruaccess handle */
cont_acc_hdl = hash_obj->u.cont_node->cont_hdl;
num_of_section = fru_get_num_sections(cont_acc_hdl, &warg->cred);
if (num_of_section == -1) {
free(hash_obj);
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
section = alloca(num_of_section * sizeof (section_t));
retval = fru_get_sections(cont_acc_hdl, section,
num_of_section, &warg->cred);
if (retval == -1) {
free(hash_obj);
unlock_container_lock(cont_tbl);
return (PICL_FAILURE);
}
hash_obj->u.cont_node->num_of_section = num_of_section;
for (count = 0; count < num_of_section; count++) {
(void) create_section_node(warg->nodeh, count,
section + count, cont_tbl);
}
unlock_container_lock(cont_tbl);
return (PICL_SUCCESS);
}
/* create container and add-segment property */
static int
create_container_prop(picl_nodehdl_t fruhdl)
{
int retval;
picl_prophdl_t prophdl;
ptree_propinfo_t prop;
prop.version = PTREE_PROPINFO_VERSION;
prop.piclinfo.type = PICL_PTYPE_UNSIGNED_INT;
prop.piclinfo.size = sizeof (uint32_t);
prop.piclinfo.accessmode = PICL_WRITE|PICL_VOLATILE;
(void) strcpy(prop.piclinfo.name, PICL_PROP_CONTAINER);
prop.read = frudata_read_callback;
prop.write = frudata_write_section; /* callback routine */
/* create a property */
retval = ptree_create_and_add_prop(fruhdl, &prop, NULL, &prophdl);
return (retval);
}
/* search for FRUDataAvailable and create container and add segment property */
static void
create_frudata_props(picl_prophdl_t fruhdl)
{
int retval;
picl_nodehdl_t chldhdl;
picl_nodehdl_t tmphdl;
for (retval = ptree_get_propval_by_name(fruhdl, PICL_PROP_CHILD,
&chldhdl, sizeof (picl_nodehdl_t)); retval != PICL_PROPNOTFOUND;
retval = ptree_get_propval_by_name(chldhdl, PICL_PROP_PEER,
&chldhdl, sizeof (picl_nodehdl_t))) {
if (retval != PICL_SUCCESS)
return;
/* Does it have a FRUDataAvailable property */
retval = ptree_get_prop_by_name(chldhdl,
PICL_PROP_FRUDATA_AVAIL, &tmphdl);
if (retval == PICL_SUCCESS) {
(void) create_container_prop(chldhdl);
}
/* Traverse tree recursively */
(void) create_frudata_props(chldhdl);
}
}
/*
* Search for the frutree config file from the platform specific
* directory to the common directory.
*
* The size of outfilename must be PATH_MAX
*/
static int
get_config_file(char *outfilename)
{
char nmbuf[SYS_NMLN];
char pname[PATH_MAX];
if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) != -1) {
(void) snprintf(pname, PATH_MAX, FRUDATA_CONFFILE_NAME, nmbuf);
if (access(pname, R_OK) == 0) {
(void) strlcpy(outfilename, pname, PATH_MAX);
return (0);
}
}
if (sysinfo(SI_MACHINE, nmbuf, sizeof (nmbuf)) != -1) {
(void) snprintf(pname, PATH_MAX, FRUDATA_CONFFILE_NAME, nmbuf);
if (access(pname, R_OK) == 0) {
(void) strlcpy(outfilename, pname, PATH_MAX);
return (0);
}
}
(void) snprintf(pname, PATH_MAX, "%s/%s", PICLD_COMMON_PLUGIN_DIR,
FRUDATA_CONFFILE_NAME);
if (access(pname, R_OK) == 0) {
(void) strlcpy(outfilename, pname, PATH_MAX);
return (0);
}
return (-1);
}
/*
* called from delete_frudata_props(), this routine delete the section node
* and free's the section hash object. it calls free_segment_node() to
* delete segment node beneath it.
*/
static void
free_section_node(hash_obj_t *sec_hash, container_tbl_t *cont_tbl)
{
hash_obj_t *seg_hash;
for (seg_hash = sec_hash->u.sec_node->segment_list; seg_hash != NULL;
seg_hash = seg_hash->u.seg_node->next) {
free_segment_node(seg_hash, seg_hash->picl_hdl, cont_tbl);
}
if (sec_hash->prev == NULL) {
cont_tbl->hash_obj[(sec_hash->picl_hdl % TABLE_SIZE)].next =
sec_hash->next;
if (sec_hash->next != NULL) {
sec_hash->next->prev = NULL;
}
} else {
sec_hash->prev->next = sec_hash->next;
if (sec_hash->next != NULL) {
sec_hash->next->prev = sec_hash->prev;
}
}
/* delete & destroy section node */
(void) ptree_delete_node(sec_hash->picl_hdl);
(void) ptree_destroy_node(sec_hash->picl_hdl);
free(sec_hash->u.sec_node);
free(sec_hash);
}
/*
* called from delete_frudata_props(), this routine free's the container
* hash object.
*/
static void
unlink_container_node(container_tbl_t *cont_hash)
{
if (cont_hash->prev == NULL) {
container_table[(cont_hash->picl_hdl % TABLE_SIZE)] =
cont_hash->next;
if (cont_hash->next != NULL) {
cont_hash->next->prev = NULL;
}
} else {
cont_hash->prev->next = cont_hash->next;
if (cont_hash->next != NULL) {
cont_hash->next->prev = cont_hash->prev;
}
}
}
/*
* called from frudata_event_handler() to free the corresponding hash object
* of the removed fru.
*/
static void
delete_frudata_props(picl_nodehdl_t fru_hdl)
{
hash_obj_t *cont_hash;
hash_obj_t *free_obj;
hash_obj_t *sec_hash;
container_tbl_t *cont_tbl;
(void) pthread_mutex_lock(&cont_tbl_lock);
cont_tbl = lookup_container_table(fru_hdl, CONTAINER_NODE);
if (cont_tbl == NULL) {
(void) pthread_mutex_unlock(&cont_tbl_lock);
return;
}
/* remove the container object from the container table */
unlink_container_node(cont_tbl);
(void) pthread_cond_broadcast(&cont_tbl->cond_var);
(void) pthread_mutex_unlock(&cont_tbl_lock);
/*
* waiting/blocking calling thread for all I/O in
* progress to complete. don't free the container
* hash before all I/O is complete.
*/
(void) pthread_rwlock_wrlock(&cont_tbl->rwlock);
(void) pthread_rwlock_unlock(&cont_tbl->rwlock);
cont_hash = lookup_node_object(fru_hdl, CONTAINER_NODE, cont_tbl);
if (cont_hash == NULL) {
return;
}
free_obj = cont_hash->u.cont_node->section_list;
/* walk through the section list */
for (sec_hash = free_obj; sec_hash != NULL; free_obj = sec_hash) {
sec_hash = sec_hash->u.sec_node->next;
free_section_node(free_obj, cont_tbl);
}
(void) fru_close_container(cont_hash->u.cont_node->cont_hdl);
free(cont_hash->u.cont_node);
free(cont_hash);
free(cont_tbl->hash_obj);
free(cont_tbl);
}
/*
* called when there is any state-change in location, fru, port nodes.
* this event handler handles only location state-changes.
*/
/* ARGSUSED */
static void
frudata_state_change_evhandler(const char *event_name, const void *event_arg,
size_t size, void *cookie)
{
int rc;
nvlist_t *nvlp;
ptree_propinfo_t prop;
picl_nodehdl_t loch, fruh;
picl_prophdl_t proph, prophdl;
char *present_state, *last_state;
char name[PICL_PROPNAMELEN_MAX];
if (strcmp(event_name, PICLEVENT_STATE_CHANGE) != 0)
return;
if (nvlist_unpack((char *)event_arg, size, &nvlp, NULL)) {
return;
}
if (nvlist_lookup_uint64(nvlp, PICLEVENTARG_NODEHANDLE,
&loch) == -1) {
nvlist_free(nvlp);
return;
}
if (ptree_get_propval_by_name(loch, PICL_PROP_CLASSNAME, name,
sizeof (name)) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
/* handle only location events */
if (strcmp(name, PICL_CLASS_LOCATION) != 0) {
nvlist_free(nvlp);
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_STATE,
&present_state)) {
nvlist_free(nvlp);
return;
}
rc = ptree_get_propval_by_name(loch, PICL_PROP_CHILD,
&fruh, sizeof (picl_nodehdl_t));
if (rc != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
/* fru removed */
if (strcmp(present_state, PICLEVENTARGVAL_EMPTY) == 0) {
delete_frudata_props(fruh);
nvlist_free(nvlp);
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_LAST_STATE,
&last_state)) {
nvlist_free(nvlp);
return;
}
/* fru added */
if ((strcmp(last_state, PICLEVENTARGVAL_EMPTY) == 0) ||
(strcmp(last_state, PICLEVENTARGVAL_UNKNOWN) == 0)) {
rc = ptree_get_prop_by_name(fruh, PICL_PROP_FRUDATA_AVAIL,
&proph);
if (rc != PICL_SUCCESS) {
if (fru_is_data_available(fruh) == 0) {
nvlist_free(nvlp);
return;
}
/* create the property */
prop.version = PTREE_PROPINFO_VERSION;
prop.piclinfo.type = PICL_PTYPE_VOID;
prop.piclinfo.accessmode = PICL_READ;
prop.piclinfo.size = 0;
(void) strncpy(prop.piclinfo.name,
PICL_PROP_FRUDATA_AVAIL,
sizeof (prop.piclinfo.name));
rc = ptree_create_prop(&prop, NULL, &prophdl);
if (rc != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
rc = ptree_add_prop(fruh, prophdl);
if (rc != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
}
(void) create_container_prop(fruh);
}
nvlist_free(nvlp);
}
/*
* called when event is posted when is fru is either added or removed from
* the picltree.
*/
/* ARGSUSED */
static void
frudata_event_handler(const char *event_name, const void *event_arg,
size_t size, void *cookie)
{
int retval;
char fullfilename[PATH_MAX];
picl_nodehdl_t fru_picl_hdl;
picl_nodehdl_t roothdl;
if (strcmp(event_name, PICL_FRU_REMOVED) == 0) {
retval = nvlist_lookup_uint64((nvlist_t *)event_arg,
PICLEVENTARG_FRUHANDLE, &fru_picl_hdl);
if (retval != PICL_SUCCESS) {
return;
}
/* free the hash object */
delete_frudata_props(fru_picl_hdl);
} else if (strcmp(event_name, PICL_FRU_ADDED) == 0) {
/*
* reparse the configuration file to create
* FRUDevicePath Prop.
*/
(void) get_config_file(fullfilename);
retval = ptree_get_root(&roothdl);
if (retval != PICL_SUCCESS) {
return;
}
(void) picld_pluginutil_parse_config_file(roothdl,
fullfilename);
retval = nvlist_lookup_uint64((nvlist_t *)event_arg,
PICLEVENTARG_PARENTHANDLE, &fru_picl_hdl);
if (retval != PICL_SUCCESS) {
return;
}
/* create container property */
create_frudata_props(fru_picl_hdl);
}
}
/*
* Function : plugin_init() is called by daemon. this routine is specified
* while registering with daemon. it performs the initialization
* of plugin module.
*/
static void
frudata_plugin_init(void)
{
int retval;
int count;
char fullfilename[PATH_MAX];
picl_nodehdl_t fru_nodehdl;
picl_nodehdl_t roothdl;
retval = ptree_get_root(&roothdl);
if (retval != PICL_SUCCESS) {
return;
}
(void) ptree_register_handler(PICL_FRU_ADDED,
frudata_event_handler, NULL);
(void) ptree_register_handler(PICL_FRU_REMOVED,
frudata_event_handler, NULL);
(void) ptree_register_handler(PICLEVENT_STATE_CHANGE,
frudata_state_change_evhandler, NULL);
(void) pthread_mutex_lock(&cont_tbl_lock);
for (count = 0; count < TABLE_SIZE; count++) {
container_table[count] = NULL;
}
(void) pthread_mutex_unlock(&cont_tbl_lock);
(void) get_config_file(fullfilename);
(void) picld_pluginutil_parse_config_file(roothdl, fullfilename);
retval = ptree_get_node_by_path(FRUTREE_PATH, &fru_nodehdl);
if (retval != PICL_SUCCESS) {
return;
}
create_frudata_props(fru_nodehdl);
}
static void
free_packet_hash_object(hash_obj_t *pkt_obj)
{
hash_obj_t *tmp_obj;
while (pkt_obj != NULL) {
tmp_obj = pkt_obj->u.pkt_node->next;
free(pkt_obj->u.pkt_node);
free(pkt_obj);
pkt_obj = tmp_obj;
}
}
static void
free_segment_hash_object(hash_obj_t *seg_obj)
{
hash_obj_t *tmp_obj;
while (seg_obj != NULL) {
free_packet_hash_object(seg_obj->u.seg_node->packet_list);
tmp_obj = seg_obj->u.seg_node->next;
free(seg_obj->u.seg_node);
free(seg_obj);
seg_obj = tmp_obj;
}
}
static void
free_hash_objects(hash_obj_t *sec_obj)
{
hash_obj_t *tmp_obj;
while (sec_obj != NULL) {
free_segment_hash_object(sec_obj->u.sec_node->segment_list);
tmp_obj = sec_obj->u.sec_node->next;
free(sec_obj->u.sec_node);
free(sec_obj);
sec_obj = tmp_obj;
}
}
/*
* called from frudata_plugin_fini() this routine walks through
* the hash table to free each and very hash object in the hash table.
*/
static void
free_hash_table(void)
{
int cnt;
picl_nodehdl_t nodehdl;
hash_obj_t *next_obj;
hash_obj_t *sec_obj;
container_tbl_t *cont_tbl;
for (cnt = 0; cnt < TABLE_SIZE; cnt++) {
while (container_table[cnt]) {
(void) pthread_mutex_lock(&cont_tbl_lock);
cont_tbl = container_table[cnt];
nodehdl = cont_tbl->picl_hdl;
cont_tbl = lookup_container_table(nodehdl,
CONTAINER_NODE);
if (cont_tbl == NULL) {
(void) pthread_mutex_unlock(&cont_tbl_lock);
break;
}
unlink_container_node(cont_tbl);
pthread_cond_broadcast(&cont_tbl->cond_var);
(void) pthread_mutex_unlock(&cont_tbl_lock);
/*
* waiting/blocking calling thread for all I/O in
* progress to complete. don't free the container
* hash until all I/O is complete.
*/
(void) pthread_rwlock_wrlock(&cont_tbl->rwlock);
(void) pthread_rwlock_unlock(&cont_tbl->rwlock);
next_obj = cont_tbl->hash_obj->next;
if (next_obj == NULL) {
break;
}
if (next_obj->object_type == CONTAINER_NODE) {
sec_obj = next_obj->u.cont_node->section_list;
free_hash_objects(sec_obj);
}
free(next_obj->u.cont_node);
free(next_obj);
container_table[cnt] = cont_tbl->next;
free(cont_tbl);
}
}
}
/*
* called by the daemon and perform frudata cleanup. hold the write lock
* over the entire hash table to free each and every hash object.
*/
static void
frudata_plugin_fini(void)
{
free_hash_table();
(void) ptree_unregister_handler(PICL_FRU_ADDED,
frudata_event_handler, NULL);
(void) ptree_unregister_handler(PICL_FRU_REMOVED,
frudata_event_handler, NULL);
(void) ptree_unregister_handler(PICLEVENT_STATE_CHANGE,
frudata_state_change_evhandler, NULL);
}