/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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, §ion->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, §ion->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, §ion->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, 0)) { 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); }