1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * These routines in this file are used to interact with SMC driver to
31 * read and write FRUID data
32 */
33
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <stdarg.h>
39 #include <synch.h>
40 #include <thread.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <syslog.h>
45 #include <stropts.h>
46 #include <poll.h>
47 #include <smclib.h>
48 #include "fru_access_impl.h"
49
50 #define POLL_TIMEOUT 10000
51 #define FRUID_CHECK_POLL_TIMEOUT 5000
52 #define SIZE_TO_READ_WRITE 20
53
54 /* IPMI fru spec Storage definition version 1.0, rev 1.1 */
55 #define IPMI_COMMON_HEADER_SIZE 8
56 #define IPMI_VERSION 1
57 #define CMN_HDR_VERSION_MASK 0x0
58 #define CMN_HDR_OFFSET 0x0
59 #define BD_MFR_OFFSET 6
60 #define BD_FIELDS_SIZE 6
61 #define AREA_TERMINATION_INDICATOR 0xc1
62
63 /* type encoding */
64 #define BINARY_TYPE 0x0
65 #define BCDPLUS_TYPE 0x1
66 #define SIX_BITASCII_TYPE 0x2
67 #define UNICODE_TYPE 0x3
68
69 /* for ascii conversion */
70 #define ASCII_MAP 0x20
71 #define BIT_MASK1 0x3f
72 #define BIT_MASK2 0x0f
73 #define BIT_MASK3 0x03
74
75 #define SUN_NAME "SUN MICROSYSTEMS, INC."
76 #define SUN_JEDEC_CODE 0x3e
77 #define MANR_MAX_LENGTH 80
78 #define FRU_DATA_MAX_SIZE 100
79
80 /* IPMI commands */
81 #define IPMI_GET_DEVICE_ID 0x1
82 #define FRU_DEVICE_ID 0x0
83 #define READ_FRU_INVENTORY_INFO 0x10
84 #define READ_FRU_INVENTORY_DATA 0x11
85 #define WRITE_FRU_INVENTORY_DATA 0x12
86
87 #define TMP_BUFFER_SIZE 10
88 #define BYTE_TO_READ_SUN_CHK 5
89
90 typedef struct {
91 uint8_t internal; /* internal use area */
92 uint8_t chassis; /* chassis info area */
93 uint8_t board; /* board area */
94 uint8_t product; /* product info area */
95 uint8_t records; /* multirecord area */
96 } fruid_offset_t;
97
98 extern void get_fru_data_info(int, int, format_t *);
99 static void convert_to_ascii(uint8_t [], uint8_t [], int, int);
100 static void bcdplus_to_ascii(uint8_t [], uint8_t [], int);
101 static time_t get_utc_time(uint8_t []);
102 static uint8_t cpu_no = 0;
103
104 /*
105 * Routine to read FRUID information from BMC
106 */
107 static int
get_alarm_fru_data(int offset,int size,void * buffer,format_t * format)108 get_alarm_fru_data(int offset, int size, void *buffer, format_t *format)
109 {
110 uint8_t datap[5];
111 sc_reqmsg_t req_pkt;
112 sc_rspmsg_t res_pkt;
113
114 if (buffer == NULL) {
115 return (-1);
116 }
117 bzero(buffer, size);
118
119 datap[0] = 0x7; /* bus id */
120 datap[1] = 0xa0; /* slave address */
121 datap[2] = size; /* count */
122 datap[3] = offset >> 8; /* MSB */
123 datap[4] = (uint8_t)offset; /* LSB */
124
125 (void) smc_init_ipmi_msg(&req_pkt, SMC_MASTER_WR_RD_I2C,
126 FRUACCESS_MSG_ID, 5, datap, DEFAULT_SEQN, format->dest,
127 SMC_NETFN_APP_REQ, SMC_BMC_LUN);
128
129 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
130 POLL_TIMEOUT) != SMC_SUCCESS) {
131 return (-1);
132 }
133 /* check the completion code */
134 if (res_pkt.data[7] != 0) {
135 return (-1);
136 }
137
138 (void) memcpy(buffer, &(res_pkt.data[8]), size);
139 return (0);
140 }
141
142 /*
143 * Routine to read FRUID information from other boards
144 */
145 static int
get_fru_data(int offset,int size,void * buffer,format_t * format)146 get_fru_data(int offset, int size, void *buffer, format_t *format)
147 {
148 sc_reqmsg_t req_pkt;
149 sc_rspmsg_t res_pkt;
150 uint8_t datap[4];
151 int ipmi = 0;
152
153 if (buffer == NULL) {
154 return (-1);
155 }
156
157 /* figure out if onboard access or ipmb access */
158 if (format->src == format->dest) {
159 ipmi = 0;
160 } else {
161 ipmi = 1;
162 }
163
164 switch (ipmi) {
165
166 case 0: /* on board info (local i2c) */
167
168 SC_MSG_CMD(&req_pkt) = SMC_EEPROM_READ;
169 SC_MSG_LEN(&req_pkt) = 4;
170 SC_MSG_ID(&req_pkt) = FRUACCESS_MSG_ID;
171
172 /* data field for request */
173 req_pkt.data[0] = format->sun_device_id; /* device id */
174 req_pkt.data[1] = (uint8_t)offset; /* (LSB) */
175 req_pkt.data[3] = size;
176
177 if (format->format == SUN_FORMAT) {
178 req_pkt.data[2] = offset >> 8;
179 } else {
180 req_pkt.data[2] = 0x0; /* (MSB) always 0x0 for IPMI */
181 }
182
183 /* make a call to smc library to send cmd */
184 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
185 POLL_TIMEOUT) != SMC_SUCCESS) {
186 return (-1);
187 }
188
189 if (SC_MSG_LEN(&res_pkt) != size) {
190 return (-1);
191 }
192 (void) memcpy(buffer, res_pkt.data, size);
193 return (0);
194
195 default:
196
197 /* data for request packet */
198 datap[0] = format->sun_device_id; /* device id */
199 datap[1] = (uint8_t)offset; /* LSB */
200 datap[3] = size; /* bytes to read */
201 if (format->format == SUN_FORMAT) {
202 datap[2] = offset >> 8;
203 } else {
204 datap[2] = 0x0; /* (MSB) always 0x0 for IPMI */
205 }
206
207 (void) smc_init_ipmi_msg(&req_pkt, READ_FRU_INVENTORY_DATA,
208 FRUACCESS_MSG_ID, 4, datap, DEFAULT_SEQN,
209 format->dest, SMC_NETFN_STORAGE_REQ, format->sun_lun);
210
211 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
212 POLL_TIMEOUT) != SMC_SUCCESS) {
213 return (-1);
214 }
215 /* check the completion code */
216 if (res_pkt.data[7] != 0) {
217 return (-1);
218 }
219
220 /* check the size */
221 if (res_pkt.data[8] != size) {
222 return (-1);
223 }
224
225 (void) memcpy(buffer, &(res_pkt.data[9]), size);
226 return (0);
227 }
228 }
229
230 /*
231 * routine to read the IPMI common header field
232 */
233 static int
read_common_header(fruid_offset_t * offset,format_t * format)234 read_common_header(fruid_offset_t *offset, format_t *format)
235 {
236 int ret = 0;
237 uint8_t data[FRU_DATA_MAX_SIZE];
238
239 ret = get_fru_data(CMN_HDR_OFFSET, IPMI_COMMON_HEADER_SIZE, data,
240 format);
241 if (ret < 0) {
242 return (-1);
243 }
244
245 /* version check */
246 if ((data[0] | CMN_HDR_VERSION_MASK) != 1) {
247 return (-1);
248 }
249
250 offset->internal = data[1] * 8;
251 offset->chassis = data[2] * 8;
252 offset->board = data[3] * 8;
253 offset->product = data[4] * 8;
254 offset->records = data[5] * 8;
255
256 return (0);
257 }
258
259 /*
260 * Read the values of each field based on FORMAT
261 */
262 /* ARGSUSED */
263 static int
read_bd_fields(uint8_t * field,int offset,format_t * format)264 read_bd_fields(uint8_t *field, int offset, format_t *format)
265 {
266
267 int ret, encode_type = 0x0, len, length, extra_bytes, alloc_size;
268 uint8_t *store;
269 uint8_t data[FRU_DATA_MAX_SIZE];
270
271 bzero(field, MANR_MAX_LENGTH);
272
273 ret = get_fru_data(offset, BD_FIELDS_SIZE, data, format);
274 if (ret < 0) {
275 return (-1);
276 }
277
278 if (data[0] == AREA_TERMINATION_INDICATOR) {
279 return (0);
280 }
281
282 encode_type = data[0] >> 6;
283 len = data[0] & 0x3f;
284 if (len <= 0) {
285 return (0);
286 }
287
288 ret = get_fru_data(offset+1, len, data, format);
289 if (ret < 0) {
290 return (-1);
291 }
292
293 switch (encode_type) {
294
295 case SIX_BITASCII_TYPE:
296
297 length = len - (len % 3);
298 extra_bytes = len % 3;
299 alloc_size = ((length/3) * 4) + extra_bytes;
300 store = (uint8_t *)malloc(sizeof (uint8_t) * alloc_size);
301 if (store == NULL) {
302 return (-1);
303 }
304 convert_to_ascii(data, store, len, extra_bytes);
305 break;
306
307 case BCDPLUS_TYPE:
308
309 alloc_size = len * 2;
310 store = (uint8_t *)malloc(sizeof (uint8_t) * alloc_size);
311 if (store == NULL) {
312 return (-1);
313 }
314
315 bcdplus_to_ascii(data, store, len);
316 break;
317
318 case BINARY_TYPE:
319 case UNICODE_TYPE:
320 default:
321 return (-1);
322 }
323
324 (void) memcpy(field, store, alloc_size);
325 free(store);
326 return (len);
327 }
328
329 static int
read_board_info(uint8_t board_offset,payload_t * manr,format_t * format)330 read_board_info(uint8_t board_offset, payload_t *manr, format_t *format)
331 {
332 time_t time;
333 uint8_t *buffer;
334 uint8_t mfg_time[4];
335 uint8_t data[FRU_DATA_MAX_SIZE];
336 int ret = 0, current_offset = 0x0;
337 int bd_area_len = 0;
338
339 /* read version, length, lang code, mfg. time */
340 ret = get_fru_data(board_offset, BD_FIELDS_SIZE, data, format);
341
342 if (ret < 0) {
343 return (-1);
344 }
345
346 /* version check */
347 if ((data[0] | CMN_HDR_VERSION_MASK) != 1) {
348 return (-1);
349 }
350
351 /* byte 2 is lang code */
352 bd_area_len = data[1] * 8;
353 mfg_time[3] = data[3];
354 mfg_time[2] = data[4];
355 mfg_time[1] = data[5];
356 mfg_time[0] = 0x0;
357 time = get_utc_time(mfg_time);
358
359 /* fill the timestamp into manr */
360 (void) memcpy(manr->timestamp, &time, MANR_TIME_LEN);
361
362 if (bd_area_len < BD_MFR_OFFSET) {
363 return (-1);
364 }
365 buffer = (uint8_t *)malloc(sizeof (uint8_t) * MANR_MAX_LENGTH);
366 if (buffer == NULL) {
367 return (-1);
368 }
369
370 /* read the board info */
371 current_offset += board_offset + BD_MFR_OFFSET;
372 current_offset += read_bd_fields(buffer, current_offset, format);
373
374 if (strncmp(SUN_NAME, (char *)buffer, sizeof (SUN_NAME)) == 0) {
375 manr->vendor_name[0] = 0x00;
376 manr->vendor_name[1] = 0x3e;
377 } else {
378 manr->vendor_name[0] = 0x00;
379 manr->vendor_name[1] = 0x00;
380 }
381
382 current_offset += 1; /* for length/type field */
383
384 current_offset += read_bd_fields(buffer, current_offset, format);
385 current_offset += 1; /* for length/type field */
386 (void) memcpy(manr->fru_short_name, buffer, MANR_FRUNAME_LEN);
387
388 current_offset += read_bd_fields(buffer, current_offset, format);
389 current_offset += 1; /* for length/type field */
390 (void) memcpy(manr->sun_serial_no, buffer, MANR_SERIALNUM_LEN);
391
392 current_offset += read_bd_fields(buffer, current_offset, format);
393 current_offset += 1; /* for length/type field */
394 (void) memcpy(manr->sun_part_no, buffer, MANR_PARTNUM_LEN);
395
396 /*
397 * We dont need the FRU FILE ID, so just skip the field
398 * and get the offset to read the custom MFG. info fields
399 */
400 current_offset += read_bd_fields(buffer, current_offset, format);
401 current_offset += 1; /* for length/type field */
402
403 current_offset += read_bd_fields(buffer, current_offset, format);
404 current_offset += 1; /* for length/type field */
405
406 /* read the custom mfg. info fields */
407 current_offset += read_bd_fields(buffer, current_offset, format);
408 current_offset += 1; /* for length/type field */
409 (void) memcpy(manr->manufacture_loc, buffer, MANR_MFRLOC_LEN);
410
411 current_offset += read_bd_fields(buffer, current_offset, format);
412 (void) memcpy(manr->fru_descr, buffer, MANR_FRUDESCR_LEN);
413
414 free(buffer);
415 return (0);
416 }
417
418 /*
419 * Read the IPMI information from hardware and translate it into
420 * MANR(SUN format)
421 */
422 int
get_manr(format_t * format,payload_t * manr)423 get_manr(format_t *format, payload_t *manr)
424 {
425 int ret = 0;
426 fruid_offset_t *offset = NULL;
427
428 offset = (fruid_offset_t *)malloc(sizeof (fruid_offset_t));
429 if (offset == NULL) {
430 return (-1);
431 }
432
433 ret = read_common_header(offset, format);
434 if (ret != 0) {
435 free(offset);
436 return (-1);
437 }
438
439 if (offset->board != 0) {
440 ret = read_board_info(offset->board, manr, format);
441 }
442
443 free(offset);
444 return (ret);
445 }
446
447 static void
convert_to_ascii(uint8_t data[],uint8_t store[],int length,int extra_bytes)448 convert_to_ascii(uint8_t data [], uint8_t store[],
449 int length, int extra_bytes)
450 {
451 uint8_t x, y;
452 int index = 0;
453 int i, idx = length - (length % 3);
454
455 for (i = 0; ; i += 3) {
456
457 x = 0x0;
458 y = 0x0;
459
460 if (i == idx && extra_bytes == 0) {
461 break;
462 }
463
464 /* get the first six bits */
465 x = (data[i] & BIT_MASK1);
466 x += ASCII_MAP;
467 store[index] = x;
468
469 if (i == idx && extra_bytes == 1) {
470 break;
471 }
472
473 /*
474 * get last 2 bits of first byte and first
475 * 4 bits of second byte
476 */
477
478 x = (data[i] >> 6);
479 y = (data[i + 1] & BIT_MASK2) << 2;
480 x |= y + ASCII_MAP;
481 store[index+1] = x;
482
483 if (i == idx) {
484 break;
485 }
486
487 /* get last 4 bits of second byte and 2 bits of last byte */
488 x = data[i + 1] >> 4;
489 y = (data[i + 2] & BIT_MASK3) << 4;
490 x |= y + ASCII_MAP;
491 store[index+2] = x;
492
493 /* get last six bits of third byte */
494 store[index + 3] = (data[i + 2] >> 2) + ASCII_MAP;
495 index += 4;
496 }
497 }
498
499 static void
bcdplus_to_ascii(uint8_t data[],uint8_t store[],int len)500 bcdplus_to_ascii(uint8_t data[], uint8_t store[], int len)
501 {
502 int i, j, index = 0;
503 uint8_t tmp = 0;
504
505 struct {
506 int a:4;
507 int b:4;
508 } val;
509
510 for (i = 0; i < len; i++) {
511 (void) memcpy(&val, &data[i], 1);
512 for (j = 0; j < 2; j++) {
513 if (j == 0) {
514 tmp = val.a;
515 } else
516 tmp = val.b;
517
518 if (tmp <= 9) {
519 /* ascii conversion */
520 store[index++] = tmp + 48;
521 continue;
522 }
523
524 switch (tmp) {
525
526 case 0xa:
527 store[index++] = ' ';
528 break;
529 case 0xb:
530 store[index++] = '-';
531 break;
532 case 0xc:
533 store[index++] = '.';
534 break;
535 default:
536 store[index++] = ' ';
537 }
538 }
539 }
540 }
541
542 /* converts ipmi format time to UTC time (unix 32 bit timestamp) */
543 static time_t
get_utc_time(uint8_t data[])544 get_utc_time(uint8_t data [])
545 {
546 time_t time;
547 struct tm tm1;
548 uint32_t ipmi_time;
549
550 (void) memcpy(&ipmi_time, data, 4);
551
552 ipmi_time *= 60; /* convert into seconds */
553
554 /* get UTC time for 0:00 1/1/96 (ipmi epoch) */
555 tm1.tm_sec = 0;
556 tm1.tm_min = 0;
557 tm1.tm_hour = 0;
558 tm1.tm_mday = 1;
559 tm1.tm_mon = 0;
560 tm1.tm_year = 96;
561
562 time = mktime(&tm1);
563 time += ipmi_time;
564
565 return (time);
566 }
567
568 /*
569 * routine to write information to BMC
570 */
571 static int
write_alarm_fru_data(const void * buffer,size_t size,off_t offset,format_t * format)572 write_alarm_fru_data(const void *buffer, size_t size,
573 off_t offset, format_t *format)
574 {
575 sc_reqmsg_t req_pkt;
576 sc_rspmsg_t res_pkt;
577 uint8_t *datap = NULL;
578
579 if (buffer == NULL) {
580 return (-1);
581 }
582 datap = (uint8_t *)malloc(sizeof (uint8_t) * (size + 5));
583 if (datap == NULL) {
584 return (-1);
585 }
586
587 datap[0] = 0x7; /* bus id */
588 datap[1] = 0xa0; /* slave address */
589 datap[2] = 0; /* count */
590 datap[3] = offset >> 8; /* MSB */
591 datap[4] = (uint8_t)offset; /* LSB */
592 (void) memcpy((void *)&(datap[5]), buffer, size);
593
594 /* initialize ipmi request packet */
595 (void) smc_init_ipmi_msg(&req_pkt, SMC_MASTER_WR_RD_I2C,
596 FRUACCESS_MSG_ID, (5 + size), datap, DEFAULT_SEQN,
597 format->dest, SMC_NETFN_APP_REQ, SMC_BMC_LUN);
598 free(datap);
599
600 /* send ipmi request packet */
601 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
602 POLL_TIMEOUT) != SMC_SUCCESS) {
603 return (-1);
604 }
605 /* check the completion code */
606 if (res_pkt.data[7] != 0) {
607 return (-1);
608 }
609 return (0);
610 }
611
612 static int
write_fru_data(const void * buffer,size_t size,off_t offset,format_t * format)613 write_fru_data(const void *buffer, size_t size,
614 off_t offset, format_t *format)
615 {
616 int ipmi = 0;
617 sc_reqmsg_t req_pkt;
618 sc_rspmsg_t res_pkt;
619 uint8_t *datap = NULL;
620
621 if (buffer == NULL) {
622 return (-1);
623 }
624
625 if (format->src == format->dest) {
626 ipmi = 0;
627 } else {
628 ipmi = 1;
629 }
630
631 switch (ipmi) {
632
633 case 0: /* on board info (local i2c) */
634
635 SC_MSG_CMD(&req_pkt) = SMC_EEPROM_WRITE;
636 SC_MSG_LEN(&req_pkt) = 4 + size;
637 SC_MSG_ID(&req_pkt) = FRUACCESS_MSG_ID;
638
639 /* data field for request */
640 req_pkt.data[0] = format->sun_device_id; /* device id */
641 req_pkt.data[1] = offset; /* (LSB) */
642 req_pkt.data[3] = size;
643 if (format->format == SUN_FORMAT) {
644 req_pkt.data[2] = offset >> 8;
645 } else {
646 req_pkt.data[2] = 0x0; /* (MSB) always 0x0 for IPMI */
647 }
648 (void) memcpy((void *)&(req_pkt.data[4]), buffer, size);
649
650 /* make a call to smc library to send cmd */
651 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
652 POLL_TIMEOUT) != SMC_SUCCESS) {
653 return (-1);
654 }
655 break;
656
657 default: /* read data from remote device (ipmi) */
658 datap = (uint8_t *)malloc(sizeof (uint8_t) * (size + 4));
659 if (datap == NULL) {
660 return (-1);
661 }
662
663 datap[0] = format->sun_device_id; /* device id */
664 datap[1] = offset; /* LSB */
665 datap[3] = size; /* nbytes */
666 if (format->format == SUN_FORMAT) {
667 datap[2] = offset >> 8;
668 } else {
669 datap[2] = 0x0; /* (MSB) always 0x0 for IPMI */
670 }
671 (void) memcpy((void *)&(datap[4]), buffer, size);
672
673 (void) smc_init_ipmi_msg(&req_pkt, WRITE_FRU_INVENTORY_DATA,
674 FRUACCESS_MSG_ID, (4 + size), datap, DEFAULT_SEQN,
675 format->dest, SMC_NETFN_STORAGE_REQ, format->sun_lun);
676 free(datap);
677
678 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
679 POLL_TIMEOUT) != SMC_SUCCESS) {
680 return (-1);
681 }
682 /* check the completion code */
683 if (res_pkt.data[7] != 0) {
684 return (-1);
685 }
686 break;
687 } /* end of switch */
688 return (0);
689 }
690
691 /*
692 * This routine splits the data to write into smaller chunks and
693 * write it to FRUID chip using SMC drv APIs
694 */
695
696 /* ARGSUSED */
697 ssize_t
pwrite_new(int fd,const void * buffer,size_t size,off_t offset,format_t * format)698 pwrite_new(int fd, const void *buffer, size_t size,
699 off_t offset, format_t *format)
700 {
701 int ret;
702 int index = 0;
703 size_t bytes = 0;
704 off_t next_offset = 0x0;
705 off_t curr_offset = offset;
706 size_t bytes_to_write = size;
707 uint8_t *data;
708 int retry = 3;
709 int (* func_ptr)(const void *, size_t, off_t, format_t *);
710
711 if (format->dest == 0x20) {
712 func_ptr = write_alarm_fru_data;
713 } else {
714 func_ptr = write_fru_data;
715 }
716
717 data = (uint8_t *)buffer;
718 while (bytes_to_write != 0) {
719
720 retry = 3;
721 ret = 1;
722
723 if (bytes_to_write > SIZE_TO_READ_WRITE) {
724 bytes = SIZE_TO_READ_WRITE;
725 next_offset = curr_offset + SIZE_TO_READ_WRITE;
726 } else {
727 bytes = bytes_to_write;
728 }
729
730 bytes_to_write = bytes_to_write - bytes;
731 while ((ret != 0) && (retry != 0)) {
732 ret = (*func_ptr)((void *)&data[index],
733 bytes, curr_offset, format);
734 retry--;
735 }
736 if (ret != 0) {
737 return (ret);
738 }
739 index = index + bytes;
740 curr_offset = next_offset;
741 }
742 return (size);
743 }
744
745 /*
746 * This routine reads the data in smaller chunks and
747 * sends it to upper layer(frudata plugin) in the sw stack
748 */
749 /* ARGSUSED */
750 ssize_t
pread_new(int fd,void * buffer,size_t size,off_t offset,format_t * format)751 pread_new(int fd, void *buffer, size_t size,
752 off_t offset, format_t *format)
753 {
754 int ret;
755 int index = 0;
756 size_t bytes = 0;
757 off_t next_offset = 0x0;
758 off_t curr_offset = offset;
759 size_t bytes_to_read = size;
760 uint8_t *data;
761 int retry = 3;
762 int (* func_ptr)(int, int, void *, format_t *);
763
764 if (format->dest == 0x20) {
765 func_ptr = get_alarm_fru_data;
766 } else {
767 func_ptr = get_fru_data;
768 }
769
770 data = (uint8_t *)buffer;
771
772 while (bytes_to_read != 0) {
773
774 retry = 3;
775 ret = 1;
776
777 if (bytes_to_read > SIZE_TO_READ_WRITE) {
778 bytes = SIZE_TO_READ_WRITE;
779 next_offset = curr_offset + SIZE_TO_READ_WRITE;
780 } else {
781 bytes = bytes_to_read;
782 }
783
784 bytes_to_read = bytes_to_read - bytes;
785
786 while ((ret != 0) && (retry != 0)) {
787 ret = (* func_ptr)(curr_offset, bytes,
788 (void *) &data[index], format);
789 retry--;
790 }
791 if (ret != 0) {
792 return (ret);
793 }
794 index = index + bytes;
795 curr_offset = next_offset;
796 }
797 return (size);
798 }
799
800 /*
801 * routine to check if IPMI fruid info is available,
802 * return 0: IPMI fruid not present
803 * return 1: IPMI fruid present
804 */
805 static int
is_ipmi_fru_data_available(int src,int dest)806 is_ipmi_fru_data_available(int src, int dest)
807 {
808 sc_reqmsg_t req_pkt;
809 sc_rspmsg_t res_pkt;
810 uint8_t datap[5];
811
812 /* on board access */
813 if (src == dest) {
814
815 SC_MSG_CMD(&req_pkt) = SMC_EEPROM_READ;
816 SC_MSG_LEN(&req_pkt) = 4;
817 SC_MSG_ID(&req_pkt) = FRUACCESS_MSG_ID;
818
819 /* data field for request */
820 req_pkt.data[0] = 0x0; /* eeprom number (ipmi format) */
821 req_pkt.data[1] = CMN_HDR_OFFSET; /* (LSB) */
822 req_pkt.data[2] = 0x0; /* (MSB) always 0x0 for IPMI */
823 req_pkt.data[3] = IPMI_COMMON_HEADER_SIZE;
824
825 /* make a call to smc library to send cmd */
826 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
827 POLL_TIMEOUT) != SMC_SUCCESS) {
828 return (0);
829 }
830
831 /* version check */
832 if (res_pkt.data[0] != IPMI_VERSION) {
833 return (0);
834 } else {
835 return (1);
836 }
837 }
838
839 /* ipmi access */
840 datap[0] = FRU_DEVICE_ID; /* fru device id - always */
841 datap[1] = 0x0; /* LSB */
842 datap[2] = 0x0; /* MSB */
843 datap[3] = 8; /* bytes to read */
844
845 (void) smc_init_ipmi_msg(&req_pkt, READ_FRU_INVENTORY_DATA,
846 FRUACCESS_MSG_ID, 4, datap, DEFAULT_SEQN,
847 IPMB_ADDR(dest), SMC_NETFN_STORAGE_REQ, SMC_BMC_LUN);
848
849 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
850 FRUID_CHECK_POLL_TIMEOUT) != SMC_SUCCESS) {
851 return (0);
852 }
853
854 if (res_pkt.data[9] == IPMI_VERSION) {
855 return (1);
856 } else {
857 return (0);
858 }
859 }
860
861 /*
862 * routine to check if fruid info is available on BMC,
863 * return 0: fruid not present
864 * return 1: fruid present
865 */
866 static int
is_alarm_frudata_available(format_t * fru_format)867 is_alarm_frudata_available(format_t *fru_format)
868 {
869 int ret;
870 char buffer[TMP_BUFFER_SIZE];
871 int fd = -1;
872 format_t format;
873
874 bzero(buffer, sizeof (buffer));
875 format.src = fru_format->src;
876 format.dest = fru_format->dest;
877 format.sun_device_id = 0x0;
878 format.sun_lun = 0x0;
879 format.format |= SUN_FORMAT;
880
881 /* passing dummy fd */
882 /* for now read the first 3 bytes and check the info */
883 ret = pread_new(fd, (void *) buffer, 3, STATIC_OFFSET, &format);
884 if (ret < 0) {
885 return (0);
886 }
887
888 if (buffer[0] != SECTION_HDR_TAG) {
889 fru_format->format = NO_FRUDATA;
890 return (0);
891 }
892
893 fru_format->format = SUN_FORMAT;
894 fru_format->sun_device_id = 0x0;
895 fru_format->sun_lun = 0x0;
896 return (1);
897 }
898
899 /*
900 * checks if the remote device intelligent device (IPMI capable) or not
901 * return 0: not ipmi capable
902 * return 1: ipmi capable
903 */
904 static int
is_ipmi_capable(int src,int dest)905 is_ipmi_capable(int src, int dest)
906 {
907 sc_reqmsg_t req_pkt;
908 sc_rspmsg_t res_pkt;
909
910 if (src == dest) {
911 return (1);
912 }
913
914 (void) smc_init_ipmi_msg(&req_pkt, IPMI_GET_DEVICE_ID,
915 FRUACCESS_MSG_ID, 0, NULL, DEFAULT_SEQN,
916 IPMB_ADDR(dest), SMC_NETFN_APP_REQ, SMC_BMC_LUN);
917
918 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
919 FRUID_CHECK_POLL_TIMEOUT) != SMC_SUCCESS) {
920 return (0);
921 }
922 return (1); /* got response */
923 }
924
925 int
is_fru_data_available(int precedence,int slot_no,format_t * fru_format)926 is_fru_data_available(int precedence, int slot_no, format_t *fru_format)
927 {
928 int ret, fd = 0;
929 uint8_t data[TMP_BUFFER_SIZE];
930
931 fru_format->format = NO_FRUDATA;
932 if (fru_format->dest == 0x20) { /* alarm card */
933 ret = is_alarm_frudata_available(fru_format);
934 return (ret);
935 }
936
937 if (cpu_no == 0) { /* get the geo_addr */
938 sc_reqmsg_t req_pkt;
939 sc_rspmsg_t rsp_pkt;
940 uint8_t size = 0;
941
942 /* initialize the request packet */
943 (void) smc_init_smc_msg(&req_pkt,
944 SMC_GET_GEOGRAPHICAL_ADDRESS, DEFAULT_SEQN, size);
945 /* make a call to smc library to send cmd */
946 if (smc_send_msg(DEFAULT_FD, &req_pkt, &rsp_pkt,
947 POLL_TIMEOUT) != SMC_SUCCESS) {
948 return (0);
949 }
950 if (SC_MSG_LEN(&rsp_pkt) == 0) {
951 return (0);
952 }
953 cpu_no = rsp_pkt.data[0];
954 }
955
956 /* check if it is IPMI intelligent or not */
957 if (slot_no != cpu_no) {
958 ret = is_ipmi_capable(cpu_no, slot_no);
959 if (ret == 0) { /* dumb I/O card */
960 return (0);
961 }
962 }
963
964 /* check if ipmi frudata is present or not */
965 ret = is_ipmi_fru_data_available(cpu_no, slot_no);
966 if (ret == 1) {
967 fru_format->format |= IPMI_FORMAT;
968 fru_format->sun_device_id = 0x0;
969 fru_format->sun_lun = 0x0;
970
971 /* no need to look for sun format */
972 if (precedence == IPMI_FORMAT) {
973 return (fru_format->format);
974 }
975 }
976
977 /* check if sun fruid is present */
978 get_fru_data_info(cpu_no, slot_no, fru_format);
979 /* check the hdr version */
980 if (fru_format->format & SUN_FORMAT) {
981 ret = pread_new(fd, &data, BYTE_TO_READ_SUN_CHK,
982 STATIC_OFFSET, fru_format);
983 if (ret != BYTE_TO_READ_SUN_CHK) {
984 fru_format->format = fru_format->format &
985 (~ (SUN_FORMAT));
986 fru_format->sun_device_id = 0x0;
987 fru_format->sun_lun = 0x0;
988 }
989 if (data[0] != SECTION_HDR_TAG) {
990 fru_format->format = fru_format->format &
991 (~ (SUN_FORMAT));
992 fru_format->sun_device_id = 0x0;
993 fru_format->sun_lun = 0x0;
994 }
995 }
996 return (fru_format->format);
997 }
998