# # 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 $(SRC)/lib/Makefile.lib SUBDIRS = $(MACH64) # conditional assignments all : TARGET= all install : TARGET= install clean : TARGET= clean clobber : TARGET= clobber _msg : TARGET= _msg sparc_HDRS= sparcv9_HDRS= i386_HDRS= amd64_HDRS= # Hammerhead: Fix recursive variable - use architecture-specific HDRS HDRS= $($(MACH64)_HDRS) ROOTHDRDIR= $(ROOT)/usr/include ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%) CHECKHDRS= $(HDRS:%.h=%.check) .KEEP_STATE: all install clean clobber: $(SUBDIRS) _msg: $(MACH64) $(ROOTHDRDIR)/%: % $(INS.file) $(ROOTHDRDIR): $(INS.dir) check: $(CHECKHDRS) $(MACH64): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # 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. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= libfruraw.a VERS= .1 OBJECTS= fruraw.o raw_access.o crcmodel.o crcutils.o # include library definitions include $(SRC)/lib/Makefile.lib CLOBBERFILES += $(LIBLINKS) LIBS = $(DYNLIB) MAPFILES = CPPFLAGS += -I.. \ -I$(SRC)/lib/libfru/include \ -I$(SRC)/lib/libfruutils CPPFLAGS += -D_REENTRANT CFLAGS += $(CCVERBOSE) CERRWARN += $(CNOWARN_UNINIT) # not linted SMATCH=off XGETFLAGS += -a POFILE= picl.po .KEEP_STATE: all : $(LIBS) chmod 755 $(DYNLIB) %.po: ../%.c $(CP) $< $<.i $(BUILD.po) _msg: $(MSGDOMAIN) $(POFILE) $(RM) $(MSGDOMAIN)/$(POFILE) $(CP) $(POFILE) $(MSGDOMAIN) # include library targets include $(SRC)/lib/Makefile.targ pics/%.o: ../%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) # # 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 ../Makefile.com include $(SRC)/lib/Makefile.lib.64 LDLIBS += -L$(SRC)/lib/libfruutils/$(MACH64) LDLIBS += -L$(SRC)/lib/libfru/$(MACH64) LDLIBS += -lc -lfru -lfruutils .KEEP_STATE: all: $(ROOTLIBDIR64) $(LIBS) $(LIBLINKS) $(LIBLINKS): FRC $(RM) $@; $(SYMLINK) $(DYNLIB) $@ $(ROOTLIBDIR64): $(INS.dir) install: all $(ROOTLIBS64) $(ROOTLINKS64) FRC: /* * 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. */ /* * * Start of crcmodel.c * * * Author : Ross Williams (ross@guest.adelaide.edu.au.). * Date : 3 June 1993. * Status : Public domain. * * Description : This is the implementation (.c) file for the reference * implementation of the Rocksoft^tm Model CRC Algorithm. For more * information on the Rocksoft^tm Model CRC Algorithm, see the document * titled "A Painless Guide to CRC Error Detection Algorithms" by Ross * Williams (ross@guest.adelaide.edu.au.). This document is likely to be in * "ftp.adelaide.edu.au/pub/rocksoft". * * Note: Rocksoft is a trademark of Rocksoft Pty Ltd, Adelaide, Australia. * * * * Implementation Notes * -------------------- * To avoid inconsistencies, the specification of each function is not echoed * here. See the header file for a description of these functions. * This package is light on checking because I want to keep it short and * simple and portable (i.e. it would be too messy to distribute my entire * C culture (e.g. assertions package) with this package. * * */ #include "crcmodel.h" /* The following definitions make the code more readable. */ #define BITMASK(X) (1L << (X)) #define MASK32 0xFFFFFFFFL #define LOCAL static LOCAL uint32_t reflect P_((uint32_t v, int b)); LOCAL uint32_t reflect(v, b) /* Returns the value v with the bottom b [0,32] bits reflected. */ /* Example: reflect(0x3e23L,3) == 0x3e26 */ uint32_t v; int b; { int i; uint32_t t = v; for (i = 0; i < b; i++) { if (t & 1L) v |= BITMASK((b-1)-i); else v &= ~BITMASK((b-1)-i); t >>= 1; } return (v); } LOCAL uint32_t widmask P_((p_cm_t)); LOCAL uint32_t widmask(p_cm) /* Returns a longword whose value is (2^p_cm->cm_width)-1. */ /* The trick is to do this portably (e.g. without doing <<32). */ p_cm_t p_cm; { return ((((1L<<(p_cm->cm_width-1))-1L)<<1)|1L); } void cm_ini(p_cm) p_cm_t p_cm; { p_cm->cm_reg = p_cm->cm_init; } void cm_nxt(p_cm, ch) p_cm_t p_cm; int ch; { int i; uint32_t uch = (uint32_t)ch; uint32_t topbit = BITMASK(p_cm->cm_width-1); if (p_cm->cm_refin) uch = reflect(uch, 8); p_cm->cm_reg ^= (uch << (p_cm->cm_width-8)); for (i = 0; i < 8; i++) { if (p_cm->cm_reg & topbit) p_cm->cm_reg = (p_cm->cm_reg << 1) ^ p_cm->cm_poly; else p_cm->cm_reg <<= 1; p_cm->cm_reg &= widmask(p_cm); } } void cm_blk(p_cm, blk_adr, blk_len) p_cm_t p_cm; p_ubyte_ blk_adr; uint32_t blk_len; { while (blk_len--) cm_nxt(p_cm, *blk_adr++); } uint32_t cm_crc(p_cm) p_cm_t p_cm; { if (p_cm->cm_refot) return (p_cm->cm_xorot ^ reflect(p_cm->cm_reg, p_cm->cm_width)); else return (p_cm->cm_xorot ^ p_cm->cm_reg); } uint32_t cm_tab(p_cm, index) p_cm_t p_cm; int index; { int i; uint32_t r; uint32_t topbit = BITMASK(p_cm->cm_width-1); uint32_t inbyte = (uint32_t)index; if (p_cm->cm_refin) inbyte = reflect(inbyte, 8); r = inbyte << (p_cm->cm_width-8); for (i = 0; i < 8; i++) if (r & topbit) r = (r << 1) ^ p_cm->cm_poly; else r <<= 1; if (p_cm->cm_refin) r = reflect(r, p_cm->cm_width); return (r & widmask(p_cm)); } /* * 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. */ #ifndef _CRCMODEL_H #define _CRCMODEL_H #include #ifdef __cplusplus extern "C" { #endif /* * * Start of crcmodel.h * * * Author : Ross Williams (ross@guest.adelaide.edu.au.). * Date : 3 June 1993. * Status : Public domain. * * Description : This is the header (.h) file for the reference * implementation of the Rocksoft^tm Model CRC Algorithm. For more * information on the Rocksoft^tm Model CRC Algorithm, see the document * titled "A Painless Guide to CRC Error Detection Algorithms" by Ross * Williams (ross@guest.adelaide.edu.au.). This document is likely to be in * "ftp.adelaide.edu.au/pub/rocksoft". * * Note: Rocksoft is a trademark of Rocksoft Pty Ltd, Adelaide, Australia. * * * * How to Use This Package * ----------------------- * Step 1: Declare a variable of type cm_t. Declare another variable * (p_cm say) of type p_cm_t and initialize it to point to the first * variable (e.g. p_cm_t p_cm = &cm_t). * * Step 2: Assign values to the parameter fields of the structure. * If you don't know what to assign, see the document cited earlier. * For example: * p_cm->cm_width = 16; * p_cm->cm_poly = 0x8005L; * p_cm->cm_init = 0L; * p_cm->cm_refin = TRUE; * p_cm->cm_refot = TRUE; * p_cm->cm_xorot = 0L; * Note: Poly is specified without its top bit (18005 becomes 8005). * Note: Width is one bit less than the raw poly width. * * Step 3: Initialize the instance with a call cm_ini(p_cm); * * Step 4: Process zero or more message bytes by placing zero or more * successive calls to cm_nxt. Example: cm_nxt(p_cm,ch); * * Step 5: Extract the CRC value at any time by calling crc = cm_crc(p_cm); * If the CRC is a 16-bit value, it will be in the bottom 16 bits. * * * * Design Notes * ------------ * PORTABILITY: This package has been coded very conservatively so that * it will run on as many machines as possible. For example, all external * identifiers have been restricted to 6 characters and all internal ones to * 8 characters. The prefix cm (for Crc Model) is used as an attempt to avoid * namespace collisions. This package is endian independent. * * EFFICIENCY: This package (and its interface) is not designed for * speed. The purpose of this package is to act as a well-defined reference * model for the specification of CRC algorithms. If you want speed, cook up * a specific table-driven implementation as described in the document cited * above. This package is designed for validation only; if you have found or * implemented a CRC algorithm and wish to describe it as a set of parameters * to the Rocksoft^tm Model CRC Algorithm, your CRC algorithm implementation * should behave identically to this package under those parameters. * */ /* The following definitions are extracted from my style header file which */ /* would be cumbersome to distribute with this package. The DONE_STYLE is the */ /* idempotence symbol used in my style header file. */ #ifndef DONE_STYLE typedef unsigned bool; typedef unsigned char *p_ubyte_; #ifndef TRUE #define FALSE 0 #define TRUE 1 #endif /* Change to the second definition if you don't have prototypes. */ #define P_(A) A /* #define P_(A) () */ /* Uncomment this definition if you don't have void. */ /* typedef int void; */ #endif /* CRC Model Abstract Type */ /* ----------------------- */ /* The following type stores the context of an executing instance of the */ /* model algorithm. Most of the fields are model parameters which must be */ /* set before the first initializing call to cm_ini. */ typedef struct { int cm_width; /* Parameter: Width in bits [8,32]. */ uint32_t cm_poly; /* Parameter: The algorithm's polynomial. */ uint32_t cm_init; /* Parameter: Initial register value. */ bool cm_refin; /* Parameter: Reflect input bytes? */ bool cm_refot; /* Parameter: Reflect output CRC? */ uint32_t cm_xorot; /* Parameter: XOR this to output CRC. */ uint32_t cm_reg; /* Context: Context during execution. */ } cm_t; typedef cm_t *p_cm_t; /* Functions That Implement The Model */ /* ---------------------------------- */ /* The following functions animate the cm_t abstraction. */ void cm_ini P_((p_cm_t p_cm)); /* Initializes the argument CRC model instance. */ /* All parameter fields must be set before calling this. */ void cm_nxt P_((p_cm_t p_cm, int ch)); /* Processes a single message byte [0,255]. */ void cm_blk P_((p_cm_t p_cm, p_ubyte_ blk_adr, uint32_t blk_len)); /* Processes a block of message bytes. */ uint32_t cm_crc P_((p_cm_t p_cm)); /* Returns the CRC value for the message bytes processed so far. */ /* Functions For Table Calculation */ /* ------------------------------- */ /* The following function can be used to calculate a CRC lookup table. */ /* It can also be used at run-time to create or check static tables. */ uint32_t cm_tab P_((p_cm_t p_cm, int index)); /* Returns the i'th entry for the lookup table for the specified algorithm. */ /* The function examines the fields cm_width, cm_poly, cm_refin, and the */ /* argument table index in the range [0,255] and returns the table entry in */ /* the bottom cm_width bytes of the return value. */ #ifdef __cplusplus } #endif #endif /* _CRCMODEL_H */ /* * 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 "crcmodel.h" #if defined(_LITTLE_ENDIAN) /* Little-endian architectures need byte-swapping. */ #define sws(x) (((x >> 8) & 0x00ff) | ((x << 8) & 0xff00)) #define swl(x) (sws(x >> 16) | (sws(x) << 16)) #define swap_short(x) (x = sws(x)) #define swap_long(x) (x = swl(x)) #else /* if !_LITTLE_ENDIAN */ /* Big-endian anchictectures don't need byte-swapping. */ #define sws(x) (x) #define swl(x) (x) #define swap_short(x) (x = sws(x)) #define swap_long(x) (x = swl(x)) #endif /* _LITTLE_ENDIAN */ unsigned char compute_crc8(unsigned char *bytes, int length) { cm_t crc_mdl; p_cm_t p_crc; int i; unsigned char aCRC; p_crc = &crc_mdl; p_crc->cm_width = 8; p_crc->cm_poly = 0x107; /* = X^8 + x^2 + x + 1 */ p_crc->cm_init = 0; p_crc->cm_refin = TRUE; p_crc->cm_refot = TRUE; p_crc->cm_xorot = 0; cm_ini(p_crc); for (i = 0; i < length; i++) { cm_nxt(p_crc, bytes[i]); } aCRC = (unsigned char)cm_crc(p_crc); return (aCRC); } uint32_t compute_crc32(unsigned char *bytes, int length) { cm_t crc_mdl; p_cm_t p_crc; int i; uint32_t aCRC; p_crc = &crc_mdl; p_crc->cm_width = 32; p_crc->cm_poly = 0x04c11db7; p_crc->cm_init = 0xffffffff; p_crc->cm_refin = TRUE; p_crc->cm_refot = TRUE; p_crc->cm_xorot = 0xffffffff; cm_ini(p_crc); for (i = 0; i < length; i++) { cm_nxt(p_crc, bytes[i]); } aCRC = (uint32_t)cm_crc(p_crc); return (aCRC); } /* * This is the max value an uint32_t value can hold... * Define this for Windows compilers which don't have "limits.h" or equivalant */ #define UINT32_T_MAX 0xFFFFFFFF uint32_t compute_checksum32(unsigned char *bytes, int length) { uint32_t regval = 0; int i, j, k; uint32_t next4bytes; unsigned char tailbytes[4] = { 0x00, 0x00, 0x00, 0x00 }; /* Grab bytes in 4-byte chunks */ for (i = 0; i < length-4; i += 4) { /* Grab chunk as an int */ (void) memcpy(&next4bytes, &(bytes[i]), 4); swap_long(next4bytes); if (next4bytes > UINT32_T_MAX - regval) { next4bytes -= UINT32_T_MAX - regval; regval = 0; } /* Add intval to regval */ regval += next4bytes; } /* Grab any remaining bytes at the end */ for (j = length-1, k = 3; j >= i; j--, k--) { tailbytes[k] = bytes[j]; } /* * Treat any remaining bytes put into tailbytes as if they were * a left-zero-padded unsigned int (uint32_t == 4 bytes!) */ (void) memcpy(&next4bytes, tailbytes, 4); swap_long(next4bytes); if (next4bytes > UINT32_T_MAX - regval) { next4bytes -= UINT32_T_MAX - regval; regval = 0; } regval += next4bytes; return ((uint32_t)regval); } /* * 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. */ #ifndef _FRU_ACCESS_H #define _FRU_ACCESS_H #include #include #include #ifdef __cplusplus extern "C" { #endif #define SEG_NAME_LEN 2 typedef uint64_t fru_hdl_t; typedef fru_hdl_t container_hdl_t; typedef fru_hdl_t section_hdl_t; typedef fru_hdl_t segment_hdl_t; typedef fru_hdl_t packet_hdl_t; typedef struct { section_hdl_t handle; /* for use in operations on section */ uint32_t offset; /* bytes from container beginning */ uint32_t length; /* length of section in bytes */ uint32_t protection; /* non-zero if section is write-protected */ int32_t version; /* version of section header, or -1 */ } section_t; typedef struct { segment_hdl_t handle; /* for operations on segment */ char name[SEG_NAME_LEN]; /* from container section header */ uint32_t descriptor; /* ditto */ uint32_t offset; /* ditto */ uint32_t length; /* ditto */ } segment_t; typedef uint64_t tag_t; typedef struct { packet_hdl_t handle; /* for use in operations on packet */ tag_t tag; } packet_t; container_hdl_t fru_open_container(picl_nodehdl_t fru); int fru_close_container(container_hdl_t container); int fru_get_num_sections(container_hdl_t container, door_cred_t *cred); int fru_get_sections(container_hdl_t container, section_t *section, int max_sections, door_cred_t *cred); int fru_get_num_segments(section_hdl_t section, door_cred_t *rarg); int fru_get_segments(section_hdl_t section, segment_t *segment, int max_segments, door_cred_t *rarg); int fru_add_segment(section_hdl_t section, segment_t *segment, section_hdl_t *newsection, door_cred_t *cred); int fru_delete_segment(segment_hdl_t segment, section_hdl_t *newsection, door_cred_t *cred); ssize_t fru_read_segment(segment_hdl_t segment, void *buffer, size_t nbytes, door_cred_t *cred); int fru_write_segment(segment_hdl_t segment, const void *data, size_t nbytes, segment_hdl_t *newsegment, door_cred_t *cred); int fru_get_num_packets(segment_hdl_t segment, door_cred_t *cred); int fru_get_packets(segment_hdl_t segment, packet_t *packet, int max_packets, door_cred_t *cred); ssize_t fru_get_payload(packet_hdl_t packet, void *buffer, size_t nbytes, door_cred_t *cred); int fru_update_payload(packet_hdl_t packet, const void *data, size_t nbytes, packet_hdl_t *newpacket, door_cred_t *cred); int fru_append_packet(segment_hdl_t segment, packet_t *packet, const void *payload, size_t nbytes, segment_hdl_t *newsegment, door_cred_t *cred); int fru_delete_packet(packet_hdl_t packet, segment_hdl_t *newsegment, door_cred_t *cred); int fru_is_data_available(picl_nodehdl_t fru); #ifdef __cplusplus } #endif #endif /* _FRU_ACCESS_H */ /* * 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. */ #ifndef _FRU_ACCESS_IMPL_H #define _FRU_ACCESS_IMPL_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* object types */ typedef enum {CONTAINER_TYPE, SECTION_TYPE, SEGMENT_TYPE, PACKET_TYPE} object_t; #define TABLE_SIZE 64 /* hash table size */ /* section header */ #define SECTION_HDR_TAG 0x08 #define SECTION_HDR_VER 0x0001 #define SECTION_HDR_LENGTH 0x06 #define SECTION_HDR_CRC8 0x00 #define SECTION_HDR_VER_BIT0 0x00 #define SECTION_HDR_VER_BIT1 0x01 #define READ_ONLY_SECTION 1 /* section is read-only */ #define GET_SEGMENT_DESCRIPTOR \ (BE_16(seg_layout->descriptor[1])| \ BE_16(seg_layout->descriptor[0] << 16)) #define GET_SECTION_HDR_VERSION \ (sec_hdr.headerversion[1]|sec_hdr.headerversion[0] << 8) /* Segment Trailer Tag */ #define SEG_TRAILER_TAG 0x0C /* defines fixed segment */ #define SEGMENT_FIXED 1 typedef union { uint32_t all_bits; struct { unsigned read_only : 1; unsigned unused : 8; unsigned : 8; unsigned : 8; unsigned : 7; } field; } sectdescbit_t; typedef enum { ENC_STANDARD = 0, /* proper fruid data */ ENC_SPD /* serial presence detect data */ } sectencoding_t; typedef struct { sectdescbit_t description; uint32_t address; /* for SEEPROMS this is the offset */ uint32_t size; sectencoding_t encoding; } sectioninfo_t; typedef uint16_t headerrev_t; #define MAX_NUMOF_SECTION 2 typedef struct { headerrev_t header_ver; int num_sections; sectioninfo_t section_info[MAX_NUMOF_SECTION]; } container_info_t; /* section header layout */ typedef struct { uint8_t headertag; /* section header tag */ uint8_t headerversion[2]; /* header version (msb) */ uint8_t headerlength; /* header length */ uint8_t headercrc8; /* crc8 */ uint8_t segmentcount; /* total number of segment */ } section_layout_t; /* segment header layout */ typedef struct { uint16_t name; /* segment name */ uint16_t descriptor[2]; /* descriptor (msb) */ uint16_t offset; /* segment data offset */ uint16_t length; /* segment length */ } segment_layout_t; /* segment information used in finding new offset for a new segment */ typedef struct { int segnum; /* segment number */ int offset; /* segment offset */ int length; /* segment length */ int fixed; /* fixed or non-fixed segment */ } seg_info_t; typedef uint64_t handle_t; struct hash_obj; /* packet hash object */ typedef struct { handle_t segment_hdl; /* segment handle */ fru_tag_t tag; int tag_size; uint8_t *payload; uint32_t paylen; uint32_t payload_offset; struct hash_obj *next; } packet_obj_t; /* segment hash object */ typedef struct { handle_t section_hdl; /* section handle */ int num_of_packets; /* in a segment */ int trailer_offset; segment_t segment; struct hash_obj *pkt_obj_list; /* packet object list */ struct hash_obj *next; } segment_obj_t; /* section hash object */ typedef struct { handle_t cont_hdl; /* container handle */ section_t section; sectencoding_t encoding; /* standard or needing interpretation */ int num_of_segment; /* in a section */ struct hash_obj *seg_obj_list; /* points to segment objects list */ struct hash_obj *next; } section_obj_t; /* container hash object */ typedef struct { char device_pathname[PATH_MAX]; /* device name */ int num_of_section; /* num of section in container */ struct hash_obj *sec_obj_list; /* points to section objects list */ } container_obj_t; /* hash object */ typedef struct hash_obj { int object_type; handle_t obj_hdl; union { container_obj_t *cont_obj; section_obj_t *sec_obj; segment_obj_t *seg_obj; packet_obj_t *pkt_obj; } u; struct hash_obj *next; struct hash_obj *prev; } hash_obj_t; unsigned char compute_crc8(unsigned char *bytes, int length); long compute_crc32(unsigned char *bytes, int length); long compute_checksum32(unsigned char *bytes, int length); #ifdef __cplusplus } #endif #endif /* _FRU_ACCESS_IMPL_H */ /* * 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include "fru_access_impl.h" #include "libfruds.h" #include "libfrup.h" #include "fru_access.h" #include "fruraw.h" raw_list_t *g_raw = NULL; /* ARGSUSED */ static raw_list_t * treehdl_to_rawlist(fru_treehdl_t handle) { return (g_raw); } static container_hdl_t treehdl_to_conthdl(fru_treehdl_t handle) { raw_list_t *ptr; ptr = treehdl_to_rawlist(handle); if (ptr == NULL) { return (-1); } return (ptr->cont); } static fru_errno_t map_errno(int err) { switch (err) { case ENFILE: case EEXIST: return (FRU_DUPSEG); case EAGAIN: return (FRU_NOSPACE); case EPERM: return (FRU_INVALPERM); default : return (FRU_IOERROR); } } static raw_list_t * make_raw(uint8_t *buffer, size_t size, char *cont_type) { raw_list_t *node; node = (raw_list_t *)malloc(sizeof (raw_list_t)); if (node == NULL) { return (NULL); } node->hdl = 0; node->raw = buffer; node->size = size; node->cont_type = strdup(cont_type); if (node->cont_type == NULL) { free(node); return (NULL); } node->segs = NULL; return (node); } /* * Arguments : * 0 - pointer to byte buffer (in) * 1 - size of buffer (in) * 2 - container type, string (in) */ static fru_errno_t frt_initialize(int num, char **args) { if (num != 3) { return (FRU_FAILURE); } g_raw = make_raw((uint8_t *)args[0], (size_t)args[1], args[2]); if (g_raw == NULL) { return (FRU_FAILURE); } g_raw->cont = open_raw_data(g_raw); if (g_raw->cont == 0) { return (FRU_FAILURE); } return (FRU_SUCCESS); } static fru_errno_t frt_shutdown(void) { segment_list_t *lptr, *lptr2; (void) fru_close_container(g_raw->cont); free(g_raw->cont_type); lptr = g_raw->segs; while (lptr) { lptr2 = lptr; lptr = lptr->next; free(lptr2); } g_raw = NULL; return (FRU_SUCCESS); } static fru_errno_t frt_get_root(fru_treehdl_t *node) { *node = g_raw->hdl; return (FRU_SUCCESS); } /* ARGSUSED */ static fru_errno_t frt_get_peer(fru_treehdl_t sibling, fru_treehdl_t *peer) { return (FRU_NODENOTFOUND); } /* ARGSUSED */ static fru_errno_t frt_get_child(fru_treehdl_t handle, fru_treehdl_t *child) { return (FRU_NODENOTFOUND); } /* ARGSUSED */ static fru_errno_t frt_get_parent(fru_treehdl_t handle, fru_treehdl_t *parent) { return (FRU_NODENOTFOUND); } /* ARGSUSED */ static fru_errno_t frt_get_name_from_hdl(fru_treehdl_t handle, char **name) { *name = strdup("unknown"); return (FRU_SUCCESS); } /* ARGSUSED */ static fru_errno_t frt_get_node_type(fru_treehdl_t node, fru_node_t *type) { *type = FRU_NODE_CONTAINER; return (FRU_SUCCESS); } static fru_errno_t add_segs_for_section(section_t *section, fru_strlist_t *list) { int i = 0; segment_t *segs = NULL; int acc_err = 0; int num_segment = fru_get_num_segments(section->handle, NULL); if (num_segment == -1) { return (map_errno(errno)); } else if (num_segment == 0) { return (FRU_SUCCESS); } segs = malloc(sizeof (*segs) * (num_segment)); if (segs == NULL) { return (FRU_FAILURE); } acc_err = fru_get_segments(section->handle, segs, num_segment, NULL); if (acc_err == -1) { free(segs); return (map_errno(errno)); } list->strs = realloc(list->strs, sizeof (char *) * (list->num + num_segment)); for (i = 0; i < num_segment; i++) { /* ensure NULL terminated. */ char *tmp = malloc(sizeof (*tmp) * (sizeof (segs[i].name)+1)); if (tmp == NULL) { free(segs); return (FRU_FAILURE); } (void) memcpy(tmp, segs[i].name, sizeof (segs[i].name)); tmp[sizeof (segs[i].name)] = '\0'; list->strs[(list->num)++] = tmp; } free(segs); return (FRU_SUCCESS); } static fru_errno_t frt_get_seg_list(fru_treehdl_t handle, fru_strlist_t *list) { fru_strlist_t rc_list; fru_errno_t err = FRU_SUCCESS; int acc_err = 0; int i = 0; int num_section = 0; section_t *sects = NULL; container_hdl_t cont; cont = treehdl_to_conthdl(handle); num_section = fru_get_num_sections(cont, NULL); if (num_section == -1) { return (map_errno(errno)); } sects = malloc(sizeof (*sects) * (num_section)); if (sects == NULL) { return (FRU_FAILURE); } acc_err = fru_get_sections(cont, sects, num_section, NULL); if (acc_err == -1) { free(sects); return (map_errno(errno)); } rc_list.num = 0; rc_list.strs = NULL; for (i = 0; i < num_section; i++) { if ((err = add_segs_for_section(&(sects[i]), &rc_list)) != FRU_SUCCESS) { fru_destroy_strlist(&rc_list); free(sects); return (err); } } list->strs = rc_list.strs; list->num = rc_list.num; return (FRU_SUCCESS); } static fru_errno_t find_seg_in_sect(section_t *sect, const char *seg_name, int *prot_flg, segment_t *segment) { int j = 0; int acc_err = 0; segment_t *segs = NULL; int num_seg = fru_get_num_segments(sect->handle, NULL); if (num_seg == -1) { return (FRU_FAILURE); } segs = malloc(sizeof (*segs) * (num_seg)); if (segs == NULL) { return (FRU_FAILURE); } acc_err = fru_get_segments(sect->handle, segs, num_seg, NULL); if (acc_err == -1) { free(segs); return (map_errno(errno)); } for (j = 0; j < num_seg; j++) { /* NULL terminate */ char tmp[SEG_NAME_LEN+1]; (void) memcpy(tmp, segs[j].name, SEG_NAME_LEN); tmp[SEG_NAME_LEN] = '\0'; if (strcmp(tmp, seg_name) == 0) { *segment = segs[j]; *prot_flg = (sect->protection ? 1 : 0); free(segs); return (FRU_SUCCESS); } } free(segs); return (FRU_INVALSEG); } static fru_errno_t find_segment(fru_treehdl_t handle, const char *seg_name, int *prot_flg, segment_t *segment) { int i = 0; int acc_err = 0; section_t *sect = NULL; container_hdl_t cont; int num_sect; cont = treehdl_to_conthdl(handle); num_sect = fru_get_num_sections(cont, NULL); if (num_sect == -1) { return (map_errno(errno)); } sect = malloc(sizeof (*sect) * (num_sect)); if (sect == NULL) { return (FRU_FAILURE); } acc_err = fru_get_sections(cont, sect, num_sect, NULL); if (acc_err == -1) { free(sect); return (map_errno(errno)); } for (i = 0; i < num_sect; i++) { if (find_seg_in_sect(&(sect[i]), seg_name, prot_flg, segment) == FRU_SUCCESS) { free(sect); return (FRU_SUCCESS); } } free(sect); return (FRU_INVALSEG); } static fru_errno_t frt_get_seg_def(fru_treehdl_t handle, const char *seg_name, fru_segdef_t *def) { fru_errno_t err = FRU_SUCCESS; int prot_flg = 0; segment_t segment; if ((err = find_segment(handle, seg_name, &prot_flg, &segment)) != FRU_SUCCESS) { return (err); } (void) memcpy(def->name, segment.name, SEG_NAME_LEN); def->name[SEG_NAME_LEN] = '\0'; def->desc.raw_data = segment.descriptor; def->size = segment.length; def->address = segment.offset; if (prot_flg == 0) def->hw_desc.field.read_only = 0; else def->hw_desc.field.read_only = 1; return (FRU_SUCCESS); } /* ARGSUSED */ static fru_errno_t frt_add_seg(fru_treehdl_t handle, fru_segdef_t *def) { /* NOT SUPPORTED */ return (FRU_NOTSUP); } /* ARGSUSED */ static fru_errno_t frt_delete_seg(fru_treehdl_t handle, const char *seg_name) { /* NOT SUPPORTED */ return (FRU_NOTSUP); } /* ARGSUSED */ static fru_errno_t frt_for_each_segment(fru_nodehdl_t node, int (*function)(fru_seghdl_t hdl, void *args), void *args) { int num_segment; int cnt; int num_sect; int each_seg; section_t *sects; segment_t *segs; segment_list_t *tmp_list; int acc_err; int status; container_hdl_t cont; cont = g_raw->cont; num_sect = fru_get_num_sections(cont, NULL); if (num_sect == -1) { return (map_errno(errno)); } sects = malloc((num_sect + 1) * sizeof (section_t)); if (sects == NULL) { return (FRU_FAILURE); } num_sect = fru_get_sections(cont, sects, num_sect, NULL); if (num_sect == -1) { free(sects); return (map_errno(errno)); } for (cnt = 0; cnt < num_sect; cnt++) { num_segment = fru_get_num_segments(sects[cnt].handle, NULL); if (num_segment == -1) { return (map_errno(errno)); } else if (num_segment == 0) { continue; } segs = malloc((num_segment + 1) * sizeof (segment_t)); if (segs == NULL) { free(sects); return (FRU_FAILURE); } acc_err = fru_get_segments(sects[cnt].handle, segs, num_segment, NULL); if (acc_err == -1) { free(sects); free(segs); return (map_errno(errno)); } for (each_seg = 0; each_seg < num_segment; each_seg++) { tmp_list = malloc(sizeof (segment_list_t)); tmp_list->segment = &segs[each_seg]; tmp_list->next = NULL; if (g_raw->segs == NULL) { g_raw->segs = tmp_list; } else { tmp_list->next = g_raw->segs; g_raw->segs = tmp_list; } if ((status = function(segs[each_seg].handle, args)) != FRU_SUCCESS) { free(segs); free(sects); return (status); } } free(segs); free(sects); } return (FRU_SUCCESS); } static fru_errno_t frt_get_segment_name(fru_seghdl_t node, char **name) { int num_sect; int acc_err; int cnt; int num_segment; section_t *sects; segment_t *segs; int each_seg; container_hdl_t cont; cont = treehdl_to_conthdl(node); num_sect = fru_get_num_sections(cont, NULL); if (num_sect == -1) { return (map_errno(errno)); } sects = malloc(sizeof (*sects) * (num_sect)); if (sects == NULL) { return (FRU_FAILURE); } acc_err = fru_get_sections(cont, sects, num_sect, NULL); if (acc_err == -1) { free(sects); return (map_errno(errno)); } for (cnt = 0; cnt < num_sect; cnt++) { num_segment = fru_get_num_segments(sects[cnt].handle, NULL); if (num_segment == -1) { free(sects); return (map_errno(errno)); } else if (num_segment == 0) { continue; } segs = malloc(sizeof (*segs) * (num_segment)); if (segs == NULL) { free(sects); return (FRU_FAILURE); } acc_err = fru_get_segments(sects[cnt].handle, segs, num_segment, NULL); if (acc_err == -1) { free(sects); free(segs); return (map_errno(errno)); } for (each_seg = 0; each_seg < num_segment; each_seg++) { if (segs[each_seg].handle == node) { *name = malloc(SEG_NAME_LEN + 1); if (*name != NULL) { (void) memcpy(*name, segs[each_seg].name, SEG_NAME_LEN); *name[SEG_NAME_LEN] = '\0'; } free(sects); free(segs); if (*name == NULL) return (FRU_FAILURE); return (FRU_SUCCESS); } } free(segs); } return (FRU_FAILURE); } /* ARGSUSED */ static fru_errno_t frt_add_tag_to_seg(fru_treehdl_t handle, const char *seg_name, fru_tag_t tag, uint8_t *data, size_t data_len) { /* NOT SUPPORTED */ return (FRU_NOTSUP); } /* ARGSUSED */ static fru_errno_t frt_get_tag_list(fru_treehdl_t handle, const char *seg_name, fru_tag_t **tags, int *number) { /* NOT SUPPORTED */ return (FRU_NOTSUP); } /* ARGSUSED */ static fru_errno_t frt_get_tag_data(fru_treehdl_t handle, const char *seg_name, fru_tag_t tag, int instance, uint8_t **data, size_t *data_len) { /* NOT SUPPORTED */ return (FRU_NOTSUP); } /* ARGSUSED */ static fru_errno_t frt_set_tag_data(fru_treehdl_t handle, const char *seg_name, fru_tag_t tag, int instance, uint8_t *data, size_t data_len) { /* NOT SUPPORTED */ return (FRU_NOTSUP); } /* ARGSUSED */ static fru_errno_t frt_delete_tag(fru_treehdl_t handle, const char *seg_name, fru_tag_t tag, int instance) { /* NOT SUPPORTED */ return (FRU_NOTSUP); } static fru_errno_t frt_for_each_packet(fru_seghdl_t node, int (*function)(fru_tag_t *tag, uint8_t *payload, size_t length, void *args), void *args) { int rc_num; int status; char *rc_data; int i; packet_t *packets = NULL; segment_list_t *tmp_list; fru_segdesc_t *descriptor; tmp_list = g_raw->segs; /* num of packet */ rc_num = fru_get_num_packets(node, NULL); if (rc_num == -1) { return (map_errno(errno)); } else if (rc_num == 0) { return (FRU_SUCCESS); } while (tmp_list) { if (node == tmp_list->segment->handle) { break; } tmp_list = tmp_list->next; } if (tmp_list) { descriptor = (fru_segdesc_t *)&tmp_list->segment->descriptor; if (descriptor->field.opaque) { return (FRU_SUCCESS); } if (descriptor->field.encrypted && (encrypt_func == NULL)) { return (FRU_SUCCESS); } } packets = malloc(sizeof (*packets) * (rc_num)); if (packets == NULL) { return (FRU_FAILURE); } /* get all packets */ if (fru_get_packets(node, packets, rc_num, NULL) == -1) { free(packets); return (map_errno(errno)); } /* number of tags */ for (i = 0; i < rc_num; i++) { size_t rc_len = get_payload_length((fru_tag_t *)&packets[i].tag); rc_data = malloc(sizeof (*rc_data) * (rc_len)); if (rc_data == NULL) { free(packets); return (FRU_FAILURE); } /* get the payload data */ (void) fru_get_payload(packets[i].handle, (void *)rc_data, rc_len, NULL); if (tmp_list) { descriptor = (fru_segdesc_t *)&tmp_list->segment->descriptor; if ((descriptor->field.encrypted) && ((status = encrypt_func(FRU_DECRYPT, (void *)rc_data, rc_len)) != FRU_SUCCESS)) { return (status); } } /* print packet */ if ((status = function((fru_tag_t *)&packets[i].tag, (uint8_t *)rc_data, rc_len, args)) != FRU_SUCCESS) { free(rc_data); free(packets); return (status); } free(rc_data); } return (FRU_SUCCESS); } /* object for libfru to link to */ fru_datasource_t data_source = { LIBFRU_DS_VER, frt_initialize, frt_shutdown, frt_get_root, frt_get_child, frt_get_peer, frt_get_parent, frt_get_name_from_hdl, frt_get_node_type, frt_get_seg_list, frt_get_seg_def, frt_add_seg, frt_delete_seg, frt_for_each_segment, frt_get_segment_name, frt_add_tag_to_seg, frt_get_tag_list, frt_get_tag_data, frt_set_tag_data, frt_delete_tag, frt_for_each_packet }; /* * 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. */ #ifndef _FRURAW_H #define _FRURAW_H #include #include #include #ifdef __cplusplus extern "C" { #endif #define FRU_CONT_CONF_SPARC "/usr/platform/sun4u/lib/fru_container.conf" #define FRU_CONT_CONF_X86 "/usr/lib/picl/plugins/fru_container.conf" #define FRU_CONT_CONF_ENV_VAR "FRU_CONTAINER_CONF" #define IGNORE_CHECK "IGNORE_CHECKSUM" typedef struct segment_list { segment_t *segment; struct segment_list *next; } segment_list_t; typedef struct raw_list { uint8_t *raw; size_t size; char *cont_type; container_hdl_t cont; segment_list_t *segs; fru_treehdl_t hdl; } raw_list_t; /* raw_access.c */ container_hdl_t open_raw_data(raw_list_t *); int fru_close_container(container_hdl_t); raw_list_t *seghdl_to_rawlist(segment_hdl_t node); #ifdef __cplusplus } #endif #endif /* _FRURAW_H */ /* * 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include "fru_access_impl.h" #include "fruraw.h" #pragma init(initialize_raw_access) static hash_obj_t *hash_table[TABLE_SIZE]; extern raw_list_t *g_raw; static void initialize_raw_access(void) { int count; for (count = 0; count < TABLE_SIZE; count++) { hash_table[count] = NULL; } } static hash_obj_t * lookup_handle_object(handle_t handle, int object_type) { handle_t index_to_hash; hash_obj_t *first_hash_obj; hash_obj_t *next_hash_obj; index_to_hash = (handle % TABLE_SIZE); first_hash_obj = hash_table[index_to_hash]; for (next_hash_obj = first_hash_obj; next_hash_obj != NULL; next_hash_obj = next_hash_obj->next) { if ((handle == next_hash_obj->obj_hdl) && (object_type == next_hash_obj->object_type)) { return (next_hash_obj); } } return (NULL); } static void add_hashobject_to_hashtable(hash_obj_t *hash_obj) { handle_t index_to_hash; static uint64_t handle_count = 0; hash_obj->obj_hdl = ++handle_count; /* store the handle */ /* where to add ? */ index_to_hash = ((hash_obj->obj_hdl) % TABLE_SIZE); hash_obj->next = hash_table[index_to_hash]; hash_table[index_to_hash] = hash_obj; /* hash obj. added */ if (hash_obj->next != NULL) { hash_obj->next->prev = hash_obj; } } static hash_obj_t * create_container_hash_object(void) { hash_obj_t *hash_obj; container_obj_t *cont_obj; cont_obj = malloc(sizeof (container_obj_t)); if (cont_obj == NULL) { return (NULL); } hash_obj = malloc(sizeof (hash_obj_t)); if (hash_obj == NULL) { free(cont_obj); return (NULL); } cont_obj->sec_obj_list = NULL; hash_obj->object_type = CONTAINER_TYPE; hash_obj->u.cont_obj = cont_obj; hash_obj->next = NULL; hash_obj->prev = NULL; return (hash_obj); } static hash_obj_t * create_section_hash_object(void) { hash_obj_t *hash_obj; section_obj_t *sec_obj; sec_obj = malloc(sizeof (section_obj_t)); if (sec_obj == NULL) { return (NULL); } hash_obj = malloc(sizeof (hash_obj_t)); if (hash_obj == NULL) { free(sec_obj); return (NULL); } sec_obj->next = NULL; sec_obj->seg_obj_list = NULL; hash_obj->u.sec_obj = sec_obj; hash_obj->object_type = SECTION_TYPE; hash_obj->next = NULL; hash_obj->prev = NULL; return (hash_obj); } static hash_obj_t * create_segment_hash_object(void) { hash_obj_t *hash_obj; segment_obj_t *seg_obj; seg_obj = malloc(sizeof (segment_obj_t)); if (seg_obj == NULL) { return (NULL); } hash_obj = malloc(sizeof (hash_obj_t)); if (hash_obj == NULL) { free(seg_obj); return (NULL); } seg_obj->next = NULL; seg_obj->pkt_obj_list = NULL; hash_obj->object_type = SEGMENT_TYPE; hash_obj->u.seg_obj = seg_obj; hash_obj->next = NULL; hash_obj->prev = NULL; return (hash_obj); } static hash_obj_t * create_packet_hash_object(void) { hash_obj_t *hash_obj; packet_obj_t *pkt_obj; pkt_obj = malloc(sizeof (packet_obj_t)); if (pkt_obj == NULL) { return (NULL); } hash_obj = malloc(sizeof (hash_obj_t)); if (hash_obj == NULL) { free(pkt_obj); return (NULL); } pkt_obj->next = NULL; hash_obj->object_type = PACKET_TYPE; hash_obj->u.pkt_obj = pkt_obj; hash_obj->next = NULL; hash_obj->prev = NULL; return (hash_obj); } static hash_obj_t * get_container_hash_object(int object_type, handle_t handle) { hash_obj_t *hash_obj = NULL; switch (object_type) { case CONTAINER_TYPE: break; case SECTION_TYPE: hash_obj = lookup_handle_object(handle, CONTAINER_TYPE); if (hash_obj == NULL) { return (NULL); } break; case SEGMENT_TYPE: hash_obj = lookup_handle_object(handle, SECTION_TYPE); if (hash_obj == NULL) { return (NULL); } hash_obj = lookup_handle_object(hash_obj->u.sec_obj->cont_hdl, CONTAINER_TYPE); break; case PACKET_TYPE: break; default: return (NULL); } return (hash_obj); } static void add_to_pkt_object_list(hash_obj_t *parent_obj, hash_obj_t *child_obj) { hash_obj_t *next_hash; /* add the packet object in the end of list */ child_obj->u.pkt_obj->segment_hdl = parent_obj->obj_hdl; if (parent_obj->u.seg_obj->pkt_obj_list == NULL) { parent_obj->u.seg_obj->pkt_obj_list = child_obj; return; } for (next_hash = parent_obj->u.seg_obj->pkt_obj_list; next_hash->u.pkt_obj->next != NULL; next_hash = next_hash->u.pkt_obj->next) { ; } next_hash->u.pkt_obj->next = child_obj; } static void free_pkt_object_list(hash_obj_t *hash_obj) { hash_obj_t *next_obj; hash_obj_t *free_obj; next_obj = hash_obj->u.seg_obj->pkt_obj_list; while (next_obj != NULL) { free_obj = next_obj; next_obj = next_obj->u.pkt_obj->next; /* if prev is NULL it's the first object in the list */ if (free_obj->prev == NULL) { hash_table[(free_obj->obj_hdl % TABLE_SIZE)] = free_obj->next; if (free_obj->next != NULL) { free_obj->next->prev = free_obj->prev; } } 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_obj->payload); free(free_obj->u.pkt_obj); free(free_obj); } hash_obj->u.seg_obj->pkt_obj_list = NULL; } static void free_segment_hash(handle_t handle, hash_obj_t *sec_hash) { hash_obj_t *seg_hash; hash_obj_t *next_hash; seg_hash = sec_hash->u.sec_obj->seg_obj_list; if (seg_hash == NULL) { return; } if (seg_hash->obj_hdl == handle) { sec_hash->u.sec_obj->seg_obj_list = seg_hash->u.seg_obj->next; } else { while (seg_hash->obj_hdl != handle) { next_hash = seg_hash; seg_hash = seg_hash->u.seg_obj->next; if (seg_hash == NULL) { return; } } next_hash->u.seg_obj->next = seg_hash->u.seg_obj->next; } if (seg_hash->prev == NULL) { hash_table[(seg_hash->obj_hdl % TABLE_SIZE)] = seg_hash->next; if (seg_hash->next != NULL) { seg_hash->next->prev = NULL; } } else { seg_hash->prev->next = seg_hash->next; if (seg_hash->next != NULL) { seg_hash->next->prev = seg_hash->prev; } } free_pkt_object_list(seg_hash); free(seg_hash->u.seg_obj); free(seg_hash); } static void add_to_sec_object_list(hash_obj_t *parent_obj, hash_obj_t *child_obj) { hash_obj_t *next_hash; child_obj->u.sec_obj->cont_hdl = parent_obj->obj_hdl; if (parent_obj->u.cont_obj->sec_obj_list == NULL) { parent_obj->u.cont_obj->sec_obj_list = child_obj; return; } for (next_hash = parent_obj->u.cont_obj->sec_obj_list; next_hash->u.sec_obj->next != NULL; next_hash = next_hash->u.sec_obj->next) { ; } next_hash->u.sec_obj->next = child_obj; } static void add_to_seg_object_list(hash_obj_t *parent_obj, hash_obj_t *child_obj) { hash_obj_t *next_hash; child_obj->u.seg_obj->section_hdl = parent_obj->obj_hdl; if (parent_obj->u.sec_obj->seg_obj_list == NULL) { parent_obj->u.sec_obj->seg_obj_list = child_obj; return; } for (next_hash = parent_obj->u.sec_obj->seg_obj_list; next_hash->u.seg_obj->next != NULL; next_hash = next_hash->u.seg_obj->next) { ; } next_hash->u.seg_obj->next = child_obj; } static char * tokenizer(char *buf, char *separator, char **nextBuf, char *matched) { int i = 0; int j = 0; for (i = 0; buf[i] != '\0'; i++) { for (j = 0; j < strlen(separator); j++) { if (buf[i] == separator[j]) { buf[i] = '\0'; *nextBuf = &(buf[i+1]); *matched = separator[j]; return (buf); } } } *nextBuf = buf; *matched = '\0'; return (NULL); } static void copy_segment_layout(segment_t *seghdr, void *layout) { segment_layout_t *seg_layout; seg_layout = (segment_layout_t *)layout; (void) memcpy(seghdr->name, &seg_layout->name, SEG_NAME_LEN); seghdr->descriptor = GET_SEGMENT_DESCRIPTOR; seghdr->offset = BE_16(seg_layout->offset); seghdr->length = BE_16(seg_layout->length); } static int get_container_info(const char *def_file, const char *cont_desc_str, container_info_t *cont_info) { char *item; char *token; char *field; char matched; char buf[1024]; int foundIt = 0; FILE *file = fopen(def_file, "r"); if (file == NULL) return (-1); cont_info->num_sections = 0; while (fgets(buf, sizeof (buf), file) != NULL) { /* ignore all comments */ token = tokenizer(buf, "#", &field, &matched); /* find the names */ token = tokenizer(buf, ":", &field, &matched); if (token != NULL) { token = tokenizer(token, "|", &item, &matched); while (token != NULL) { if (strcmp(token, cont_desc_str) == 0) { foundIt = 1; goto found; } token = tokenizer(item, "|", &item, &matched); } /* check the last remaining item */ if ((item != NULL) && (strcmp(item, cont_desc_str) == 0)) { foundIt = 1; goto found; } } } found : if (foundIt == 1) { token = tokenizer(field, ":", &field, &matched); if (token == NULL) { (void) fclose(file); return (-1); } cont_info->header_ver = (headerrev_t)atoi(token); token = tokenizer(field, ":\n", &field, &matched); while (token != NULL) { token = tokenizer(token, ",", &item, &matched); if (token == NULL) { (void) fclose(file); return (-1); } if (atoi(token) == 1) { cont_info->section_info[cont_info-> num_sections].description.field.read_only = 1; } else if (atoi(token) == 0) { cont_info->section_info[cont_info-> num_sections].description.field.read_only = 0; } else { (void) fclose(file); return (-1); } token = tokenizer(item, ",", &item, &matched); if (token == NULL) { (void) fclose(file); return (-1); } cont_info->section_info[cont_info-> num_sections].address = atoi(token); if (item == NULL) { (void) fclose(file); return (-1); } cont_info->section_info[cont_info->num_sections].size = atoi(item); (cont_info->num_sections)++; token = tokenizer(field, ":\n ", &field, &matched); } } (void) fclose(file); return (0); } /* ARGSUSED */ int fru_get_segments(section_hdl_t section, segment_t *segment, int maxseg, door_cred_t *cred) { int count; hash_obj_t *sec_object; hash_obj_t *seg_object; section_obj_t *sec_obj; sec_object = lookup_handle_object(section, SECTION_TYPE); if (sec_object == NULL) { return (-1); } sec_obj = sec_object->u.sec_obj; if (sec_obj == NULL) { return (-1); } if (sec_obj->num_of_segment > maxseg) { return (-1); } seg_object = sec_object->u.sec_obj->seg_obj_list; if (seg_object == NULL) { return (-1); } for (count = 0; count < sec_obj->num_of_segment; count++) { /* populate segment_t */ segment->handle = seg_object->obj_hdl; (void) memcpy(segment->name, seg_object->u.seg_obj->segment.name, SEG_NAME_LEN); segment->descriptor = seg_object->u.seg_obj->segment.descriptor; segment->offset = seg_object->u.seg_obj->segment.offset; segment->length = seg_object->u.seg_obj->segment.length; seg_object = seg_object->u.seg_obj->next; segment++; } return (0); } static int raw_memcpy(void *buffer, raw_list_t *rawlist, int offset, int size) { if (offset + size > rawlist->size) { size = rawlist->size - offset; } (void) memcpy(buffer, &rawlist->raw[offset], size); return (size); } static int verify_header_crc8(headerrev_t head_ver, unsigned char *bytes, int length) { int crc_offset = 0; unsigned char orig_crc8 = 0; unsigned char calc_crc8 = 0; switch (head_ver) { case SECTION_HDR_VER: crc_offset = 4; break; default: errno = EINVAL; return (0); } orig_crc8 = bytes[crc_offset]; bytes[crc_offset] = 0x00; /* clear for calc */ calc_crc8 = compute_crc8(bytes, length); bytes[crc_offset] = orig_crc8; /* restore */ return (orig_crc8 == calc_crc8); } static int get_section(raw_list_t *rawlist, hash_obj_t *sec_hash, section_t *section) { int retval; int size; int count; uint16_t hdrver; hash_obj_t *seg_hash; unsigned char *buffer; section_obj_t *sec_obj; section_layout_t sec_hdr; segment_layout_t *seg_hdr; segment_layout_t *seg_buf; sec_obj = sec_hash->u.sec_obj; if (sec_obj == NULL) { return (-1); } /* populate section_t */ section->handle = sec_hash->obj_hdl; section->offset = sec_obj->section.offset; section->length = sec_obj->section.length; section->protection = sec_obj->section.protection; section->version = sec_obj->section.version; /* read section header layout */ retval = raw_memcpy(&sec_hdr, rawlist, sec_obj->section.offset, sizeof (sec_hdr)); if (retval != sizeof (sec_hdr)) { return (-1); } hdrver = GET_SECTION_HDR_VERSION; if ((sec_hdr.headertag != SECTION_HDR_TAG) && (hdrver != section->version)) { return (-1); } /* size = section layout + total sizeof segment header */ size = sizeof (sec_hdr) + ((sec_hdr.segmentcount) * sizeof (segment_layout_t)); buffer = alloca(size); if (buffer == NULL) { return (-1); } /* segment header buffer */ seg_buf = alloca(size - sizeof (sec_hdr)); if (seg_buf == NULL) { return (-1); } /* read segment header */ retval = raw_memcpy(seg_buf, rawlist, sec_obj->section.offset + sizeof (sec_hdr), size - sizeof (sec_hdr)); if (retval != (size - sizeof (sec_hdr))) { return (-1); } /* copy section header layout */ (void) memcpy(buffer, &sec_hdr, sizeof (sec_hdr)); /* copy segment header layout */ (void) memcpy(buffer + sizeof (sec_hdr), seg_buf, size - sizeof (sec_hdr)); /* verify crc8 */ retval = verify_header_crc8(hdrver, buffer, size); if (retval != TRUE) { return (-1); } section->version = hdrver; sec_obj->section.version = hdrver; seg_hdr = (segment_layout_t *)seg_buf; /* bug fix for frutool */ if (sec_hash->u.sec_obj->seg_obj_list != NULL) { return (0); } else { sec_obj->num_of_segment = 0; } for (count = 0; count < sec_hdr.segmentcount; count++, seg_hdr++) { seg_hash = create_segment_hash_object(); if (seg_hash == NULL) { return (-1); } add_hashobject_to_hashtable(seg_hash); copy_segment_layout(&seg_hash->u.seg_obj->segment, seg_hdr); add_to_seg_object_list(sec_hash, seg_hash); sec_obj->num_of_segment++; } return (0); } /* ARGSUSED */ int fru_get_sections(container_hdl_t container, section_t *section, int maxsec, door_cred_t *cred) { int count; int num_sec = 0; hash_obj_t *cont_object; hash_obj_t *sec_hash; cont_object = lookup_handle_object(container, CONTAINER_TYPE); if (cont_object == NULL) { return (-1); } if (cont_object->u.cont_obj->num_of_section > maxsec) { return (-1); } sec_hash = cont_object->u.cont_obj->sec_obj_list; if (sec_hash == NULL) { return (-1); } for (count = 0; count < cont_object->u.cont_obj->num_of_section; count++) { section->version = -1; /* populate section_t */ if (get_section(g_raw, sec_hash, section) == 0) { section++; num_sec++; } sec_hash = sec_hash->u.sec_obj->next; } return (num_sec); } static uint32_t get_checksum_crc(hash_obj_t *seg_hash, int data_size) { int protection; int offset = 0; uint32_t crc; hash_obj_t *sec_hash; hash_obj_t *pkt_hash; unsigned char *buffer; sec_hash = lookup_handle_object(seg_hash->u.seg_obj->section_hdl, SECTION_TYPE); if (sec_hash == NULL) { return ((uint32_t)-1); } buffer = alloca(data_size); if (buffer == NULL) { return ((uint32_t)-1); } /* traverse the packet object list for all the tags and payload */ for (pkt_hash = seg_hash->u.seg_obj->pkt_obj_list; pkt_hash != NULL; pkt_hash = pkt_hash->u.pkt_obj->next) { (void) memcpy(buffer + offset, &pkt_hash->u.pkt_obj->tag, pkt_hash->u.pkt_obj->tag_size); offset += pkt_hash->u.pkt_obj->tag_size; (void) memcpy(buffer + offset, pkt_hash->u.pkt_obj->payload, pkt_hash->u.pkt_obj->paylen); offset += pkt_hash->u.pkt_obj->paylen; } protection = sec_hash->u.sec_obj->section.protection; if (protection == READ_ONLY_SECTION) { /* read-only section */ crc = compute_crc32(buffer, data_size); } else { /* read/write section */ crc = compute_checksum32(buffer, data_size); } return (crc); /* computed crc */ } static int get_packet(raw_list_t *rawlist, void *buffer, int size, int offset) { int retval; retval = raw_memcpy(buffer, rawlist, offset, size); if (retval != -1) { return (0); } return (-1); } static int get_packets(hash_obj_t *seg_hash, raw_list_t *rawlist, int offset, int length) { int tag_size; int paylen; int retval; int seg_limit = 0; int pktcnt = 0; char *data; uint32_t crc; uint32_t origcrc; fru_tag_t tag; hash_obj_t *pkt_hash_obj; hash_obj_t *sec_hash; fru_segdesc_t *segdesc; fru_tagtype_t tagtype; char *ignore_flag; retval = get_packet(rawlist, &tag, sizeof (fru_tag_t), offset); if (retval == -1) { return (-1); } /* section hash object */ sec_hash = lookup_handle_object(seg_hash->u.seg_obj->section_hdl, SECTION_TYPE); if (sec_hash == NULL) { return (-1); } seg_hash->u.seg_obj->trailer_offset = offset; data = (char *)&tag; while (data[0] != SEG_TRAILER_TAG) { tagtype = get_tag_type(&tag); /* verify tag type */ if (tagtype == -1) { return (-1); } tag_size = get_tag_size(tagtype); if (tag_size == -1) { return (-1); } seg_limit += tag_size; if (seg_limit > length) { return (-1); } paylen = get_payload_length((void *)&tag); if (paylen == -1) { return (-1); } seg_limit += paylen; if (seg_limit > length) { return (-1); } if ((offset + tag_size + paylen) > (sec_hash->u.sec_obj->section.offset + sec_hash->u.sec_obj->section.length)) { return (-1); } pkt_hash_obj = create_packet_hash_object(); if (pkt_hash_obj == NULL) { return (-1); } pkt_hash_obj->u.pkt_obj->payload = malloc(paylen); if (pkt_hash_obj->u.pkt_obj->payload == NULL) { free(pkt_hash_obj); return (-1); } offset += tag_size; retval = raw_memcpy(pkt_hash_obj->u.pkt_obj->payload, rawlist, offset, paylen); if (retval != paylen) { free(pkt_hash_obj->u.pkt_obj->payload); free(pkt_hash_obj); return (-1); } /* don't change this */ pkt_hash_obj->u.pkt_obj->tag.raw_data = 0; (void) memcpy(&pkt_hash_obj->u.pkt_obj->tag, &tag, tag_size); pkt_hash_obj->u.pkt_obj->paylen = paylen; pkt_hash_obj->u.pkt_obj->tag_size = tag_size; pkt_hash_obj->u.pkt_obj->payload_offset = offset; offset += paylen; add_hashobject_to_hashtable(pkt_hash_obj); add_to_pkt_object_list(seg_hash, pkt_hash_obj); pktcnt++; retval = get_packet(rawlist, &tag, sizeof (fru_tag_t), offset); if (retval == -1) { return (retval); } data = (char *)&tag; } segdesc = (fru_segdesc_t *)&seg_hash->u.seg_obj->segment.descriptor; seg_hash->u.seg_obj->trailer_offset = offset; if (!segdesc->field.ignore_checksum) { crc = get_checksum_crc(seg_hash, seg_limit); offset = seg_hash->u.seg_obj->segment.offset; retval = raw_memcpy(&origcrc, rawlist, offset + seg_limit + 1, sizeof (origcrc)); ignore_flag = getenv(IGNORE_CHECK); if (ignore_flag != NULL) { return (pktcnt); } if (retval != sizeof (origcrc)) { return (-1); } origcrc = BE_32(origcrc); if (origcrc != crc) { seg_hash->u.seg_obj->trailer_offset = offset; return (-1); } } return (pktcnt); } /* ARGSUSED */ int fru_get_num_sections(container_hdl_t container, door_cred_t *cred) { hash_obj_t *hash_object; hash_object = lookup_handle_object(container, CONTAINER_TYPE); if (hash_object == NULL) { return (-1); } return (hash_object->u.cont_obj->num_of_section); } /* ARGSUSED */ int fru_get_num_segments(section_hdl_t section, door_cred_t *cred) { hash_obj_t *sec_object; section_obj_t *sec_obj; sec_object = lookup_handle_object(section, SECTION_TYPE); if (sec_object == NULL) { return (-1); } sec_obj = sec_object->u.sec_obj; if (sec_obj == NULL) { return (-1); } return (sec_obj->num_of_segment); } /* ARGSUSED */ int fru_get_num_packets(segment_hdl_t segment, door_cred_t *cred) { int pktcnt; int length; uint16_t offset; hash_obj_t *cont_hash_obj; hash_obj_t *seg_hash; hash_obj_t *sec_hash; fru_segdesc_t *segdesc; segment_obj_t *segment_object; seg_hash = lookup_handle_object(segment, SEGMENT_TYPE); if (seg_hash == NULL) { return (-1); } segment_object = seg_hash->u.seg_obj; if (segment_object == NULL) { return (-1); } segdesc = (fru_segdesc_t *)&segment_object->segment.descriptor; if (segdesc->field.opaque) { return (0); } offset = segment_object->segment.offset; length = segment_object->segment.length; cont_hash_obj = get_container_hash_object(SEGMENT_TYPE, segment_object->section_hdl); if (cont_hash_obj == NULL) { return (-1); } if (seg_hash->u.seg_obj->pkt_obj_list != NULL) { return (segment_object->num_of_packets); } /* section hash object */ sec_hash = lookup_handle_object(seg_hash->u.seg_obj->section_hdl, SECTION_TYPE); if (sec_hash == NULL) { return (-1); } /* valid segment header b'cos crc8 already validated */ if (offset < sec_hash->u.sec_obj->section.offset) { return (-1); } segment_object->num_of_packets = 0; pktcnt = get_packets(seg_hash, g_raw, offset, length); if (pktcnt == -1) { free_pkt_object_list(seg_hash); seg_hash->u.seg_obj->pkt_obj_list = NULL; } segment_object->num_of_packets = pktcnt; return (segment_object->num_of_packets); } /* ARGSUSED */ int fru_get_packets(segment_hdl_t segment, packet_t *packet, int maxpackets, door_cred_t *cred) { int count; hash_obj_t *seg_hash_obj; hash_obj_t *pkt_hash_obj; /* segment hash object */ seg_hash_obj = lookup_handle_object(segment, SEGMENT_TYPE); if (seg_hash_obj == NULL) { return (-1); } if (seg_hash_obj->u.seg_obj->num_of_packets != maxpackets) { return (-1); } pkt_hash_obj = seg_hash_obj->u.seg_obj->pkt_obj_list; if (pkt_hash_obj == NULL) { return (-1); } for (count = 0; count < maxpackets; count++, packet++) { packet->handle = pkt_hash_obj->obj_hdl; packet->tag = 0; (void) memcpy(&packet->tag, &pkt_hash_obj->u.pkt_obj->tag, pkt_hash_obj->u.pkt_obj->tag_size); pkt_hash_obj = pkt_hash_obj->u.pkt_obj->next; } return (0); } /* ARGSUSED */ ssize_t fru_get_payload(packet_hdl_t packet, void *buffer, size_t nbytes, door_cred_t *cred) { hash_obj_t *packet_hash_obj; /* packet hash object */ packet_hash_obj = lookup_handle_object(packet, PACKET_TYPE); if (packet_hash_obj == NULL) { return (-1); } /* verify payload length */ if (nbytes != packet_hash_obj->u.pkt_obj->paylen) { return (-1); } (void) memcpy(buffer, packet_hash_obj->u.pkt_obj->payload, nbytes); return (nbytes); } container_hdl_t open_raw_data(raw_list_t *node) { char *cont_conf_file = NULL; hash_obj_t *cont_hash_obj; hash_obj_t *sec_hash_obj; container_info_t cont_info; int retval; int count; cont_hash_obj = create_container_hash_object(); if (cont_hash_obj == NULL) { return (0); } add_hashobject_to_hashtable(cont_hash_obj); (void) strncpy(cont_hash_obj->u.cont_obj->device_pathname, "unknown", sizeof (cont_hash_obj->u.cont_obj->device_pathname)); cont_conf_file = getenv(FRU_CONT_CONF_ENV_VAR); if (cont_conf_file == NULL) { cont_conf_file = FRU_CONT_CONF_SPARC; retval = get_container_info(cont_conf_file, node->cont_type, &cont_info); if (retval < 0) { cont_conf_file = FRU_CONT_CONF_X86; retval = get_container_info(cont_conf_file, node->cont_type, &cont_info); } } else { retval = get_container_info(cont_conf_file, node->cont_type, &cont_info); } if (retval < 0) { return (0); } cont_hash_obj->u.cont_obj->num_of_section = cont_info.num_sections; cont_hash_obj->u.cont_obj->sec_obj_list = NULL; for (count = 0; count < cont_info.num_sections; count++) { sec_hash_obj = create_section_hash_object(); if (sec_hash_obj == NULL) { return (0); } add_hashobject_to_hashtable(sec_hash_obj); sec_hash_obj->u.sec_obj->section.offset = cont_info.section_info[count].address; sec_hash_obj->u.sec_obj->section.protection = cont_info.section_info[count].description.field.read_only; sec_hash_obj->u.sec_obj->section.length = cont_info.section_info[count].size; sec_hash_obj->u.sec_obj->section.version = cont_info.header_ver; add_to_sec_object_list(cont_hash_obj, sec_hash_obj); } return (cont_hash_obj->obj_hdl); } int fru_close_container(container_hdl_t container) { hash_obj_t *hash_obj; hash_obj_t *prev_hash; hash_obj_t *sec_hash_obj; handle_t obj_hdl; /* lookup for container hash object */ hash_obj = lookup_handle_object(container, CONTAINER_TYPE); if (hash_obj == NULL) { return (0); } /* points to section object list */ sec_hash_obj = hash_obj->u.cont_obj->sec_obj_list; /* traverse section object list */ while (sec_hash_obj != NULL) { /* traverse segment hash object in the section */ while (sec_hash_obj->u.sec_obj->seg_obj_list != NULL) { /* object handle of the segment hash object */ obj_hdl = sec_hash_obj->u.sec_obj->seg_obj_list->obj_hdl; free_segment_hash(obj_hdl, sec_hash_obj); } /* going to free section hash object, relink the hash object */ if (sec_hash_obj->prev == NULL) { hash_table[(sec_hash_obj->obj_hdl % TABLE_SIZE)] = sec_hash_obj->next; if (sec_hash_obj->next != NULL) { sec_hash_obj->next->prev = NULL; } } else { sec_hash_obj->prev->next = sec_hash_obj->next; if (sec_hash_obj->next != NULL) { sec_hash_obj->next->prev = sec_hash_obj->prev; } } prev_hash = sec_hash_obj; sec_hash_obj = sec_hash_obj->u.sec_obj->next; free(prev_hash->u.sec_obj); /* free section hash object */ free(prev_hash); /* free section hash */ } /* free container hash object */ if (hash_obj->prev == NULL) { hash_table[(hash_obj->obj_hdl % TABLE_SIZE)] = hash_obj->next; if (hash_obj->next != NULL) { hash_obj->next->prev = NULL; } } else { hash_obj->prev->next = hash_obj->next; if (hash_obj->next != NULL) { hash_obj->next->prev = hash_obj->prev; } } free(hash_obj->u.cont_obj); free(hash_obj); return (0); }