xref: /titanic_50/usr/src/cmd/picl/plugins/sun4u/snowbird/lib/fruaccess/piclsmc.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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