1 /*
2  * normal_fru.c
3  *
4  * "normal" (IPMI-specified) fru handling
5  *
6  * Author: MontaVista Software, Inc.
7  *         Corey Minyard <minyard@mvista.com>
8  *         source@mvista.com
9  *
10  * Copyright 2002,2003 MontaVista Software Inc.
11  *
12  * Note that this file was originally written by Thomas Kanngieser
13  * <thomas.kanngieser@fci.com> of FORCE Computers, but I've pretty
14  * much gutted it and rewritten it, nothing really remained the same.
15  * Thomas' code was helpful, though and many thanks go to him.
16  *
17  *  This program is free software; you can redistribute it and/or
18  *  modify it under the terms of the GNU Lesser General Public License
19  *  as published by the Free Software Foundation; either version 2 of
20  *  the License, or (at your option) any later version.
21  *
22  *
23  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
24  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
29  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
31  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
32  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  *  You should have received a copy of the GNU Lesser General Public
35  *  License along with this program; if not, write to the Free
36  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
37  */
38 
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdint.h>
42 #include <errno.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <arpa/inet.h>
46 
47 #include <OpenIPMI/ipmiif.h>
48 #include <OpenIPMI/ipmi_fru.h>
49 #include <OpenIPMI/ipmi_err.h>
50 #include <OpenIPMI/ipmi_msgbits.h>
51 
52 #include <OpenIPMI/internal/locked_list.h>
53 #include <OpenIPMI/internal/ipmi_domain.h>
54 #include <OpenIPMI/internal/ipmi_int.h>
55 #include <OpenIPMI/internal/ipmi_utils.h>
56 #include <OpenIPMI/internal/ipmi_oem.h>
57 #include <OpenIPMI/internal/ipmi_fru.h>
58 
59 #define IPMI_LANG_CODE_ENGLISH	25
60 
61 /***********************************************************************
62  *
63  * Normal fru info.
64  *
65  **********************************************************************/
66 
67 /* Records used to hold the FRU. */
68 typedef struct ipmi_fru_record_s ipmi_fru_record_t;
69 
70 typedef struct fru_string_s
71 {
72     enum ipmi_str_type_e type;
73     unsigned int         length;
74     char                 *str;
75 
76     /* The raw offset from the start of the area, and the raw length
77        of this string.  This is the offset and length in the raw FRU
78        data. */
79     unsigned short       offset;
80     unsigned short       raw_len;
81     unsigned char        *raw_data;
82 
83     /* Has this value been changed locally since it has been read?
84        Use to know that this needs to be written. */
85     char                 changed;
86 } fru_string_t;
87 
88 typedef struct fru_variable_s
89 {
90     unsigned short len;
91     unsigned short next;
92     fru_string_t   *strings;
93 } fru_variable_t;
94 
95 typedef struct fru_area_info_s {
96     unsigned short num_fixed_fields;
97     unsigned short field_start;
98     unsigned short empty_length;
99     fru_variable_t *(*get_fields)(ipmi_fru_record_t *rec);
100     void (*free)(ipmi_fru_record_t *rec);
101     unsigned short extra_len;
102     int (*decode)(ipmi_fru_t        *fru,
103 		  unsigned char     *data,
104 		  unsigned int      data_len,
105 		  ipmi_fru_record_t **rrec);
106     int (*encode)(ipmi_fru_t *fru, unsigned char *data);
107     int (*setup_new)(ipmi_fru_record_t *rec, int full_init);
108 } fru_area_info_t;
109 
110 /* Forward declaration */
111 static fru_area_info_t fru_area_info[IPMI_FRU_FTR_NUMBER];
112 
113 struct ipmi_fru_record_s
114 {
115     fru_area_info_t       *handlers;
116     void                  *data;
117 
118 
119     /* Where does this area start in the FRU and how much memory is
120        available? */
121     unsigned int          offset;
122     unsigned int          length;
123 
124     /* How much of the area is currently used? */
125     unsigned int          used_length;
126 
127     /* Length of the used length in the  */
128     unsigned int          orig_used_length;
129 
130     /* Has this value been changed locally since it has been read?
131        Use to know that something in the record needs to be written,
132        the header needs to be rewritten, and the checksum needs to be
133        recalculated. */
134     char                  changed;
135 
136     /* Does the whole area require a rewrite?  This would be true if
137        the position changed or the length was increased. */
138     char                 rewrite;
139 };
140 
141 static void fru_record_destroy(ipmi_fru_record_t *rec);
142 
143 typedef struct normal_fru_rec_data_s
144 {
145     int               version;
146 
147     /* Has an offset changed (thus causing the header to need to be
148        rewritten)? */
149     int               header_changed;
150 
151     ipmi_fru_record_t *recs[IPMI_FRU_FTR_NUMBER];
152 } normal_fru_rec_data_t;
153 
154 static normal_fru_rec_data_t *setup_normal_fru(ipmi_fru_t    *fru,
155 					       unsigned char version);
156 
157 static ipmi_fru_record_t **
normal_fru_get_recs(ipmi_fru_t * fru)158 normal_fru_get_recs(ipmi_fru_t *fru)
159 {
160     normal_fru_rec_data_t *info = i_ipmi_fru_get_rec_data(fru);
161     return info->recs;
162 }
163 
164 /***********************************************************************
165  *
166  * Normal fru data formatting.
167  *
168  **********************************************************************/
169 
170 static unsigned char
checksum(unsigned char * data,unsigned int length)171 checksum(unsigned char *data, unsigned int length)
172 {
173     unsigned char sum = 0;
174 
175     while (length) {
176 	sum += *data;
177 	data++;
178 	length--;
179     }
180 
181     return sum;
182 }
183 
184 /* 820476000 is seconds between 1970.01.01 00:00:00 and 1996.01.01 00:00:00 */
185 #define FRU_TIME_TO_UNIX_TIME(t) (((t) * 60) + 820476000)
186 #define UNIX_TIME_TO_FRU_TIME(t) ((((t) - 820476000) + 30) / 60)
187 
188 static int
read_fru_time(unsigned char ** data,unsigned int * len,time_t * time)189 read_fru_time(unsigned char **data,
190 	      unsigned int  *len,
191 	      time_t        *time)
192 {
193     unsigned int  t;
194     unsigned char *d = *data;
195 
196     if (*len < 3)
197 	return EBADF;
198 
199     t = *d++;
200     t += *d++ * 256;
201     t += *d++ * 256 * 256;
202 
203     *len -= 3;
204     *data += 3;
205 
206     *time = FRU_TIME_TO_UNIX_TIME(t);
207     return 0;
208 }
209 
210 static void
write_fru_time(unsigned char * d,time_t time)211 write_fru_time(unsigned char *d, time_t time)
212 {
213     unsigned int t;
214 
215     t = UNIX_TIME_TO_FRU_TIME(time);
216 
217     *d++ = t & 0xff;
218     t >>= 8;
219     *d++ = t & 0xff;
220     t >>= 8;
221     *d++ = t & 0xff;
222     t >>= 8;
223 }
224 
225 static int
fru_encode_fields(ipmi_fru_t * fru,ipmi_fru_record_t * rec,fru_variable_t * v,unsigned char * data,unsigned int offset)226 fru_encode_fields(ipmi_fru_t        *fru,
227 		  ipmi_fru_record_t *rec,
228 		  fru_variable_t    *v,
229 		  unsigned char     *data,
230 		  unsigned int      offset)
231 {
232     int i;
233     int rv;
234 
235     for (i=0; i<v->next; i++) {
236 	fru_string_t *s = v->strings + i;
237 	unsigned int len;
238 
239 	if (offset != s->offset) {
240 	    /* Bug in the FRU code.  Return a unique error code so it
241 	       can be identified, but don't pass it to the user. */
242 	    return EBADF;
243 	}
244 
245 	if (s->raw_data) {
246 	    memcpy(data+offset, s->raw_data, s->raw_len);
247 	    len = s->raw_len;
248 	} else if (s->str) {
249 	    len = IPMI_MAX_STR_LEN;
250 	    ipmi_set_device_string2(s->str, s->type, s->length,
251 				    data+offset, 1, &len,
252 				    ipmi_fru_get_options(fru));
253 	} else {
254 	    data[offset] = 0xc0;
255 	    len = 1;
256 	}
257 	if (s->changed && !rec->rewrite) {
258 	    rv = i_ipmi_fru_new_update_record(fru, offset+rec->offset, len);
259 	    if (rv)
260 		return rv;
261 	}
262 	offset += len;
263     }
264     /* Now the end marker */
265     data[offset] = 0xc1;
266     /* If the record changed, put out the end marker */
267     if (rec->changed && !rec->rewrite) {
268 	rv = i_ipmi_fru_new_update_record(fru, offset+rec->offset, 1);
269 	if (rv)
270 	    return rv;
271     }
272     offset++;
273     /* We are not adding the checksum, so remove it from the check */
274     if (offset != (rec->used_length-1)) {
275 	return EBADF;
276     }
277     return 0;
278 }
279 
280 /***********************************************************************
281  *
282  * Custom field handling for FRUs.  This is a variable-length array
283  * of strings.
284  *
285  **********************************************************************/
286 
287 static int
fru_setup_min_field(ipmi_fru_record_t * rec,int area,int changed)288 fru_setup_min_field(ipmi_fru_record_t *rec, int area, int changed)
289 {
290     unsigned int   i;
291     unsigned int   min;
292     unsigned int   start_offset;
293     fru_variable_t *v;
294 
295     if (!fru_area_info[area].get_fields)
296 	return 0;
297 
298     v = fru_area_info[area].get_fields(rec);
299     min = fru_area_info[area].num_fixed_fields;
300     start_offset = fru_area_info[area].field_start;
301 
302     if (min == 0)
303 	return 0;
304 
305     v->strings = ipmi_mem_alloc(min * sizeof(fru_string_t));
306     if (!v->strings)
307 	return ENOMEM;
308     memset(v->strings, 0, min * sizeof(fru_string_t));
309     for (i=0; i<min; i++) {
310 	v->strings[i].changed = changed;
311 	v->strings[i].offset = start_offset;
312 	start_offset++;
313 	v->strings[i].raw_len = 1;
314     }
315     v->len = min;
316     v->next = min;
317     return 0;
318 }
319 
320 static int
fru_string_set(ipmi_fru_t * fru,enum ipmi_str_type_e type,char * str,unsigned int len,ipmi_fru_record_t * rec,fru_variable_t * vals,unsigned int num,int is_custom)321 fru_string_set(ipmi_fru_t           *fru,
322 	       enum ipmi_str_type_e type,
323 	       char                 *str,
324 	       unsigned int         len,
325 	       ipmi_fru_record_t    *rec,
326 	       fru_variable_t       *vals,
327 	       unsigned int         num,
328 	       int                  is_custom)
329 {
330     char         *newval;
331     fru_string_t *val = vals->strings + num;
332     unsigned char tstr[IPMI_MAX_STR_LEN+1];
333     unsigned int  raw_len = sizeof(tstr);
334     int           raw_diff;
335     int           i;
336 
337     if (str) {
338 	/* First calculate if it will fit into the record area. */
339 
340 	/* Truncate if too long. */
341 	if (len > 63)
342 	    len = 63;
343 	ipmi_set_device_string2(str, type, len, tstr, 1, &raw_len,
344 				ipmi_fru_get_options(fru));
345 	raw_diff = raw_len - val->raw_len;
346 	if ((raw_diff > 0) && (rec->used_length+raw_diff > rec->length))
347 	    return ENOSPC;
348 	if (len == 0)
349 	    newval = ipmi_mem_alloc(1);
350 	else
351 	    newval = ipmi_mem_alloc(len);
352 	if (!newval)
353 	    return ENOMEM;
354 	memcpy(newval, str, len);
355     } else {
356 	newval = NULL;
357 	len = 0;
358 	raw_diff = 1 - val->raw_len;
359     }
360 
361     if (val->str)
362 	ipmi_mem_free(val->str);
363     if (val->raw_data) {
364 	ipmi_mem_free(val->raw_data);
365 	val->raw_data = NULL;
366     }
367 
368     if (!is_custom || newval) {
369 	/* Either it's not a custom value (and thus is always there)
370 	   or there is a value to put in.  Modify the length and
371 	   reduce the offset of all the following strings. */
372 	val->str = newval;
373 	val->length = len;
374 	val->type = type;
375 	val->raw_len += raw_diff;
376 	val->changed = 1;
377 	if (raw_diff) {
378 	    for (i=num+1; i<vals->next; i++) {
379 		vals->strings[i].offset += raw_diff;
380 		vals->strings[i].changed = 1;
381 	    }
382 	}
383     } else {
384 	/* A custom value that is being cleared.  Nuke it by moving
385 	   all the strings following this back. */
386 	raw_diff = -val->raw_len;
387 	vals->next--;
388 	for (i=num; i<vals->next; i++) {
389 	    vals->strings[i] = vals->strings[i+1];
390 	    vals->strings[i].offset += raw_diff;
391 	    vals->strings[i].changed = 1;
392 	}
393     }
394 
395     rec->used_length += raw_diff;
396     rec->changed |= 1;
397 
398     return 0;
399 }
400 
401 static int
fru_decode_string(ipmi_fru_t * fru,unsigned char * start_pos,unsigned char ** in,unsigned int * in_len,int lang_code,int force_english,fru_variable_t * strs,unsigned int num)402 fru_decode_string(ipmi_fru_t     *fru,
403 		  unsigned char  *start_pos,
404 		  unsigned char  **in,
405 		  unsigned int   *in_len,
406 		  int            lang_code,
407 		  int            force_english,
408 		  fru_variable_t *strs,
409 		  unsigned int   num)
410 {
411     char          str[IPMI_MAX_STR_LEN+1];
412     int           force_unicode;
413     fru_string_t  *out = strs->strings + num;
414     unsigned char *in_start;
415     int           rv;
416 
417     out->offset = *in - start_pos;
418     in_start = *in;
419     force_unicode = !force_english && (lang_code != IPMI_LANG_CODE_ENGLISH);
420     rv = ipmi_get_device_string(in, *in_len, str,
421 				IPMI_STR_FRU_SEMANTICS, force_unicode,
422 				&out->type, sizeof(str), &out->length);
423     if (rv)
424 	return rv;
425     out->raw_len = *in - in_start;
426     *in_len -= out->raw_len;
427     out->raw_data = ipmi_mem_alloc(out->raw_len);
428     if (!out->raw_data)
429 	return ENOMEM;
430     memcpy(out->raw_data, in_start, out->raw_len);
431 
432     if (out->length != 0) {
433 	out->str = ipmi_mem_alloc(out->length);
434 	if (!out->str) {
435 	    ipmi_mem_free(out->raw_data);
436 	    return ENOMEM;
437 	}
438 	memcpy(out->str, str, out->length);
439     } else {
440 	out->str = ipmi_mem_alloc(1);
441 	if (!out->str) {
442 	    ipmi_mem_free(out->raw_data);
443 	    return ENOMEM;
444 	}
445     }
446     return 0;
447 }
448 
449 static int
fru_string_to_out(char * out,unsigned int * length,fru_string_t * in)450 fru_string_to_out(char *out, unsigned int *length, fru_string_t *in)
451 {
452     unsigned int clen;
453 
454     if (!in->str)
455 	return ENOSYS;
456 
457     if (in->length > *length)
458 	clen = *length;
459     else
460 	clen = in->length;
461     memcpy(out, in->str, clen);
462 
463     if (in->type == IPMI_ASCII_STR) {
464 	/* NIL terminate the ASCII string. */
465 	if (clen == *length)
466 	    clen--;
467 
468 	out[clen] = '\0';
469     }
470 
471     *length = clen;
472 
473     return 0;
474 }
475 
476 static void
fru_free_string(fru_string_t * str)477 fru_free_string(fru_string_t *str)
478 {
479     if (str->str)
480 	ipmi_mem_free(str->str);
481     if (str->raw_data)
482 	ipmi_mem_free(str->raw_data);
483 }
484 
485 
486 static int
fru_variable_string_set(ipmi_fru_t * fru,ipmi_fru_record_t * rec,fru_variable_t * val,unsigned int first_custom,unsigned int num,enum ipmi_str_type_e type,char * str,unsigned int len,int is_custom)487 fru_variable_string_set(ipmi_fru_t           *fru,
488 			ipmi_fru_record_t    *rec,
489 			fru_variable_t       *val,
490 			unsigned int         first_custom,
491 			unsigned int         num,
492 			enum ipmi_str_type_e type,
493 			char                 *str,
494 			unsigned int         len,
495 			int                  is_custom)
496 {
497     int rv;
498 
499     if (is_custom) {
500 	/* Renumber to get the custom fields.  We do this a little
501 	   strangly to avoid overflows if the user passes in MAX_INT
502 	   for the num. */
503 	if (num > val->next - first_custom)
504 	    num = val->next;
505 	else
506 	    num += first_custom;
507     }
508     if (num >= val->next) {
509 	if (len == 0) {
510 	    /* Don't expand if we are deleting an invalid field,
511 	       return an error. */
512 	    return EINVAL;
513 	}
514 	num = val->next;
515 	/* If not enough room, expand the array by a set amount (to
516 	   keep from thrashing memory when adding lots of things). */
517 	if (val->next >= val->len) {
518 	    fru_string_t *newval;
519 	    unsigned int alloc_num = val->len + 16;
520 
521 	    newval = ipmi_mem_alloc(sizeof(fru_string_t) * alloc_num);
522 	    if (!newval)
523 		return ENOMEM;
524 	    memset(newval, 0, sizeof(fru_string_t) * alloc_num);
525 	    if (val->strings) {
526 		memcpy(newval, val->strings, sizeof(fru_string_t) * val->next);
527 		ipmi_mem_free(val->strings);
528 	    }
529 	    val->strings = newval;
530 	    val->len = alloc_num;
531 	}
532 	val->strings[num].str = NULL;
533 	val->strings[num].raw_data = NULL;
534 	/* Subtract 2 below because of the end marker and the checksum. */
535 	val->strings[num].offset = rec->used_length-2;
536 	val->strings[num].length = 0;
537 	val->strings[num].raw_len = 0;
538 	val->next++;
539     }
540 
541     rv = fru_string_set(fru, type, str, len, rec, val, num, is_custom);
542     return rv;
543 }
544 
545 static int
fru_variable_string_ins(ipmi_fru_t * fru,ipmi_fru_record_t * rec,fru_variable_t * val,unsigned int first_custom,unsigned int num,enum ipmi_str_type_e type,char * str,unsigned int len)546 fru_variable_string_ins(ipmi_fru_t           *fru,
547 			ipmi_fru_record_t    *rec,
548 			fru_variable_t       *val,
549 			unsigned int         first_custom,
550 			unsigned int         num,
551 			enum ipmi_str_type_e type,
552 			char                 *str,
553 			unsigned int         len)
554 {
555     int rv;
556     int i;
557     int offset;
558 
559     /* Renumber to get the custom fields.  We do this a little
560        strangly to avoid overflows if the user passes in MAX_INT
561        for the num. */
562     if (num > val->next - first_custom)
563 	num = val->next;
564     else
565 	num += first_custom;
566 
567     if (num > val->next)
568 	return EINVAL;
569 
570     if (!str)
571 	return EINVAL;
572 
573     if ((rec->used_length + 1) > rec->length)
574 	return ENOSPC;
575 
576     /* If not enough room, expand the array by a set amount (to
577        keep from thrashing memory when adding lots of things). */
578     if (val->next >= val->len) {
579 	fru_string_t *newval;
580 	unsigned int alloc_num = val->len + 16;
581 
582 	newval = ipmi_mem_alloc(sizeof(fru_string_t) * alloc_num);
583 	if (!newval)
584 	    return ENOMEM;
585 	memset(newval, 0, sizeof(fru_string_t) * alloc_num);
586 	if (val->strings) {
587 	    memcpy(newval, val->strings, sizeof(fru_string_t) * val->next);
588 	    ipmi_mem_free(val->strings);
589 	}
590 	val->strings = newval;
591 	val->len = alloc_num;
592     }
593 
594     if (num == val->next)
595 	/* Subtract 2 below because of the end marker and the checksum. */
596 	offset = rec->used_length-2;
597     else
598 	offset = val->strings[num].offset;
599 
600     for (i=val->next; i>(int)num; i--) {
601 	val->strings[i] = val->strings[i-1];
602 	val->strings[i].changed = 1;
603     }
604 
605     val->strings[num].str = NULL;
606     val->strings[num].raw_data = NULL;
607     val->strings[num].offset = offset;
608     val->strings[num].length = 0;
609     val->strings[num].raw_len = 0;
610     val->next++;
611 
612     rv = fru_string_set(fru, type, str, len, rec, val, num, 1);
613     return rv;
614 }
615 
616 static int
fru_decode_variable_string(ipmi_fru_t * fru,unsigned char * start_pos,unsigned char ** in,unsigned int * in_len,int lang_code,fru_variable_t * v)617 fru_decode_variable_string(ipmi_fru_t     *fru,
618 			   unsigned char  *start_pos,
619 			   unsigned char  **in,
620 			   unsigned int   *in_len,
621 			   int            lang_code,
622 			   fru_variable_t *v)
623 {
624     int err;
625 
626     if (v->next == v->len) {
627 #define FRU_STR_ALLOC_INCREMENT	5
628 	fru_string_t *n;
629 	int          n_len = v->len + FRU_STR_ALLOC_INCREMENT;
630 
631 	n = ipmi_mem_alloc(sizeof(fru_string_t) * n_len);
632 	if (!n)
633 	    return ENOMEM;
634 
635 	if (v->strings) {
636 	    memcpy(n, v->strings, sizeof(fru_string_t) * v->len);
637 	    ipmi_mem_free(v->strings);
638 	}
639 	memset(n + v->len, 0,
640 	       sizeof(fru_string_t) * FRU_STR_ALLOC_INCREMENT);
641 	v->strings = n;
642 	v->len = n_len;
643     }
644 
645     err = fru_decode_string(fru, start_pos, in, in_len, lang_code, 0,
646 			    v, v->next);
647     if (!err)
648 	v->next++;
649     return err;
650 }
651 
652 static int
fru_variable_string_to_out(fru_variable_t * in,unsigned int num,char * out,unsigned int * length)653 fru_variable_string_to_out(fru_variable_t *in,
654 			   unsigned int   num,
655 			   char           *out,
656 			   unsigned int   *length)
657 {
658     if (num >= in->next)
659 	return E2BIG;
660 
661     return fru_string_to_out(out, length, &in->strings[num]);
662 }
663 
664 static int
fru_variable_string_length(fru_variable_t * in,unsigned int num,unsigned int * length)665 fru_variable_string_length(fru_variable_t *in,
666 			   unsigned int   num,
667 			   unsigned int   *length)
668 {
669     if (num >= in->next)
670 	return E2BIG;
671 
672     if (in->strings[num].type == IPMI_ASCII_STR)
673 	*length = in->strings[num].length + 1;
674     else
675 	*length = in->strings[num].length;
676     return 0;
677 }
678 
679 static int
fru_variable_string_type(fru_variable_t * in,unsigned int num,enum ipmi_str_type_e * type)680 fru_variable_string_type(fru_variable_t       *in,
681 			 unsigned int         num,
682 			 enum ipmi_str_type_e *type)
683 {
684     if (num >= in->next)
685 	return E2BIG;
686 
687     *type = in->strings[num].type;
688     return 0;
689 }
690 
691 static void
fru_free_variable_string(fru_variable_t * v)692 fru_free_variable_string(fru_variable_t *v)
693 {
694     int i;
695 
696     for (i=0; i<v->next; i++)
697 	fru_free_string(&v->strings[i]);
698 
699     if (v->strings)
700 	ipmi_mem_free(v->strings);
701 }
702 
703 
704 /***********************************************************************
705  *
706  * Here is the basic FRU handling.
707  *
708  **********************************************************************/
709 
710 static ipmi_fru_record_t *
fru_record_alloc(int area,int full_init,unsigned int length)711 fru_record_alloc(int area, int full_init, unsigned int length)
712 {
713     ipmi_fru_record_t *rec;
714     unsigned short    extra_len = fru_area_info[area].extra_len;
715 
716     rec = ipmi_mem_alloc(sizeof(ipmi_fru_record_t) + extra_len);
717     if (!rec)
718 	return NULL;
719 
720     memset(rec, 0, sizeof(ipmi_fru_record_t)+extra_len);
721 
722     rec->handlers = fru_area_info + area;
723     rec->data = ((char *) rec) + sizeof(ipmi_fru_record_t);
724     rec->length = length;
725 
726     if (fru_area_info[area].setup_new) {
727 	int rv;
728 	rv = fru_area_info[area].setup_new(rec, full_init);
729 	if (rv) {
730 	    ipmi_mem_free(rec);
731 	    rec = NULL;
732 	}
733     }
734 
735     return rec;
736 }
737 
738 static void *
fru_record_get_data(ipmi_fru_record_t * rec)739 fru_record_get_data(ipmi_fru_record_t *rec)
740 {
741     return rec->data;
742 }
743 
744 static void
fru_record_free(ipmi_fru_record_t * rec)745 fru_record_free(ipmi_fru_record_t *rec)
746 {
747     ipmi_mem_free(rec);
748 }
749 
750 
751 /***********************************************************************
752  *
753  * Various macros for common handling.
754  *
755  **********************************************************************/
756 
757 #define HANDLE_STR_DECODE(ucname, fname, force_english) \
758     err = fru_decode_string(fru, orig_data, &data, &data_len, u->lang_code, \
759 			    force_english, &u->fields,		\
760 			    ucname ## _ ## fname);		\
761     if (err)							\
762 	goto out_err
763 
764 #define HANDLE_CUSTOM_DECODE(ucname) \
765 do {									\
766     while ((data_len > 0) && (*data != 0xc1)) {				\
767 	err = fru_decode_variable_string(fru, orig_data, &data, &data_len, \
768 					 u->lang_code,			\
769 					 &u->fields);			\
770 	if (err)							\
771 	    goto out_err;						\
772     }									\
773 } while (0)
774 
775 #define GET_DATA_PREFIX(lcname, ucname) \
776     ipmi_fru_ ## lcname ## _area_t *u;				\
777     ipmi_fru_record_t              **recs;			\
778     ipmi_fru_record_t              *rec;			\
779     if (!i_ipmi_fru_is_normal_fru(fru))				\
780 	return ENOSYS;						\
781     i_ipmi_fru_lock(fru);					\
782     recs = normal_fru_get_recs(fru);				\
783     rec = recs[IPMI_FRU_FTR_## ucname ## _AREA];		\
784     if (!rec) {							\
785 	i_ipmi_fru_unlock(fru);					\
786 	return ENOSYS;						\
787     }								\
788     u = fru_record_get_data(rec);
789 
790 #define GET_DATA_STR(lcname, ucname, fname) \
791 int									\
792 ipmi_fru_get_ ## lcname ## _ ## fname ## _len(ipmi_fru_t   *fru,	\
793 					      unsigned int *length)	\
794 {									\
795     int rv;								\
796     GET_DATA_PREFIX(lcname, ucname);					\
797     rv = fru_variable_string_length(&u->fields,				\
798 				    ucname ## _ ## fname,		\
799                                     length);				\
800     i_ipmi_fru_unlock(fru);						\
801     return rv;								\
802 }									\
803 int									\
804 ipmi_fru_get_ ## lcname ## _ ## fname ## _type(ipmi_fru_t           *fru,\
805 					       enum ipmi_str_type_e *type)\
806 {									\
807     int rv;								\
808     GET_DATA_PREFIX(lcname, ucname);					\
809     rv = fru_variable_string_type(&u->fields,				\
810 				  ucname ## _ ## fname,			\
811                                   type);				\
812     i_ipmi_fru_unlock(fru);						\
813     return rv;								\
814 }									\
815 int									\
816 ipmi_fru_get_ ## lcname ## _ ## fname(ipmi_fru_t	*fru,		\
817 				      char              *str,		\
818 				      unsigned int      *strlen)	\
819 {									\
820     int rv;								\
821     GET_DATA_PREFIX(lcname, ucname);					\
822     rv = fru_variable_string_to_out(&u->fields,				\
823 				    ucname ## _ ## fname,		\
824                                     str, strlen);			\
825     i_ipmi_fru_unlock(fru);						\
826     return rv;								\
827 }									\
828 int									\
829 ipmi_fru_set_ ## lcname ## _ ## fname(ipmi_fru_t	   *fru,	\
830 				      enum ipmi_str_type_e type,	\
831 				      char                 *str,	\
832 				      unsigned int         len)		\
833 {									\
834     int rv;								\
835     GET_DATA_PREFIX(lcname, ucname);					\
836     rv = fru_variable_string_set(fru, rec,				\
837 				 &u->fields,				\
838 				 0, ucname ## _ ## fname,		\
839                                  type, str, len, 0);			\
840     i_ipmi_fru_unlock(fru);						\
841     return rv;								\
842 }
843 
844 #define GET_CUSTOM_STR(lcname, ucname) \
845 int									\
846 ipmi_fru_get_ ## lcname ## _ ## custom ## _len(ipmi_fru_t   *fru,	\
847 					       unsigned int num,	\
848 					       unsigned int *length)	\
849 {									\
850     int rv;								\
851     GET_DATA_PREFIX(lcname, ucname);					\
852     rv = fru_variable_string_length(&u->fields,				\
853 				    ucname ## _ ## custom_start + num,	\
854                                     length);				\
855     i_ipmi_fru_unlock(fru);						\
856     return rv;								\
857 }									\
858 int									\
859 ipmi_fru_get_ ## lcname ## _ ## custom ## _type(ipmi_fru_t   *fru,	\
860 					        unsigned int num,	\
861 					        enum ipmi_str_type_e *type) \
862 {									\
863     int rv;								\
864     GET_DATA_PREFIX(lcname, ucname);					\
865     rv = fru_variable_string_type(&u->fields,				\
866 				  ucname ## _ ## custom_start + num,	\
867                                   type);				\
868     i_ipmi_fru_unlock(fru);						\
869     return rv;								\
870 }									\
871 int									\
872 ipmi_fru_get_ ## lcname ## _ ## custom(ipmi_fru_t	 *fru,		\
873 				       unsigned int      num,		\
874 				       char              *str,		\
875 				       unsigned int      *strlen)	\
876 {									\
877     int rv;								\
878     GET_DATA_PREFIX(lcname, ucname);					\
879     rv = fru_variable_string_to_out(&u->fields,				\
880 				    ucname ## _ ## custom_start + num,	\
881                                     str, strlen);			\
882     i_ipmi_fru_unlock(fru);						\
883     return rv;								\
884 }									\
885 int									\
886 ipmi_fru_set_ ## lcname ## _ ## custom(ipmi_fru_t	    *fru,	\
887 				       unsigned int         num,	\
888 				       enum ipmi_str_type_e type,	\
889 				       char                 *str,	\
890 				       unsigned int         len)	\
891 {									\
892     int rv;								\
893     GET_DATA_PREFIX(lcname, ucname);					\
894     rv = fru_variable_string_set(fru, rec,				\
895 				 &u->fields,				\
896 				 ucname ## _ ## custom_start, num,	\
897                                  type, str, len, 1);			\
898     i_ipmi_fru_unlock(fru);						\
899     return rv;								\
900 }									\
901 int									\
902 ipmi_fru_ins_ ## lcname ## _ ## custom(ipmi_fru_t	    *fru,	\
903 				       unsigned int         num,	\
904 				       enum ipmi_str_type_e type,	\
905 				       char                 *str,	\
906 				       unsigned int         len)	\
907 {									\
908     int rv;								\
909     GET_DATA_PREFIX(lcname, ucname);					\
910     rv = fru_variable_string_ins(fru, rec,				\
911 				 &u->fields,				\
912 				 ucname ## _ ## custom_start, num,	\
913                                  type, str, len);			\
914     i_ipmi_fru_unlock(fru);						\
915     return rv;								\
916 }
917 
918 /***********************************************************************
919  *
920  * Handling for FRU internal use areas.
921  *
922  **********************************************************************/
923 
924 typedef struct ipmi_fru_internal_use_area_s
925 {
926     /* version bit 7-4 reserved (0000), bit 3-0 == 0001 */
927     unsigned char  version;
928     unsigned short length;
929     unsigned char  *data;
930 } ipmi_fru_internal_use_area_t;
931 
932 
933 static void
internal_use_area_free(ipmi_fru_record_t * rec)934 internal_use_area_free(ipmi_fru_record_t *rec)
935 {
936     ipmi_fru_internal_use_area_t *u = fru_record_get_data(rec);
937 
938     ipmi_mem_free(u->data);
939     fru_record_free(rec);
940 }
941 
942 static int
internal_use_area_setup(ipmi_fru_record_t * rec,int full_setup)943 internal_use_area_setup(ipmi_fru_record_t *rec, int full_setup)
944 {
945     ipmi_fru_internal_use_area_t *u = fru_record_get_data(rec);
946 
947     u->version = 1;
948     if (full_setup) {
949 	u->length = rec->length - 1;
950 	u->data = ipmi_mem_alloc(u->length);
951 	if (!u->data)
952 	    return ENOMEM;
953 	memset(u->data, 0, u->length);
954     }
955     return 0;
956 }
957 
958 static int
fru_decode_internal_use_area(ipmi_fru_t * fru,unsigned char * data,unsigned int data_len,ipmi_fru_record_t ** rrec)959 fru_decode_internal_use_area(ipmi_fru_t        *fru,
960 			     unsigned char     *data,
961 			     unsigned int      data_len,
962 			     ipmi_fru_record_t **rrec)
963 {
964     ipmi_fru_internal_use_area_t *u;
965     ipmi_fru_record_t            *rec;
966 
967     rec = fru_record_alloc(IPMI_FRU_FTR_INTERNAL_USE_AREA, 0, data_len);
968     if (!rec)
969 	return ENOMEM;
970 
971     rec->used_length = data_len;
972     rec->orig_used_length = data_len;
973 
974     u = fru_record_get_data(rec);
975 
976     u->version = *data;
977     u->length = data_len-1;
978     u->data = ipmi_mem_alloc(u->length);
979     if (!u->data) {
980 	ipmi_mem_free(rec);
981 	return ENOMEM;
982     }
983 
984     memcpy(u->data, data+1, u->length);
985 
986     *rrec = rec;
987 
988     return 0;
989 }
990 
991 int
ipmi_fru_get_internal_use_version(ipmi_fru_t * fru,unsigned char * version)992 ipmi_fru_get_internal_use_version(ipmi_fru_t    *fru,
993 				  unsigned char *version)
994 {
995     GET_DATA_PREFIX(internal_use, INTERNAL_USE);
996 
997     *version = u->version;
998 
999     i_ipmi_fru_unlock(fru);
1000 
1001     return 0;
1002 }
1003 
1004 static int
ipmi_fru_set_internal_use_version(ipmi_fru_t * fru,unsigned char data)1005 ipmi_fru_set_internal_use_version(ipmi_fru_t *fru, unsigned char data)
1006 {
1007     return EPERM;
1008 }
1009 
1010 int
ipmi_fru_get_internal_use_len(ipmi_fru_t * fru,unsigned int * length)1011 ipmi_fru_get_internal_use_len(ipmi_fru_t   *fru,
1012 			      unsigned int *length)
1013 {
1014     GET_DATA_PREFIX(internal_use, INTERNAL_USE);
1015 
1016     *length = u->length;
1017 
1018     i_ipmi_fru_unlock(fru);
1019 
1020     return 0;
1021 }
1022 
1023 
1024 int
ipmi_fru_get_internal_use(ipmi_fru_t * fru,unsigned char * data,unsigned int * max_len)1025 ipmi_fru_get_internal_use(ipmi_fru_t    *fru,
1026 			  unsigned char *data,
1027 			  unsigned int  *max_len)
1028 {
1029     int l;
1030     GET_DATA_PREFIX(internal_use, INTERNAL_USE);
1031 
1032     l = *max_len;
1033 
1034     if (l > u->length)
1035 	l = u->length;
1036 
1037     memcpy(data, u->data, l);
1038 
1039     *max_len = l;
1040 
1041     i_ipmi_fru_unlock(fru);
1042 
1043     return 0;
1044 }
1045 
1046 int
ipmi_fru_set_internal_use(ipmi_fru_t * fru,unsigned char * data,unsigned int len)1047 ipmi_fru_set_internal_use(ipmi_fru_t *fru, unsigned char *data,
1048 			  unsigned int len)
1049 {
1050     unsigned char *new_val;
1051 
1052     GET_DATA_PREFIX(internal_use, INTERNAL_USE);
1053 
1054     if (len > rec->length-1) {
1055 	i_ipmi_fru_unlock(fru);
1056 	return E2BIG;
1057     }
1058 
1059     new_val = ipmi_mem_alloc(len);
1060     if (!new_val) {
1061 	i_ipmi_fru_unlock(fru);
1062 	return ENOMEM;
1063     }
1064     if (u->data)
1065 	ipmi_mem_free(u->data);
1066     u->data = new_val;
1067     memcpy(u->data, data, len);
1068     u->length = len;
1069     rec->changed = 1;
1070     rec->used_length = len + 1;
1071     rec->orig_used_length = rec->used_length;
1072 
1073     i_ipmi_fru_unlock(fru);
1074 
1075     return 0;
1076 }
1077 
1078 static int
fru_encode_internal_use_area(ipmi_fru_t * fru,unsigned char * data)1079 fru_encode_internal_use_area(ipmi_fru_t *fru, unsigned char *data)
1080 {
1081     ipmi_fru_record_t **recs = normal_fru_get_recs(fru);
1082     ipmi_fru_record_t *rec = recs[IPMI_FRU_FTR_INTERNAL_USE_AREA];
1083     ipmi_fru_internal_use_area_t *u;
1084     int               rv;
1085 
1086     if (!rec)
1087 	return 0;
1088 
1089     u = fru_record_get_data(rec);
1090     data += rec->offset;
1091     memset(data, 0, rec->length);
1092     data[0] = 1; /* Version */
1093     memcpy(data+1, u->data, u->length);
1094     if (rec->changed && !rec->rewrite) {
1095 	rv = i_ipmi_fru_new_update_record(fru, rec->offset, u->length+1);
1096 	if (rv)
1097 	    return rv;
1098     }
1099     return 0;
1100 }
1101 
1102 /***********************************************************************
1103  *
1104  * Handling for FRU chassis info areas
1105  *
1106  **********************************************************************/
1107 
1108 #define CHASSIS_INFO_part_number	0
1109 #define CHASSIS_INFO_serial_number	1
1110 #define CHASSIS_INFO_custom_start	2
1111 
1112 typedef struct ipmi_fru_chassis_info_area_s
1113 {
1114     /* version bit 7-4 reserved (0000), bit 3-0 == 0001 */
1115     unsigned char  version;
1116     unsigned char  type;  /* chassis type CT_xxxx */
1117     unsigned char  lang_code;
1118     fru_variable_t fields;
1119 } ipmi_fru_chassis_info_area_t;
1120 
1121 static void
chassis_info_area_free(ipmi_fru_record_t * rec)1122 chassis_info_area_free(ipmi_fru_record_t *rec)
1123 {
1124     ipmi_fru_chassis_info_area_t *u = fru_record_get_data(rec);
1125 
1126     fru_free_variable_string(&u->fields);
1127     fru_record_free(rec);
1128 }
1129 
1130 static int
chassis_info_area_setup(ipmi_fru_record_t * rec,int full_init)1131 chassis_info_area_setup(ipmi_fru_record_t *rec, int full_init)
1132 {
1133     ipmi_fru_chassis_info_area_t *u = fru_record_get_data(rec);
1134 
1135     u->version = 1;
1136     if (full_init) {
1137 	u->type = 0;
1138 	u->lang_code = 0;
1139     }
1140     return 0;
1141 }
1142 
1143 static fru_variable_t *
chassis_info_get_fields(ipmi_fru_record_t * rec)1144 chassis_info_get_fields(ipmi_fru_record_t *rec)
1145 {
1146     ipmi_fru_chassis_info_area_t *u;
1147     u = fru_record_get_data(rec);
1148     return &u->fields;
1149 }
1150 
1151 static int
fru_decode_chassis_info_area(ipmi_fru_t * fru,unsigned char * data,unsigned int data_len,ipmi_fru_record_t ** rrec)1152 fru_decode_chassis_info_area(ipmi_fru_t        *fru,
1153 			     unsigned char     *data,
1154 			     unsigned int      data_len,
1155 			     ipmi_fru_record_t **rrec)
1156 {
1157     ipmi_fru_chassis_info_area_t *u;
1158     ipmi_fru_record_t            *rec;
1159     int                          err;
1160     unsigned char                version;
1161     unsigned char                length;
1162     unsigned char                *orig_data = data;
1163 
1164     version = *data;
1165     length = (*(data+1)) * 8;
1166     if ((length == 0) || (length > data_len)) {
1167 	ipmi_log(IPMI_LOG_ERR_INFO,
1168 		 "%snormal_fru.c(fru_decode_chassis_info_area):"
1169 		 " FRU string goes past data length",
1170 		 i_ipmi_fru_get_iname(fru));
1171 	return EBADF;
1172     }
1173 
1174     if (checksum(data, length) != 0) {
1175 	ipmi_log(IPMI_LOG_ERR_INFO,
1176 		 "%snormal_fru.c(fru_decode_chassis_info_area):"
1177 		 " FRU string checksum failed",
1178 		 i_ipmi_fru_get_iname(fru));
1179 	return EBADF;
1180     }
1181 
1182     data_len--; /* remove the checksum */
1183 
1184     rec = fru_record_alloc(IPMI_FRU_FTR_CHASSIS_INFO_AREA, 0, length);
1185     if (!rec)
1186 	return ENOMEM;
1187 
1188     err = fru_setup_min_field(rec, IPMI_FRU_FTR_CHASSIS_INFO_AREA, 0);
1189     if (err)
1190 	goto out_err;
1191 
1192     u = fru_record_get_data(rec);
1193 
1194     u->version = version;
1195     data += 2;
1196     data_len -= 2;
1197     u->type = *data;
1198     data++;
1199     data_len--;
1200     u->lang_code = IPMI_LANG_CODE_ENGLISH;
1201     HANDLE_STR_DECODE(CHASSIS_INFO, part_number, 1);
1202     HANDLE_STR_DECODE(CHASSIS_INFO, serial_number, 1);
1203     HANDLE_CUSTOM_DECODE(CHASSIS_INFO);
1204     rec->used_length = data - orig_data + 2; /* add 1 for the checksum, 1 for term */
1205     rec->orig_used_length = rec->used_length;
1206 
1207     *rrec = rec;
1208 
1209     return 0;
1210 
1211  out_err:
1212     chassis_info_area_free(rec);
1213     return err;
1214 }
1215 
1216 int
ipmi_fru_get_chassis_info_version(ipmi_fru_t * fru,unsigned char * version)1217 ipmi_fru_get_chassis_info_version(ipmi_fru_t    *fru,
1218 				  unsigned char *version)
1219 {
1220     GET_DATA_PREFIX(chassis_info, CHASSIS_INFO);
1221 
1222     *version = u->version;
1223 
1224     i_ipmi_fru_unlock(fru);
1225 
1226     return 0;
1227 }
1228 
1229 static int
ipmi_fru_set_chassis_info_version(ipmi_fru_t * fru,unsigned char data)1230 ipmi_fru_set_chassis_info_version(ipmi_fru_t *fru, unsigned char data)
1231 {
1232     return EPERM;
1233 }
1234 
1235 int
ipmi_fru_get_chassis_info_type(ipmi_fru_t * fru,unsigned char * type)1236 ipmi_fru_get_chassis_info_type(ipmi_fru_t    *fru,
1237 			       unsigned char *type)
1238 {
1239     GET_DATA_PREFIX(chassis_info, CHASSIS_INFO);
1240 
1241     *type = u->type;
1242 
1243     i_ipmi_fru_unlock(fru);
1244 
1245     return 0;
1246 }
1247 
1248 int
ipmi_fru_set_chassis_info_type(ipmi_fru_t * fru,unsigned char type)1249 ipmi_fru_set_chassis_info_type(ipmi_fru_t    *fru,
1250 			       unsigned char type)
1251 {
1252     GET_DATA_PREFIX(chassis_info, CHASSIS_INFO);
1253 
1254     rec->changed |= u->type != type;
1255     u->type = type;
1256 
1257     i_ipmi_fru_unlock(fru);
1258 
1259     return 0;
1260 }
1261 
GET_DATA_STR(chassis_info,CHASSIS_INFO,part_number)1262 GET_DATA_STR(chassis_info, CHASSIS_INFO, part_number)
1263 GET_DATA_STR(chassis_info, CHASSIS_INFO, serial_number)
1264 GET_CUSTOM_STR(chassis_info, CHASSIS_INFO)
1265 
1266 static int
1267 fru_encode_chassis_info_area(ipmi_fru_t *fru, unsigned char *data)
1268 {
1269     ipmi_fru_record_t **recs = normal_fru_get_recs(fru);
1270     ipmi_fru_record_t *rec = recs[IPMI_FRU_FTR_CHASSIS_INFO_AREA];
1271     ipmi_fru_chassis_info_area_t *u;
1272     int               rv;
1273 
1274     if (!rec)
1275 	return 0;
1276 
1277     u = fru_record_get_data(rec);
1278     data += rec->offset;
1279     memset(data, 0, rec->length);
1280     data[0] = 1; /* Version */
1281     data[1] = rec->length / 8;
1282     data[2] = u->type;
1283     if (rec->changed && !rec->rewrite) {
1284 	rv = i_ipmi_fru_new_update_record(fru, rec->offset, 3);
1285 	if (rv)
1286 	    return rv;
1287     }
1288     rv = fru_encode_fields(fru, rec, &u->fields, data, 3);
1289     if (rv)
1290 	return rv;
1291     data[rec->length-1] = -checksum(data, rec->length-1);
1292     if (rec->changed && !rec->rewrite) {
1293 	/* Write any zeros that need to be written if the data got
1294 	   shorter. */
1295 	if (rec->used_length < rec->orig_used_length) {
1296 	    rv = i_ipmi_fru_new_update_record(fru,
1297 					     rec->offset + rec->used_length - 1,
1298 					     (rec->orig_used_length
1299 					      - rec->used_length));
1300 	    if (rv)
1301 		return rv;
1302 	}
1303 	/* Write the checksum */
1304 	rv = i_ipmi_fru_new_update_record(fru, rec->offset+rec->length-1, 1);
1305 	if (rv)
1306 	    return rv;
1307     }
1308     return 0;
1309 }
1310 
1311 /***********************************************************************
1312  *
1313  * Handling for FRU board info areas
1314  *
1315  **********************************************************************/
1316 
1317 #define BOARD_INFO_board_manufacturer	0
1318 #define BOARD_INFO_board_product_name	1
1319 #define BOARD_INFO_board_serial_number	2
1320 #define BOARD_INFO_board_part_number	3
1321 #define BOARD_INFO_fru_file_id		4
1322 #define BOARD_INFO_custom_start		5
1323 
1324 typedef struct ipmi_fru_board_info_area_s
1325 {
1326     /* version bit 7-4 reserved (0000), bit 3-0 == 0001 */
1327     unsigned char  version;
1328     unsigned char  lang_code;
1329     time_t         mfg_time;
1330     fru_variable_t fields;
1331 } ipmi_fru_board_info_area_t;
1332 
1333 static void
board_info_area_free(ipmi_fru_record_t * rec)1334 board_info_area_free(ipmi_fru_record_t *rec)
1335 {
1336     ipmi_fru_board_info_area_t *u = fru_record_get_data(rec);
1337 
1338     fru_free_variable_string(&u->fields);
1339     fru_record_free(rec);
1340 }
1341 
1342 static int
board_info_area_setup(ipmi_fru_record_t * rec,int full_init)1343 board_info_area_setup(ipmi_fru_record_t *rec, int full_init)
1344 {
1345     ipmi_fru_board_info_area_t *u = fru_record_get_data(rec);
1346 
1347     u->version = 1;
1348     if (full_init) {
1349 	u->lang_code = 0;
1350 	u->mfg_time = 0;
1351     }
1352     return 0;
1353 }
1354 
1355 static fru_variable_t *
board_info_get_fields(ipmi_fru_record_t * rec)1356 board_info_get_fields(ipmi_fru_record_t *rec)
1357 {
1358     ipmi_fru_board_info_area_t *u;
1359     u = fru_record_get_data(rec);
1360     return &u->fields;
1361 }
1362 
1363 static int
fru_decode_board_info_area(ipmi_fru_t * fru,unsigned char * data,unsigned int data_len,ipmi_fru_record_t ** rrec)1364 fru_decode_board_info_area(ipmi_fru_t        *fru,
1365 			   unsigned char     *data,
1366 			   unsigned int      data_len,
1367 			   ipmi_fru_record_t **rrec)
1368 {
1369     ipmi_fru_board_info_area_t *u;
1370     ipmi_fru_record_t          *rec;
1371     int                        err;
1372     unsigned char              version;
1373     unsigned int               length;
1374     unsigned char              *orig_data = data;
1375 
1376     version = *data;
1377     length = (*(data+1)) * 8;
1378     if ((length == 0) || (length > data_len)) {
1379 	ipmi_log(IPMI_LOG_ERR_INFO,
1380 		 "%snormal_fru.c(fru_decode_board_info_area):"
1381 		 " FRU string goes past data length",
1382 		 i_ipmi_fru_get_iname(fru));
1383 	return EBADF;
1384     }
1385 
1386     if (checksum(data, length) != 0) {
1387 	ipmi_log(IPMI_LOG_ERR_INFO,
1388 		 "%snormal_fru.c(fru_decode_board_info_area):"
1389 		 " FRU string checksum failed",
1390 		 i_ipmi_fru_get_iname(fru));
1391 	return EBADF;
1392     }
1393 
1394     data_len--; /* remove the checksum */
1395 
1396     rec = fru_record_alloc(IPMI_FRU_FTR_BOARD_INFO_AREA, 0, length);
1397     if (!rec)
1398 	return ENOMEM;
1399 
1400     err = fru_setup_min_field(rec, IPMI_FRU_FTR_BOARD_INFO_AREA, 0);
1401     if (err)
1402 	goto out_err;
1403 
1404     u = fru_record_get_data(rec);
1405 
1406     u->version = version;
1407     data += 2;
1408     data_len -= 2;
1409     u->lang_code = *data;
1410     if (u->lang_code == 0)
1411 	u->lang_code = IPMI_LANG_CODE_ENGLISH;
1412     data++;
1413     data_len--;
1414 
1415     err = read_fru_time(&data, &data_len, &u->mfg_time);
1416     if (err)
1417 	goto out_err;
1418 
1419     HANDLE_STR_DECODE(BOARD_INFO, board_manufacturer, 0);
1420     HANDLE_STR_DECODE(BOARD_INFO, board_product_name, 0);
1421     HANDLE_STR_DECODE(BOARD_INFO, board_serial_number, 1);
1422     HANDLE_STR_DECODE(BOARD_INFO, board_part_number, 1);
1423     HANDLE_STR_DECODE(BOARD_INFO, fru_file_id, 1);
1424     HANDLE_CUSTOM_DECODE(BOARD_INFO);
1425     rec->used_length = data - orig_data + 2; /* add 1 for the checksum, 1 for term */
1426     rec->orig_used_length = rec->used_length;
1427 
1428     *rrec = rec;
1429 
1430     return 0;
1431 
1432  out_err:
1433     board_info_area_free(rec);
1434     return err;
1435 }
1436 
1437 int
ipmi_fru_get_board_info_version(ipmi_fru_t * fru,unsigned char * version)1438 ipmi_fru_get_board_info_version(ipmi_fru_t    *fru,
1439 				unsigned char *version)
1440 {
1441     GET_DATA_PREFIX(board_info, BOARD_INFO);
1442 
1443     *version = u->version;
1444 
1445     i_ipmi_fru_unlock(fru);
1446 
1447     return 0;
1448 }
1449 
1450 static int
ipmi_fru_set_board_info_version(ipmi_fru_t * fru,unsigned char data)1451 ipmi_fru_set_board_info_version(ipmi_fru_t *fru, unsigned char data)
1452 {
1453     return EPERM;
1454 }
1455 
1456 int
ipmi_fru_get_board_info_lang_code(ipmi_fru_t * fru,unsigned char * type)1457 ipmi_fru_get_board_info_lang_code(ipmi_fru_t    *fru,
1458 				  unsigned char *type)
1459 {
1460     GET_DATA_PREFIX(board_info, BOARD_INFO);
1461 
1462     *type = u->lang_code;
1463 
1464     i_ipmi_fru_unlock(fru);
1465 
1466     return 0;
1467 }
1468 
1469 int
ipmi_fru_set_board_info_lang_code(ipmi_fru_t * fru,unsigned char lang)1470 ipmi_fru_set_board_info_lang_code(ipmi_fru_t    *fru,
1471 				  unsigned char lang)
1472 {
1473     GET_DATA_PREFIX(board_info, BOARD_INFO);
1474 
1475     rec->changed |= u->lang_code != lang;
1476     u->lang_code = lang;
1477 
1478     i_ipmi_fru_unlock(fru);
1479 
1480     return 0;
1481 }
1482 
1483 int
ipmi_fru_get_board_info_mfg_time(ipmi_fru_t * fru,time_t * time)1484 ipmi_fru_get_board_info_mfg_time(ipmi_fru_t *fru,
1485 				 time_t     *time)
1486 {
1487     GET_DATA_PREFIX(board_info, BOARD_INFO);
1488 
1489     *time = u->mfg_time;
1490 
1491     i_ipmi_fru_unlock(fru);
1492 
1493     return 0;
1494 }
1495 
1496 int
ipmi_fru_set_board_info_mfg_time(ipmi_fru_t * fru,time_t time)1497 ipmi_fru_set_board_info_mfg_time(ipmi_fru_t *fru,
1498 				 time_t     time)
1499 {
1500     GET_DATA_PREFIX(board_info, BOARD_INFO);
1501 
1502     rec->changed |= u->mfg_time != time;
1503     u->mfg_time = time;
1504 
1505     i_ipmi_fru_unlock(fru);
1506 
1507     return 0;
1508 }
1509 
GET_DATA_STR(board_info,BOARD_INFO,board_manufacturer)1510 GET_DATA_STR(board_info, BOARD_INFO, board_manufacturer)
1511 GET_DATA_STR(board_info, BOARD_INFO, board_product_name)
1512 GET_DATA_STR(board_info, BOARD_INFO, board_serial_number)
1513 GET_DATA_STR(board_info, BOARD_INFO, board_part_number)
1514 GET_DATA_STR(board_info, BOARD_INFO, fru_file_id)
1515 GET_CUSTOM_STR(board_info, BOARD_INFO)
1516 
1517 static int
1518 fru_encode_board_info_area(ipmi_fru_t *fru, unsigned char *data)
1519 {
1520     ipmi_fru_record_t **recs = normal_fru_get_recs(fru);
1521     ipmi_fru_record_t *rec = recs[IPMI_FRU_FTR_BOARD_INFO_AREA];
1522     ipmi_fru_board_info_area_t *u;
1523     int               rv;
1524 
1525     if (!rec)
1526 	return 0;
1527 
1528     u = fru_record_get_data(rec);
1529     data += rec->offset;
1530     data[0] = 1; /* Version */
1531     data[1] = rec->length / 8;
1532     data[2] = u->lang_code;
1533     write_fru_time(data+3, u->mfg_time);
1534 
1535     if (rec->changed && !rec->rewrite) {
1536 	rv = i_ipmi_fru_new_update_record(fru, rec->offset, 6);
1537 	if (rv)
1538 	    return rv;
1539     }
1540     rv = fru_encode_fields(fru, rec, &u->fields, data, 6);
1541     if (rv)
1542 	return rv;
1543     data[rec->length-1] = -checksum(data, rec->length-1);
1544     if (rec->changed && !rec->rewrite) {
1545 	/* Write any zeros that need to be written if the data got
1546 	   shorter.  Subtract off 1 for the checksum since it is in
1547 	   the used length */
1548 	if (rec->used_length < rec->orig_used_length) {
1549 	    rv = i_ipmi_fru_new_update_record(fru,
1550 					     rec->offset + rec->used_length - 1,
1551 					     (rec->orig_used_length
1552 					      - rec->used_length));
1553 	    if (rv)
1554 		return rv;
1555 	}
1556 	/* Write the checksum */
1557 	rv = i_ipmi_fru_new_update_record(fru, rec->offset+rec->length-1, 1);
1558 	if (rv)
1559 	    return rv;
1560     }
1561     return 0;
1562 }
1563 
1564 /***********************************************************************
1565  *
1566  * Handling for FRU product info areas
1567  *
1568  **********************************************************************/
1569 
1570 #define PRODUCT_INFO_manufacturer_name		0
1571 #define PRODUCT_INFO_product_name		1
1572 #define PRODUCT_INFO_product_part_model_number	2
1573 #define PRODUCT_INFO_product_version		3
1574 #define PRODUCT_INFO_product_serial_number	4
1575 #define PRODUCT_INFO_asset_tag			5
1576 #define PRODUCT_INFO_fru_file_id		6
1577 #define PRODUCT_INFO_custom_start		7
1578 
1579 typedef struct ipmi_fru_product_info_area_s
1580 {
1581     /* version bit 7-4 reserved (0000), bit 3-0 == 0001 */
1582     unsigned char  version;
1583     unsigned char  lang_code;
1584     fru_variable_t fields;
1585 } ipmi_fru_product_info_area_t;
1586 
1587 static void
product_info_area_free(ipmi_fru_record_t * rec)1588 product_info_area_free(ipmi_fru_record_t *rec)
1589 {
1590     ipmi_fru_product_info_area_t *u = fru_record_get_data(rec);
1591 
1592     fru_free_variable_string(&u->fields);
1593     fru_record_free(rec);
1594 }
1595 
1596 static int
product_info_area_setup(ipmi_fru_record_t * rec,int full_init)1597 product_info_area_setup(ipmi_fru_record_t *rec, int full_init)
1598 {
1599     ipmi_fru_product_info_area_t *u = fru_record_get_data(rec);
1600 
1601     u->version = 1;
1602     if (full_init) {
1603 	u->lang_code = 0;
1604     }
1605     return 0;
1606 }
1607 
1608 static fru_variable_t *
product_info_get_fields(ipmi_fru_record_t * rec)1609 product_info_get_fields(ipmi_fru_record_t *rec)
1610 {
1611     ipmi_fru_product_info_area_t *u;
1612     u = fru_record_get_data(rec);
1613     return &u->fields;
1614 }
1615 
1616 static int
fru_decode_product_info_area(ipmi_fru_t * fru,unsigned char * data,unsigned int data_len,ipmi_fru_record_t ** rrec)1617 fru_decode_product_info_area(ipmi_fru_t        *fru,
1618 			     unsigned char     *data,
1619 			     unsigned int      data_len,
1620 			     ipmi_fru_record_t **rrec)
1621 {
1622     ipmi_fru_product_info_area_t *u;
1623     ipmi_fru_record_t            *rec;
1624     int                          err;
1625     unsigned char                version;
1626     unsigned int                 length;
1627     unsigned char                *orig_data = data;
1628 
1629     version = *data;
1630     length = (*(data+1)) * 8;
1631     if ((length == 0) || (length > data_len)) {
1632 	ipmi_log(IPMI_LOG_ERR_INFO,
1633 		 "%snormal_fru.c(fru_decode_product_info_area):"
1634 		 " FRU string goes past data length",
1635 		 i_ipmi_fru_get_iname(fru));
1636 	return EBADF;
1637     }
1638 
1639     if (checksum(data, length) != 0) {
1640 	ipmi_log(IPMI_LOG_ERR_INFO,
1641 		 "%snormal_fru.c(fru_decode_product_info_area):"
1642 		 " FRU string checksum failed",
1643 		 i_ipmi_fru_get_iname(fru));
1644 	return EBADF;
1645     }
1646 
1647     data_len--; /* remove the checksum */
1648 
1649     rec = fru_record_alloc(IPMI_FRU_FTR_PRODUCT_INFO_AREA, 0, length);
1650     if (!rec)
1651 	return ENOMEM;
1652 
1653     err = fru_setup_min_field(rec, IPMI_FRU_FTR_PRODUCT_INFO_AREA, 0);
1654     if (err)
1655 	goto out_err;
1656 
1657     u = fru_record_get_data(rec);
1658 
1659     u->version = version;
1660     data += 2;
1661     data_len -= 2;
1662     u->lang_code = *data;
1663     if (u->lang_code == 0)
1664 	u->lang_code = IPMI_LANG_CODE_ENGLISH;
1665     data++;
1666     data_len--;
1667     HANDLE_STR_DECODE(PRODUCT_INFO, manufacturer_name, 0);
1668     HANDLE_STR_DECODE(PRODUCT_INFO, product_name, 0);
1669     HANDLE_STR_DECODE(PRODUCT_INFO, product_part_model_number, 0);
1670     HANDLE_STR_DECODE(PRODUCT_INFO, product_version, 0);
1671     HANDLE_STR_DECODE(PRODUCT_INFO, product_serial_number, 1);
1672     HANDLE_STR_DECODE(PRODUCT_INFO, asset_tag, 0);
1673     HANDLE_STR_DECODE(PRODUCT_INFO, fru_file_id, 1);
1674     HANDLE_CUSTOM_DECODE(PRODUCT_INFO);
1675     rec->used_length = data - orig_data + 2; /* add 1 for the checksum, 1 for term */
1676     rec->orig_used_length = rec->used_length;
1677 
1678     *rrec = rec;
1679 
1680     return 0;
1681 
1682  out_err:
1683     product_info_area_free(rec);
1684     return err;
1685 }
1686 
1687 int
ipmi_fru_get_product_info_version(ipmi_fru_t * fru,unsigned char * version)1688 ipmi_fru_get_product_info_version(ipmi_fru_t    *fru,
1689 				  unsigned char *version)
1690 {
1691     GET_DATA_PREFIX(product_info, PRODUCT_INFO);
1692 
1693     *version = u->version;
1694 
1695     i_ipmi_fru_unlock(fru);
1696 
1697     return 0;
1698 }
1699 
1700 static int
ipmi_fru_set_product_info_version(ipmi_fru_t * fru,unsigned char data)1701 ipmi_fru_set_product_info_version(ipmi_fru_t *fru, unsigned char data)
1702 {
1703     return EPERM;
1704 }
1705 
1706 int
ipmi_fru_get_product_info_lang_code(ipmi_fru_t * fru,unsigned char * type)1707 ipmi_fru_get_product_info_lang_code(ipmi_fru_t    *fru,
1708 				    unsigned char *type)
1709 {
1710     GET_DATA_PREFIX(product_info, PRODUCT_INFO);
1711 
1712     *type = u->lang_code;
1713 
1714     i_ipmi_fru_unlock(fru);
1715 
1716     return 0;
1717 }
1718 
1719 int
ipmi_fru_set_product_info_lang_code(ipmi_fru_t * fru,unsigned char lang)1720 ipmi_fru_set_product_info_lang_code(ipmi_fru_t    *fru,
1721 				    unsigned char lang)
1722 {
1723     GET_DATA_PREFIX(product_info, PRODUCT_INFO);
1724 
1725     rec->changed |= u->lang_code != lang;
1726     u->lang_code = lang;
1727 
1728     i_ipmi_fru_unlock(fru);
1729 
1730     return 0;
1731 }
1732 
GET_DATA_STR(product_info,PRODUCT_INFO,manufacturer_name)1733 GET_DATA_STR(product_info, PRODUCT_INFO, manufacturer_name)
1734 GET_DATA_STR(product_info, PRODUCT_INFO, product_name)
1735 GET_DATA_STR(product_info, PRODUCT_INFO, product_part_model_number)
1736 GET_DATA_STR(product_info, PRODUCT_INFO, product_version)
1737 GET_DATA_STR(product_info, PRODUCT_INFO, product_serial_number)
1738 GET_DATA_STR(product_info, PRODUCT_INFO, asset_tag)
1739 GET_DATA_STR(product_info, PRODUCT_INFO, fru_file_id)
1740 GET_CUSTOM_STR(product_info, PRODUCT_INFO)
1741 
1742 static int
1743 fru_encode_product_info_area(ipmi_fru_t *fru, unsigned char *data)
1744 {
1745     ipmi_fru_record_t **recs = normal_fru_get_recs(fru);
1746     ipmi_fru_record_t *rec = recs[IPMI_FRU_FTR_PRODUCT_INFO_AREA];
1747     ipmi_fru_product_info_area_t *u;
1748     int               rv;
1749 
1750     if (!rec)
1751 	return 0;
1752 
1753     u = fru_record_get_data(rec);
1754     data += rec->offset;
1755     memset(data, 0, rec->length);
1756     data[0] = 1; /* Version */
1757     data[1] = rec->length / 8;
1758     data[2] = u->lang_code;
1759 
1760     if (rec->changed && !rec->rewrite) {
1761 	rv = i_ipmi_fru_new_update_record(fru, rec->offset, 3);
1762 	if (rv)
1763 	    return rv;
1764     }
1765     rv = fru_encode_fields(fru, rec, &u->fields, data, 3);
1766     if (rv)
1767 	return rv;
1768 	/* Write any zeros that need to be written if the data got
1769 	   shorter. */
1770     data[rec->length-1] = -checksum(data, rec->length-1);
1771     if (rec->changed && !rec->rewrite) {
1772 	if (rec->used_length < rec->orig_used_length) {
1773 	    rv = i_ipmi_fru_new_update_record(fru,
1774 					     rec->offset + rec->used_length - 1,
1775 					     (rec->orig_used_length
1776 					      - rec->used_length));
1777 	    if (rv)
1778 		return rv;
1779 	}
1780 	/* Write the checksum */
1781 	rv = i_ipmi_fru_new_update_record(fru, rec->offset+rec->length-1, 1);
1782 	if (rv)
1783 	    return rv;
1784     }
1785     return 0;
1786 }
1787 
1788 /***********************************************************************
1789  *
1790  * Handling for FRU multi-records
1791  *
1792  **********************************************************************/
1793 typedef struct ipmi_fru_record_elem_s
1794 {
1795     /* Where relative to the beginning of the record area does this
1796        record start? */
1797     unsigned int  offset;
1798 
1799     /* Has this record been changed (needs to be written)? */
1800     char          changed;
1801 
1802     unsigned char type;
1803     unsigned char format_version;
1804     unsigned char length;
1805     unsigned char *data;
1806 } ipmi_fru_record_elem_t;
1807 
1808 typedef struct ipmi_fru_multi_record_s
1809 {
1810     /* Actual length of the array. */
1811     unsigned int           rec_len;
1812 
1813     /* Number of used elements in the array */
1814     unsigned int           num_records;
1815     ipmi_fru_record_elem_t *records;
1816 
1817     /* Dummy field to keep the macros happy */
1818     int                    version;
1819 } ipmi_fru_multi_record_area_t;
1820 
1821 static void
multi_record_area_free(ipmi_fru_record_t * rec)1822 multi_record_area_free(ipmi_fru_record_t *rec)
1823 {
1824     ipmi_fru_multi_record_area_t *u = fru_record_get_data(rec);
1825     unsigned int                 i;
1826 
1827     if (u->records) {
1828 	for (i=0; i<u->num_records; i++) {
1829 	    if (u->records[i].data)
1830 		ipmi_mem_free(u->records[i].data);
1831 	}
1832 	ipmi_mem_free(u->records);
1833     }
1834     fru_record_free(rec);
1835 }
1836 
1837 static int
fru_decode_multi_record_area(ipmi_fru_t * fru,unsigned char * data,unsigned int data_len,ipmi_fru_record_t ** rrec)1838 fru_decode_multi_record_area(ipmi_fru_t        *fru,
1839 			     unsigned char     *data,
1840 			     unsigned int      data_len,
1841 			     ipmi_fru_record_t **rrec)
1842 {
1843     ipmi_fru_record_t       *rec;
1844     int                     err;
1845     unsigned int            i;
1846     unsigned int            num_records;
1847     unsigned char           *orig_data = data;
1848     unsigned int            orig_data_len = data_len;
1849     ipmi_fru_multi_record_area_t *u;
1850     ipmi_fru_record_elem_t  *r;
1851     unsigned char           sum;
1852     unsigned int            length;
1853     unsigned int            start_offset = 0;
1854     unsigned int            left = data_len;
1855 
1856     /* First scan for the number of records. */
1857     num_records = 0;
1858     for (;;) {
1859 	unsigned char eol;
1860 
1861 	if (left < 5) {
1862 	    ipmi_log(IPMI_LOG_ERR_INFO,
1863 		     "%snormal_fru.c(fru_decode_multi_record_area):"
1864 		     " Data not long enough for multi record",
1865 		     i_ipmi_fru_get_iname(fru));
1866 	    return EBADF;
1867 	}
1868 
1869 	if (checksum(data, 5) != 0) {
1870 	    ipmi_log(IPMI_LOG_ERR_INFO,
1871 		     "%snormal_fru.c(fru_decode_multi_record_area):"
1872 		     " Header checksum for record %d failed",
1873 		     i_ipmi_fru_get_iname(fru), num_records+1);
1874 	    return EBADF;
1875 	}
1876 
1877 	length = data[2];
1878 	if ((length + 5) > left) {
1879 	    ipmi_log(IPMI_LOG_ERR_INFO,
1880 		     "%snormal_fru.c(fru_decode_multi_record_area):"
1881 		     " Record went past end of data",
1882 		     i_ipmi_fru_get_iname(fru));
1883 	    return EBADF;
1884 	}
1885 
1886 	sum = checksum(data+5, length) + data[3];
1887 	if (sum != 0) {
1888 	    ipmi_log(IPMI_LOG_ERR_INFO,
1889 		     "%snormal_fru.c(fru_decode_multi_record_area):"
1890 		     " Data checksum for record %d failed",
1891 		     i_ipmi_fru_get_iname(fru), num_records+1);
1892 	    return EBADF;
1893 	}
1894 
1895 	num_records++;
1896 
1897 	eol = data[1] & 0x80;
1898 
1899 	data += length + 5;
1900 	left -= length + 5;
1901 
1902 	if (eol)
1903 	    /* End of list */
1904 	    break;
1905     }
1906 
1907     rec = fru_record_alloc(IPMI_FRU_FTR_MULTI_RECORD_AREA, 0, data_len);
1908     if (!rec)
1909 	return ENOMEM;
1910 
1911     rec->used_length = data - orig_data;
1912     rec->orig_used_length = rec->used_length;
1913 
1914     u = fru_record_get_data(rec);
1915     u->num_records = num_records;
1916     u->rec_len = num_records;
1917     u->records = ipmi_mem_alloc(sizeof(ipmi_fru_record_elem_t) * num_records);
1918     if (!u->records) {
1919 	err = ENOMEM;
1920 	goto out_err;
1921     }
1922     memset(u->records, 0, sizeof(ipmi_fru_record_elem_t) * num_records);
1923 
1924     data = orig_data;
1925     data_len = orig_data_len;
1926     for (i=0; i<num_records; i++) {
1927 	/* No checks required, they've already been done above. */
1928 	length = data[2];
1929 	r = u->records + i;
1930 	if (length == 0)
1931 	    r->data = ipmi_mem_alloc(1);
1932 	else
1933 	    r->data = ipmi_mem_alloc(length);
1934 	if (!r->data) {
1935 	    err = ENOMEM;
1936 	    goto out_err;
1937 	}
1938 
1939 	memcpy(r->data, data+5, length);
1940 	r->length = length;
1941 	r->type = data[0];
1942 	r->format_version = data[1] & 0xf;
1943 	r->offset = start_offset;
1944 
1945 	data += length + 5;
1946 	start_offset += length + 5;
1947     }
1948 
1949     *rrec = rec;
1950 
1951     return 0;
1952 
1953  out_err:
1954     multi_record_area_free(rec);
1955     return err;
1956 }
1957 
1958 unsigned int
ipmi_fru_get_num_multi_records(ipmi_fru_t * fru)1959 ipmi_fru_get_num_multi_records(ipmi_fru_t *fru)
1960 {
1961     ipmi_fru_record_t            **recs;
1962     ipmi_fru_multi_record_area_t *u;
1963     unsigned int                 num;
1964 
1965     if (!i_ipmi_fru_is_normal_fru(fru))
1966 	return 0;
1967 
1968     i_ipmi_fru_lock(fru);
1969     recs = normal_fru_get_recs(fru);
1970     if (!recs[IPMI_FRU_FTR_MULTI_RECORD_AREA]) {
1971 	i_ipmi_fru_unlock(fru);
1972 	return 0;
1973     }
1974 
1975     u = fru_record_get_data(recs[IPMI_FRU_FTR_MULTI_RECORD_AREA]);
1976     num = u->num_records;
1977     i_ipmi_fru_unlock(fru);
1978     return num;
1979 }
1980 
1981 static int
validate_and_lock_multi_record(ipmi_fru_t * fru,unsigned int num,ipmi_fru_multi_record_area_t ** ru,ipmi_fru_record_t ** rrec)1982 validate_and_lock_multi_record(ipmi_fru_t                   *fru,
1983 			       unsigned int                 num,
1984 			       ipmi_fru_multi_record_area_t **ru,
1985 			       ipmi_fru_record_t            **rrec)
1986 {
1987     ipmi_fru_record_t            **recs;
1988     ipmi_fru_multi_record_area_t *u;
1989 
1990     if (!i_ipmi_fru_is_normal_fru(fru))
1991 	return ENOSYS;
1992 
1993     i_ipmi_fru_lock(fru);
1994     recs = normal_fru_get_recs(fru);
1995     if (!recs[IPMI_FRU_FTR_MULTI_RECORD_AREA]) {
1996 	i_ipmi_fru_unlock(fru);
1997 	return ENOSYS;
1998     }
1999     u = fru_record_get_data(recs[IPMI_FRU_FTR_MULTI_RECORD_AREA]);
2000     if (num >= u->num_records) {
2001 	i_ipmi_fru_unlock(fru);
2002 	return E2BIG;
2003     }
2004     *ru = u;
2005     if (rrec)
2006 	*rrec = recs[IPMI_FRU_FTR_MULTI_RECORD_AREA];
2007     return 0;
2008 }
2009 
2010 int
ipmi_fru_get_multi_record_type(ipmi_fru_t * fru,unsigned int num,unsigned char * type)2011 ipmi_fru_get_multi_record_type(ipmi_fru_t    *fru,
2012 			       unsigned int  num,
2013 			       unsigned char *type)
2014 {
2015     ipmi_fru_multi_record_area_t *u;
2016     int                          rv;
2017 
2018     rv = validate_and_lock_multi_record(fru, num, &u, NULL);
2019     if (rv)
2020 	return rv;
2021     *type = u->records[num].type;
2022     i_ipmi_fru_unlock(fru);
2023     return 0;
2024 }
2025 
2026 int
ipmi_fru_set_multi_record_type(ipmi_fru_t * fru,unsigned int num,unsigned char type)2027 ipmi_fru_set_multi_record_type(ipmi_fru_t    *fru,
2028 			       unsigned int  num,
2029 			       unsigned char type)
2030 {
2031     ipmi_fru_multi_record_area_t *u;
2032     int                          rv;
2033 
2034     rv = validate_and_lock_multi_record(fru, num, &u, NULL);
2035     if (rv)
2036 	return rv;
2037     u->records[num].type = type;
2038     i_ipmi_fru_unlock(fru);
2039     return 0;
2040 }
2041 
2042 int
ipmi_fru_get_multi_record_format_version(ipmi_fru_t * fru,unsigned int num,unsigned char * ver)2043 ipmi_fru_get_multi_record_format_version(ipmi_fru_t    *fru,
2044 					 unsigned int  num,
2045 					 unsigned char *ver)
2046 {
2047     ipmi_fru_multi_record_area_t *u;
2048     int                          rv;
2049 
2050     rv = validate_and_lock_multi_record(fru, num, &u, NULL);
2051     if (rv)
2052 	return rv;
2053     *ver = u->records[num].format_version;
2054     i_ipmi_fru_unlock(fru);
2055     return 0;
2056 }
2057 
2058 int
ipmi_fru_get_multi_record_data_len(ipmi_fru_t * fru,unsigned int num,unsigned int * len)2059 ipmi_fru_get_multi_record_data_len(ipmi_fru_t   *fru,
2060 				   unsigned int num,
2061 				   unsigned int *len)
2062 {
2063     ipmi_fru_multi_record_area_t *u;
2064     int                          rv;
2065 
2066     rv = validate_and_lock_multi_record(fru, num, &u, NULL);
2067     if (rv)
2068 	return rv;
2069     *len = u->records[num].length;
2070     i_ipmi_fru_unlock(fru);
2071     return 0;
2072 }
2073 
2074 int
ipmi_fru_get_multi_record_data(ipmi_fru_t * fru,unsigned int num,unsigned char * data,unsigned int * length)2075 ipmi_fru_get_multi_record_data(ipmi_fru_t    *fru,
2076 			       unsigned int  num,
2077 			       unsigned char *data,
2078 			       unsigned int  *length)
2079 {
2080     ipmi_fru_multi_record_area_t *u;
2081     int                          rv;
2082 
2083     rv = validate_and_lock_multi_record(fru, num, &u, NULL);
2084     if (rv)
2085 	return rv;
2086     if (*length < u->records[num].length) {
2087 	i_ipmi_fru_unlock(fru);
2088 	return EINVAL;
2089     }
2090     memcpy(data, u->records[num].data, u->records[num].length);
2091     *length = u->records[num].length;
2092     i_ipmi_fru_unlock(fru);
2093     return 0;
2094 }
2095 
2096 int
ipmi_fru_get_multi_record_slice(ipmi_fru_t * fru,unsigned int num,unsigned int offset,unsigned int length,unsigned char * data)2097 ipmi_fru_get_multi_record_slice(ipmi_fru_t    *fru,
2098 				unsigned int  num,
2099 				unsigned int  offset,
2100 				unsigned int  length,
2101 				unsigned char *data)
2102 {
2103     ipmi_fru_multi_record_area_t *u;
2104     int                          rv;
2105 
2106     rv = validate_and_lock_multi_record(fru, num, &u, NULL);
2107     if (rv)
2108 	return rv;
2109 
2110     if ((offset + length) > u->records[num].length) {
2111 	i_ipmi_fru_unlock(fru);
2112 	return EINVAL;
2113     }
2114 
2115     memcpy(data, u->records[num].data+offset, length);
2116     i_ipmi_fru_unlock(fru);
2117     return 0;
2118 }
2119 
2120 int
ipmi_fru_set_multi_record_data(ipmi_fru_t * fru,unsigned int num,unsigned char * data,unsigned int length)2121 ipmi_fru_set_multi_record_data(ipmi_fru_t    *fru,
2122 			       unsigned int  num,
2123 			       unsigned char *data,
2124 			       unsigned int  length)
2125 {
2126     ipmi_fru_multi_record_area_t *u;
2127     ipmi_fru_record_t            *rec;
2128     int                          raw_diff;
2129     unsigned int                 i;
2130     unsigned char                *new_data;
2131     int                          rv;
2132 
2133     if (length > 255)
2134 	return EINVAL;
2135 
2136     rv = validate_and_lock_multi_record(fru, num, &u, &rec);
2137     if (rv)
2138 	return rv;
2139 
2140     raw_diff = length - u->records[num].length;
2141 
2142     /* Is there enough space? */
2143     if ((rec->used_length + raw_diff) > rec->length)
2144 	return ENOSPC;
2145 
2146     /* Modifying the record. */
2147     if (length == 0)
2148 	new_data = ipmi_mem_alloc(1);
2149     else
2150 	new_data = ipmi_mem_alloc(length);
2151     if (!new_data) {
2152 	i_ipmi_fru_unlock(fru);
2153 	return ENOMEM;
2154     }
2155     memcpy(new_data, data, length);
2156     if (u->records[num].data)
2157 	ipmi_mem_free(u->records[num].data);
2158     u->records[num].data = new_data;
2159     u->records[num].length = length;
2160     if (raw_diff) {
2161 	for (i=num+1; i<u->num_records; i++) {
2162 	    u->records[i].offset += raw_diff;
2163 	    u->records[i].changed = 1;
2164 	}
2165     }
2166 
2167     rec->used_length += raw_diff;
2168     rec->changed |= 1;
2169     i_ipmi_fru_unlock(fru);
2170     return 0;
2171 }
2172 
2173 int
ipmi_fru_set_multi_record(ipmi_fru_t * fru,unsigned int num,unsigned char type,unsigned char version,unsigned char * data,unsigned int length)2174 ipmi_fru_set_multi_record(ipmi_fru_t    *fru,
2175 			  unsigned int  num,
2176 			  unsigned char type,
2177 			  unsigned char version,
2178 			  unsigned char *data,
2179 			  unsigned int  length)
2180 {
2181     normal_fru_rec_data_t        *info = i_ipmi_fru_get_rec_data(fru);
2182     ipmi_fru_record_t            **recs;
2183     ipmi_fru_multi_record_area_t *u;
2184     unsigned char                *new_data;
2185     ipmi_fru_record_t            *rec;
2186     int                          raw_diff = 0;
2187     unsigned int                 i;
2188 
2189     if (data && version != 2)
2190 	return EINVAL;
2191 
2192     if (length > 255)
2193 	return EINVAL;
2194 
2195     if (!i_ipmi_fru_is_normal_fru(fru))
2196 	return ENOSYS;
2197 
2198     i_ipmi_fru_lock(fru);
2199     recs = normal_fru_get_recs(fru);
2200     rec = recs[IPMI_FRU_FTR_MULTI_RECORD_AREA];
2201     if (!rec) {
2202 	i_ipmi_fru_unlock(fru);
2203 	return ENOSYS;
2204     }
2205 
2206     u = fru_record_get_data(rec);
2207 
2208     if (num >= u->num_records) {
2209 	if (!data) {
2210 	    /* Don't expand if we are deleting an invalid field,
2211 	       return an error. */
2212 	    i_ipmi_fru_unlock(fru);
2213 	    return EINVAL;
2214 	}
2215 
2216 	num = u->num_records;
2217 	/* If not enough room, expand the array by a set amount (to
2218 	   keep from thrashing memory when adding lots of things). */
2219 	if (u->num_records >= u->rec_len) {
2220 	    unsigned int           new_len = u->rec_len + 16;
2221 	    ipmi_fru_record_elem_t *new_recs;
2222 
2223 	    new_recs = ipmi_mem_alloc(new_len * sizeof(*new_recs));
2224 	    if (!new_recs) {
2225 		i_ipmi_fru_unlock(fru);
2226 		return ENOMEM;
2227 	    }
2228 	    memset(new_recs, 0, new_len * sizeof(*new_recs));
2229 	    if (u->records) {
2230 		memcpy(new_recs, u->records, u->rec_len * sizeof(*new_recs));
2231 		ipmi_mem_free(u->records);
2232 	    }
2233 	    u->records = new_recs;
2234 	    u->rec_len = new_len;
2235 	}
2236 	if (u->num_records == 0)
2237 	    info->header_changed = 1;
2238 	u->num_records++;
2239 	u->records[num].offset = rec->used_length;
2240 	u->records[num].length = 0;
2241 	u->records[num].changed = 1;
2242 	u->records[num].data = NULL;
2243 	raw_diff = 5; /* Header size */
2244     }
2245 
2246     if (data) {
2247 	raw_diff += length - u->records[num].length;
2248 
2249 	/* Is there enough space? */
2250 	if ((rec->used_length + raw_diff) > rec->length)
2251 	    return ENOSPC;
2252 
2253 	/* Modifying the record. */
2254 	if (length == 0)
2255 	    new_data = ipmi_mem_alloc(1);
2256 	else
2257 	    new_data = ipmi_mem_alloc(length);
2258 	if (!new_data) {
2259 	    i_ipmi_fru_unlock(fru);
2260 	    return ENOMEM;
2261 	}
2262 	memcpy(new_data, data, length);
2263 	if (u->records[num].data)
2264 	    ipmi_mem_free(u->records[num].data);
2265 	u->records[num].data = new_data;
2266 	u->records[num].type = type;
2267 	u->records[num].format_version = version;
2268 	u->records[num].length = length;
2269 	if (raw_diff) {
2270 	    for (i=num+1; i<u->num_records; i++) {
2271 		u->records[i].offset += raw_diff;
2272 		u->records[i].changed = 1;
2273 	    }
2274 	}
2275     } else {
2276 	/* Deleting the record. */
2277 	if (u->records[num].data)
2278 	    ipmi_mem_free(u->records[num].data);
2279 	u->num_records--;
2280 	raw_diff = - (5 + u->records[num].length);
2281 	for (i=num; i<u->num_records; i++) {
2282 	    u->records[i] = u->records[i+1];
2283 	    u->records[i].offset += raw_diff;
2284 	    u->records[i].changed = 1;
2285 	}
2286 	if (u->num_records == 0)
2287 	    /* Need to write "0" for the multi-records. */
2288 	    info->header_changed = 1;
2289     }
2290 
2291     rec->used_length += raw_diff;
2292     rec->changed |= 1;
2293     i_ipmi_fru_unlock(fru);
2294     return 0;
2295 }
2296 
2297 int
ipmi_fru_ins_multi_record(ipmi_fru_t * fru,unsigned int num,unsigned char type,unsigned char version,unsigned char * data,unsigned int length)2298 ipmi_fru_ins_multi_record(ipmi_fru_t    *fru,
2299 			  unsigned int  num,
2300 			  unsigned char type,
2301 			  unsigned char version,
2302 			  unsigned char *data,
2303 			  unsigned int  length)
2304 {
2305     normal_fru_rec_data_t        *info = i_ipmi_fru_get_rec_data(fru);
2306     ipmi_fru_record_t            **recs;
2307     ipmi_fru_multi_record_area_t *u;
2308     unsigned char                *new_data;
2309     ipmi_fru_record_t            *rec;
2310     int                          raw_diff = 0;
2311     unsigned int                 i;
2312     int                          offset;
2313 
2314     if (data && version != 2)
2315 	return EINVAL;
2316 
2317     if (length > 255)
2318 	return EINVAL;
2319 
2320     if (!i_ipmi_fru_is_normal_fru(fru))
2321 	return ENOSYS;
2322 
2323     i_ipmi_fru_lock(fru);
2324     recs = normal_fru_get_recs(fru);
2325     rec = recs[IPMI_FRU_FTR_MULTI_RECORD_AREA];
2326     if (!rec) {
2327 	i_ipmi_fru_unlock(fru);
2328 	return ENOSYS;
2329     }
2330 
2331     u = fru_record_get_data(rec);
2332 
2333     if (num >= u->num_records) {
2334 	num = u->num_records;
2335 	/* If not enough room, expand the array by a set amount (to
2336 	   keep from thrashing memory when adding lots of things). */
2337 	if (u->num_records >= u->rec_len) {
2338 	    unsigned int           new_len = u->rec_len + 16;
2339 	    ipmi_fru_record_elem_t *new_recs;
2340 
2341 	    new_recs = ipmi_mem_alloc(new_len * sizeof(*new_recs));
2342 	    if (!new_recs) {
2343 		i_ipmi_fru_unlock(fru);
2344 		return ENOMEM;
2345 	    }
2346 	    memset(new_recs, 0, new_len * sizeof(*new_recs));
2347 	    if (u->records) {
2348 		memcpy(new_recs, u->records, u->rec_len * sizeof(*new_recs));
2349 		ipmi_mem_free(u->records);
2350 	    }
2351 	    u->records = new_recs;
2352 	    u->rec_len = new_len;
2353 	}
2354     }
2355 
2356     raw_diff = 5 + length;
2357 
2358     /* Is there enough space? */
2359     if ((rec->used_length + raw_diff) > rec->length)
2360 	return ENOSPC;
2361 
2362     /* Modifying the record. */
2363     if (length == 0)
2364 	new_data = ipmi_mem_alloc(1);
2365     else
2366 	new_data = ipmi_mem_alloc(length);
2367     if (!new_data) {
2368 	i_ipmi_fru_unlock(fru);
2369 	return ENOMEM;
2370     }
2371     memcpy(new_data, data, length);
2372 
2373     if (num == u->num_records)
2374 	offset = rec->used_length;
2375     else
2376 	offset = u->records[num].offset;
2377 
2378     for (i=u->num_records; i>num; i--) {
2379 	u->records[i] = u->records[i-1];
2380 	u->records[i].offset += raw_diff;
2381 	u->records[i].changed = 1;
2382     }
2383 
2384     if (u->num_records == 0)
2385 	info->header_changed = 1;
2386     u->num_records++;
2387     u->records[num].offset = offset;
2388     u->records[num].changed = 1;
2389     u->records[num].data = new_data;
2390     u->records[num].type = type;
2391     u->records[num].format_version = version;
2392     u->records[num].length = length;
2393 
2394     rec->used_length += raw_diff;
2395     rec->changed |= 1;
2396     i_ipmi_fru_unlock(fru);
2397     return 0;
2398 }
2399 
2400 int
ipmi_fru_ovw_multi_record_data(ipmi_fru_t * fru,unsigned int num,unsigned char * data,unsigned int offset,unsigned int length)2401 ipmi_fru_ovw_multi_record_data(ipmi_fru_t    *fru,
2402 			       unsigned int  num,
2403 			       unsigned char *data,
2404 			       unsigned int  offset,
2405 			       unsigned int  length)
2406 {
2407     ipmi_fru_multi_record_area_t *u;
2408     ipmi_fru_record_t            *rec;
2409     int                          rv;
2410 
2411     rv = validate_and_lock_multi_record(fru, num, &u, &rec);
2412     if (rv)
2413 	return rv;
2414 
2415     if ((offset + length) > u->records[num].length) {
2416 	i_ipmi_fru_unlock(fru);
2417 	return EINVAL;
2418     }
2419 
2420     memcpy(u->records[num].data+offset, data, length);
2421     rec->changed |= 1;
2422     i_ipmi_fru_unlock(fru);
2423     return 0;
2424 }
2425 
2426 int
ipmi_fru_ins_multi_record_data(ipmi_fru_t * fru,unsigned int num,unsigned char * data,unsigned int offset,unsigned int length)2427 ipmi_fru_ins_multi_record_data(ipmi_fru_t    *fru,
2428 			       unsigned int  num,
2429 			       unsigned char *data,
2430 			       unsigned int  offset,
2431 			       unsigned int  length)
2432 {
2433     ipmi_fru_multi_record_area_t *u;
2434     ipmi_fru_record_t            *rec;
2435     int                          new_length;
2436     unsigned int                 i;
2437     unsigned char                *new_data;
2438     int                          rv;
2439 
2440     rv = validate_and_lock_multi_record(fru, num, &u, &rec);
2441     if (rv)
2442 	return rv;
2443 
2444     if (offset > u->records[num].length) {
2445 	i_ipmi_fru_unlock(fru);
2446 	return EINVAL;
2447     }
2448 
2449     new_length = length + u->records[num].length;
2450     if (new_length > 255) {
2451 	i_ipmi_fru_unlock(fru);
2452 	return EINVAL;
2453     }
2454 
2455     /* Is there enough space? */
2456     if ((rec->used_length + length) > rec->length) {
2457 	i_ipmi_fru_unlock(fru);
2458 	return ENOSPC;
2459     }
2460 
2461     /* Modifying the record. */
2462     if (length == 0)
2463 	new_data = ipmi_mem_alloc(1);
2464     else
2465 	new_data = ipmi_mem_alloc(new_length);
2466     if (!new_data) {
2467 	i_ipmi_fru_unlock(fru);
2468 	return ENOMEM;
2469     }
2470     if (u->records[num].data) {
2471 	memcpy(new_data, u->records[num].data, offset);
2472 	memcpy(new_data+offset+length, u->records[num].data+offset,
2473 	       u->records[num].length-offset);
2474 	ipmi_mem_free(u->records[num].data);
2475     }
2476     memcpy(new_data+offset, data, length);
2477     u->records[num].data = new_data;
2478     u->records[num].length = new_length;
2479     u->records[num].changed = 1;
2480     if (length) {
2481 	for (i=num+1; i<u->num_records; i++) {
2482 	    u->records[i].offset += length;
2483 	    u->records[i].changed = 1;
2484 	}
2485     }
2486 
2487     rec->used_length += length;
2488     rec->changed |= 1;
2489     i_ipmi_fru_unlock(fru);
2490     return 0;
2491 }
2492 
2493 int
ipmi_fru_del_multi_record_data(ipmi_fru_t * fru,unsigned int num,unsigned int offset,unsigned int length)2494 ipmi_fru_del_multi_record_data(ipmi_fru_t    *fru,
2495 			       unsigned int  num,
2496 			       unsigned int  offset,
2497 			       unsigned int  length)
2498 {
2499     ipmi_fru_multi_record_area_t *u;
2500     ipmi_fru_record_t            *rec;
2501     int                          new_length;
2502     unsigned int                 i;
2503     unsigned char                *new_data;
2504     int                          rv;
2505 
2506     rv = validate_and_lock_multi_record(fru, num, &u, &rec);
2507     if (rv)
2508 	return rv;
2509 
2510     if ((offset + length) > u->records[num].length) {
2511 	i_ipmi_fru_unlock(fru);
2512 	return EINVAL;
2513     }
2514 
2515     new_length = u->records[num].length - length;
2516     if (new_length < 0) {
2517 	i_ipmi_fru_unlock(fru);
2518 	return EINVAL;
2519     }
2520 
2521     /* Modifying the record. */
2522     if (new_length == 0)
2523 	new_data = ipmi_mem_alloc(1);
2524     else
2525 	new_data = ipmi_mem_alloc(new_length);
2526     if (!new_data) {
2527 	i_ipmi_fru_unlock(fru);
2528 	return ENOMEM;
2529     }
2530     if (u->records[num].data) {
2531 	memcpy(new_data, u->records[num].data, offset);
2532 	memcpy(new_data+offset, u->records[num].data+offset+length,
2533 	       u->records[num].length-offset-length);
2534 	ipmi_mem_free(u->records[num].data);
2535     }
2536 
2537     u->records[num].data = new_data;
2538     u->records[num].length = new_length;
2539     if (length) {
2540 	for (i=num+1; i<u->num_records; i++) {
2541 	    u->records[i].offset -= length;
2542 	    u->records[i].changed = 1;
2543 	}
2544     }
2545 
2546     rec->used_length -= length;
2547     rec->changed |= 1;
2548     i_ipmi_fru_unlock(fru);
2549     return 0;
2550 }
2551 
2552 static int
fru_encode_multi_record(ipmi_fru_t * fru,ipmi_fru_record_t * rec,ipmi_fru_multi_record_area_t * u,unsigned int idx,unsigned char * data,unsigned int * offset)2553 fru_encode_multi_record(ipmi_fru_t             *fru,
2554 			ipmi_fru_record_t      *rec,
2555 			ipmi_fru_multi_record_area_t *u,
2556 			unsigned int           idx,
2557 			unsigned char          *data,
2558 			unsigned int           *offset)
2559 {
2560     unsigned int           o = *offset;
2561     ipmi_fru_record_elem_t *elem = u->records + idx;
2562     int                    rv;
2563 
2564     if (o != elem->offset)
2565 	return EBADF;
2566 
2567     data += o;
2568     data[0] = elem->type;
2569     data[1] = 2; /* Version */
2570     if (idx+1 == u->num_records)
2571 	data[1] |= 0x80; /* Last record */
2572     data[2] = elem->length;
2573     data[3] = -checksum(elem->data, elem->length);
2574     data[4] = -checksum(data, 4);
2575     memcpy(data+5, elem->data, elem->length);
2576 
2577     if (rec->changed && !rec->rewrite) {
2578 	rv = i_ipmi_fru_new_update_record(fru, rec->offset+elem->offset,
2579 					 elem->length+5);
2580 	if (rv)
2581 	    return rv;
2582     }
2583 
2584     *offset = o + elem->length + 5;
2585     return 0;
2586 }
2587 
2588 static int
fru_encode_multi_record_area(ipmi_fru_t * fru,unsigned char * data)2589 fru_encode_multi_record_area(ipmi_fru_t *fru, unsigned char *data)
2590 {
2591     ipmi_fru_record_t **recs = normal_fru_get_recs(fru);
2592     ipmi_fru_record_t *rec = recs[IPMI_FRU_FTR_MULTI_RECORD_AREA];
2593     ipmi_fru_multi_record_area_t *u;
2594     int               rv;
2595     unsigned int      i;
2596     unsigned int      offset;
2597 
2598     if (!rec)
2599 	return 0;
2600 
2601     u = fru_record_get_data(rec);
2602     data += rec->offset;
2603     memset(data, 0, rec->length);
2604 
2605     if (u->num_records == 0)
2606 	return 0;
2607 
2608     offset = 0;
2609     for (i=0; i<u->num_records; i++) {
2610 	rv = fru_encode_multi_record(fru, rec, u, i, data, &offset);
2611 	if (rv)
2612 	    return rv;
2613     }
2614     return 0;
2615 }
2616 
2617 
2618 /***********************************************************************
2619  *
2620  * Area processing
2621  *
2622  **********************************************************************/
2623 
2624 static fru_area_info_t fru_area_info[IPMI_FRU_FTR_NUMBER] =
2625 {
2626     { 0, 0,  1, NULL,                    internal_use_area_free,
2627       sizeof(ipmi_fru_internal_use_area_t),
2628       fru_decode_internal_use_area, fru_encode_internal_use_area,
2629       internal_use_area_setup },
2630     { 2, 3,  7, chassis_info_get_fields, chassis_info_area_free,
2631       sizeof(ipmi_fru_chassis_info_area_t),
2632       fru_decode_chassis_info_area, fru_encode_chassis_info_area,
2633       chassis_info_area_setup },
2634     { 5, 6, 13, board_info_get_fields,   board_info_area_free,
2635       sizeof(ipmi_fru_board_info_area_t),
2636       fru_decode_board_info_area, fru_encode_board_info_area,
2637       board_info_area_setup },
2638     { 7, 3, 12, product_info_get_fields, product_info_area_free,
2639       sizeof(ipmi_fru_product_info_area_t),
2640       fru_decode_product_info_area, fru_encode_product_info_area,
2641       product_info_area_setup },
2642     { 0, 0,  0, NULL,                    multi_record_area_free,
2643       sizeof(ipmi_fru_multi_record_area_t),
2644       fru_decode_multi_record_area, fru_encode_multi_record_area,
2645       NULL },
2646 };
2647 
2648 static int
check_rec_position(ipmi_fru_t * fru,int recn,unsigned int offset,unsigned int length)2649 check_rec_position(ipmi_fru_t   *fru,
2650 		   int          recn,
2651 		   unsigned int offset,
2652 		   unsigned int length)
2653 {
2654     ipmi_fru_record_t **recs = normal_fru_get_recs(fru);
2655     int               pos;
2656     unsigned int      data_len = i_ipmi_fru_get_data_len(fru);
2657     unsigned int      max_start = data_len - 8;
2658 
2659     /* Zero is invalid, and it must be a multiple of 8. */
2660     if ((offset == 0) || ((offset % 8) != 0))
2661 	return EINVAL;
2662 
2663     /* Make sure the used area still fits. */
2664     if (recs[recn] && (length < recs[recn]->used_length))
2665 	return E2BIG;
2666 
2667     /* FRU data record starts cannot exceed 2040 bytes.  The offsets
2668        are in multiples of 8 and the sizes are 8-bits, thus 8 *
2669        255.  The end of the data can go till the end of the FRU. */
2670     if (max_start > 2040)
2671 	max_start = 2040;
2672     if ((offset > max_start) || ((offset + length) > data_len))
2673 	return EINVAL;
2674 
2675     /* Check that this is not in the previous record's space. */
2676     pos = recn - 1;
2677     while ((pos >= 0) && !recs[pos])
2678 	pos--;
2679     if (pos >= 0) {
2680 	if (offset < (recs[pos]->offset + recs[pos]->length))
2681 	    return EINVAL;
2682     }
2683 
2684     /* Check that this is not in the next record's space. */
2685     pos = recn + 1;
2686     while ((pos < IPMI_FRU_FTR_NUMBER) && !recs[pos])
2687 	pos++;
2688     if (pos < IPMI_FRU_FTR_NUMBER) {
2689 	if ((offset + length) > recs[pos]->offset)
2690 	    return EINVAL;
2691     }
2692 
2693     return 0;
2694 }
2695 
2696 int
ipmi_fru_add_area(ipmi_fru_t * fru,unsigned int area,unsigned int offset,unsigned int length)2697 ipmi_fru_add_area(ipmi_fru_t   *fru,
2698 		  unsigned int area,
2699 		  unsigned int offset,
2700 		  unsigned int length)
2701 {
2702     normal_fru_rec_data_t *info = i_ipmi_fru_get_rec_data(fru);
2703     ipmi_fru_record_t     **recs;
2704     ipmi_fru_record_t     *rec;
2705     int                   rv;
2706 
2707     if (area >= IPMI_FRU_FTR_NUMBER)
2708 	return EINVAL;
2709 
2710     if (!i_ipmi_fru_is_normal_fru(fru)) {
2711 	/* This was not a normal FRU.  Convert it over to a normal one. */
2712 	info = setup_normal_fru(fru, 1);
2713 	if (!info)
2714 	    return ENOMEM;
2715     }
2716 
2717     if (length == 0)
2718 	length = fru_area_info[area].empty_length;
2719 
2720     /* Round up the length to a multiple of 8. */
2721     length = (length + 7) & ~(8-1);
2722 
2723     if (length < fru_area_info[area].empty_length)
2724 	return EINVAL;
2725 
2726     i_ipmi_fru_lock(fru);
2727     recs = normal_fru_get_recs(fru);
2728     if (recs[area]) {
2729 	i_ipmi_fru_unlock(fru);
2730 	return EEXIST;
2731     }
2732 
2733     rv = check_rec_position(fru, area, offset, length);
2734     if (rv) {
2735 	i_ipmi_fru_unlock(fru);
2736 	return rv;
2737     }
2738 
2739     rec = fru_record_alloc(area, 1, length);
2740     if (!rec) {
2741 	i_ipmi_fru_unlock(fru);
2742 	return ENOMEM;
2743     }
2744     rec->changed = 1;
2745     rec->rewrite = 1;
2746     rec->used_length = fru_area_info[area].empty_length;
2747     rec->orig_used_length = rec->used_length;
2748     rec->offset = offset;
2749     info->header_changed = 1;
2750 
2751     rv = fru_setup_min_field(rec, area, 1);
2752     if (rv) {
2753 	i_ipmi_fru_unlock(fru);
2754 	return rv;
2755     }
2756 
2757     recs[area] = rec;
2758     i_ipmi_fru_unlock(fru);
2759     return 0;
2760 }
2761 
2762 int
ipmi_fru_delete_area(ipmi_fru_t * fru,int area)2763 ipmi_fru_delete_area(ipmi_fru_t *fru, int area)
2764 {
2765     ipmi_fru_record_t **recs;
2766 
2767     if (!i_ipmi_fru_is_normal_fru(fru))
2768 	return ENOSYS;
2769 
2770     if (area >= IPMI_FRU_FTR_NUMBER)
2771 	return EINVAL;
2772 
2773     i_ipmi_fru_lock(fru);
2774     recs = normal_fru_get_recs(fru);
2775     fru_record_destroy(recs[area]);
2776     recs[area] = NULL;
2777     i_ipmi_fru_unlock(fru);
2778     return 0;
2779 }
2780 
2781 int
ipmi_fru_area_get_offset(ipmi_fru_t * fru,unsigned int area,unsigned int * offset)2782 ipmi_fru_area_get_offset(ipmi_fru_t   *fru,
2783 			 unsigned int area,
2784 			 unsigned int *offset)
2785 {
2786     ipmi_fru_record_t **recs;
2787 
2788     if (!i_ipmi_fru_is_normal_fru(fru))
2789 	return ENOSYS;
2790 
2791     if (area >= IPMI_FRU_FTR_NUMBER)
2792 	return EINVAL;
2793     i_ipmi_fru_lock(fru);
2794     recs = normal_fru_get_recs(fru);
2795     if (!recs[area]) {
2796 	i_ipmi_fru_unlock(fru);
2797 	return ENOENT;
2798     }
2799 
2800     *offset = recs[area]->offset;
2801 
2802     i_ipmi_fru_unlock(fru);
2803     return 0;
2804 }
2805 
2806 int
ipmi_fru_area_get_length(ipmi_fru_t * fru,unsigned int area,unsigned int * length)2807 ipmi_fru_area_get_length(ipmi_fru_t   *fru,
2808 			 unsigned int area,
2809 			 unsigned int *length)
2810 {
2811     ipmi_fru_record_t **recs;
2812 
2813     if (!i_ipmi_fru_is_normal_fru(fru))
2814 	return ENOSYS;
2815 
2816     if (area >= IPMI_FRU_FTR_NUMBER)
2817 	return EINVAL;
2818 
2819     i_ipmi_fru_lock(fru);
2820     recs = normal_fru_get_recs(fru);
2821     if (!recs[area]) {
2822 	i_ipmi_fru_unlock(fru);
2823 	return ENOENT;
2824     }
2825 
2826     *length = recs[area]->length;
2827 
2828     i_ipmi_fru_unlock(fru);
2829     return 0;
2830 }
2831 
2832 int
ipmi_fru_area_set_offset(ipmi_fru_t * fru,unsigned int area,unsigned int offset)2833 ipmi_fru_area_set_offset(ipmi_fru_t   *fru,
2834 			 unsigned int area,
2835 			 unsigned int offset)
2836 {
2837     normal_fru_rec_data_t *info = i_ipmi_fru_get_rec_data(fru);
2838     ipmi_fru_record_t     **recs;
2839     int                   rv;
2840 
2841     if (!i_ipmi_fru_is_normal_fru(fru))
2842 	return ENOSYS;
2843 
2844     if (area >= IPMI_FRU_FTR_NUMBER)
2845 	return EINVAL;
2846 
2847     i_ipmi_fru_lock(fru);
2848     recs = normal_fru_get_recs(fru);
2849     if (!recs[area]) {
2850 	i_ipmi_fru_unlock(fru);
2851 	return ENOENT;
2852     }
2853 
2854     if (recs[area]->offset == offset) {
2855 	i_ipmi_fru_unlock(fru);
2856 	return 0;
2857     }
2858 
2859     if (area == IPMI_FRU_FTR_MULTI_RECORD_AREA) {
2860 	/* Multi-record lengths are not defined, but just goto the end.
2861 	   So adjust the length for comparison here. */
2862 	int newlength = (recs[area]->length
2863 			 + recs[area]->offset - offset);
2864 	rv = check_rec_position(fru, area, offset, newlength);
2865     } else {
2866 	rv = check_rec_position(fru, area, offset, recs[area]->length);
2867     }
2868     if (!rv) {
2869 	if (area == IPMI_FRU_FTR_MULTI_RECORD_AREA)
2870 	    recs[area]->length += recs[area]->offset - offset;
2871 	recs[area]->offset = offset;
2872 	recs[area]->changed = 1;
2873 	recs[area]->rewrite = 1;
2874 	info->header_changed = 1;
2875     }
2876 
2877     i_ipmi_fru_unlock(fru);
2878     return rv;
2879 }
2880 
2881 int
ipmi_fru_area_set_length(ipmi_fru_t * fru,unsigned int area,unsigned int length)2882 ipmi_fru_area_set_length(ipmi_fru_t   *fru,
2883 			 unsigned int area,
2884 			 unsigned int length)
2885 {
2886     ipmi_fru_record_t **recs;
2887     int               rv;
2888 
2889     if (!i_ipmi_fru_is_normal_fru(fru))
2890 	return ENOSYS;
2891 
2892     /* Truncate the length to a multiple of 8. */
2893     length = length & ~(8-1);
2894 
2895     if (area >= IPMI_FRU_FTR_NUMBER)
2896 	return EINVAL;
2897     if (length == 0)
2898 	return EINVAL;
2899     i_ipmi_fru_lock(fru);
2900     recs = normal_fru_get_recs(fru);
2901     if (!recs[area]) {
2902 	i_ipmi_fru_unlock(fru);
2903 	return ENOENT;
2904     }
2905 
2906     if (recs[area]->length == length) {
2907 	i_ipmi_fru_unlock(fru);
2908 	return 0;
2909     }
2910 
2911     rv = check_rec_position(fru, area, recs[area]->offset, length);
2912     if (!rv) {
2913 	if (length > recs[area]->length)
2914 	    /* Only need to rewrite the whole record (to get the zeroes
2915 	       into the unused area) if we increase the length. */
2916 	    recs[area]->rewrite = 1;
2917 	recs[area]->length = length;
2918 	recs[area]->changed = 1;
2919     }
2920 
2921     i_ipmi_fru_unlock(fru);
2922     return rv;
2923 }
2924 
2925 #define AREA_GENERIC(n1, n2) \
2926 static int								      \
2927 ipmi_fru_get_ ## n1 ## _offset(ipmi_fru_t *fru, int *offset)		      \
2928 {									      \
2929     unsigned int v;							      \
2930     int          rv;							      \
2931     rv = ipmi_fru_area_get_offset(fru, IPMI_FRU_FTR_ ## n2 ##_AREA, &v);      \
2932     if (rv == ENOENT) {							      \
2933 	rv = 0;								      \
2934 	*offset = 0;							      \
2935     } else if (!rv)							      \
2936 	*offset = v;							      \
2937     return rv;								      \
2938 }									      \
2939 static int								      \
2940 ipmi_fru_set_ ## n1 ## _offset(ipmi_fru_t *fru, int offset)		      \
2941 {									      \
2942     int rv;								      \
2943     if (offset == 0) {							      \
2944 	rv = ipmi_fru_delete_area(fru, IPMI_FRU_FTR_ ## n2 ##_AREA);	      \
2945 	return rv;							      \
2946     }									      \
2947     rv = ipmi_fru_area_set_offset(fru, IPMI_FRU_FTR_ ## n2 ##_AREA, offset);  \
2948     if (rv == ENOENT)							      \
2949 	rv = ipmi_fru_add_area(fru, IPMI_FRU_FTR_ ## n2 ##_AREA, offset, 0);  \
2950     return rv;								      \
2951 }									      \
2952 static int								      \
2953 ipmi_fru_get_ ## n1 ## _length(ipmi_fru_t *fru, int *length)		      \
2954 {									      \
2955     unsigned int v;							      \
2956     int          rv;							      \
2957     rv = ipmi_fru_area_get_length(fru, IPMI_FRU_FTR_ ## n2 ##_AREA, &v);      \
2958     if (rv == ENOENT) {							      \
2959 	rv = 0;								      \
2960 	*length = 0;							      \
2961     } else if (!rv)							      \
2962 	*length = v;							      \
2963     return rv;								      \
2964 }									      \
2965 static int								      \
2966 ipmi_fru_set_ ## n1 ## _length(ipmi_fru_t *fru, int length)		      \
2967 {									      \
2968     return ipmi_fru_area_set_length(fru, IPMI_FRU_FTR_ ## n2 ##_AREA, length);\
2969 }
2970 
AREA_GENERIC(internal_use2,INTERNAL_USE)2971 AREA_GENERIC(internal_use2, INTERNAL_USE)
2972 AREA_GENERIC(chassis_info, CHASSIS_INFO)
2973 AREA_GENERIC(board_info, BOARD_INFO)
2974 AREA_GENERIC(product_info, PRODUCT_INFO)
2975 AREA_GENERIC(multi_record, MULTI_RECORD)
2976 
2977 static int
2978 ipmi_fru_get_fru_length(ipmi_fru_t *fru, int *length)
2979 {
2980     *length = ipmi_fru_get_data_length(fru);
2981     return 0;
2982 }
2983 
2984 static int
ipmi_fru_set_fru_length(ipmi_fru_t * fru,int length)2985 ipmi_fru_set_fru_length(ipmi_fru_t *fru, int length)
2986 {
2987     return ENOSYS;
2988 }
2989 
2990 int
ipmi_fru_area_get_used_length(ipmi_fru_t * fru,unsigned int area,unsigned int * used_length)2991 ipmi_fru_area_get_used_length(ipmi_fru_t *fru,
2992 			      unsigned int area,
2993 			      unsigned int *used_length)
2994 {
2995     ipmi_fru_record_t **recs;
2996 
2997     if (!i_ipmi_fru_is_normal_fru(fru))
2998 	return ENOSYS;
2999 
3000     if (area >= IPMI_FRU_FTR_NUMBER)
3001 	return EINVAL;
3002 
3003     i_ipmi_fru_lock(fru);
3004     recs = normal_fru_get_recs(fru);
3005     if (!recs[area]) {
3006 	i_ipmi_fru_unlock(fru);
3007 	return ENOENT;
3008     }
3009 
3010     *used_length = recs[area]->used_length;
3011 
3012     i_ipmi_fru_unlock(fru);
3013     return 0;
3014 }
3015 
3016 /***********************************************************************
3017  *
3018  * Handling for FRU generic interface.
3019  *
3020  **********************************************************************/
3021 
3022 typedef struct fru_data_rep_s
3023 {
3024     char                      *name;
3025     enum ipmi_fru_data_type_e type;
3026     unsigned int              hasnum : 1;
3027     unsigned int              settable : 1;
3028 
3029     union {
3030 	struct {
3031 	    int (*fetch_uchar)(ipmi_fru_t *fru, unsigned char *data);
3032 	    int (*set_uchar)(ipmi_fru_t *fru, unsigned char data);
3033 	    int (*fetch_int)(ipmi_fru_t *fru, int *data);
3034 	    int (*set_int)(ipmi_fru_t *fru, int data);
3035 	} inttype;
3036 
3037 	struct {
3038 	    int (*fetch_uchar)(ipmi_fru_t *fru, unsigned int num,
3039 			       unsigned char *data);
3040 	    int (*set_uchar)(ipmi_fru_t *fru, unsigned int num,
3041 			     unsigned char data);
3042 	} intnumtype;
3043 
3044 	struct {
3045 	    int (*fetch)(ipmi_fru_t *fru, double *data);
3046 	    int (*set)(ipmi_fru_t *fru, double data);
3047 	} floattype;
3048 
3049 	struct {
3050 	    int (*fetch)(ipmi_fru_t *fru, unsigned int num, double *data);
3051 	    int (*set)(ipmi_fru_t *fru, unsigned int num, double data);
3052 	} floatnumtype;
3053 
3054 	struct {
3055 	    int (*fetch)(ipmi_fru_t *fru, time_t *data);
3056 	    int (*set)(ipmi_fru_t *fru, time_t data);
3057 	} timetype;
3058 
3059 	struct {
3060 	    int (*fetch)(ipmi_fru_t *fru, unsigned int num,
3061 			 time_t *data);
3062 	    int (*set)(ipmi_fru_t *fru, unsigned int num,
3063 		       time_t data);
3064 	} timenumtype;
3065 
3066 	struct {
3067 	    int (*fetch_len)(ipmi_fru_t *fru, unsigned int *len);
3068 	    int (*fetch_type)(ipmi_fru_t *fru, enum ipmi_str_type_e *type);
3069 	    int (*fetch_data)(ipmi_fru_t *fru, char *data,
3070 			      unsigned int *max_len);
3071 	    int (*set)(ipmi_fru_t *fru, enum ipmi_str_type_e type,
3072 		       char *data, unsigned int len);
3073 	} strtype;
3074 
3075 	struct {
3076 	    int (*fetch_len)(ipmi_fru_t *fru, unsigned int num,
3077 			     unsigned int *len);
3078 	    int (*fetch_type)(ipmi_fru_t *fru, unsigned int num,
3079 			      enum ipmi_str_type_e *type);
3080 	    int (*fetch_data)(ipmi_fru_t *fru, unsigned int num,
3081 			      char *data, unsigned int *max_len);
3082 	    int (*set)(ipmi_fru_t *fru, unsigned int num,
3083 		       enum ipmi_str_type_e type, char *data,
3084 		       unsigned int len);
3085 	    int (*ins)(ipmi_fru_t *fru, unsigned int num,
3086 		       enum ipmi_str_type_e type, char *data,
3087 		       unsigned int len);
3088 	} strnumtype;
3089 
3090 	struct {
3091 	    int (*fetch_len)(ipmi_fru_t *fru, unsigned int *len);
3092 	    int (*fetch_data)(ipmi_fru_t *fru, unsigned char *data,
3093 			      unsigned int *max_len);
3094 	    int (*set)(ipmi_fru_t *fru, unsigned char *data,
3095 		       unsigned int len);
3096 	} bintype;
3097 
3098 	struct {
3099 	    int (*fetch_len)(ipmi_fru_t *fru, unsigned int num,
3100 			     unsigned int *len);
3101 	    int (*fetch_data)(ipmi_fru_t *fru, unsigned intnum,
3102 			      unsigned char *data, unsigned int *max_len);
3103 	    int (*set)(ipmi_fru_t *fru, unsigned int num,
3104 		       unsigned char *data, unsigned int len);
3105 	    int (*ins)(ipmi_fru_t *fru, unsigned int num,
3106 		       unsigned char *data, unsigned int len);
3107 	} binnumtype;
3108     } u;
3109 } fru_data_rep_t;
3110 
3111 #define F_UCHAR(x,s) { .name = #x, .type = IPMI_FRU_DATA_INT,		     \
3112 		       .hasnum = 0, .settable = s,			     \
3113 		       .u = { .inttype = { .fetch_uchar = ipmi_fru_get_ ## x,\
3114 					   .set_uchar = ipmi_fru_set_ ## x }}}
3115 #define F_INT(x,s) { .name = #x, .type = IPMI_FRU_DATA_INT,		     \
3116 		     .hasnum = 0, .settable = s,			     \
3117 		     .u = { .inttype = { .fetch_int = ipmi_fru_get_ ## x,\
3118 					 .set_int = ipmi_fru_set_ ## x }}}
3119 #define F_NUM_UCHAR(x,s) { .name = #x, .type = IPMI_FRU_DATA_INT,	     \
3120 			   .hasnum = 1, .settable = s,			     \
3121 			   .u = { .intnumtype = {			     \
3122 				  .fetch_uchar = ipmi_fru_get_ ## x,	     \
3123 				  .set_uchar = ipmi_fru_set_ ## x }}}
3124 #define F_TIME(x,s) { .name = #x, .type = IPMI_FRU_DATA_TIME,		     \
3125 		      .hasnum = 0, .settable = s,			     \
3126 		      .u = { .timetype = { .fetch = ipmi_fru_get_ ## x,      \
3127 					 .set = ipmi_fru_set_ ## x }}}
3128 #define F_NUM_TIME(x,s) { .name = #x, .type = IPMI_FRU_DATA_TIME,	     \
3129 			  .hasnum = 1, .settable = s,			     \
3130 		          .u = { .timenumtype = { .fetch = ipmi_fru_get_ ## x,\
3131 					          .set = ipmi_fru_set_ ## x }}}
3132 #define F_STR(x,s) { .name = #x, .type = IPMI_FRU_DATA_ASCII,		     \
3133 		     .hasnum = 0, .settable = s,			     \
3134 		     .u = { .strtype = {				     \
3135 			    .fetch_len = ipmi_fru_get_ ## x ## _len,	     \
3136 		            .fetch_type = ipmi_fru_get_ ## x ## _type,	     \
3137 		            .fetch_data = ipmi_fru_get_ ## x,		     \
3138 			    .set = ipmi_fru_set_ ## x }}}
3139 #define F_NUM_STR(x,s) { .name = #x, .type = IPMI_FRU_DATA_ASCII,	     \
3140 			 .hasnum = 1, .settable = s,			     \
3141 		         .u = { .strnumtype = {                              \
3142 			        .fetch_len = ipmi_fru_get_ ## x ## _len,     \
3143 		                .fetch_type = ipmi_fru_get_ ## x ## _type,   \
3144 		                .fetch_data = ipmi_fru_get_ ## x,            \
3145 			        .set = ipmi_fru_set_ ## x,		     \
3146 				.ins = ipmi_fru_ins_ ## x }}}
3147 #define F_BIN(x,s) { .name = #x, .type = IPMI_FRU_DATA_BINARY,		     \
3148 		     .hasnum = 0, .settable = s,			     \
3149 		     .u = { .bintype = {				     \
3150 			    .fetch_len = ipmi_fru_get_ ## x ## _len,	     \
3151 		   	    .fetch_data = ipmi_fru_get_ ## x,		     \
3152 			    .set = ipmi_fru_set_ ## x }}}
3153 #define F_NUM_BIN(x,s) { .name = #x, .type = IPMI_FRU_DATA_BINARY,	     \
3154 			 .hasnum = 1, .settable = s,			     \
3155 		         .u = { .binnumtype = {				     \
3156 			        .fetch_len = ipmi_fru_get_ ## x ## _len,     \
3157 		       	        .fetch_data = ipmi_fru_get_ ## x,	     \
3158 			        .set = ipmi_fru_set_ ## x,		     \
3159 			        .ins = ipmi_fru_ins_ ## x }}}
3160 static fru_data_rep_t frul[] =
3161 {
3162     F_UCHAR(internal_use_version, 0),
3163     F_BIN(internal_use, 1),
3164     F_UCHAR(chassis_info_version, 0),
3165     F_UCHAR(chassis_info_type, 1),
3166     F_STR(chassis_info_part_number, 1),
3167     F_STR(chassis_info_serial_number, 1),
3168     F_NUM_STR(chassis_info_custom, 1),
3169     F_UCHAR(board_info_version, 0),
3170     F_UCHAR(board_info_lang_code, 1),
3171     F_TIME(board_info_mfg_time, 1),
3172     F_STR(board_info_board_manufacturer, 1),
3173     F_STR(board_info_board_product_name, 1),
3174     F_STR(board_info_board_serial_number, 1),
3175     F_STR(board_info_board_part_number, 1),
3176     F_STR(board_info_fru_file_id, 1),
3177     F_NUM_STR(board_info_custom, 1),
3178     F_UCHAR(product_info_version, 0),
3179     F_UCHAR(product_info_lang_code, 1),
3180     F_STR(product_info_manufacturer_name, 1),
3181     F_STR(product_info_product_name, 1),
3182     F_STR(product_info_product_part_model_number, 1),
3183     F_STR(product_info_product_version, 1),
3184     F_STR(product_info_product_serial_number, 1),
3185     F_STR(product_info_asset_tag, 1),
3186     F_STR(product_info_fru_file_id, 1),
3187     F_NUM_STR(product_info_custom, 1),
3188     F_INT(fru_length, 0),
3189     { .name = "internal_use_offset", .type = IPMI_FRU_DATA_INT,
3190       .hasnum = 0, .settable = 1,
3191       .u = { .inttype = { .fetch_int = ipmi_fru_get_internal_use2_offset,
3192 			  .set_int = ipmi_fru_set_internal_use2_offset }}},
3193     { .name = "internal_use_length", .type = IPMI_FRU_DATA_INT,
3194       .hasnum = 0, .settable = 1,
3195       .u = { .inttype = { .fetch_int = ipmi_fru_get_internal_use2_length,
3196 			  .set_int = ipmi_fru_set_internal_use2_length }}},
3197     F_INT(chassis_info_offset, 1),
3198     F_INT(chassis_info_length, 1),
3199     F_INT(board_info_offset, 1),
3200     F_INT(board_info_length, 1),
3201     F_INT(product_info_offset, 1),
3202     F_INT(product_info_length, 1),
3203     F_INT(multi_record_offset, 1),
3204     F_INT(multi_record_length, 1),
3205 };
3206 #define NUM_FRUL_ENTRIES (sizeof(frul) / sizeof(fru_data_rep_t))
3207 
3208 int
ipmi_fru_str_to_index(char * name)3209 ipmi_fru_str_to_index(char *name)
3210 {
3211     unsigned int i;
3212     for (i=0; i<NUM_FRUL_ENTRIES; i++) {
3213 	if (strcmp(name, frul[i].name) == 0)
3214 	    return i;
3215     }
3216     return -1;
3217 }
3218 
3219 char *
ipmi_fru_index_to_str(int index)3220 ipmi_fru_index_to_str(int index)
3221 {
3222     if ((index < 0) || (index >= (int) NUM_FRUL_ENTRIES))
3223 	return NULL;
3224 
3225     return frul[index].name;
3226 }
3227 
3228 int
ipmi_fru_get(ipmi_fru_t * fru,int index,const char ** name,int * num,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,char ** data,unsigned int * data_len)3229 ipmi_fru_get(ipmi_fru_t                *fru,
3230 	     int                       index,
3231 	     const char                **name,
3232 	     int                       *num,
3233 	     enum ipmi_fru_data_type_e *dtype,
3234 	     int                       *intval,
3235 	     time_t                    *time,
3236 	     char                      **data,
3237 	     unsigned int              *data_len)
3238 {
3239     fru_data_rep_t *p;
3240     unsigned char  ucval, dummy_ucval;
3241     unsigned int   dummy_uint;
3242     time_t         dummy_time;
3243     int            rv = 0, rv2 = 0;
3244     unsigned int   len;
3245     char           *dval = NULL;
3246     enum ipmi_fru_data_type_e rdtype;
3247     enum ipmi_str_type_e stype;
3248 
3249 
3250     if ((index < 0) || (index >= (int) NUM_FRUL_ENTRIES))
3251 	return EINVAL;
3252 
3253     p = frul + index;
3254 
3255     if (name)
3256 	*name = p->name;
3257 
3258     rdtype = p->type;
3259 
3260     switch (p->type) {
3261     case IPMI_FRU_DATA_INT:
3262 	if (intval) {
3263 	    if (! p->hasnum) {
3264 		if (p->u.inttype.fetch_uchar) {
3265 		    rv = p->u.inttype.fetch_uchar(fru, &ucval);
3266 		    if (!rv)
3267 			*intval = ucval;
3268 		} else
3269 		    rv = p->u.inttype.fetch_int(fru, intval);
3270 	    } else {
3271 		rv = p->u.intnumtype.fetch_uchar(fru, *num, &ucval);
3272 		rv2 = p->u.intnumtype.fetch_uchar(fru, (*num)+1, &dummy_ucval);
3273 		if (!rv)
3274 		    *intval = ucval;
3275 	    }
3276 	}
3277 	break;
3278 
3279     case IPMI_FRU_DATA_TIME:
3280 	if (time) {
3281 	    if (! p->hasnum) {
3282 		rv = p->u.timetype.fetch(fru, time);
3283 	    } else {
3284 		rv = p->u.timenumtype.fetch(fru, *num, time);
3285 		rv2 = p->u.timenumtype.fetch(fru, (*num)+1, &dummy_time);
3286 	    }
3287 	}
3288 	break;
3289 
3290     case IPMI_FRU_DATA_ASCII:
3291 	if (dtype) {
3292 	    if (! p->hasnum) {
3293 		rv = p->u.strtype.fetch_type(fru, &stype);
3294 	    } else {
3295 		rv = p->u.strnumtype.fetch_type(fru, *num, &stype);
3296 	    }
3297 	    if (rv) {
3298 		break;
3299 	    } else {
3300 		switch (stype) {
3301 		case IPMI_UNICODE_STR: rdtype = IPMI_FRU_DATA_UNICODE; break;
3302 		case IPMI_BINARY_STR: rdtype = IPMI_FRU_DATA_BINARY; break;
3303 		case IPMI_ASCII_STR: break;
3304 		}
3305 	    }
3306 	}
3307 
3308 	if (data_len || data) {
3309 	    if (! p->hasnum) {
3310 		rv = p->u.strtype.fetch_len(fru, &len);
3311 	    } else {
3312 		rv = p->u.strnumtype.fetch_len(fru, *num, &len);
3313 	    }
3314 	    if (rv)
3315 		break;
3316 
3317 	    if (data) {
3318 		dval = ipmi_mem_alloc(len);
3319 		if (!dval) {
3320 		    rv = ENOMEM;
3321 		    break;
3322 		}
3323 		if (! p->hasnum) {
3324 		    rv = p->u.strtype.fetch_data(fru, dval, &len);
3325 		} else {
3326 		    rv = p->u.strnumtype.fetch_data(fru, *num, dval, &len);
3327 		}
3328 		if (rv)
3329 		    break;
3330 		*data = dval;
3331 	    }
3332 
3333 	    if (data_len)
3334 		*data_len = len;
3335 	}
3336 
3337 	if (p->hasnum)
3338 	    rv2 = p->u.strnumtype.fetch_len(fru, (*num)+1, &dummy_uint);
3339 	break;
3340 
3341     case IPMI_FRU_DATA_BINARY:
3342 	if (data_len || data) {
3343 	    if (! p->hasnum) {
3344 		rv = p->u.bintype.fetch_len(fru, &len);
3345 	    } else {
3346 		rv = p->u.binnumtype.fetch_len(fru, *num, &len);
3347 	    }
3348 	    if (rv)
3349 		break;
3350 
3351 	    if (data) {
3352 		dval = ipmi_mem_alloc(len);
3353 		if (!dval) {
3354 		    rv = ENOMEM;
3355 		    break;
3356 		}
3357 		if (! p->hasnum) {
3358 		    rv = p->u.bintype.fetch_data(fru, (unsigned char *) dval,
3359 						 &len);
3360 		} else {
3361 		    rv = p->u.binnumtype.fetch_data(fru, *num,
3362 						    (unsigned char *) dval,
3363 						    &len);
3364 		}
3365 		if (rv)
3366 		    break;
3367 		*data = dval;
3368 	    }
3369 
3370 	    if (data_len)
3371 		*data_len = len;
3372 	}
3373 
3374 	if (p->hasnum)
3375 	    rv2 = p->u.binnumtype.fetch_len(fru, (*num)+1, &dummy_uint);
3376 	break;
3377 
3378     default:
3379 	break;
3380     }
3381 
3382     if (rv) {
3383 	if (dval)
3384 	    ipmi_mem_free(dval);
3385 	return rv;
3386     }
3387 
3388     if (p->hasnum) {
3389 	if (rv2)
3390 	    *num = -1;
3391 	else
3392 	    *num = (*num) + 1;
3393     }
3394 
3395     if (dtype)
3396 	*dtype = rdtype;
3397 
3398     return 0;
3399 }
3400 
3401 int
ipmi_fru_set_int_val(ipmi_fru_t * fru,int index,int num,int val)3402 ipmi_fru_set_int_val(ipmi_fru_t *fru,
3403 		     int        index,
3404 		     int        num,
3405 		     int        val)
3406 {
3407     fru_data_rep_t *p;
3408     int            rv;
3409 
3410     if ((index < 0) || (index >= (int) NUM_FRUL_ENTRIES))
3411 	return EINVAL;
3412 
3413     p = frul + index;
3414 
3415     if (p->type != IPMI_FRU_DATA_INT)
3416 	return EINVAL;
3417 
3418     if (! p->hasnum) {
3419 	if (p->u.inttype.set_uchar)
3420 	    rv = p->u.inttype.set_uchar(fru, val);
3421 	else
3422 	    rv = p->u.inttype.set_int(fru, val);
3423     } else {
3424 	rv = p->u.intnumtype.set_uchar(fru, num, val);
3425     }
3426 
3427     return rv;
3428 }
3429 
3430 int
ipmi_fru_set_float_val(ipmi_fru_t * fru,int index,int num,double val)3431 ipmi_fru_set_float_val(ipmi_fru_t *fru,
3432 		       int        index,
3433 		       int        num,
3434 		       double     val)
3435 {
3436     fru_data_rep_t *p;
3437     int            rv;
3438 
3439     if ((index < 0) || (index >= (int) NUM_FRUL_ENTRIES))
3440 	return EINVAL;
3441 
3442     p = frul + index;
3443 
3444     if (p->type != IPMI_FRU_DATA_FLOAT)
3445 	return EINVAL;
3446 
3447     if (! p->hasnum) {
3448 	rv = p->u.floattype.set(fru, val);
3449     } else {
3450 	rv = p->u.floatnumtype.set(fru, num, val);
3451     }
3452 
3453     return rv;
3454 }
3455 
3456 int
ipmi_fru_set_time_val(ipmi_fru_t * fru,int index,int num,time_t val)3457 ipmi_fru_set_time_val(ipmi_fru_t *fru,
3458 		      int        index,
3459 		      int        num,
3460 		      time_t     val)
3461 {
3462     fru_data_rep_t *p;
3463     int            rv;
3464 
3465 
3466     if ((index < 0) || (index >= (int) NUM_FRUL_ENTRIES))
3467 	return EINVAL;
3468 
3469     p = frul + index;
3470 
3471     if (p->type != IPMI_FRU_DATA_TIME)
3472 	return EINVAL;
3473 
3474     if (! p->hasnum) {
3475 	rv = p->u.timetype.set(fru, val);
3476     } else {
3477 	rv = p->u.timenumtype.set(fru, num, val);
3478     }
3479 
3480     return rv;
3481 }
3482 
3483 int
ipmi_fru_set_data_val(ipmi_fru_t * fru,int index,int num,enum ipmi_fru_data_type_e dtype,char * data,unsigned int len)3484 ipmi_fru_set_data_val(ipmi_fru_t                *fru,
3485 		      int                       index,
3486 		      int                       num,
3487 		      enum ipmi_fru_data_type_e dtype,
3488 		      char                      *data,
3489 		      unsigned int              len)
3490 {
3491     fru_data_rep_t       *p;
3492     int                  rv;
3493     enum ipmi_str_type_e stype;
3494 
3495 
3496     if ((index < 0) || (index >= (int) NUM_FRUL_ENTRIES))
3497 	return EINVAL;
3498 
3499     p = frul + index;
3500 
3501     switch (dtype) {
3502     case IPMI_FRU_DATA_UNICODE: stype = IPMI_UNICODE_STR; break;
3503     case IPMI_FRU_DATA_BINARY: stype = IPMI_BINARY_STR; break;
3504     case IPMI_FRU_DATA_ASCII: stype = IPMI_ASCII_STR; break;
3505     default:
3506 	return EINVAL;
3507     }
3508 
3509     switch (p->type)
3510     {
3511     case IPMI_FRU_DATA_UNICODE:
3512     case IPMI_FRU_DATA_ASCII:
3513 	if (! p->hasnum) {
3514 	    rv = p->u.strtype.set(fru, stype, data, len);
3515 	} else {
3516 	    rv = p->u.strnumtype.set(fru, num, stype, data, len);
3517 	}
3518 	break;
3519 
3520     case IPMI_FRU_DATA_BINARY:
3521 	if (! p->hasnum) {
3522 	    rv = p->u.bintype.set(fru, (unsigned char *) data, len);
3523 	} else {
3524 	    rv = p->u.binnumtype.set(fru, num, (unsigned char *) data, len);
3525 	}
3526 	break;
3527 
3528     default:
3529 	return EINVAL;
3530     }
3531 
3532     return rv;
3533 }
3534 
3535 int
ipmi_fru_ins_data_val(ipmi_fru_t * fru,int index,int num,enum ipmi_fru_data_type_e dtype,char * data,unsigned int len)3536 ipmi_fru_ins_data_val(ipmi_fru_t                *fru,
3537 		      int                       index,
3538 		      int                       num,
3539 		      enum ipmi_fru_data_type_e dtype,
3540 		      char                      *data,
3541 		      unsigned int              len)
3542 {
3543     fru_data_rep_t       *p;
3544     int                  rv;
3545     enum ipmi_str_type_e stype;
3546 
3547     if ((index < 0) || (index >= (int) NUM_FRUL_ENTRIES))
3548 	return EINVAL;
3549 
3550     p = frul + index;
3551 
3552     switch (dtype) {
3553     case IPMI_FRU_DATA_UNICODE: stype = IPMI_UNICODE_STR; break;
3554     case IPMI_FRU_DATA_BINARY: stype = IPMI_BINARY_STR; break;
3555     case IPMI_FRU_DATA_ASCII: stype = IPMI_ASCII_STR; break;
3556     default:
3557 	return EINVAL;
3558     }
3559 
3560     switch (p->type)
3561     {
3562     case IPMI_FRU_DATA_UNICODE:
3563     case IPMI_FRU_DATA_ASCII:
3564 	if (! p->hasnum)
3565 	    return ENOSYS;
3566 	rv = p->u.strnumtype.ins(fru, num, stype, data, len);
3567 	break;
3568 
3569     case IPMI_FRU_DATA_BINARY:
3570 	if (! p->hasnum)
3571 	    return ENOSYS;
3572 	rv = p->u.binnumtype.ins(fru, num, (unsigned char *) data, len);
3573 	break;
3574 
3575     default:
3576 	return EINVAL;
3577     }
3578 
3579     return rv;
3580 }
3581 
3582 /***********************************************************************
3583  *
3584  * FRU node handling
3585  *
3586  **********************************************************************/
3587 static void
fru_node_destroy(ipmi_fru_node_t * node)3588 fru_node_destroy(ipmi_fru_node_t *node)
3589 {
3590     ipmi_fru_t *fru = i_ipmi_fru_node_get_data(node);
3591 
3592     ipmi_fru_deref(fru);
3593 }
3594 
3595 typedef struct fru_mr_array_idx_s
3596 {
3597     int             index;
3598     const char      *name;
3599     ipmi_fru_node_t *mr_node;
3600     ipmi_fru_t      *fru;
3601 } fru_mr_array_idx_t;
3602 
3603 static void
fru_mr_array_idx_destroy(ipmi_fru_node_t * node)3604 fru_mr_array_idx_destroy(ipmi_fru_node_t *node)
3605 {
3606     fru_mr_array_idx_t *info = i_ipmi_fru_node_get_data(node);
3607     ipmi_fru_t         *fru = info->fru;
3608 
3609     ipmi_fru_deref(fru);
3610     if (info->mr_node)
3611 	ipmi_fru_put_node(info->mr_node);
3612     ipmi_mem_free(info);
3613 }
3614 
3615 static int
fru_mr_array_idx_set_field(ipmi_fru_node_t * pnode,unsigned int index,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)3616 fru_mr_array_idx_set_field(ipmi_fru_node_t           *pnode,
3617 			   unsigned int              index,
3618 			   enum ipmi_fru_data_type_e dtype,
3619 			   int                       intval,
3620 			   time_t                    time,
3621 			   double                    floatval,
3622 			   char                      *data,
3623 			   unsigned int              data_len)
3624 {
3625     fru_mr_array_idx_t *info = i_ipmi_fru_node_get_data(pnode);
3626 
3627     switch (index) {
3628     case 0:
3629 	if (dtype != IPMI_FRU_DATA_INT)
3630 	    return EINVAL;
3631 	return ipmi_fru_set_multi_record_type(info->fru, info->index, intval);
3632 
3633     case 2:
3634 	if (dtype != IPMI_FRU_DATA_BINARY)
3635 	    return EINVAL;
3636 	return ipmi_fru_set_multi_record_data(info->fru, info->index,
3637 					      (unsigned char *) data,
3638 					      data_len);
3639 
3640     case 1:
3641     case 3:
3642 	return EPERM;
3643     default:
3644 	return EINVAL;
3645     }
3646     return 0;
3647 }
3648 
3649 static int
fru_mr_array_idx_settable(ipmi_fru_node_t * pnode,unsigned int index)3650 fru_mr_array_idx_settable(ipmi_fru_node_t *pnode,
3651 			  unsigned int    index)
3652 {
3653     switch (index) {
3654     case 0:
3655     case 2:
3656 	return 0;
3657     case 1:
3658     case 3:
3659 	return EPERM;
3660     default:
3661 	return EINVAL;
3662     }
3663 }
3664 
3665 static int
fru_mr_array_idx_get_field(ipmi_fru_node_t * pnode,unsigned int index,const char ** name,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)3666 fru_mr_array_idx_get_field(ipmi_fru_node_t           *pnode,
3667 			   unsigned int              index,
3668 			   const char                **name,
3669 			   enum ipmi_fru_data_type_e *dtype,
3670 			   int                       *intval,
3671 			   time_t                    *time,
3672 			   double                    *floatval,
3673 			   char                      **data,
3674 			   unsigned int              *data_len,
3675 			   ipmi_fru_node_t           **sub_node)
3676 {
3677     fru_mr_array_idx_t *info = i_ipmi_fru_node_get_data(pnode);
3678     int                rv;
3679     unsigned int       rlen;
3680     char               *rdata;
3681 
3682     if (index == 0) {
3683 	/* Record type */
3684 	unsigned char type;
3685 
3686 	rv = ipmi_fru_get_multi_record_type(info->fru, info->index, &type);
3687 	if (rv)
3688 	    return rv;
3689 	if (intval)
3690 	    *intval = type;
3691 	if (dtype)
3692 	    *dtype = IPMI_FRU_DATA_INT;
3693 	if (name)
3694 	    *name = "type";
3695 	return 0;
3696     } else if (index == 1) {
3697 	/* Record format version */
3698 	unsigned char ver;
3699 
3700 	rv = ipmi_fru_get_multi_record_format_version(info->fru, info->index,
3701 						      &ver);
3702 	if (rv)
3703 	    return rv;
3704 	if (intval)
3705 	    *intval = ver;
3706 	if (dtype)
3707 	    *dtype = IPMI_FRU_DATA_INT;
3708 	if (name)
3709 	    *name = "format version";
3710 	return 0;
3711     } else if (index == 2) {
3712 	/* Raw FRU data */
3713 	rv = ipmi_fru_get_multi_record_data_len(info->fru, info->index, &rlen);
3714 	if (rv)
3715 	    return rv;
3716 	if (data) {
3717 	    rdata = ipmi_mem_alloc(rlen);
3718 	    if (!rdata)
3719 		return ENOMEM;
3720 	    rv = ipmi_fru_get_multi_record_data(info->fru, info->index,
3721 						(unsigned char *) rdata,
3722 						&rlen);
3723 	    if (rv) {
3724 		ipmi_mem_free(rdata);
3725 		return rv;
3726 	    }
3727 	    *data = rdata;
3728 	}
3729 
3730 	if (data_len)
3731 	    *data_len = rlen;
3732 
3733 	if (dtype)
3734 	    *dtype = IPMI_FRU_DATA_BINARY;
3735 
3736 	if (name)
3737 	    *name = "raw-data";
3738 
3739 	return 0;
3740     } else if (index == 3) {
3741 	/* FRU node itself. */
3742 	if (info->mr_node == NULL)
3743 	    return EINVAL;
3744 
3745 	if (intval)
3746 	    *intval = -1;
3747 	if (name)
3748 	    *name = info->name;
3749 	if (dtype)
3750 	    *dtype = IPMI_FRU_DATA_SUB_NODE;
3751 	if (sub_node) {
3752 	    ipmi_fru_get_node(info->mr_node);
3753 	    *sub_node = info->mr_node;
3754 	}
3755 	return 0;
3756     } else
3757 	return EINVAL;
3758 }
3759 
3760 static int
fru_mr_array_get_field(ipmi_fru_node_t * pnode,unsigned int index,const char ** name,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)3761 fru_mr_array_get_field(ipmi_fru_node_t           *pnode,
3762 		       unsigned int              index,
3763 		       const char                **name,
3764 		       enum ipmi_fru_data_type_e *dtype,
3765 		       int                       *intval,
3766 		       time_t                    *time,
3767 		       double                    *floatval,
3768 		       char                      **data,
3769 		       unsigned int              *data_len,
3770 		       ipmi_fru_node_t           **sub_node)
3771 {
3772     fru_mr_array_idx_t *info;
3773     ipmi_fru_t         *fru = i_ipmi_fru_node_get_data(pnode);
3774     ipmi_fru_node_t    *node;
3775     ipmi_fru_node_t    *snode;
3776     const char         *sname;
3777     int                rv;
3778 
3779     if (index >= ipmi_fru_get_num_multi_records(fru))
3780 	return EINVAL;
3781 
3782     if (name)
3783 	*name = NULL;
3784     if (dtype)
3785 	*dtype = IPMI_FRU_DATA_SUB_NODE;
3786     if (intval)
3787 	*intval = -1;
3788     if (sub_node) {
3789 	node = i_ipmi_fru_node_alloc(fru);
3790 	if (!node)
3791 	    return ENOMEM;
3792 	info = ipmi_mem_alloc(sizeof(*info));
3793 	if (!info) {
3794 	    ipmi_fru_put_node(node);
3795 	    return ENOMEM;
3796 	}
3797 	memset(info, 0, sizeof(*info));
3798 	info->index = index;
3799 	info->fru = fru;
3800 	ipmi_fru_ref(fru);
3801 	i_ipmi_fru_node_set_data(node, info);
3802 
3803 	rv = ipmi_fru_multi_record_get_root_node(fru, index, &sname, &snode);
3804 	if (rv) {
3805 	    /* No decode data, just do a "raw" node. */
3806 	    info->mr_node = NULL;
3807 	    info->name = "multirecord";
3808 	} else {
3809 	    info->mr_node = snode;
3810 	    info->name = sname;
3811 	}
3812 	i_ipmi_fru_node_set_get_field(node, fru_mr_array_idx_get_field);
3813 	i_ipmi_fru_node_set_set_field(node, fru_mr_array_idx_set_field);
3814 	i_ipmi_fru_node_set_settable(node, fru_mr_array_idx_settable);
3815 	i_ipmi_fru_node_set_destructor(node, fru_mr_array_idx_destroy);
3816 
3817 	*sub_node = node;
3818     }
3819 
3820     /* We always succeed if we can get the memory, even if we don't
3821        have a decoder. */
3822     return 0;
3823 }
3824 
3825 static int
fru_mr_array_get_subtype(ipmi_fru_node_t * pnode,enum ipmi_fru_data_type_e * dtype)3826 fru_mr_array_get_subtype(ipmi_fru_node_t           *pnode,
3827 			 enum ipmi_fru_data_type_e *dtype)
3828 {
3829     *dtype = IPMI_FRU_DATA_SUB_NODE;
3830     return 0;
3831 }
3832 
3833 static int
fru_mr_array_set_field(ipmi_fru_node_t * pnode,unsigned int index,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)3834 fru_mr_array_set_field(ipmi_fru_node_t           *pnode,
3835 		       unsigned int              index,
3836 		       enum ipmi_fru_data_type_e dtype,
3837 		       int                       intval,
3838 		       time_t                    time,
3839 		       double                    floatval,
3840 		       char                      *data,
3841 		       unsigned int              data_len)
3842 {
3843     ipmi_fru_t    *fru = i_ipmi_fru_node_get_data(pnode);
3844     unsigned char type = 0, version = 2;
3845 
3846     if (dtype != IPMI_FRU_DATA_SUB_NODE)
3847 	return EINVAL;
3848 
3849     if (data) {
3850 	if (data_len >= 1) {
3851 	    type = data[0];
3852 	    data++;
3853 	    data_len--;
3854 	}
3855 	if (data_len >= 1) {
3856 	    version = data[0];
3857 	    data++;
3858 	    data_len--;
3859 	}
3860 	/* First two bytes are the type and version */
3861 	return ipmi_fru_set_multi_record(fru, index, type, version,
3862 					 (unsigned char *) data, data_len);
3863     } else
3864 	return ipmi_fru_set_multi_record(fru, index, 0, 0, NULL, 0);
3865 }
3866 
3867 static int
fru_mr_array_settable(ipmi_fru_node_t * node,unsigned int index)3868 fru_mr_array_settable(ipmi_fru_node_t           *node,
3869 		      unsigned int              index)
3870 {
3871     /* Array elements are not. */
3872     return EPERM;
3873 }
3874 
3875 typedef struct fru_array_s
3876 {
3877     int        index;
3878     ipmi_fru_t *fru;
3879 } fru_array_t;
3880 
3881 static void
fru_array_idx_destroy(ipmi_fru_node_t * node)3882 fru_array_idx_destroy(ipmi_fru_node_t *node)
3883 {
3884     fru_array_t *info = i_ipmi_fru_node_get_data(node);
3885     ipmi_fru_t  *fru = info->fru;
3886 
3887     ipmi_fru_deref(fru);
3888     ipmi_mem_free(info);
3889 }
3890 
3891 static int
fru_array_idx_get_field(ipmi_fru_node_t * pnode,unsigned int index,const char ** name,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)3892 fru_array_idx_get_field(ipmi_fru_node_t           *pnode,
3893 			unsigned int              index,
3894 			const char                **name,
3895 			enum ipmi_fru_data_type_e *dtype,
3896 			int                       *intval,
3897 			time_t                    *time,
3898 			double                    *floatval,
3899 			char                      **data,
3900 			unsigned int              *data_len,
3901 			ipmi_fru_node_t           **sub_node)
3902 {
3903     fru_array_t *info = i_ipmi_fru_node_get_data(pnode);
3904     int         num = index;
3905     int         rv;
3906 
3907     if (name)
3908 	*name = NULL;
3909 
3910     rv = ipmi_fru_get(info->fru, info->index, NULL, &num, dtype,
3911 		      intval, time, data, data_len);
3912     if ((rv == E2BIG) || (rv == ENOSYS))
3913 	rv = EINVAL;
3914     return rv;
3915 }
3916 
3917 static int
fru_array_idx_set_field(ipmi_fru_node_t * pnode,unsigned int index,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)3918 fru_array_idx_set_field(ipmi_fru_node_t           *pnode,
3919 			unsigned int              index,
3920 			enum ipmi_fru_data_type_e dtype,
3921 			int                       intval,
3922 			time_t                    time,
3923 			double                    floatval,
3924 			char                      *data,
3925 			unsigned int              data_len)
3926 {
3927     fru_array_t *info = i_ipmi_fru_node_get_data(pnode);
3928 
3929     return ipmi_fru_set_data_val(info->fru, info->index, index,
3930 				 dtype, data, data_len);
3931 }
3932 
3933 static int
fru_array_get_subtype(ipmi_fru_node_t * pnode,enum ipmi_fru_data_type_e * dtype)3934 fru_array_get_subtype(ipmi_fru_node_t           *pnode,
3935 		      enum ipmi_fru_data_type_e *dtype)
3936 {
3937     *dtype = IPMI_FRU_DATA_ASCII;
3938     return 0;
3939 }
3940 
3941 static int
fru_node_get_field(ipmi_fru_node_t * pnode,unsigned int index,const char ** name,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)3942 fru_node_get_field(ipmi_fru_node_t           *pnode,
3943 		   unsigned int              index,
3944 		   const char                **name,
3945 		   enum ipmi_fru_data_type_e *dtype,
3946 		   int                       *intval,
3947 		   time_t                    *time,
3948 		   double                    *floatval,
3949 		   char                      **data,
3950 		   unsigned int              *data_len,
3951 		   ipmi_fru_node_t           **sub_node)
3952 {
3953     ipmi_fru_record_t            **recs;
3954     ipmi_fru_multi_record_area_t *u;
3955     ipmi_fru_t                   *fru = i_ipmi_fru_node_get_data(pnode);
3956     ipmi_fru_node_t              *node;
3957     int                          rv;
3958     int                          num;
3959     int                          len;
3960 
3961     if (index < NUM_FRUL_ENTRIES) {
3962 	num = 0;
3963 	rv = ipmi_fru_get(fru, index, name, &num, NULL, NULL, NULL, NULL,
3964 			  NULL);
3965 	if (rv)
3966 	    return rv;
3967 
3968 	if (num != 0) {
3969 	    fru_array_t               *info;
3970 	    enum ipmi_fru_data_type_e ldtype;
3971 	    int                       num2 = 0;
3972 
3973 	    /* Determine if the value exists or if the array is empty. */
3974 	    num2 = 0;
3975 	    rv = ipmi_fru_get(fru, index, name, &num2, &ldtype, NULL, NULL,
3976 			      NULL, NULL);
3977 	    if (rv) {
3978 		if (rv != E2BIG)
3979 		    /* No support for this field. */
3980 		    return rv;
3981 		else if (rv == E2BIG)
3982 		    len = 0;
3983 	    }
3984 	    else
3985 		len = 1;
3986 
3987 	    /* name is set by the previous call */
3988 	    if (dtype)
3989 		*dtype = IPMI_FRU_DATA_SUB_NODE;
3990 	    if (intval) {
3991 		/* Get the length of the array by searching. */
3992 		while (num != -1) {
3993 		    len++;
3994 		    rv = ipmi_fru_get(fru, index, NULL, &num, NULL, NULL,
3995 				      NULL, NULL, NULL);
3996 		    if (rv)
3997 			return rv;
3998 		}
3999 		*intval = len;
4000 	    }
4001 	    if (sub_node) {
4002 		node = i_ipmi_fru_node_alloc(fru);
4003 		if (!node)
4004 		    return ENOMEM;
4005 		info = ipmi_mem_alloc(sizeof(*info));
4006 		if (!info) {
4007 		    ipmi_fru_put_node(node);
4008 		    return ENOMEM;
4009 		}
4010 		info->index = index;
4011 		info->fru = fru;
4012 		i_ipmi_fru_node_set_data(node, info);
4013 		i_ipmi_fru_node_set_get_field(node, fru_array_idx_get_field);
4014 		i_ipmi_fru_node_set_set_field(node, fru_array_idx_set_field);
4015 		i_ipmi_fru_node_set_get_subtype(node, fru_array_get_subtype);
4016 		i_ipmi_fru_node_set_destructor(node, fru_array_idx_destroy);
4017 		ipmi_fru_ref(fru);
4018 
4019 		*sub_node = node;
4020 	    }
4021 	    return 0;
4022 	} else
4023 	    /* Not an array, everything is ok. */
4024 	    return ipmi_fru_get(fru, index, name, NULL, dtype, intval, time,
4025 				data, data_len);
4026 
4027     } else if (index == NUM_FRUL_ENTRIES) {
4028 	/* Handle multi-records. */
4029 	i_ipmi_fru_lock(fru);
4030 	recs = normal_fru_get_recs(fru);
4031 	if (!recs[IPMI_FRU_FTR_MULTI_RECORD_AREA]) {
4032 	    i_ipmi_fru_unlock(fru);
4033 	    return ENOSYS;
4034 	}
4035 	if (intval) {
4036 	    u = fru_record_get_data(recs[IPMI_FRU_FTR_MULTI_RECORD_AREA]);
4037 	    *intval = u->num_records;
4038 	}
4039 	i_ipmi_fru_unlock(fru);
4040 
4041 	if (name)
4042 	    *name = "multirecords";
4043 	if (dtype)
4044 	    *dtype = IPMI_FRU_DATA_SUB_NODE;
4045 	if (sub_node) {
4046 	    node = i_ipmi_fru_node_alloc(fru);
4047 	    if (!node)
4048 		return ENOMEM;
4049 	    i_ipmi_fru_node_set_data(node, fru);
4050 	    i_ipmi_fru_node_set_get_field(node, fru_mr_array_get_field);
4051 	    i_ipmi_fru_node_set_set_field(node, fru_mr_array_set_field);
4052 	    i_ipmi_fru_node_set_get_subtype(node, fru_mr_array_get_subtype);
4053 	    i_ipmi_fru_node_set_settable(node, fru_mr_array_settable);
4054 	    i_ipmi_fru_node_set_destructor(node, fru_node_destroy);
4055 	    ipmi_fru_ref(fru);
4056 
4057 	    *sub_node = node;
4058 	}
4059 	return 0;
4060     } else
4061 	return EINVAL;
4062 }
4063 
4064 static int
fru_node_set_field(ipmi_fru_node_t * pnode,unsigned int index,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)4065 fru_node_set_field(ipmi_fru_node_t           *pnode,
4066 		   unsigned int              index,
4067 		   enum ipmi_fru_data_type_e dtype,
4068 		   int                       intval,
4069 		   time_t                    time,
4070 		   double                    floatval,
4071 		   char                      *data,
4072 		   unsigned int              data_len)
4073 {
4074     ipmi_fru_t     *fru = i_ipmi_fru_node_get_data(pnode);
4075 
4076     if (index > NUM_FRUL_ENTRIES)
4077 	return EINVAL;
4078 
4079     if (index < NUM_FRUL_ENTRIES) {
4080 	fru_data_rep_t *p = frul + index;
4081 
4082 	if (p->hasnum) {
4083 	    /* Insert/delete array indexes. */
4084 	    if (intval >= 0) {
4085 		if (!data) {
4086 		    data = "";
4087 		    data_len = 0;
4088 		}
4089 		return ipmi_fru_ins_data_val(fru, index, intval,
4090 					     IPMI_FRU_DATA_ASCII, data,
4091 					     data_len);
4092 	    } else {
4093 		intval = (-intval) - 1;
4094 		data = NULL;
4095 		data_len = 0;
4096 		return ipmi_fru_set_data_val(fru, index, intval,
4097 					     IPMI_FRU_DATA_ASCII, data,
4098 					     data_len);
4099 	    }
4100 	}
4101 
4102 	switch (dtype) {
4103 	case IPMI_FRU_DATA_INT:
4104 	    return ipmi_fru_set_int_val(fru, index, 0, intval);
4105 	case IPMI_FRU_DATA_FLOAT:
4106 	    return ipmi_fru_set_float_val(fru, index, 0, floatval);
4107 	case IPMI_FRU_DATA_TIME:
4108 	    return ipmi_fru_set_time_val(fru, index, 0, time);
4109 	default:
4110 	    return ipmi_fru_set_data_val(fru, index, 0, dtype, data, data_len);
4111 	}
4112     } else if (index == (int) NUM_FRUL_ENTRIES) {
4113 	/* Insert/delete multirecords. */
4114 	unsigned char type = 0, version = 2;
4115 
4116 	if (data) {
4117 	    /* First two bytes are the type and version */
4118 	    if (data_len >= 1) {
4119 		type = data[0];
4120 		data++;
4121 		data_len--;
4122 	    }
4123 	    if (data_len >= 1) {
4124 		version = data[0];
4125 		data++;
4126 		data_len--;
4127 	    }
4128 	}
4129 
4130 	if (intval >= 0) {
4131 	    if (!data) {
4132 		data = "";
4133 		data_len = 0;
4134 	    }
4135 	    return ipmi_fru_ins_multi_record(fru, intval, type, version,
4136 					     (unsigned char *) data, data_len);
4137 	} else {
4138 	    intval = (-intval) - 1;
4139 	    return ipmi_fru_set_multi_record(fru, intval, 0, 0, NULL, 0);
4140 	}
4141     } else
4142 	return EINVAL;
4143 }
4144 
4145 static int
fru_node_settable(ipmi_fru_node_t * node,unsigned int index)4146 fru_node_settable(ipmi_fru_node_t           *node,
4147 		  unsigned int              index)
4148 {
4149     fru_data_rep_t *p;
4150 
4151     if (index < NUM_FRUL_ENTRIES) {
4152 	p = frul + index;
4153 	if (p->settable)
4154 	    return 0;
4155 	else
4156 	    return EPERM;
4157     } else if (index == (int) NUM_FRUL_ENTRIES)
4158 	/* The multirecord array is settable. */
4159 	return 0;
4160     else
4161 	return EINVAL;
4162 }
4163 
4164 /***********************************************************************
4165  *
4166  * Normal-fru-specific processing
4167  *
4168  **********************************************************************/
4169 static void
fru_record_destroy(ipmi_fru_record_t * rec)4170 fru_record_destroy(ipmi_fru_record_t *rec)
4171 {
4172     if (rec)
4173 	rec->handlers->free(rec);
4174 }
4175 
4176 static void
fru_cleanup_recs(ipmi_fru_t * fru)4177 fru_cleanup_recs(ipmi_fru_t *fru)
4178 {
4179     normal_fru_rec_data_t *info = i_ipmi_fru_get_rec_data(fru);
4180     int                   i;
4181 
4182     if (!info)
4183 	return;
4184 
4185     for (i=0; i<IPMI_FRU_FTR_NUMBER; i++)
4186 	fru_record_destroy(info->recs[i]);
4187 
4188     ipmi_mem_free(info);
4189 }
4190 
4191 static void
fru_write_complete(ipmi_fru_t * fru)4192 fru_write_complete(ipmi_fru_t *fru)
4193 {
4194     ipmi_fru_record_t **recs = normal_fru_get_recs(fru);
4195     int               i;
4196 
4197     for (i=0; i<IPMI_FRU_FTR_NUMBER; i++) {
4198 	ipmi_fru_record_t *rec = recs[i];
4199 	if (rec) {
4200 	    rec->rewrite = 0;
4201 	    rec->changed = 0;
4202 	    rec->orig_used_length = rec->used_length;
4203 	    if (rec->handlers->get_fields) {
4204 		fru_variable_t *f = rec->handlers->get_fields(rec);
4205 		int j;
4206 		for (j=0; j<f->next; j++)
4207 		    f->strings[i].changed = 0;
4208 	    }
4209 	}
4210     }
4211 }
4212 
4213 static int
fru_write(ipmi_fru_t * fru)4214 fru_write(ipmi_fru_t *fru)
4215 {
4216     normal_fru_rec_data_t *info = i_ipmi_fru_get_rec_data(fru);
4217     ipmi_fru_record_t     **recs = normal_fru_get_recs(fru);
4218     int                   i;
4219     int                   rv;
4220     unsigned char         *data = i_ipmi_fru_get_data_ptr(fru);
4221 
4222     data[0] = 1; /* Version */
4223     for (i=0; i<IPMI_FRU_FTR_MULTI_RECORD_AREA; i++) {
4224 	if (recs[i])
4225 	    data[i+1] = recs[i]->offset / 8;
4226 	else
4227 	    data[i+1] = 0;
4228     }
4229     if (recs[i] && recs[i]->used_length)
4230 	data[i+1] = recs[i]->offset / 8;
4231     else
4232 	data[i+1] = 0;
4233     data[6] = 0;
4234     data[7] = -checksum(data, 7);
4235 
4236     if (info->header_changed) {
4237 	rv = i_ipmi_fru_new_update_record(fru, 0, 8);
4238 	if (rv)
4239 	    return rv;
4240     }
4241 
4242     for (i=0; i<IPMI_FRU_FTR_NUMBER; i++) {
4243 	ipmi_fru_record_t *rec = recs[i];
4244 	unsigned int      length;
4245 
4246 	if (rec) {
4247 	    rv = rec->handlers->encode(fru, data);
4248 	    if (rv)
4249 		return rv;
4250 	    if (rec->rewrite) {
4251 		if (i == IPMI_FRU_FTR_MULTI_RECORD_AREA)
4252 		    length = rec->used_length;
4253 		else
4254 		    length = rec->length;
4255 		if (length == 0)
4256 		    continue;
4257 		rv = i_ipmi_fru_new_update_record(fru, rec->offset, length);
4258 		if (rv)
4259 		    return rv;
4260 	    }
4261 	}
4262     }
4263 
4264     return 0;
4265 }
4266 
4267 static int
fru_get_root_node(ipmi_fru_t * fru,const char ** name,ipmi_fru_node_t ** rnode)4268 fru_get_root_node(ipmi_fru_t *fru, const char **name, ipmi_fru_node_t **rnode)
4269 {
4270     ipmi_fru_node_t *node;
4271 
4272     if (name)
4273 	*name = "standard FRU";
4274     if (rnode) {
4275 	node = i_ipmi_fru_node_alloc(fru);
4276 	if (!node)
4277 	    return ENOMEM;
4278 	i_ipmi_fru_node_set_data(node, fru);
4279 	i_ipmi_fru_node_set_get_field(node, fru_node_get_field);
4280 	i_ipmi_fru_node_set_set_field(node, fru_node_set_field);
4281 	i_ipmi_fru_node_set_settable(node, fru_node_settable);
4282 	i_ipmi_fru_node_set_destructor(node, fru_node_destroy);
4283 	ipmi_fru_ref(fru);
4284 	*rnode = node;
4285     }
4286     return 0;
4287 }
4288 
4289 /************************************************************************
4290  *
4291  * For OEM-specific FRU multi-record decode and field get
4292  *
4293  ************************************************************************/
4294 
4295 static locked_list_t *fru_multi_record_oem_handlers;
4296 
4297 typedef struct fru_multi_record_oem_handlers_s {
4298     unsigned int                               manufacturer_id;
4299     unsigned char                              record_type_id;
4300     ipmi_fru_oem_multi_record_get_root_node_cb get_root;
4301     void                                       *cb_data;
4302 } fru_multi_record_oem_handlers_t;
4303 
4304 int
i_ipmi_fru_register_multi_record_oem_handler(unsigned int manufacturer_id,unsigned char record_type_id,ipmi_fru_oem_multi_record_get_root_node_cb get_root,void * cb_data)4305 i_ipmi_fru_register_multi_record_oem_handler
4306 (unsigned int                               manufacturer_id,
4307  unsigned char                              record_type_id,
4308  ipmi_fru_oem_multi_record_get_root_node_cb get_root,
4309  void                                       *cb_data)
4310 {
4311     fru_multi_record_oem_handlers_t *new_item;
4312 
4313     new_item = ipmi_mem_alloc(sizeof(*new_item));
4314     if (!new_item)
4315 	return ENOMEM;
4316 
4317     new_item->manufacturer_id = manufacturer_id;
4318     new_item->record_type_id = record_type_id;
4319     new_item->get_root = get_root;
4320     new_item->cb_data = cb_data;
4321 
4322     if (!locked_list_add(fru_multi_record_oem_handlers, new_item, NULL)) {
4323         ipmi_mem_free(new_item);
4324 	return ENOMEM;
4325     }
4326     return 0;
4327 }
4328 
4329 static int
fru_multi_record_oem_handler_cmp_dereg(void * cb_data,void * item1,void * item2)4330 fru_multi_record_oem_handler_cmp_dereg(void *cb_data, void *item1, void *item2)
4331 {
4332     fru_multi_record_oem_handlers_t *hndlr = item1;
4333     fru_multi_record_oem_handlers_t *cmp = cb_data;
4334 
4335     if ((hndlr->manufacturer_id == cmp->manufacturer_id)
4336 	&& (hndlr->record_type_id == cmp->record_type_id))
4337     {
4338 	/* We re-use the cb_data as a marker to tell we found it. */
4339         cmp->cb_data = cmp;
4340         locked_list_remove(fru_multi_record_oem_handlers, item1, item2);
4341         ipmi_mem_free(hndlr);
4342 	return LOCKED_LIST_ITER_STOP;
4343     }
4344     return LOCKED_LIST_ITER_CONTINUE;
4345 }
4346 
4347 int
i_ipmi_fru_deregister_multi_record_oem_handler(unsigned int manufacturer_id,unsigned char record_type_id)4348 i_ipmi_fru_deregister_multi_record_oem_handler(unsigned int manufacturer_id,
4349 					      unsigned char record_type_id)
4350 {
4351     fru_multi_record_oem_handlers_t tmp;
4352 
4353     tmp.manufacturer_id = manufacturer_id;
4354     tmp.record_type_id = record_type_id;
4355     tmp.cb_data = NULL;
4356     locked_list_iterate(fru_multi_record_oem_handlers,
4357                         fru_multi_record_oem_handler_cmp_dereg,
4358                         &tmp);
4359     if (!tmp.cb_data)
4360 	return ENOENT;
4361     return 0;
4362 }
4363 
4364 typedef struct oem_search_node_s
4365 {
4366     unsigned int    mr_rec_num;
4367     unsigned int    manufacturer_id;
4368     unsigned char   record_type_id;
4369     ipmi_fru_t      *fru;
4370     ipmi_fru_node_t *node;
4371     unsigned char   *mr_data;
4372     unsigned char   mr_data_len;
4373     const char      *name;
4374     int             rv;
4375 } oem_search_node_t;
4376 
4377 static int
get_root_node(void * cb_data,void * item1,void * item2)4378 get_root_node(void *cb_data, void *item1, void *item2)
4379 {
4380     fru_multi_record_oem_handlers_t *hndlr = item1;
4381     oem_search_node_t               *cmp = cb_data;
4382 
4383     if ((hndlr->record_type_id == cmp->record_type_id)
4384 	&& ((hndlr->record_type_id < 0xc0)
4385 	    || (hndlr->manufacturer_id == cmp->manufacturer_id)))
4386     {
4387 	cmp->rv = hndlr->get_root(cmp->fru, cmp->mr_rec_num,
4388 				  cmp->manufacturer_id,
4389 				  cmp->record_type_id,
4390 				  cmp->mr_data, cmp->mr_data_len,
4391 				  hndlr->cb_data, &cmp->name, &cmp->node);
4392 
4393 	return LOCKED_LIST_ITER_STOP;
4394     } else {
4395         cmp->rv = EINVAL;
4396     }
4397     return LOCKED_LIST_ITER_CONTINUE;
4398 }
4399 
4400 int
ipmi_fru_multi_record_get_root_node(ipmi_fru_t * fru,unsigned int record_num,const char ** name,ipmi_fru_node_t ** node)4401 ipmi_fru_multi_record_get_root_node(ipmi_fru_t      *fru,
4402 				    unsigned int    record_num,
4403 				    const char      **name,
4404 				    ipmi_fru_node_t **node)
4405 {
4406     ipmi_fru_record_t            **recs;
4407     ipmi_fru_multi_record_area_t *u;
4408     unsigned char                *d;
4409     oem_search_node_t            cmp;
4410 
4411     if (!i_ipmi_fru_is_normal_fru(fru))
4412 	return ENOSYS;
4413 
4414     i_ipmi_fru_lock(fru);
4415     recs = normal_fru_get_recs(fru);
4416     if (!recs[IPMI_FRU_FTR_MULTI_RECORD_AREA]) {
4417 	i_ipmi_fru_unlock(fru);
4418 	return ENOSYS;
4419     }
4420     u = fru_record_get_data(recs[IPMI_FRU_FTR_MULTI_RECORD_AREA]);
4421     if (record_num >= u->num_records) {
4422 	i_ipmi_fru_unlock(fru);
4423 	return E2BIG;
4424     }
4425     if (u->records[record_num].length < 3) {
4426 	i_ipmi_fru_unlock(fru);
4427 	return EINVAL;
4428     }
4429     d = ipmi_mem_alloc(u->records[record_num].length);
4430     if (!d) {
4431 	i_ipmi_fru_unlock(fru);
4432 	return ENOMEM;
4433     }
4434 
4435     memcpy(d, u->records[record_num].data, u->records[record_num].length);
4436     cmp.mr_rec_num = record_num;
4437     cmp.manufacturer_id = d[0] | (d[1] << 8) | (d[2] << 16);
4438     cmp.record_type_id = u->records[record_num].type;
4439     cmp.fru = fru;
4440     cmp.node = NULL;
4441     cmp.mr_data = d;
4442     cmp.mr_data_len = u->records[record_num].length;
4443     cmp.name = NULL;
4444     cmp.rv = 0;
4445     i_ipmi_fru_unlock(fru);
4446 
4447     locked_list_iterate(fru_multi_record_oem_handlers, get_root_node, &cmp);
4448     ipmi_mem_free(d);
4449     if (cmp.rv)
4450 	return cmp.rv;
4451     if (node)
4452 	*node = cmp.node;
4453     else
4454 	ipmi_fru_put_node(cmp.node);
4455     if (name)
4456 	*name = cmp.name;
4457     return 0;
4458 }
4459 
4460 /************************************************************************
4461  *
4462  * Standard multi-record handlers.
4463  *
4464  ************************************************************************/
4465 
4466 static ipmi_mr_floattab_item_t pow_supply_intfloat =
4467 {
4468     .count = 4,
4469     .defval = 0.0,
4470     .table = { {  11.9,  12.0,  12.1, "12.0" },
4471 	       { -12.1, -12.0, -11.9, "-12.0" },
4472 	       {   4.9,   5.0,   5.1, "5.0" },
4473 	       {   3.2,   3.3,   3.4, "3.3" } }
4474 };
4475 static ipmi_mr_item_layout_t pow_supply_items[] = {
4476     { .name = "overall capacity", .dtype = IPMI_FRU_DATA_INT, .settable = 1,
4477       .start = 0, .length = 2,
4478       .set_field = ipmi_mr_int_set_field, .get_field = ipmi_mr_int_get_field },
4479     { .name = "peak VA", .dtype = IPMI_FRU_DATA_INT, .settable = 1,
4480       .start = 2, .length = 2,
4481       .set_field = ipmi_mr_int_set_field, .get_field = ipmi_mr_int_get_field },
4482     { .name = "inrush current", .dtype = IPMI_FRU_DATA_INT, .settable = 1,
4483       .start = 4, .length = 1,
4484       .set_field = ipmi_mr_int_set_field, .get_field = ipmi_mr_int_get_field },
4485     { .name = "inrush current", .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4486       .start = 5, .length = 1,
4487       .u = { .multiplier = 0.001 },
4488       .set_field = ipmi_mr_intfloat_set_field,
4489       .get_field = ipmi_mr_intfloat_get_field },
4490     { .name = "low input voltage 1", .dtype = IPMI_FRU_DATA_FLOAT,
4491       .settable = 1,
4492       .start = 6, .length = 2,
4493       .u = { .multiplier = 0.01 },
4494       .set_field = ipmi_mr_intfloat_set_field,
4495       .get_field = ipmi_mr_intfloat_get_field },
4496     { .name = "high input voltage 1", .dtype = IPMI_FRU_DATA_FLOAT,
4497       .settable = 1,
4498       .start = 8, .length = 2,
4499       .u = { .multiplier = 0.01 },
4500       .set_field = ipmi_mr_intfloat_set_field,
4501       .get_field = ipmi_mr_intfloat_get_field },
4502     { .name = "low input voltage 2", .dtype = IPMI_FRU_DATA_FLOAT,
4503       .settable = 1,
4504       .start = 10, .length = 2,
4505       .u = { .multiplier = 0.01 },
4506       .set_field = ipmi_mr_intfloat_set_field,
4507       .get_field = ipmi_mr_intfloat_get_field },
4508     { .name = "high input voltage 2", .dtype = IPMI_FRU_DATA_FLOAT,
4509       .settable = 1,
4510       .start = 12, .length = 2,
4511       .u = { .multiplier = 0.01 },
4512       .set_field = ipmi_mr_intfloat_set_field,
4513       .get_field = ipmi_mr_intfloat_get_field },
4514     { .name = "low frequency", .dtype = IPMI_FRU_DATA_INT, .settable = 1,
4515       .start = 14, .length = 1,
4516       .set_field = ipmi_mr_int_set_field, .get_field = ipmi_mr_int_get_field },
4517     { .name = "high frequency", .dtype = IPMI_FRU_DATA_INT, .settable = 1,
4518       .start = 15, .length = 1,
4519       .set_field = ipmi_mr_int_set_field, .get_field = ipmi_mr_int_get_field },
4520     { .name = "A/C dropout tolerance", .dtype = IPMI_FRU_DATA_FLOAT,
4521       .settable = 1,
4522       .start = 16, .length = 1,
4523       .u = { .multiplier = 0.001 },
4524       .set_field = ipmi_mr_intfloat_set_field,
4525       .get_field = ipmi_mr_intfloat_get_field },
4526     { .name = "tach pulses per rotation", .dtype = IPMI_FRU_DATA_BOOLEAN,
4527       .settable = 1,
4528       .start = 140, .length = 1,
4529       .set_field = ipmi_mr_bitint_set_field,
4530       .get_field = ipmi_mr_bitint_get_field },
4531     { .name = "hot swap support", .dtype = IPMI_FRU_DATA_BOOLEAN,
4532       .settable = 1,
4533       .start = 139, .length = 1,
4534       .set_field = ipmi_mr_bitint_set_field,
4535       .get_field = ipmi_mr_bitint_get_field },
4536     { .name = "autoswitch", .dtype = IPMI_FRU_DATA_BOOLEAN,
4537       .settable = 1,
4538       .start = 138, .length = 1,
4539       .set_field = ipmi_mr_bitint_set_field,
4540       .get_field = ipmi_mr_bitint_get_field },
4541     { .name = "power factor correction", .dtype = IPMI_FRU_DATA_BOOLEAN,
4542       .settable = 1,
4543       .start = 137, .length = 1,
4544       .set_field = ipmi_mr_bitint_set_field,
4545       .get_field = ipmi_mr_bitint_get_field },
4546     { .name = "predictive fail support", .dtype = IPMI_FRU_DATA_BOOLEAN,
4547       .settable = 1,
4548       .start = 136, .length = 1,
4549       .set_field = ipmi_mr_bitint_set_field,
4550       .get_field = ipmi_mr_bitint_get_field },
4551     { .name = "peak capacity hold up time", .dtype = IPMI_FRU_DATA_INT,
4552       .settable = 1,
4553       .start = 156, .length = 4,
4554       .set_field = ipmi_mr_bitint_set_field,
4555       .get_field = ipmi_mr_bitint_get_field },
4556     { .name = "peak capacity", .dtype = IPMI_FRU_DATA_INT,
4557       .settable = 1,
4558       .start = 144, .length = 12,
4559       .set_field = ipmi_mr_bitint_set_field,
4560       .get_field = ipmi_mr_bitint_get_field },
4561     { .name = "combined wattage voltage 1", .dtype = IPMI_FRU_DATA_FLOAT,
4562       .settable = 1,
4563       .start = 164, .length = 4,
4564       .u = { .tab_data = &pow_supply_intfloat },
4565       .set_field = ipmi_mr_bitfloatvaltab_set_field,
4566       .get_field = ipmi_mr_bitfloatvaltab_get_field,
4567       .get_enum  = ipmi_mr_bitfloatvaltab_get_enum },
4568     { .name = "combined wattage voltage 2", .dtype = IPMI_FRU_DATA_FLOAT,
4569       .settable = 1,
4570       .start = 160, .length = 4,
4571       .u = { .tab_data = &pow_supply_intfloat },
4572       .set_field = ipmi_mr_bitfloatvaltab_set_field,
4573       .get_field = ipmi_mr_bitfloatvaltab_get_field,
4574       .get_enum  = ipmi_mr_bitfloatvaltab_get_enum },
4575     { .name = "combined wattage", .dtype = IPMI_FRU_DATA_INT, .settable = 1,
4576       .start = 21, .length = 2,
4577       .set_field = ipmi_mr_int_set_field, .get_field = ipmi_mr_int_get_field },
4578     { .name = "predictive fail tack low threshold", .dtype = IPMI_FRU_DATA_INT,
4579       .settable = 1,
4580       .start = 23, .length = 1,
4581       .set_field = ipmi_mr_int_set_field, .get_field = ipmi_mr_int_get_field }
4582 };
4583 static ipmi_mr_struct_layout_t pow_supply = {
4584     .name = "Power Supply Information", .length = 24,
4585     .item_count = 22, .items = pow_supply_items,
4586     .array_count = 0, .arrays = NULL,
4587     .cleanup = ipmi_mr_struct_cleanup
4588 };
4589 
4590 static ipmi_mr_item_layout_t dc_output_items[] = {
4591     { .name = "output number", .dtype = IPMI_FRU_DATA_INT,
4592       .settable = 1,
4593       .start = 0, .length = 4,
4594       .set_field = ipmi_mr_bitint_set_field,
4595       .get_field = ipmi_mr_bitint_get_field },
4596     { .name = "standby", .dtype = IPMI_FRU_DATA_BOOLEAN,
4597       .settable = 1,
4598       .start = 7, .length = 1,
4599       .set_field = ipmi_mr_bitint_set_field,
4600       .get_field = ipmi_mr_bitint_get_field },
4601     { .name = "nominal voltage", .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4602       .start = 1, .length = 2,
4603       .u = { .multiplier = 0.01 },
4604       .set_field = ipmi_mr_intfloat_set_field,
4605       .get_field = ipmi_mr_intfloat_get_field },
4606     { .name = "max negative voltage deviation",
4607       .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4608       .start = 3, .length = 2,
4609       .u = { .multiplier = 0.01 },
4610       .set_field = ipmi_mr_intfloat_set_field,
4611       .get_field = ipmi_mr_intfloat_get_field },
4612     { .name = "max positive voltage deviation",
4613       .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4614       .start = 5, .length = 2,
4615       .u = { .multiplier = 0.01 },
4616       .set_field = ipmi_mr_intfloat_set_field,
4617       .get_field = ipmi_mr_intfloat_get_field },
4618     { .name = "ripple", .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4619       .start = 7, .length = 2,
4620       .u = { .multiplier = 0.001 },
4621       .set_field = ipmi_mr_intfloat_set_field,
4622       .get_field = ipmi_mr_intfloat_get_field },
4623     { .name = "min current", .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4624       .start = 9, .length = 2,
4625       .u = { .multiplier = 0.01 },
4626       .set_field = ipmi_mr_intfloat_set_field,
4627       .get_field = ipmi_mr_intfloat_get_field },
4628     { .name = "max current", .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4629       .start = 11, .length = 2,
4630       .u = { .multiplier = 0.01 },
4631       .set_field = ipmi_mr_intfloat_set_field,
4632       .get_field = ipmi_mr_intfloat_get_field },
4633 };
4634 static ipmi_mr_struct_layout_t dc_output = {
4635     .name = "DC Output", .length = 13,
4636     .item_count = 8, .items = dc_output_items,
4637     .array_count = 0, .arrays = NULL,
4638     .cleanup = ipmi_mr_struct_cleanup
4639 };
4640 
4641 static ipmi_mr_item_layout_t dc_load_items[] = {
4642     { .name = "output number", .dtype = IPMI_FRU_DATA_INT,
4643       .settable = 1,
4644       .start = 0, .length = 4,
4645       .set_field = ipmi_mr_bitint_set_field,
4646       .get_field = ipmi_mr_bitint_get_field },
4647     { .name = "nominal voltage", .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4648       .start = 1, .length = 2,
4649       .u = { .multiplier = 0.01 },
4650       .set_field = ipmi_mr_intfloat_set_field,
4651       .get_field = ipmi_mr_intfloat_get_field },
4652     { .name = "min voltage",
4653       .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4654       .start = 3, .length = 2,
4655       .u = { .multiplier = 0.01 },
4656       .set_field = ipmi_mr_intfloat_set_field,
4657       .get_field = ipmi_mr_intfloat_get_field },
4658     { .name = "max voltage",
4659       .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4660       .start = 5, .length = 2,
4661       .u = { .multiplier = 0.01 },
4662       .set_field = ipmi_mr_intfloat_set_field,
4663       .get_field = ipmi_mr_intfloat_get_field },
4664     { .name = "ripple", .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4665       .start = 7, .length = 2,
4666       .u = { .multiplier = 0.001 },
4667       .set_field = ipmi_mr_intfloat_set_field,
4668       .get_field = ipmi_mr_intfloat_get_field },
4669     { .name = "min current", .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4670       .start = 9, .length = 2,
4671       .u = { .multiplier = 0.01 },
4672       .set_field = ipmi_mr_intfloat_set_field,
4673       .get_field = ipmi_mr_intfloat_get_field },
4674     { .name = "max current", .dtype = IPMI_FRU_DATA_FLOAT, .settable = 1,
4675       .start = 11, .length = 2,
4676       .u = { .multiplier = 0.01 },
4677       .set_field = ipmi_mr_intfloat_set_field,
4678       .get_field = ipmi_mr_intfloat_get_field },
4679 };
4680 static ipmi_mr_struct_layout_t dc_load = {
4681     .name = "DC Load", .length = 13,
4682     .item_count = 7, .items = dc_load_items,
4683     .array_count = 0, .arrays = NULL,
4684     .cleanup = ipmi_mr_struct_cleanup
4685 };
4686 
4687 static int
std_get_mr_root(ipmi_fru_t * fru,unsigned int mr_rec_num,unsigned int manufacturer_id,unsigned char record_type_id,unsigned char * mr_data,unsigned int mr_data_len,void * cb_data,const char ** name,ipmi_fru_node_t ** node)4688 std_get_mr_root(ipmi_fru_t          *fru,
4689 		unsigned int	    mr_rec_num,
4690 		unsigned int        manufacturer_id,
4691 		unsigned char       record_type_id,
4692 		unsigned char       *mr_data,
4693 		unsigned int        mr_data_len,
4694 		void                *cb_data,
4695 		const char          **name,
4696 		ipmi_fru_node_t     **node)
4697 {
4698     switch (record_type_id) {
4699     case 0x00:
4700 	return ipmi_mr_struct_root(fru, mr_rec_num, mr_data, mr_data_len,
4701 				   &pow_supply,
4702 				   name, node);
4703     case 0x01:
4704 	return ipmi_mr_struct_root(fru, mr_rec_num, mr_data, mr_data_len,
4705 				   &dc_output,
4706 				   name, node);
4707     case 0x02:
4708 	return ipmi_mr_struct_root(fru, mr_rec_num, mr_data, mr_data_len,
4709 				   &dc_load,
4710 				   name, node);
4711     default:
4712 	return EINVAL;
4713     }
4714 }
4715 
4716 /***********************************************************************
4717  *
4718  * FRU decoding
4719  *
4720  **********************************************************************/
4721 
4722 typedef struct fru_offset_s
4723 {
4724     int          type;
4725     unsigned int offset;
4726 } fru_offset_t;
4727 
4728 static normal_fru_rec_data_t *
setup_normal_fru(ipmi_fru_t * fru,unsigned char version)4729 setup_normal_fru(ipmi_fru_t *fru, unsigned char version)
4730 {
4731     normal_fru_rec_data_t *info;
4732 
4733     info = ipmi_mem_alloc(sizeof(*info));
4734     if (!info)
4735 	return NULL;
4736     memset(info, 0, sizeof(*info));
4737 
4738     i_ipmi_fru_set_rec_data(fru, info);
4739 
4740     info->version = version;
4741 
4742     i_ipmi_fru_set_op_cleanup_recs(fru, fru_cleanup_recs);
4743     i_ipmi_fru_set_op_write_complete(fru, fru_write_complete);
4744     i_ipmi_fru_set_op_write(fru, fru_write);
4745     i_ipmi_fru_set_op_get_root_node(fru, fru_get_root_node);
4746 
4747     i_ipmi_fru_set_is_normal_fru(fru, 1);
4748     return info;
4749 }
4750 
4751 static int
process_fru_info(ipmi_fru_t * fru)4752 process_fru_info(ipmi_fru_t *fru)
4753 {
4754     normal_fru_rec_data_t *info;
4755     ipmi_fru_record_t **recs;
4756     unsigned char     *data = i_ipmi_fru_get_data_ptr(fru);
4757     unsigned int      data_len = i_ipmi_fru_get_data_len(fru);
4758     fru_offset_t      foff[IPMI_FRU_FTR_NUMBER];
4759     int               i, j;
4760     int               err = 0;
4761     unsigned char     version;
4762 
4763     if (checksum(data, 8) != 0)
4764 	return EBADF;
4765 
4766     version = *data;
4767     if ((version != 1) && (version != 2))
4768 	/* Only support version 1 */
4769 	/* The IPMI 0.9 to IPMI 1.0 Change Summary and Porting Considerations
4770 	 * from October 1, 1998 mention under FRU changes (Pg. 4)
4771 	 * "The FRU format version has been updated to 02h from 01h"
4772 	 * Unfortunately, some companies (such as Fujitsu Siemens Computers)
4773 	 * used this information for production tools.
4774 	 */
4775 	return EBADF;
4776 
4777     for (i=0; i<IPMI_FRU_FTR_NUMBER; i++) {
4778 	foff[i].type = i;
4779 	if (! (i_ipmi_fru_get_fetch_mask(fru) & (1 << i))) {
4780 	    foff[i].offset = 0;
4781 	    continue;
4782 	}
4783 	foff[i].offset = data[i+1] * 8;
4784 	if (foff[i].offset >= data_len) {
4785 	    ipmi_log(IPMI_LOG_ERR_INFO,
4786 		     "%snormal_fru.c(process_fru_info):"
4787 		     " FRU offset exceeds data length",
4788 		     i_ipmi_fru_get_iname(fru));
4789 	    return EBADF;
4790 	}
4791     }
4792 
4793     /* Fields are *supposed* to occur in the specified order.  Verify
4794        this. */
4795     for (i=0, j=1; j<IPMI_FRU_FTR_NUMBER; i=j, j++) {
4796 	if (foff[i].offset == 0)
4797 	    continue;
4798 	while (foff[j].offset == 0) {
4799 	    j++;
4800 	    if (j >= IPMI_FRU_FTR_NUMBER)
4801 	        goto check_done;
4802 	}
4803 	if (foff[i].offset >= foff[j].offset) {
4804 	    ipmi_log(IPMI_LOG_WARNING,
4805 		     "%snormal_fru.c(process_fru_info):"
4806 		     " FRU fields did not occur in the correct order",
4807 		     i_ipmi_fru_get_iname(fru));
4808 	}
4809     }
4810  check_done:
4811 
4812     info = setup_normal_fru(fru, version);
4813     if (!info)
4814 	return ENOMEM;
4815 
4816     recs = info->recs;
4817     for (i=0; i<IPMI_FRU_FTR_NUMBER; i++) {
4818 	int plen, next_off, offset;
4819 
4820 	offset = foff[i].offset;
4821 	if (offset == 0)
4822 	    continue;
4823 
4824 	for (j=i+1; j<IPMI_FRU_FTR_NUMBER; j++) {
4825 	    if (foff[j].offset)
4826 		break;
4827 	}
4828 
4829 	if (j >= IPMI_FRU_FTR_NUMBER)
4830 	    next_off = data_len;
4831 	else
4832 	    next_off = foff[j].offset;
4833 	plen = next_off - offset;
4834 
4835 	err = fru_area_info[i].decode(fru, data+offset, plen, &recs[i]);
4836 	if (err)
4837 	    goto out_err;
4838 
4839 	if (recs[i])
4840 	    recs[i]->offset = offset;
4841     }
4842 
4843     return 0;
4844 
4845  out_err:
4846     /* Clear out the FRU information. */
4847     i_ipmi_fru_set_op_cleanup_recs(fru, NULL);
4848     i_ipmi_fru_set_op_write_complete(fru, NULL);
4849     i_ipmi_fru_set_op_write(fru, NULL);
4850     i_ipmi_fru_set_op_get_root_node(fru, NULL);
4851 
4852     /* This must be after setting cleanup_recs() */
4853     fru_cleanup_recs(fru);
4854     i_ipmi_fru_set_rec_data(fru, NULL);
4855 
4856     i_ipmi_fru_set_is_normal_fru(fru, 0);
4857 
4858     return err;
4859 }
4860 
4861 /************************************************************************
4862  *
4863  * Init/shutdown
4864  *
4865  ************************************************************************/
4866 
4867 static int fru_initialized;
4868 
4869 int
i_ipmi_normal_fru_init(void)4870 i_ipmi_normal_fru_init(void)
4871 {
4872     int rv;
4873 
4874     if (fru_initialized)
4875 	return 0;
4876 
4877     fru_multi_record_oem_handlers = locked_list_alloc
4878 	(ipmi_get_global_os_handler());
4879     if (!fru_multi_record_oem_handlers)
4880         return ENOMEM;
4881 
4882     rv = i_ipmi_fru_register_multi_record_oem_handler(0,
4883 						      0x00,
4884 						      std_get_mr_root,
4885 						      NULL);
4886     if (rv) {
4887 	locked_list_destroy(fru_multi_record_oem_handlers);
4888 	fru_multi_record_oem_handlers = NULL;
4889 	return rv;
4890     }
4891 
4892     rv = i_ipmi_fru_register_multi_record_oem_handler(0,
4893 						      0x01,
4894 						      std_get_mr_root,
4895 						      NULL);
4896     if (rv) {
4897 	i_ipmi_fru_deregister_multi_record_oem_handler(0, 0x00);
4898 	locked_list_destroy(fru_multi_record_oem_handlers);
4899 	fru_multi_record_oem_handlers = NULL;
4900 	return rv;
4901     }
4902 
4903     rv = i_ipmi_fru_register_multi_record_oem_handler(0,
4904 						      0x02,
4905 						      std_get_mr_root,
4906 						      NULL);
4907     if (rv) {
4908 	i_ipmi_fru_deregister_multi_record_oem_handler(0, 0x01);
4909 	i_ipmi_fru_deregister_multi_record_oem_handler(0, 0x00);
4910 	locked_list_destroy(fru_multi_record_oem_handlers);
4911 	fru_multi_record_oem_handlers = NULL;
4912 	return rv;
4913     }
4914 
4915     rv = i_ipmi_fru_register_decoder(process_fru_info);
4916     if (rv) {
4917 	i_ipmi_fru_deregister_multi_record_oem_handler(0, 0x02);
4918 	i_ipmi_fru_deregister_multi_record_oem_handler(0, 0x01);
4919 	i_ipmi_fru_deregister_multi_record_oem_handler(0, 0x00);
4920 	locked_list_destroy(fru_multi_record_oem_handlers);
4921 	fru_multi_record_oem_handlers = NULL;
4922 	return rv;
4923     }
4924 
4925     fru_initialized = 1;
4926 
4927     return 0;
4928 }
4929 
4930 void
i_ipmi_normal_fru_shutdown(void)4931 i_ipmi_normal_fru_shutdown(void)
4932 {
4933     if (fru_initialized) {
4934 	i_ipmi_fru_deregister_decoder(process_fru_info);
4935 	i_ipmi_fru_deregister_multi_record_oem_handler(0, 0x00);
4936 	i_ipmi_fru_deregister_multi_record_oem_handler(0, 0x01);
4937 	i_ipmi_fru_deregister_multi_record_oem_handler(0, 0x02);
4938 	locked_list_destroy(fru_multi_record_oem_handlers);
4939 	fru_multi_record_oem_handlers = NULL;
4940 	fru_initialized = 0;
4941     }
4942 }
4943 
4944 /***********************************************************************
4945  *
4946  * Table-driven data engine for handling multirecord fru info.
4947  *
4948  **********************************************************************/
4949 static int ipmi_mr_node_struct_get_field(ipmi_fru_node_t           *node,
4950 					 unsigned int              index,
4951 					 const char                **name,
4952 					 enum ipmi_fru_data_type_e *dtype,
4953 					 int                       *intval,
4954 					 time_t                    *time,
4955 					 double                    *floatval,
4956 					 char                      **data,
4957 					 unsigned int              *data_len,
4958 					 ipmi_fru_node_t           **sub_node);
4959 static int ipmi_mr_node_struct_get_enum(ipmi_fru_node_t *node,
4960 					unsigned int    index,
4961 					int             *pos,
4962 					int             *nextpos,
4963 					const char      **data);
4964 static int ipmi_mr_node_struct_set_field(ipmi_fru_node_t           *node,
4965 					 unsigned int              index,
4966 					 enum ipmi_fru_data_type_e dtype,
4967 					 int                       intval,
4968 					 time_t                    time,
4969 					 double                    floatval,
4970 					 char                      *data,
4971 					 unsigned int              data_len);
4972 static int ipmi_mr_node_struct_settable(ipmi_fru_node_t *node,
4973 					unsigned int    index);
4974 
4975 uint8_t
ipmi_mr_full_offset(ipmi_mr_offset_t * o)4976 ipmi_mr_full_offset(ipmi_mr_offset_t *o)
4977 {
4978     uint8_t rv = 0;
4979 
4980     while (o) {
4981 	rv += o->offset;
4982 	o = o->parent;
4983     }
4984     return rv;
4985 }
4986 
4987 void
ipmi_mr_adjust_len(ipmi_mr_offset_t * o,int len)4988 ipmi_mr_adjust_len(ipmi_mr_offset_t *o, int len)
4989 {
4990     while (o) {
4991 	ipmi_mr_offset_t *l = o->next;
4992 	while (l) {
4993 	    l->offset += len;
4994 	    l = l->next;
4995 	}
4996 	o->length += len;
4997 	o = o->parent;
4998     }
4999 }
5000 
5001 void
ipmi_mr_struct_cleanup(ipmi_mr_struct_info_t * rec)5002 ipmi_mr_struct_cleanup(ipmi_mr_struct_info_t *rec)
5003 {
5004     unsigned int i;
5005 
5006     if (rec->data)
5007 	ipmi_mem_free(rec->data);
5008     if (rec->arrays) {
5009 	ipmi_mr_struct_layout_t *layout = rec->layout;
5010 	for (i=0; i<layout->array_count; i++) {
5011 	    if (rec->arrays[i].layout)
5012 		rec->arrays[i].layout->cleanup(rec->arrays+i);
5013 	}
5014 	ipmi_mem_free(rec->arrays);
5015     }
5016     ipmi_mem_free(rec);
5017 }
5018 
5019 void
ipmi_mr_item_cleanup(ipmi_mr_item_info_t * rec)5020 ipmi_mr_item_cleanup(ipmi_mr_item_info_t *rec)
5021 {
5022     if (rec->data)
5023 	ipmi_mem_free(rec->data);
5024     ipmi_mem_free(rec);
5025 }
5026 
5027 static void
ipmi_mr_struct_root_destroy(ipmi_fru_node_t * node)5028 ipmi_mr_struct_root_destroy(ipmi_fru_node_t *node)
5029 {
5030     ipmi_mr_struct_info_t *rec      = i_ipmi_fru_node_get_data(node);
5031     ipmi_mr_fru_info_t    *finfo    = i_ipmi_fru_node_get_data2(node);
5032     ipmi_mr_struct_layout_t *layout = rec->layout;
5033     ipmi_fru_deref(finfo->fru);
5034     layout->cleanup(rec);
5035     ipmi_mem_free(finfo);
5036 }
5037 
5038 static void
ipmi_mr_sub_destroy(ipmi_fru_node_t * node)5039 ipmi_mr_sub_destroy(ipmi_fru_node_t *node)
5040 {
5041     ipmi_fru_node_t *root_node = i_ipmi_fru_node_get_data2(node);
5042     ipmi_fru_put_node(root_node);
5043 }
5044 
5045 static int
ins_array_item(ipmi_mr_array_info_t * arec,ipmi_mr_fru_info_t * finfo,ipmi_mr_offset_t * poff,int index,char * data,unsigned int data_len,unsigned char ** rdata)5046 ins_array_item(ipmi_mr_array_info_t *arec,
5047 	       ipmi_mr_fru_info_t   *finfo,
5048 	       ipmi_mr_offset_t     *poff,
5049 	       int                  index,
5050 	       char                 *data,
5051 	       unsigned int         data_len,
5052 	       unsigned char        **rdata)
5053 {
5054     ipmi_mr_offset_t **newa;
5055     ipmi_mr_offset_t **olda;
5056     unsigned char    *sdata;
5057     int              i, j;
5058     int              rv;
5059     int              no;
5060 
5061     /* insert a new entry */
5062     if (index > arec->count)
5063 	index = arec->count;
5064 
5065     if (arec->count >= 255)
5066 	return E2BIG;
5067 
5068     newa = ipmi_mem_alloc(sizeof(*newa) * (arec->count+1));
5069     if (!newa)
5070 	return ENOMEM;
5071     sdata = ipmi_mem_alloc(arec->layout->min_elem_size);
5072     if (!sdata) {
5073 	ipmi_mem_free(newa);
5074 	return ENOMEM;
5075     }
5076     memset(sdata, 0, arec->layout->min_elem_size);
5077     if (data) {
5078 	if (data_len > arec->layout->min_elem_size)
5079 	    memcpy(sdata, data, arec->layout->min_elem_size);
5080 	else
5081 	    memcpy(sdata, data, data_len);
5082     }
5083 
5084     poff->parent = &arec->offset;
5085     poff->length = arec->layout->min_elem_size;
5086     if (index == arec->count) {
5087 	poff->offset = arec->offset.length;
5088 	poff->next = NULL;
5089     } else {
5090 	ipmi_mr_offset_t *o = arec->items[index];
5091 	poff->offset = o->offset;
5092 	poff->next = o;
5093     }
5094 
5095     rv = ipmi_fru_ins_multi_record_data(finfo->fru, finfo->mr_rec_num,
5096 					sdata,
5097 					ipmi_mr_full_offset(poff),
5098 					arec->layout->min_elem_size);
5099     if (rv) {
5100 	ipmi_mem_free(sdata);
5101 	ipmi_mem_free(newa);
5102 	return rv;
5103     }
5104 
5105     if (index > 0) {
5106 	ipmi_mr_offset_t *o = arec->items[index-1];
5107 	o->next = poff;
5108     }
5109     ipmi_mr_adjust_len(&arec->offset, arec->layout->min_elem_size);
5110 
5111     if (arec->items) {
5112 	no = 0;
5113 	for (i=0, j=0; i<(int)arec->count; j++) {
5114 	    ipmi_mr_offset_t *o = arec->items[i];
5115 	    if (j == (int) index) {
5116 		no = arec->layout->min_elem_size;
5117 		continue;
5118 	    }
5119 	    newa[j] = o;
5120 	    o->offset += no;
5121 	    i++;
5122 	}
5123     }
5124     newa[index] = poff;
5125 
5126     no = arec->layout->min_elem_size;
5127 
5128     arec->count += 1;
5129 
5130     /* Adjust the arrays that come after me in the record. */
5131     for (j=0; j<arec->nr_after; j++) {
5132 	ipmi_mr_array_info_t *ai = arec + j + 1;
5133 
5134 	ai->offset.offset += no;
5135 	for (i=0; i<(int)ai->count; i++) {
5136 	    ipmi_mr_offset_t *o = ai->items[i];
5137 	    o->offset += no;
5138 	}
5139     }
5140 
5141     olda = arec->items;
5142     arec->items = newa;
5143     if (arec->layout->has_count)
5144 	ipmi_fru_ovw_multi_record_data(finfo->fru, finfo->mr_rec_num,
5145 				       &arec->count,
5146 				       ipmi_mr_full_offset(&arec->offset), 1);
5147     if (olda)
5148 	ipmi_mem_free(olda);
5149 
5150     *rdata = sdata;
5151     return 0;
5152 }
5153 
5154 static int
del_array_item(ipmi_mr_array_info_t * arec,ipmi_mr_fru_info_t * finfo,int index,ipmi_mr_offset_t ** delitem)5155 del_array_item(ipmi_mr_array_info_t *arec,
5156 	       ipmi_mr_fru_info_t   *finfo,
5157 	       int                  index,
5158 	       ipmi_mr_offset_t     **delitem)
5159 {
5160     ipmi_mr_offset_t **newa;
5161     ipmi_mr_offset_t **olda;
5162     int              i, j;
5163     int              rv;
5164     int              no;
5165     ipmi_mr_offset_t *poff;
5166 
5167     index = (-index) - 1;
5168     if (index > arec->count)
5169 	return EINVAL;
5170 
5171     poff = arec->items[index];
5172 
5173     newa = ipmi_mem_alloc(sizeof(*newa) * (arec->count-1));
5174     if (!newa)
5175 	return ENOMEM;
5176     rv = ipmi_fru_del_multi_record_data(finfo->fru, finfo->mr_rec_num,
5177 					ipmi_mr_full_offset(poff),
5178 					poff->length);
5179     if (rv) {
5180 	ipmi_mem_free(newa);
5181 	return rv;
5182     }
5183 
5184     if (index > 0) {
5185 	ipmi_mr_offset_t *o = arec->items[index-1];
5186 	o->next = poff->next;
5187     }
5188 
5189     ipmi_mr_adjust_len(&arec->offset, - (int) poff->length);
5190 
5191     no = 0;
5192     for (i=0, j=0; j<(int)arec->count; j++) {
5193 	ipmi_mr_offset_t *o = arec->items[j];
5194 	if (j == (int) index) {
5195 	    no = - poff->length;
5196 	    continue;
5197 	}
5198 	newa[i] = o;
5199 	o->offset += no;
5200 	i++;
5201     }
5202     no = - poff->length;
5203 
5204     arec->count -= 1;
5205 
5206     /* Adjust the arrays that come after me in the record. */
5207     for (j=0; j<arec->nr_after; j++) {
5208 	ipmi_mr_array_info_t *ai = arec + j + 1;
5209 
5210 	ai->offset.offset += no;
5211 	for (i=0; i<(int)ai->count; i++) {
5212 	    ipmi_mr_offset_t *o = ai->items[i];
5213 	    o->offset += no;
5214 	}
5215     }
5216 
5217     olda = arec->items;
5218     arec->items = newa;
5219     if (arec->layout->has_count)
5220 	ipmi_fru_ovw_multi_record_data(finfo->fru, finfo->mr_rec_num,
5221 				       &arec->count,
5222 				       ipmi_mr_full_offset(&arec->offset), 1);
5223     if (olda)
5224 	ipmi_mem_free(olda);
5225 
5226     *delitem = poff;
5227 
5228     return 0;
5229 }
5230 
5231 void
ipmi_mr_array_array_cleanup(ipmi_mr_array_info_t * arec)5232 ipmi_mr_array_array_cleanup(ipmi_mr_array_info_t *arec)
5233 {
5234     int i;
5235 
5236     if (arec->items) {
5237 	for (i=0; i<arec->count; i++) {
5238 	    if (arec->items[i]) {
5239 		ipmi_mr_array_layout_t *layout = arec->layout->elem_layout;
5240 		layout->cleanup((void *) arec->items[i]);
5241 	    }
5242 	}
5243 	ipmi_mem_free(arec->items);
5244     }
5245 }
5246 
5247 void
ipmi_mr_item_array_cleanup(ipmi_mr_array_info_t * arec)5248 ipmi_mr_item_array_cleanup(ipmi_mr_array_info_t *arec)
5249 {
5250     int i;
5251 
5252     if (arec->items) {
5253 	for (i=0; i<arec->count; i++) {
5254 	    if (arec->items[i]) {
5255 		ipmi_mr_item_info_t *irec = (void *) arec->items[i];
5256 		if (irec->data)
5257 		    ipmi_mem_free(irec->data);
5258 		ipmi_mem_free(irec);
5259 	    }
5260 	}
5261 	ipmi_mem_free(arec->items);
5262     }
5263 }
5264 
5265 static int
ipmi_mr_node_item_array_set_field(ipmi_fru_node_t * node,unsigned int index,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)5266 ipmi_mr_node_item_array_set_field(ipmi_fru_node_t           *node,
5267 				  unsigned int              index,
5268 				  enum ipmi_fru_data_type_e dtype,
5269 				  int                       intval,
5270 				  time_t                    time,
5271 				  double                    floatval,
5272 				  char                      *data,
5273 				  unsigned int              data_len)
5274 {
5275     ipmi_mr_array_info_t  *arec = i_ipmi_fru_node_get_data(node);
5276     ipmi_fru_node_t       *rnode = i_ipmi_fru_node_get_data2(node);
5277     ipmi_mr_item_info_t   *info;
5278     ipmi_mr_item_layout_t *layout = arec->layout->elem_layout;
5279     ipmi_mr_fru_info_t    *finfo =i_ipmi_fru_node_get_data2(rnode);
5280     ipmi_mr_getset_t      gs = { layout, NULL, NULL, finfo };
5281     int                   rv = EINVAL;
5282 
5283     i_ipmi_fru_lock(finfo->fru);
5284     if (index >= arec->count) {
5285 	rv = EINVAL;
5286 	goto out;
5287     }
5288 
5289     info = (void *) arec->items[index];
5290     gs.rdata = info->data;
5291     gs.offset = arec->items[index];
5292     rv = layout->set_field(&gs,
5293 			   dtype, intval, time, floatval, data, data_len);
5294  out:
5295     i_ipmi_fru_unlock(finfo->fru);
5296     return rv;
5297 }
5298 
5299 static int
ipmi_mr_node_item_array_get_subtype(ipmi_fru_node_t * node,enum ipmi_fru_data_type_e * dtype)5300 ipmi_mr_node_item_array_get_subtype(ipmi_fru_node_t           *node,
5301 				    enum ipmi_fru_data_type_e *dtype)
5302 {
5303     ipmi_mr_array_info_t  *arec = i_ipmi_fru_node_get_data(node);
5304     ipmi_mr_item_layout_t *layout = arec->layout->elem_layout;
5305 
5306     *dtype = layout->dtype;
5307     return 0;
5308 }
5309 
5310 static int
ipmi_mr_node_item_array_settable(ipmi_fru_node_t * node,unsigned int index)5311 ipmi_mr_node_item_array_settable(ipmi_fru_node_t *node,
5312 				 unsigned int    index)
5313 {
5314     ipmi_mr_array_info_t  *arec = i_ipmi_fru_node_get_data(node);
5315     ipmi_mr_item_layout_t *layout = arec->layout->elem_layout;
5316 
5317     if (layout->settable)
5318 	return 0;
5319     else
5320 	return EPERM;
5321 }
5322 
5323 static int
ipmi_mr_node_item_array_get_field(ipmi_fru_node_t * node,unsigned int index,const char ** name,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)5324 ipmi_mr_node_item_array_get_field(ipmi_fru_node_t           *node,
5325 				  unsigned int              index,
5326 				  const char                **name,
5327 				  enum ipmi_fru_data_type_e *dtype,
5328 				  int                       *intval,
5329 				  time_t                    *time,
5330 				  double                    *floatval,
5331 				  char                      **data,
5332 				  unsigned int              *data_len,
5333 				  ipmi_fru_node_t           **sub_node)
5334 {
5335     ipmi_mr_array_info_t  *arec = i_ipmi_fru_node_get_data(node);
5336     ipmi_mr_item_layout_t *layout = arec->layout->elem_layout;
5337     ipmi_mr_item_info_t   *info;
5338     ipmi_fru_node_t       *rnode =i_ipmi_fru_node_get_data2(node);
5339     ipmi_mr_fru_info_t    *finfo =i_ipmi_fru_node_get_data2(rnode);
5340     ipmi_mr_getset_t      gs = { layout, NULL, NULL, finfo };
5341     int                   rv = EINVAL;
5342 
5343     i_ipmi_fru_lock(finfo->fru);
5344     if (index >= arec->count) {
5345 	rv = EINVAL;
5346 	goto out;
5347     }
5348 
5349     info = (void *) arec->items[index];
5350     gs.rdata = info->data;
5351     gs.offset = arec->items[index];
5352     rv = layout->get_field(&gs,
5353 			   dtype, intval, time, floatval, data, data_len);
5354  out:
5355     i_ipmi_fru_unlock(finfo->fru);
5356     return rv;
5357 }
5358 
5359 static int
ipmi_mr_node_item_array_get_enum(ipmi_fru_node_t * node,unsigned int index,int * pos,int * nextpos,const char ** data)5360 ipmi_mr_node_item_array_get_enum(ipmi_fru_node_t *node,
5361 				 unsigned int    index,
5362 				 int             *pos,
5363 				 int             *nextpos,
5364 				 const char      **data)
5365 {
5366     ipmi_mr_array_info_t  *arec = i_ipmi_fru_node_get_data(node);
5367     ipmi_mr_item_layout_t *layout = arec->layout->elem_layout;
5368     ipmi_mr_item_info_t   *info;
5369     ipmi_fru_node_t       *rnode =i_ipmi_fru_node_get_data2(node);
5370     ipmi_mr_fru_info_t    *finfo =i_ipmi_fru_node_get_data2(rnode);
5371     ipmi_mr_getset_t      gs = { layout, NULL, NULL, finfo };
5372     int                   rv;
5373 
5374     i_ipmi_fru_lock(finfo->fru);
5375     if (index >= arec->count) {
5376 	rv = EINVAL;
5377 	goto out;
5378     }
5379 
5380     if (!layout->get_enum) {
5381 	rv = ENOSYS;
5382 	goto out;
5383     }
5384 
5385     info = (void *) arec->items[index];
5386     gs.rdata = info->data;
5387     gs.offset = arec->items[index];
5388     rv = layout->get_enum(&gs, pos, nextpos, data);
5389  out:
5390     i_ipmi_fru_unlock(finfo->fru);
5391     return rv;
5392 }
5393 
5394 int
ipmi_mr_item_array_set_field(ipmi_mr_array_info_t * arec,ipmi_mr_fru_info_t * finfo,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)5395 ipmi_mr_item_array_set_field(ipmi_mr_array_info_t      *arec,
5396 			     ipmi_mr_fru_info_t        *finfo,
5397 			     enum ipmi_fru_data_type_e dtype,
5398 			     int                       intval,
5399 			     time_t                    time,
5400 			     double                    floatval,
5401 			     char                      *data,
5402 			     unsigned int              data_len)
5403 {
5404     int index = intval;
5405     int rv;
5406 
5407     if (index >= 0) {
5408 	ipmi_mr_item_info_t *newv;
5409 
5410 	newv = ipmi_mem_alloc(sizeof(*newv));
5411 	if (!newv)
5412 	    return ENOMEM;
5413 	memset(newv, 0, sizeof(*newv));
5414 	newv->layout = arec->layout->elem_layout;
5415 
5416 	rv = ins_array_item(arec, finfo, &newv->offset, index, data, data_len,
5417 			    &newv->data);
5418 	if (rv)
5419 	    ipmi_mem_free(newv);
5420     } else {
5421 	ipmi_mr_offset_t      *delo;
5422 	ipmi_mr_item_info_t *delv;
5423 
5424 	rv = del_array_item(arec, finfo, index, &delo);
5425 	if (!rv) {
5426 	    delv = (void *) delo;
5427 	    if (delv->data)
5428 		ipmi_mem_free(delv->data);
5429 	    ipmi_mem_free(delv);
5430 	}
5431     }
5432 
5433     return rv;
5434 }
5435 
5436 int
ipmi_mr_item_array_get_field(ipmi_mr_array_info_t * arec,ipmi_fru_node_t * rnode,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)5437 ipmi_mr_item_array_get_field(ipmi_mr_array_info_t      *arec,
5438 			     ipmi_fru_node_t           *rnode,
5439 			     enum ipmi_fru_data_type_e *dtype,
5440 			     int                       *intval,
5441 			     time_t                    *time,
5442 			     double                    *floatval,
5443 			     char                      **data,
5444 			     unsigned int              *data_len,
5445 			     ipmi_fru_node_t           **sub_node)
5446 {
5447     ipmi_fru_node_t    *node;
5448     ipmi_mr_fru_info_t *finfo = i_ipmi_fru_node_get_data2(rnode);
5449 
5450     if (dtype)
5451 	*dtype = IPMI_FRU_DATA_SUB_NODE;
5452     if (intval)
5453 	*intval = arec->count;
5454     if (sub_node) {
5455 	node = i_ipmi_fru_node_alloc(finfo->fru);
5456 	if (!node)
5457 	    return ENOMEM;
5458 	ipmi_fru_get_node(rnode);
5459 	i_ipmi_fru_node_set_data(node, arec);
5460 	i_ipmi_fru_node_set_data2(node, rnode);
5461 	i_ipmi_fru_node_set_get_field(node,
5462 				      ipmi_mr_node_item_array_get_field);
5463 	i_ipmi_fru_node_set_get_enum(node,
5464 				     ipmi_mr_node_item_array_get_enum);
5465 	i_ipmi_fru_node_set_set_field(node,
5466 				      ipmi_mr_node_item_array_set_field);
5467 	i_ipmi_fru_node_set_settable(node, ipmi_mr_node_item_array_settable);
5468 	i_ipmi_fru_node_set_get_subtype(node,
5469 					ipmi_mr_node_item_array_get_subtype);
5470 	i_ipmi_fru_node_set_destructor(node, ipmi_mr_sub_destroy);
5471 	*sub_node = node;
5472     }
5473     return 0;
5474 }
5475 
5476 void
ipmi_mr_struct_array_cleanup(ipmi_mr_array_info_t * arec)5477 ipmi_mr_struct_array_cleanup(ipmi_mr_array_info_t *arec)
5478 {
5479     int i;
5480 
5481     if (arec->items) {
5482 	for (i=0; i<arec->count; i++) {
5483 	    if (arec->items[i]) {
5484 		ipmi_mr_struct_layout_t *layout = arec->layout->elem_layout;
5485 		layout->cleanup((void *) arec->items[i]);
5486 	    }
5487 	}
5488 	ipmi_mem_free(arec->items);
5489     }
5490 }
5491 
5492 static int
ipmi_mr_node_struct_array_set_field(ipmi_fru_node_t * node,unsigned int index,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)5493 ipmi_mr_node_struct_array_set_field(ipmi_fru_node_t           *node,
5494 				    unsigned int              index,
5495 				    enum ipmi_fru_data_type_e dtype,
5496 				    int                       intval,
5497 				    time_t                    time,
5498 				    double                    floatval,
5499 				    char                      *data,
5500 				    unsigned int              data_len)
5501 {
5502     return EPERM;
5503 }
5504 
5505 static int
ipmi_mr_node_struct_array_get_subtype(ipmi_fru_node_t * node,enum ipmi_fru_data_type_e * dtype)5506 ipmi_mr_node_struct_array_get_subtype(ipmi_fru_node_t           *node,
5507 				      enum ipmi_fru_data_type_e *dtype)
5508 {
5509     *dtype = IPMI_FRU_DATA_SUB_NODE;
5510     return 0;
5511 }
5512 
5513 static int
ipmi_mr_node_struct_array_settable(ipmi_fru_node_t * node,unsigned int index)5514 ipmi_mr_node_struct_array_settable(ipmi_fru_node_t *node,
5515 				   unsigned int    index)
5516 {
5517     return EPERM;
5518 }
5519 
5520 static int
ipmi_mr_node_struct_array_get_field(ipmi_fru_node_t * node,unsigned int index,const char ** name,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)5521 ipmi_mr_node_struct_array_get_field(ipmi_fru_node_t           *node,
5522 				    unsigned int              index,
5523 				    const char                **name,
5524 				    enum ipmi_fru_data_type_e *dtype,
5525 				    int                       *intval,
5526 				    time_t                    *time,
5527 				    double                    *floatval,
5528 				    char                      **data,
5529 				    unsigned int              *data_len,
5530 				    ipmi_fru_node_t           **sub_node)
5531 {
5532     ipmi_mr_array_info_t *arec = i_ipmi_fru_node_get_data(node);
5533     ipmi_fru_node_t      *rnode = i_ipmi_fru_node_get_data2(node);
5534     ipmi_mr_fru_info_t   *finfo = i_ipmi_fru_node_get_data2(rnode);
5535     int                  rv = 0;
5536 
5537     i_ipmi_fru_lock(finfo->fru);
5538     if (index >= arec->count) {
5539 	rv = EINVAL;
5540 	goto out;
5541     }
5542 
5543     if (name)
5544 	*name = NULL; /* We are an array */
5545     if (dtype)
5546 	*dtype = IPMI_FRU_DATA_SUB_NODE;
5547     if (intval)
5548 	*intval = -1; /* Sub element is not an array */
5549     if (sub_node) {
5550 	node = i_ipmi_fru_node_alloc(finfo->fru);
5551 	if (!node) {
5552 	    rv = ENOMEM;
5553 	    goto out;
5554 	}
5555 
5556 	ipmi_fru_get_node(rnode);
5557 	i_ipmi_fru_node_set_data(node, arec->items[index]);
5558 	i_ipmi_fru_node_set_data2(node, rnode);
5559 	i_ipmi_fru_node_set_get_field(node, ipmi_mr_node_struct_get_field);
5560 	i_ipmi_fru_node_set_get_enum(node, ipmi_mr_node_struct_get_enum);
5561 	i_ipmi_fru_node_set_set_field(node, ipmi_mr_node_struct_set_field);
5562 	i_ipmi_fru_node_set_settable(node, ipmi_mr_node_struct_settable);
5563 	i_ipmi_fru_node_set_destructor(node, ipmi_mr_sub_destroy);
5564 
5565 	*sub_node = node;
5566     }
5567 
5568  out:
5569     i_ipmi_fru_unlock(finfo->fru);
5570     return rv;
5571 }
5572 
5573 int
ipmi_mr_struct_array_set_field(ipmi_mr_array_info_t * arec,ipmi_mr_fru_info_t * finfo,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)5574 ipmi_mr_struct_array_set_field(ipmi_mr_array_info_t      *arec,
5575 			       ipmi_mr_fru_info_t        *finfo,
5576 			       enum ipmi_fru_data_type_e dtype,
5577 			       int                       intval,
5578 			       time_t                    time,
5579 			       double                    floatval,
5580 			       char                      *data,
5581 			       unsigned int              data_len)
5582 {
5583     int index = intval;
5584     int rv;
5585 
5586     if (index >= 0) {
5587 	ipmi_mr_struct_info_t *newv;
5588 
5589 	newv = ipmi_mem_alloc(sizeof(*newv));
5590 	if (!newv)
5591 	    return ENOMEM;
5592 	memset(newv, 0, sizeof(*newv));
5593 	newv->layout = arec->layout->elem_layout;
5594 
5595 	rv = ins_array_item(arec, finfo, &newv->offset, index, data, data_len,
5596 			    &newv->data);
5597 	if (rv)
5598 	    ipmi_mem_free(newv);
5599     } else {
5600 	ipmi_mr_offset_t      *delo;
5601 	ipmi_mr_struct_info_t *delv;
5602 
5603 	rv = del_array_item(arec, finfo, index, &delo);
5604 	if (!rv) {
5605 	    delv = (void *) delo;
5606 	    delv->layout->cleanup(delv);
5607 	}
5608     }
5609 
5610     return rv;
5611 }
5612 
5613 int
ipmi_mr_struct_array_get_field(ipmi_mr_array_info_t * arec,ipmi_fru_node_t * rnode,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)5614 ipmi_mr_struct_array_get_field(ipmi_mr_array_info_t      *arec,
5615 			       ipmi_fru_node_t           *rnode,
5616 			       enum ipmi_fru_data_type_e *dtype,
5617 			       int                       *intval,
5618 			       time_t                    *time,
5619 			       double                    *floatval,
5620 			       char                      **data,
5621 			       unsigned int              *data_len,
5622 			       ipmi_fru_node_t           **sub_node)
5623 {
5624     ipmi_fru_node_t    *node;
5625     ipmi_mr_fru_info_t *finfo = i_ipmi_fru_node_get_data2(rnode);
5626 
5627     if (dtype)
5628 	*dtype = IPMI_FRU_DATA_SUB_NODE;
5629     if (intval)
5630 	*intval = arec->count;
5631     if (sub_node) {
5632 	node = i_ipmi_fru_node_alloc(finfo->fru);
5633 	if (!node)
5634 	    return ENOMEM;
5635 	ipmi_fru_get_node(rnode);
5636 	i_ipmi_fru_node_set_data(node, arec);
5637 	i_ipmi_fru_node_set_data2(node, rnode);
5638 	i_ipmi_fru_node_set_get_field(node,
5639 				      ipmi_mr_node_struct_array_get_field);
5640 	i_ipmi_fru_node_set_set_field(node,
5641 				      ipmi_mr_node_struct_array_set_field);
5642 	i_ipmi_fru_node_set_settable(node, ipmi_mr_node_struct_array_settable);
5643 	i_ipmi_fru_node_set_get_subtype(node,
5644 					ipmi_mr_node_struct_array_get_subtype);
5645 	i_ipmi_fru_node_set_destructor(node, ipmi_mr_sub_destroy);
5646 	*sub_node = node;
5647     }
5648     return 0;
5649 }
5650 
5651 static int
ipmi_mr_node_struct_set_field(ipmi_fru_node_t * node,unsigned int index,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)5652 ipmi_mr_node_struct_set_field(ipmi_fru_node_t           *node,
5653 			      unsigned int              index,
5654 			      enum ipmi_fru_data_type_e dtype,
5655 			      int                       intval,
5656 			      time_t                    time,
5657 			      double                    floatval,
5658 			      char                      *data,
5659 			      unsigned int              data_len)
5660 {
5661     ipmi_mr_struct_info_t   *rec = i_ipmi_fru_node_get_data(node);
5662     ipmi_fru_node_t         *rnode = i_ipmi_fru_node_get_data2(node);
5663     ipmi_mr_struct_layout_t *layout = rec->layout;
5664     ipmi_mr_fru_info_t      *finfo = i_ipmi_fru_node_get_data2(rnode);
5665     int                     rv = EINVAL;
5666 
5667     i_ipmi_fru_lock(finfo->fru);
5668     if (index < layout->item_count) {
5669 	ipmi_mr_item_layout_t *ilayout = layout->items+index;
5670 	ipmi_mr_getset_t      gs = { ilayout, &rec->offset,
5671 				     rec->data, finfo };
5672 	if (!layout->items[index].set_field)
5673 	    rv = EPERM;
5674 	else
5675 	    rv = layout->items[index].set_field(&gs,
5676 						dtype, intval, time, floatval,
5677 						data, data_len);
5678     } else {
5679 	index -= layout->item_count;
5680 	if (index < layout->array_count)
5681 	    rv = layout->arrays[index].set_field(rec->arrays+index, finfo,
5682 						 dtype, intval, time,
5683 						 floatval, data, data_len);
5684     }
5685     i_ipmi_fru_unlock(finfo->fru);
5686 
5687     return rv;
5688 }
5689 
5690 static int
ipmi_mr_root_node_struct_set_field(ipmi_fru_node_t * node,unsigned int index,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)5691 ipmi_mr_root_node_struct_set_field(ipmi_fru_node_t           *node,
5692 				   unsigned int              index,
5693 				   enum ipmi_fru_data_type_e dtype,
5694 				   int                       intval,
5695 				   time_t                    time,
5696 				   double                    floatval,
5697 				   char                      *data,
5698 				   unsigned int              data_len)
5699 {
5700     ipmi_mr_struct_info_t   *rec = i_ipmi_fru_node_get_data(node);
5701     ipmi_mr_struct_layout_t *layout = rec->layout;
5702     ipmi_mr_fru_info_t      *finfo = i_ipmi_fru_node_get_data2(node);
5703     int                     rv = EINVAL;
5704 
5705     i_ipmi_fru_lock(finfo->fru);
5706     if (index < layout->item_count) {
5707 	ipmi_mr_getset_t gs = { layout->items+index, &rec->offset,
5708 				rec->data, finfo };
5709 	rv = layout->items[index].set_field(&gs,
5710 					    dtype, intval, time, floatval,
5711 					    data, data_len);
5712     } else {
5713 	index -= layout->item_count;
5714 	if (index < layout->array_count)
5715 	    rv = layout->arrays[index].set_field(rec->arrays+index, finfo,
5716 						 dtype, intval, time, floatval,
5717 						 data, data_len);
5718     }
5719     i_ipmi_fru_unlock(finfo->fru);
5720 
5721     return rv;
5722 }
5723 
5724 static int
ipmi_mr_node_struct_settable(ipmi_fru_node_t * node,unsigned int index)5725 ipmi_mr_node_struct_settable(ipmi_fru_node_t *node,
5726 			     unsigned int    index)
5727 {
5728     ipmi_mr_struct_info_t   *rec = i_ipmi_fru_node_get_data(node);
5729     ipmi_mr_struct_layout_t *layout = rec->layout;
5730     ipmi_fru_node_t         *rnode =i_ipmi_fru_node_get_data2(node);
5731     ipmi_mr_fru_info_t      *finfo =i_ipmi_fru_node_get_data2(rnode);
5732     int                     rv = EINVAL;
5733 
5734     i_ipmi_fru_lock(finfo->fru);
5735     if (index < layout->item_count) {
5736 	if (layout->items[index].settable)
5737 	    rv = 0;
5738 	else
5739 	    rv = EPERM;
5740     } else {
5741 	index -= layout->item_count;
5742 	if (index < layout->array_count) {
5743 	    if (layout->arrays[index].settable)
5744 		rv = 0;
5745 	    else
5746 		rv = EPERM;
5747 	}
5748     }
5749     i_ipmi_fru_unlock(finfo->fru);
5750 
5751     return rv;
5752 }
5753 
5754 static int
ipmi_mr_root_node_struct_settable(ipmi_fru_node_t * node,unsigned int index)5755 ipmi_mr_root_node_struct_settable(ipmi_fru_node_t *node,
5756 				  unsigned int    index)
5757 {
5758     ipmi_mr_struct_info_t   *rec = i_ipmi_fru_node_get_data(node);
5759     ipmi_mr_struct_layout_t *layout = rec->layout;
5760     ipmi_mr_fru_info_t      *finfo =i_ipmi_fru_node_get_data2(node);
5761     int                     rv = EINVAL;
5762 
5763     i_ipmi_fru_lock(finfo->fru);
5764     if (index < layout->item_count) {
5765 	if (layout->items[index].settable)
5766 	    rv = 0;
5767 	else
5768 	    rv = EPERM;
5769     } else {
5770 	index -= layout->item_count;
5771 	if (index < layout->array_count) {
5772 	    if (layout->arrays[index].settable)
5773 		rv = 0;
5774 	    else
5775 		rv = EPERM;
5776 	}
5777     }
5778     i_ipmi_fru_unlock(finfo->fru);
5779 
5780     return rv;
5781 }
5782 
5783 static int
ipmi_mr_node_struct_get_enum(ipmi_fru_node_t * node,unsigned int index,int * pos,int * nextpos,const char ** data)5784 ipmi_mr_node_struct_get_enum(ipmi_fru_node_t *node,
5785 			     unsigned int    index,
5786 			     int             *pos,
5787 			     int             *nextpos,
5788 			     const char      **data)
5789 {
5790     ipmi_mr_struct_info_t   *rec = i_ipmi_fru_node_get_data(node);
5791     ipmi_mr_struct_layout_t *layout = rec->layout;
5792     ipmi_fru_node_t         *rnode =i_ipmi_fru_node_get_data2(node);
5793     ipmi_mr_fru_info_t      *finfo =i_ipmi_fru_node_get_data2(rnode);
5794     int                     rv = EINVAL;
5795 
5796     i_ipmi_fru_lock(finfo->fru);
5797     if (index < layout->item_count) {
5798 	ipmi_mr_getset_t gs = { layout->items+index, &rec->offset,
5799 				rec->data, finfo };
5800 	if (! layout->items[index].get_enum)
5801 	    rv = ENOSYS;
5802 	else
5803 	    rv = layout->items[index].get_enum(&gs, pos, nextpos, data);
5804     } else {
5805 	index -= layout->item_count;
5806 	if (index < layout->array_count)
5807 	    rv = ENOSYS;
5808     }
5809     i_ipmi_fru_unlock(finfo->fru);
5810 
5811     return rv;
5812 }
5813 
5814 static int
ipmi_mr_node_struct_get_field(ipmi_fru_node_t * node,unsigned int index,const char ** name,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)5815 ipmi_mr_node_struct_get_field(ipmi_fru_node_t           *node,
5816 			      unsigned int              index,
5817 			      const char                **name,
5818 			      enum ipmi_fru_data_type_e *dtype,
5819 			      int                       *intval,
5820 			      time_t                    *time,
5821 			      double                    *floatval,
5822 			      char                      **data,
5823 			      unsigned int              *data_len,
5824 			      ipmi_fru_node_t           **sub_node)
5825 {
5826     ipmi_mr_struct_info_t   *rec = i_ipmi_fru_node_get_data(node);
5827     ipmi_mr_struct_layout_t *layout = rec->layout;
5828     ipmi_fru_node_t         *rnode =i_ipmi_fru_node_get_data2(node);
5829     ipmi_mr_fru_info_t      *finfo =i_ipmi_fru_node_get_data2(rnode);
5830     int                     rv = EINVAL;
5831 
5832     i_ipmi_fru_lock(finfo->fru);
5833     if (index < layout->item_count) {
5834 	ipmi_mr_getset_t gs = { layout->items+index, &rec->offset,
5835 				rec->data, finfo };
5836 	if (name)
5837 	    *name = layout->items[index].name;
5838 	rv = layout->items[index].get_field(&gs, dtype,
5839 					    intval, time, floatval,
5840 					    data, data_len);
5841     } else {
5842 	index -= layout->item_count;
5843 	if (index < layout->array_count) {
5844 	    if (name)
5845 		*name = layout->arrays[index].name;
5846 
5847 	    rv = layout->arrays[index].get_field(rec->arrays+index, rnode,
5848 						 dtype, intval, time, floatval,
5849 						 data, data_len, sub_node);
5850 	}
5851     }
5852     i_ipmi_fru_unlock(finfo->fru);
5853 
5854     return rv;
5855 }
5856 
5857 static int
ipmi_mr_root_node_struct_get_field(ipmi_fru_node_t * node,unsigned int index,const char ** name,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len,ipmi_fru_node_t ** sub_node)5858 ipmi_mr_root_node_struct_get_field(ipmi_fru_node_t           *node,
5859 				   unsigned int              index,
5860 				   const char                **name,
5861 				   enum ipmi_fru_data_type_e *dtype,
5862 				   int                       *intval,
5863 				   time_t                    *time,
5864 				   double                    *floatval,
5865 				   char                      **data,
5866 				   unsigned int              *data_len,
5867 				   ipmi_fru_node_t           **sub_node)
5868 {
5869     ipmi_mr_struct_info_t   *rec = i_ipmi_fru_node_get_data(node);
5870     ipmi_mr_struct_layout_t *layout = rec->layout;
5871     ipmi_mr_fru_info_t      *finfo =i_ipmi_fru_node_get_data2(node);
5872     int                     rv = EINVAL;
5873 
5874     i_ipmi_fru_lock(finfo->fru);
5875     if (index < layout->item_count) {
5876 	ipmi_mr_getset_t gs = { layout->items+index, &rec->offset,
5877 				rec->data, finfo };
5878 	if (name)
5879 	    *name = layout->items[index].name;
5880 	rv = layout->items[index].get_field(&gs, dtype,
5881 					    intval, time, floatval,
5882 					    data, data_len);
5883     } else {
5884 	index -= layout->item_count;
5885 	if (index < layout->array_count) {
5886 	    if (name)
5887 		*name = layout->arrays[index].name;
5888 
5889 	    rv = layout->arrays[index].get_field(rec->arrays+index,
5890 						 node, dtype,
5891 						 intval, time, floatval,
5892 						 data, data_len, sub_node);
5893 	}
5894     }
5895     i_ipmi_fru_unlock(finfo->fru);
5896 
5897     return rv;
5898 }
5899 
5900 static int
ipmi_mr_root_node_struct_get_enum(ipmi_fru_node_t * node,unsigned int index,int * pos,int * nextpos,const char ** data)5901 ipmi_mr_root_node_struct_get_enum(ipmi_fru_node_t *node,
5902 				  unsigned int    index,
5903 				  int             *pos,
5904 				  int             *nextpos,
5905 				  const char      **data)
5906 {
5907     ipmi_mr_struct_info_t   *rec = i_ipmi_fru_node_get_data(node);
5908     ipmi_mr_struct_layout_t *layout = rec->layout;
5909     ipmi_mr_fru_info_t      *finfo =i_ipmi_fru_node_get_data2(node);
5910     int                     rv = EINVAL;
5911 
5912     i_ipmi_fru_lock(finfo->fru);
5913     if (index < layout->item_count) {
5914 	ipmi_mr_getset_t gs = { layout->items+index, &rec->offset,
5915 				rec->data, finfo };
5916 	if (! layout->items[index].get_enum)
5917 	    rv = ENOSYS;
5918 	else
5919 	    rv = layout->items[index].get_enum(&gs, pos, nextpos, data);
5920     } else {
5921 	index -= layout->item_count;
5922 	if (index < layout->array_count)
5923 	    rv = ENOSYS;
5924     }
5925     i_ipmi_fru_unlock(finfo->fru);
5926 
5927     return rv;
5928 }
5929 
5930 int
ipmi_mr_struct_elem_check(void * vlayout,unsigned char ** rmr_data,unsigned int * rmr_data_len)5931 ipmi_mr_struct_elem_check(void          *vlayout,
5932 			  unsigned char **rmr_data,
5933 			  unsigned int  *rmr_data_len)
5934 {
5935     ipmi_mr_struct_layout_t *layout = vlayout;
5936     unsigned char           *mr_data = *rmr_data;
5937     unsigned int            mr_data_len = *rmr_data_len;
5938     int                     i, j;
5939     int                     rv;
5940 
5941     if (mr_data_len < layout->length)
5942 	return EINVAL;
5943 
5944     mr_data += layout->length;
5945     mr_data_len -= layout->length;
5946 
5947     for (i=0; i<(int)layout->array_count; i++) {
5948 	ipmi_mr_array_layout_t *al = layout->arrays + i;
5949 	unsigned int           count;
5950 
5951 	if (al->has_count) {
5952 	    if (mr_data_len < 1)
5953 		return EINVAL;
5954 	    count = *mr_data;
5955 	    mr_data++;
5956 	    mr_data_len--;
5957 	    for (j=0; j<(int)count; j++) {
5958 		rv = al->elem_check(al->elem_layout, &mr_data, &mr_data_len);
5959 		if (rv)
5960 		    return rv;
5961 	    }
5962 	} else {
5963 	    count = 0;
5964 	    while (mr_data_len > 0) {
5965 		rv = al->elem_check(al->elem_layout, &mr_data, &mr_data_len);
5966 		if (rv)
5967 		    return rv;
5968 		count++;
5969 	    }
5970 	}
5971     }
5972 
5973     *rmr_data = mr_data;
5974     *rmr_data_len = mr_data_len;
5975 
5976     return 0;
5977 }
5978 
5979 int
ipmi_mr_struct_decode(void * vlayout,unsigned int offset,ipmi_mr_offset_t * offset_parent,ipmi_mr_offset_t ** rrec,unsigned char ** rmr_data,unsigned int * rmr_data_len)5980 ipmi_mr_struct_decode(void             *vlayout,
5981 		      unsigned int     offset,
5982 		      ipmi_mr_offset_t *offset_parent,
5983 		      ipmi_mr_offset_t **rrec,
5984 		      unsigned char    **rmr_data,
5985 		      unsigned int     *rmr_data_len)
5986 {
5987     unsigned char           *mr_data = *rmr_data;
5988     unsigned int            mr_data_len = *rmr_data_len;
5989     int                     i, j;
5990     ipmi_mr_struct_layout_t *layout = vlayout;
5991     int                     rv;
5992     ipmi_mr_struct_info_t   *rec;
5993     ipmi_mr_array_info_t    *ap;
5994 
5995     if (mr_data_len < layout->length)
5996 	return EINVAL;
5997 
5998     rec = ipmi_mem_alloc(sizeof(*rec));
5999     if (!rec)
6000 	return ENOMEM;
6001     memset(rec, 0, sizeof(*rec));
6002 
6003     rec->offset.offset = offset;
6004     rec->offset.parent = offset_parent;
6005     rec->offset.next = NULL;
6006     rec->layout = layout;
6007 
6008     if (layout->length > 0) {
6009 	rec->data = ipmi_mem_alloc(layout->length);
6010 	if (!rec->data) {
6011 	    rv = ENOMEM;
6012 	    goto out_err;
6013 	}
6014 	memcpy(rec->data, mr_data, layout->length);
6015 	mr_data += layout->length;
6016 	mr_data_len -= layout->length;
6017     }
6018 
6019     if (layout->array_count > 0) {
6020 	rec->arrays = ipmi_mem_alloc(sizeof(*(rec->arrays))
6021 				     * layout->array_count);
6022 	if (!rec->arrays) {
6023 	    rv = ENOMEM;
6024 	    goto out_err;
6025 	}
6026 	memset(rec->arrays, 0, sizeof(*(rec->arrays)) * layout->array_count);
6027     }
6028 
6029     ap = NULL;
6030     for (i=0; i<(int)layout->array_count; i++) {
6031 	ipmi_mr_array_layout_t *al = layout->arrays + i;
6032 	ipmi_mr_array_info_t   *ai = rec->arrays + i;
6033 	unsigned int           count;
6034 	unsigned char          *astart_mr_data = mr_data;
6035 
6036 	ai->offset.offset = mr_data - *rmr_data;
6037 	ai->offset.parent = &(rec->offset);
6038 	ai->offset.next = NULL;
6039 	if (ap)
6040 	    ap->offset.next = &ai->offset;
6041 
6042 	ai->nr_after = layout->array_count - i - 1;
6043 	ai->layout = al;
6044 	if (al->has_count) {
6045 	    if (mr_data_len < 1) {
6046 		rv = EINVAL;
6047 		goto out_err;
6048 	    }
6049 	    count = *mr_data;
6050 	    mr_data++;
6051 	    mr_data_len--;
6052 	} else {
6053 	    unsigned char *d = mr_data;
6054 	    unsigned int  l = mr_data_len;
6055 
6056 	    count = 0;
6057 	    while (l > 0) {
6058 		rv = al->elem_check(al->elem_layout, &d, &l);
6059 		if (rv)
6060 		    goto out_err;
6061 		count++;
6062 	    }
6063 	}
6064 	if (count > 0) {
6065 	    ipmi_mr_offset_t *p;
6066 
6067 	    ai->count = count;
6068 	    ai->items = ipmi_mem_alloc(sizeof(*(ai->items)) * count);
6069 	    if (!ai->items)
6070 		return ENOMEM;
6071 	    memset(ai->items, 0, sizeof(*(ai->items)) * count);
6072 	    p = NULL;
6073 	    for (j=0; j<(int)count; j++) {
6074 		ipmi_mr_offset_t *r;
6075 
6076 		rv = al->elem_decode(al->elem_layout,
6077 				     mr_data - astart_mr_data,
6078 				     &ai->offset,
6079 				     &r,
6080 				     &mr_data,
6081 				     &mr_data_len);
6082 		if (rv)
6083 		    goto out_err;
6084 
6085 		if (p)
6086 		    p->next = r;
6087 		ai->items[j] = r;
6088 		p = r;
6089 	    }
6090 	}
6091 	ai->offset.length = mr_data - astart_mr_data;
6092 	ap = ai;
6093     }
6094 
6095     rec->offset.length = mr_data - *rmr_data;
6096     *rmr_data = mr_data;
6097     *rmr_data_len = mr_data_len;
6098     *rrec = &rec->offset;
6099 
6100     return 0;
6101 
6102  out_err:
6103     ipmi_mr_struct_cleanup(rec);
6104     return rv;
6105 }
6106 
6107 int
ipmi_mr_item_elem_check(void * vlayout,unsigned char ** rmr_data,unsigned int * rmr_data_len)6108 ipmi_mr_item_elem_check(void          *vlayout,
6109 			unsigned char **rmr_data,
6110 			unsigned int  *rmr_data_len)
6111 {
6112     ipmi_mr_item_layout_t *layout = vlayout;
6113     unsigned char         *mr_data = *rmr_data;
6114     unsigned int          mr_data_len = *rmr_data_len;
6115 
6116     if (mr_data_len < layout->length)
6117 	return EINVAL;
6118 
6119     mr_data += layout->length;
6120     mr_data_len -= layout->length;
6121 
6122     *rmr_data = mr_data;
6123     *rmr_data_len = mr_data_len;
6124 
6125     return 0;
6126 }
6127 
6128 int
ipmi_mr_item_decode(void * vlayout,unsigned int offset,ipmi_mr_offset_t * offset_parent,ipmi_mr_offset_t ** rrec,unsigned char ** rmr_data,unsigned int * rmr_data_len)6129 ipmi_mr_item_decode(void             *vlayout,
6130 		    unsigned int     offset,
6131 		    ipmi_mr_offset_t *offset_parent,
6132 		    ipmi_mr_offset_t **rrec,
6133 		    unsigned char    **rmr_data,
6134 		    unsigned int     *rmr_data_len)
6135 {
6136     unsigned char         *mr_data = *rmr_data;
6137     unsigned int          mr_data_len = *rmr_data_len;
6138     ipmi_mr_item_layout_t *layout = vlayout;
6139     int                   rv;
6140     ipmi_mr_item_info_t   *rec;
6141 
6142     if (mr_data_len < layout->length)
6143 	return EINVAL;
6144 
6145     rec = ipmi_mem_alloc(sizeof(*rec));
6146     if (!rec)
6147 	return ENOMEM;
6148     memset(rec, 0, sizeof(*rec));
6149 
6150     rec->offset.offset = offset;
6151     rec->offset.parent = offset_parent;
6152     rec->offset.next = NULL;
6153     rec->layout = layout;
6154 
6155     if (layout->length > 0) {
6156 	rec->data = ipmi_mem_alloc(layout->length);
6157 	if (!rec->data) {
6158 	    rv = ENOMEM;
6159 	    goto out_err;
6160 	}
6161 	memcpy(rec->data, mr_data, layout->length);
6162 	mr_data += layout->length;
6163 	mr_data_len -= layout->length;
6164     }
6165 
6166     rec->offset.length = mr_data - *rmr_data;
6167     *rmr_data = mr_data;
6168     *rmr_data_len = mr_data_len;
6169 
6170     *rrec = &rec->offset;
6171 
6172     return 0;
6173 
6174  out_err:
6175     ipmi_mr_item_cleanup(rec);
6176     return rv;
6177 }
6178 
6179 int
ipmi_mr_struct_root(ipmi_fru_t * fru,unsigned int mr_rec_num,unsigned char * rmr_data,unsigned int rmr_data_len,ipmi_mr_struct_layout_t * layout,const char ** name,ipmi_fru_node_t ** rnode)6180 ipmi_mr_struct_root(ipmi_fru_t              *fru,
6181 		    unsigned int            mr_rec_num,
6182 		    unsigned char           *rmr_data,
6183 		    unsigned int            rmr_data_len,
6184 		    ipmi_mr_struct_layout_t *layout,
6185 		    const char              **name,
6186 		    ipmi_fru_node_t         **rnode)
6187 {
6188     unsigned char         *mr_data = rmr_data;
6189     unsigned int          mr_data_len = rmr_data_len;
6190     ipmi_mr_offset_t      *orec;
6191     ipmi_fru_node_t       *node;
6192     ipmi_mr_fru_info_t    *finfo = NULL;
6193     int                   rv;
6194 
6195     if (mr_data_len == 0)
6196 	return EINVAL;
6197 
6198     i_ipmi_fru_lock(fru);
6199     rv = ipmi_mr_struct_decode(layout, 4, NULL, &orec, &mr_data, &mr_data_len);
6200     if (rv) {
6201         i_ipmi_fru_unlock(fru);
6202 	return rv;
6203     }
6204 
6205     finfo = ipmi_mem_alloc(sizeof(*finfo));
6206     if (!finfo)
6207 	goto out_no_mem;
6208     i_ipmi_fru_ref_nolock(fru);
6209     finfo->fru = fru;
6210     finfo->mr_rec_num = mr_rec_num;
6211 
6212     node = i_ipmi_fru_node_alloc(fru);
6213     if (!node)
6214 	goto out_no_mem;
6215 
6216     i_ipmi_fru_node_set_data(node, orec);
6217     i_ipmi_fru_node_set_data2(node, finfo);
6218     i_ipmi_fru_node_set_get_field(node, ipmi_mr_root_node_struct_get_field);
6219     i_ipmi_fru_node_set_get_enum(node, ipmi_mr_root_node_struct_get_enum);
6220     i_ipmi_fru_node_set_set_field(node, ipmi_mr_root_node_struct_set_field);
6221     i_ipmi_fru_node_set_settable(node, ipmi_mr_root_node_struct_settable);
6222     i_ipmi_fru_node_set_destructor(node, ipmi_mr_struct_root_destroy);
6223 
6224     *rnode = node;
6225 
6226     if (name)
6227 	*name = layout->name;
6228     i_ipmi_fru_unlock(fru);
6229 
6230     return 0;
6231 
6232  out_no_mem:
6233     i_ipmi_fru_unlock(fru);
6234     rv = ENOMEM;
6235 
6236     if (finfo) {
6237 	ipmi_fru_deref(fru);
6238 	ipmi_mem_free(finfo);
6239     }
6240     ipmi_mr_struct_cleanup((void *) orec);
6241     return rv;
6242 }
6243 
6244 
6245 /***********************************************************************
6246  *
6247  * Generic field encoders and decoders.
6248  *
6249  **********************************************************************/
6250 
6251 int
ipmi_mr_int_set_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)6252 ipmi_mr_int_set_field(ipmi_mr_getset_t          *getset,
6253 		      enum ipmi_fru_data_type_e dtype,
6254 		      int                       intval,
6255 		      time_t                    time,
6256 		      double                    floatval,
6257 		      char                      *data,
6258 		      unsigned int              data_len)
6259 {
6260     unsigned char *c = getset->rdata + getset->layout->start;
6261     unsigned int  val = intval;
6262     int           i;
6263 
6264     if (dtype != getset->layout->dtype)
6265 	return EINVAL;
6266 
6267     if (dtype == IPMI_FRU_DATA_BOOLEAN)
6268 	val = !!val;
6269 
6270     for (i=0; i<getset->layout->length; i++) {
6271 	*c = val & 0xff;
6272 	val >>= 8;
6273 	c++;
6274     }
6275     c = getset->rdata + getset->layout->start;
6276     ipmi_fru_ovw_multi_record_data(getset->finfo->fru,
6277 				   getset->finfo->mr_rec_num, c,
6278 				   (ipmi_mr_full_offset(getset->offset)
6279 				    +getset->layout->start),
6280 				   getset->layout->length);
6281     return 0;
6282 }
6283 
6284 int
ipmi_mr_int_get_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len)6285 ipmi_mr_int_get_field(ipmi_mr_getset_t          *getset,
6286 		      enum ipmi_fru_data_type_e *dtype,
6287 		      int                       *intval,
6288 		      time_t                    *time,
6289 		      double                    *floatval,
6290 		      char                      **data,
6291 		      unsigned int              *data_len)
6292 {
6293     unsigned char *c = getset->rdata + getset->layout->start;
6294     int           val = 0;
6295     int           shift = 0;
6296     int           i;
6297 
6298     if (dtype)
6299 	*dtype = getset->layout->dtype;
6300     if (intval) {
6301 	for (i=0; i<getset->layout->length; i++) {
6302 	    val |= ((int) *c) << shift;
6303 	    c++;
6304 	    shift += 8;
6305 	}
6306 	*intval = val;
6307     }
6308     return 0;
6309 }
6310 
6311 int
ipmi_mr_intfloat_set_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)6312 ipmi_mr_intfloat_set_field(ipmi_mr_getset_t          *getset,
6313 			   enum ipmi_fru_data_type_e dtype,
6314 			   int                       intval,
6315 			   time_t                    time,
6316 			   double                    floatval,
6317 			   char                      *data,
6318 			   unsigned int              data_len)
6319 {
6320     unsigned char *c = getset->rdata + getset->layout->start;
6321     unsigned int  val;
6322     int           i;
6323 
6324     if (dtype != IPMI_FRU_DATA_FLOAT)
6325 	return EINVAL;
6326 
6327     val = (unsigned int) ((floatval / getset->layout->u.multiplier) + 0.5);
6328 
6329     for (i=0; i<getset->layout->length; i++) {
6330 	*c = val & 0xff;
6331 	val >>= 8;
6332 	c++;
6333     }
6334     c = getset->rdata + getset->layout->start;
6335     ipmi_fru_ovw_multi_record_data(getset->finfo->fru, getset->finfo->mr_rec_num,
6336 				   c, ipmi_mr_full_offset(getset->offset)+getset->layout->start,
6337 				   getset->layout->length);
6338     return 0;
6339 }
6340 
6341 int
ipmi_mr_intfloat_get_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len)6342 ipmi_mr_intfloat_get_field(ipmi_mr_getset_t          *getset,
6343 			   enum ipmi_fru_data_type_e *dtype,
6344 			   int                       *intval,
6345 			   time_t                    *time,
6346 			   double                    *floatval,
6347 			   char                      **data,
6348 			   unsigned int              *data_len)
6349 {
6350     unsigned char *c = getset->rdata + getset->layout->start;
6351     int           val = 0;
6352     int           shift = 0;
6353     int           i;
6354 
6355     if (dtype)
6356 	*dtype = IPMI_FRU_DATA_FLOAT;
6357     if (floatval) {
6358 	for (i=0; i<getset->layout->length; i++) {
6359 	    val |= ((int) *c) << shift;
6360 	    c++;
6361 	    shift += 8;
6362 	}
6363 	*floatval = ((double) val) * getset->layout->u.multiplier;
6364     }
6365     return 0;
6366 }
6367 
6368 int
ipmi_mr_bitint_set_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)6369 ipmi_mr_bitint_set_field(ipmi_mr_getset_t          *getset,
6370 			 enum ipmi_fru_data_type_e dtype,
6371 			 int                       intval,
6372 			 time_t                    time,
6373 			 double                    floatval,
6374 			 char                      *data,
6375 			 unsigned int              data_len)
6376 {
6377     unsigned char *c = getset->rdata + getset->layout->start / 8;
6378     unsigned char *end = getset->rdata + (getset->layout->start
6379 					  + getset->layout->length) / 8;
6380     int           val = intval;
6381     int           shift = getset->layout->start % 8;
6382     int           offset = 8 - shift;
6383     unsigned char mask1 = (~0) << shift;
6384     unsigned char mask2 = (~0) << ((getset->layout->start
6385 				    + getset->layout->length) % 8);
6386 
6387     if (dtype != getset->layout->dtype)
6388 	return EINVAL;
6389 
6390     if (dtype == IPMI_FRU_DATA_BOOLEAN)
6391 	val = !!val;
6392 
6393     while (c != end) {
6394 	*c = (*c & ~mask1) | (val << shift);
6395 	val >>= offset;
6396 	mask1 = 0xff;
6397 	shift = 0;
6398 	offset = 8;
6399 	c++;
6400     }
6401     mask1 = ~mask1 | mask2;
6402     *c = (*c & mask1 ) | ((val << shift) & ~mask1);
6403 
6404     c = getset->rdata + getset->layout->start / 8;
6405     ipmi_fru_ovw_multi_record_data(getset->finfo->fru,
6406 				   getset->finfo->mr_rec_num, c,
6407 				   (ipmi_mr_full_offset(getset->offset)
6408 				    + (c - getset->rdata)),
6409 				   end - c + 1);
6410     return 0;
6411 }
6412 
6413 int
ipmi_mr_bitint_get_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len)6414 ipmi_mr_bitint_get_field(ipmi_mr_getset_t          *getset,
6415 			 enum ipmi_fru_data_type_e *dtype,
6416 			 int                       *intval,
6417 			 time_t                    *time,
6418 			 double                    *floatval,
6419 			 char                      **data,
6420 			 unsigned int              *data_len)
6421 {
6422     unsigned char *c = getset->rdata + getset->layout->start / 8;
6423     unsigned char *end = getset->rdata + (getset->layout->start
6424 					  + getset->layout->length) / 8;
6425     int           val = 0;
6426     int           offset = getset->layout->start % 8;
6427     int           shift = 8 - offset;
6428     unsigned int  mask = (~0) << getset->layout->length;
6429 
6430     if (dtype)
6431 	*dtype = getset->layout->dtype;
6432 
6433     if (intval) {
6434 	val = *c >> offset;
6435 	while (c != end) {
6436 	    c++;
6437 	    val |= ((int) *c) << shift;
6438 	    shift += 8;
6439 	}
6440 	val &= ~mask;
6441 
6442 	*intval = val;
6443     }
6444     return 0;
6445 }
6446 
6447 int
ipmi_mr_bitvaltab_set_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)6448 ipmi_mr_bitvaltab_set_field(ipmi_mr_getset_t          *getset,
6449 			    enum ipmi_fru_data_type_e dtype,
6450 			    int                       intval,
6451 			    time_t                    time,
6452 			    double                    floatval,
6453 			    char                      *data,
6454 			    unsigned int              data_len)
6455 {
6456     unsigned char      *c = getset->rdata + getset->layout->start / 8;
6457     unsigned char      *end = getset->rdata + (getset->layout->start
6458 					       + getset->layout->length) / 8;
6459     int                val;
6460     int                shift = getset->layout->start % 8;
6461     int                offset = 8 - shift;
6462     unsigned char      mask1 = (~0) << shift;
6463     unsigned char      mask2 = (~0) << ((getset->layout->start
6464 					 + getset->layout->length) % 8);
6465     ipmi_mr_tab_item_t *tab = getset->layout->u.tab_data;
6466 
6467     if (dtype != getset->layout->dtype)
6468 	return EINVAL;
6469 
6470     for (val=0; val<(int)tab->count; val++) {
6471 	if (!tab->table[val])
6472 	    continue;
6473 	if (strcasecmp(data, tab->table[val]) == 0)
6474 	    break;
6475     }
6476     if (val == (int)tab->count)
6477 	return EINVAL;
6478 
6479     while (c != end) {
6480 	*c = (*c & ~mask1) | (val << shift);
6481 	val >>= offset;
6482 	mask1 = 0xff;
6483 	shift = 0;
6484 	offset = 8;
6485 	c++;
6486     }
6487     mask1 = ~mask1 | mask2;
6488     *c = (*c & mask1 ) | ((val << shift) & ~mask1);
6489 
6490     c = getset->rdata + getset->layout->start / 8;
6491     ipmi_fru_ovw_multi_record_data(getset->finfo->fru,
6492 				   getset->finfo->mr_rec_num, c,
6493 				   (ipmi_mr_full_offset(getset->offset)
6494 				    + (c - getset->rdata)),
6495 				   end - c + 1);
6496     return 0;
6497 }
6498 
6499 int
ipmi_mr_bitvaltab_get_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len)6500 ipmi_mr_bitvaltab_get_field(ipmi_mr_getset_t          *getset,
6501 			    enum ipmi_fru_data_type_e *dtype,
6502 			    int                       *intval,
6503 			    time_t                    *time,
6504 			    double                    *floatval,
6505 			    char                      **data,
6506 			    unsigned int              *data_len)
6507 {
6508     unsigned char      *c = getset->rdata + getset->layout->start / 8;
6509     unsigned char      *end = getset->rdata + (getset->layout->start
6510 					       + getset->layout->length) / 8;
6511     int                val = 0;
6512     int                offset = getset->layout->start % 8;
6513     int                shift = 8 - offset;
6514     unsigned int       mask = (~0) << getset->layout->length;
6515     const char         *str;
6516     ipmi_mr_tab_item_t *tab = getset->layout->u.tab_data;
6517 
6518     if (dtype)
6519 	*dtype = getset->layout->dtype;
6520 
6521     val = *c >> offset;
6522     while (c != end) {
6523 	c++;
6524 	val |= ((int) *c) << shift;
6525 	shift += 8;
6526     }
6527     val &= ~mask;
6528 
6529     if (val >= (int)tab->count)
6530 	str = "?";
6531     else if (!tab->table[val])
6532 	str = "?";
6533     else
6534 	str = tab->table[val];
6535     if (data_len)
6536 	*data_len = strlen(str);
6537     if (data) {
6538 	*data = ipmi_strdup(str);
6539 	if (!(*data))
6540 	    return ENOMEM;
6541     }
6542     return 0;
6543 }
6544 
6545 int
ipmi_mr_bitvaltab_get_enum(ipmi_mr_getset_t * getset,int * pos,int * nextpos,const char ** data)6546 ipmi_mr_bitvaltab_get_enum(ipmi_mr_getset_t *getset,
6547 			   int              *pos,
6548 			   int              *nextpos,
6549 			   const char       **data)
6550 {
6551     ipmi_mr_tab_item_t *tab = getset->layout->u.tab_data;
6552     int                p = *pos;
6553 
6554     if (p < 0) {
6555 	p = 0;
6556 	while ((p < (int) tab->count) && !tab->table[p])
6557 	    p++;
6558     }
6559 
6560     if (p > (int) tab->count)
6561 	return EINVAL;
6562 
6563     if (data) {
6564 	if (!tab->table[p])
6565 	    *data = "?";
6566 	else
6567 	    *data = tab->table[p];
6568     }
6569     *pos = p;
6570 
6571     if (nextpos) {
6572 	p++;
6573 	while ((p < (int) tab->count) && !tab->table[p])
6574 	    p++;
6575 	if (p >= (int) tab->count)
6576 	    *nextpos = -1;
6577 	else
6578 	    *nextpos = p;
6579     }
6580 
6581     return 0;
6582 }
6583 
6584 int
ipmi_mr_bitfloatvaltab_set_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)6585 ipmi_mr_bitfloatvaltab_set_field(ipmi_mr_getset_t          *getset,
6586 				 enum ipmi_fru_data_type_e dtype,
6587 				 int                       intval,
6588 				 time_t                    time,
6589 				 double                    floatval,
6590 				 char                      *data,
6591 				 unsigned int              data_len)
6592 {
6593     unsigned char           *c = getset->rdata + getset->layout->start / 8;
6594     unsigned char           *end = getset->rdata + (getset->layout->start
6595 						 + getset->layout->length) / 8;
6596     int                     val;
6597     int                     shift = getset->layout->start % 8;
6598     int                     offset = 8 - shift;
6599     unsigned char           mask1 = (~0) << shift;
6600     unsigned char           mask2 = (~0) << ((getset->layout->start
6601 					      + getset->layout->length) % 8);
6602     ipmi_mr_floattab_item_t *tab = getset->layout->u.tab_data;
6603 
6604     if (dtype != getset->layout->dtype)
6605 	return EINVAL;
6606 
6607     for (val=0; val<(int)tab->count; val++) {
6608 	if ((floatval >= tab->table[val].low)
6609 	    && (floatval <= tab->table[val].high))
6610 	    break;
6611     }
6612     if (val == (int)tab->count)
6613 	return EINVAL;
6614 
6615     while (c != end) {
6616 	*c = (*c & ~mask1) | (val << shift);
6617 	val >>= offset;
6618 	mask1 = 0xff;
6619 	shift = 0;
6620 	offset = 8;
6621 	c++;
6622     }
6623     mask1 = ~mask1 | mask2;
6624     *c = (*c & mask1 ) | ((val << shift) & ~mask1);
6625 
6626     c = getset->rdata + getset->layout->start / 8;
6627     ipmi_fru_ovw_multi_record_data(getset->finfo->fru,
6628 				   getset->finfo->mr_rec_num, c,
6629 				   (ipmi_mr_full_offset(getset->offset)
6630 				    + (c - getset->rdata)),
6631 				   end - c + 1);
6632     return 0;
6633 }
6634 
6635 int
ipmi_mr_bitfloatvaltab_get_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len)6636 ipmi_mr_bitfloatvaltab_get_field(ipmi_mr_getset_t          *getset,
6637 				 enum ipmi_fru_data_type_e *dtype,
6638 				 int                       *intval,
6639 				 time_t                    *time,
6640 				 double                    *floatval,
6641 				 char                      **data,
6642 				 unsigned int              *data_len)
6643 {
6644     unsigned char           *c = getset->rdata + getset->layout->start / 8;
6645     unsigned char           *end = getset->rdata + (getset->layout->start
6646 						 + getset->layout->length) / 8;
6647     int                     val = 0;
6648     int                     offset = getset->layout->start % 8;
6649     int                     shift = 8 - offset;
6650     unsigned int            mask = (~0) << getset->layout->length;
6651     ipmi_mr_floattab_item_t *tab = getset->layout->u.tab_data;
6652 
6653     if (dtype)
6654 	*dtype = getset->layout->dtype;
6655 
6656     if (floatval) {
6657 	val = *c >> offset;
6658 	while (c != end) {
6659 	    c++;
6660 	    val |= ((int) *c) << shift;
6661 	    shift += 8;
6662 	}
6663 	val &= ~mask;
6664 
6665 	if (val >= (int)tab->count)
6666 	    *floatval = tab->defval;
6667 	else
6668 	    *floatval = tab->table[val].nominal;
6669     }
6670     return 0;
6671 }
6672 
6673 int
ipmi_mr_bitfloatvaltab_get_enum(ipmi_mr_getset_t * getset,int * pos,int * nextpos,const char ** data)6674 ipmi_mr_bitfloatvaltab_get_enum(ipmi_mr_getset_t *getset,
6675 				int              *pos,
6676 				int              *nextpos,
6677 				const char       **data)
6678 {
6679     ipmi_mr_floattab_item_t *tab = getset->layout->u.tab_data;
6680     int                     p = *pos;
6681 
6682     if (p < 0) {
6683 	p = 0;
6684 	while ((p < (int) tab->count) && !tab->table[p].nominal_str)
6685 	    p++;
6686     }
6687 
6688     if (p > (int) tab->count)
6689 	return EINVAL;
6690 
6691     if (data) {
6692 	if (!tab->table[p].nominal_str)
6693 	    *data = "?";
6694 	else
6695 	    *data = tab->table[p].nominal_str;
6696     }
6697 
6698     if (nextpos) {
6699 	p++;
6700 	while ((p < (int) tab->count) && !tab->table[p].nominal_str)
6701 	    p++;
6702 	if (p >= (int) tab->count)
6703 	    *nextpos = -1;
6704 	else
6705 	    *nextpos = p;
6706     }
6707     return 0;
6708 }
6709 
6710 int
ipmi_mr_str_set_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)6711 ipmi_mr_str_set_field(ipmi_mr_getset_t          *getset,
6712 		      enum ipmi_fru_data_type_e dtype,
6713 		      int                       intval,
6714 		      time_t                    time,
6715 		      double                    floatval,
6716 		      char                      *data,
6717 		      unsigned int              data_len)
6718 {
6719     unsigned char        *c = getset->rdata + getset->layout->start;
6720     enum ipmi_str_type_e stype;
6721     unsigned int         len;
6722 
6723     if (!data)
6724 	return ENOSYS;
6725     switch (dtype) {
6726     case IPMI_FRU_DATA_ASCII: stype = IPMI_ASCII_STR; break;
6727     case IPMI_FRU_DATA_BINARY: stype = IPMI_UNICODE_STR; break;
6728     case IPMI_FRU_DATA_UNICODE: stype = IPMI_BINARY_STR; break;
6729     default:
6730 	return EINVAL;
6731     }
6732     memset(c, 0, getset->layout->length);
6733     len = getset->layout->length;
6734     ipmi_set_device_string2(data, stype, data_len, c, 0, &len,
6735 			    ipmi_fru_get_options(getset->finfo->fru));
6736     ipmi_fru_ovw_multi_record_data(getset->finfo->fru,
6737 				   getset->finfo->mr_rec_num, c,
6738 				   (ipmi_mr_full_offset(getset->offset)
6739 				    + getset->layout->start),
6740 				   getset->layout->length);
6741     return 0;
6742 }
6743 
6744 int
ipmi_mr_str_get_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len)6745 ipmi_mr_str_get_field(ipmi_mr_getset_t          *getset,
6746 		      enum ipmi_fru_data_type_e *dtype,
6747 		      int                       *intval,
6748 		      time_t                    *time,
6749 		      double                    *floatval,
6750 		      char                      **data,
6751 		      unsigned int              *data_len)
6752 {
6753     unsigned char        *c = getset->rdata + getset->layout->start;
6754     char                 str[64];
6755     unsigned int         len;
6756     enum ipmi_str_type_e type;
6757     int                  rv;
6758 
6759     rv = ipmi_get_device_string(&c, getset->layout->length, str,
6760 				IPMI_STR_FRU_SEMANTICS, 0,
6761 				&type, sizeof(str), &len);
6762     if (rv)
6763 	return rv;
6764 
6765     if (dtype) {
6766 	switch (type) {
6767 	case IPMI_ASCII_STR: *dtype = IPMI_FRU_DATA_ASCII; break;
6768 	case IPMI_UNICODE_STR: *dtype = IPMI_FRU_DATA_UNICODE; break;
6769 	case IPMI_BINARY_STR: *dtype = IPMI_FRU_DATA_BINARY; break;
6770 	}
6771     }
6772     if (data_len)
6773 	*data_len = len;
6774     if (data) {
6775 	if (type == IPMI_ASCII_STR)
6776 	    len += 1;
6777 	else if (len == 0)
6778 	    len = 1;
6779 	*data = ipmi_mem_alloc(len);
6780 	if (!(*data))
6781 	    return ENOMEM;
6782 	if (type == IPMI_ASCII_STR) {
6783 	    memcpy(*data, str, len-1);
6784 	    (*data)[len-1] = '\0';
6785 	} else
6786 	    memcpy(*data, str, len);
6787     }
6788     return 0;
6789 }
6790 
6791 int
ipmi_mr_binary_set_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)6792 ipmi_mr_binary_set_field(ipmi_mr_getset_t          *getset,
6793 			 enum ipmi_fru_data_type_e dtype,
6794 			 int                       intval,
6795 			 time_t                    time,
6796 			 double                    floatval,
6797 			 char                      *data,
6798 			 unsigned int              data_len)
6799 {
6800     unsigned char        *c = getset->rdata + getset->layout->start;
6801 
6802     if (!data)
6803 	return ENOSYS;
6804     if (dtype != getset->layout->dtype)
6805 	return EINVAL;
6806     if (data_len > getset->layout->length)
6807 	return EINVAL;
6808 
6809     memcpy(c, data, data_len);
6810     ipmi_fru_ovw_multi_record_data(getset->finfo->fru,
6811 				   getset->finfo->mr_rec_num, c,
6812 				   (ipmi_mr_full_offset(getset->offset)
6813 				    + getset->layout->start),
6814 				   data_len);
6815     return 0;
6816 }
6817 
6818 int
ipmi_mr_binary_get_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len)6819 ipmi_mr_binary_get_field(ipmi_mr_getset_t          *getset,
6820 			 enum ipmi_fru_data_type_e *dtype,
6821 			 int                       *intval,
6822 			 time_t                    *time,
6823 			 double                    *floatval,
6824 			 char                      **data,
6825 			 unsigned int              *data_len)
6826 {
6827     unsigned char *c = getset->rdata + getset->layout->start;
6828 
6829     if (dtype)
6830 	*dtype = IPMI_FRU_DATA_BINARY;
6831     if (data_len)
6832 	*data_len = getset->layout->length;
6833     if (data) {
6834 	*data = ipmi_mem_alloc(getset->layout->length);
6835 	if (!(*data))
6836 	    return ENOMEM;
6837 	memcpy(*data, c, getset->layout->length);
6838     }
6839     return 0;
6840 }
6841 
6842 int
ipmi_mr_ip_set_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e dtype,int intval,time_t time,double floatval,char * data,unsigned int data_len)6843 ipmi_mr_ip_set_field(ipmi_mr_getset_t          *getset,
6844 		     enum ipmi_fru_data_type_e dtype,
6845 		     int                       intval,
6846 		     time_t                    time,
6847 		     double                    floatval,
6848 		     char                      *data,
6849 		     unsigned int              data_len)
6850 {
6851     unsigned char  *c = getset->rdata + getset->layout->start;
6852     void           *addr;
6853     int            addr_len;
6854     int            af;
6855     struct in_addr ip_addr;
6856     int            rv;
6857 
6858     if (dtype != IPMI_FRU_DATA_ASCII)
6859 	return EINVAL;
6860 
6861     if (strncmp(data, "ip:", 3) == 0) {
6862 	af = AF_INET;
6863 	data += 3;
6864 	addr = &ip_addr;
6865 	addr_len = sizeof(ip_addr);
6866     } else
6867 	return EINVAL;
6868 
6869     rv = inet_pton(af, data, addr);
6870     if (rv <= 0)
6871 	return EINVAL;
6872     memcpy(c, addr, addr_len);
6873     ipmi_fru_ovw_multi_record_data(getset->finfo->fru,
6874 				   getset->finfo->mr_rec_num, c,
6875 				   (ipmi_mr_full_offset(getset->offset)
6876 				    + getset->layout->start),
6877 				   addr_len);
6878 
6879     return 0;
6880 }
6881 
6882 int
ipmi_mr_ip_get_field(ipmi_mr_getset_t * getset,enum ipmi_fru_data_type_e * dtype,int * intval,time_t * time,double * floatval,char ** data,unsigned int * data_len)6883 ipmi_mr_ip_get_field(ipmi_mr_getset_t          *getset,
6884 		     enum ipmi_fru_data_type_e *dtype,
6885 		     int                       *intval,
6886 		     time_t                    *time,
6887 		     double                    *floatval,
6888 		     char                      **data,
6889 		     unsigned int              *data_len)
6890 {
6891     unsigned char *c = getset->rdata + getset->layout->start;
6892     char          ipstr[19]; /* worst case size */
6893     int           len;
6894 
6895     sprintf(ipstr, "ip:%d.%d.%d.%d", c[0], c[1], c[2], c[3]);
6896     len = strlen(ipstr);
6897     if (dtype)
6898 	*dtype = IPMI_FRU_DATA_ASCII;
6899     if (data_len)
6900 	*data_len = len;
6901     if (data) {
6902 	*data = ipmi_strdup(ipstr);
6903 	if (!(*data))
6904 	    return ENOMEM;
6905     }
6906     return 0;
6907 }
6908 
6909 /************************************************************************
6910  *
6911  * Cruft
6912  *
6913  ************************************************************************/
6914 
6915 int
ipmi_fru_get_internal_use_data(ipmi_fru_t * fru,unsigned char * data,unsigned int * max_len)6916 ipmi_fru_get_internal_use_data(ipmi_fru_t    *fru,
6917 			       unsigned char *data,
6918 			       unsigned int  *max_len)
6919 {
6920     return ipmi_fru_get_internal_use(fru, data, max_len);
6921 }
6922 
6923 int
ipmi_fru_get_internal_use_length(ipmi_fru_t * fru,unsigned int * length)6924 ipmi_fru_get_internal_use_length(ipmi_fru_t   *fru,
6925 				 unsigned int *length)
6926 {
6927     return ipmi_fru_get_internal_use_len(fru, length);
6928 }
6929