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