root/usr/src/lib/libfru/libfruraw/fruraw.c
/*
 * 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 <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#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
};