17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include "fru_access_impl.h"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate static uchar_t			sp_sec_hdr[] = SP_SEC_HDR;
307c478bd9Sstevel@tonic-gate static uchar_t			sp_seg_hdr[] = SP_SEG_HDR;
317c478bd9Sstevel@tonic-gate static uchar_t			sp_seg_body[] = SP_DATA;
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /*
347c478bd9Sstevel@tonic-gate  * function to return section header for simulated SPD fruid
357c478bd9Sstevel@tonic-gate  *
367c478bd9Sstevel@tonic-gate  * parameters:
377c478bd9Sstevel@tonic-gate  *	sec_hdr		buffer to receive section header
387c478bd9Sstevel@tonic-gate  *	sec_hdr_len	size of buffer sec_hdr
397c478bd9Sstevel@tonic-gate  * return value:
407c478bd9Sstevel@tonic-gate  *	size of returned data (0 if sec_hdr_len too small)
417c478bd9Sstevel@tonic-gate  */
427c478bd9Sstevel@tonic-gate size_t
get_sp_sec_hdr(void * sec_hdr,size_t sec_hdr_len)437c478bd9Sstevel@tonic-gate get_sp_sec_hdr(void *sec_hdr, size_t sec_hdr_len)
447c478bd9Sstevel@tonic-gate {
457c478bd9Sstevel@tonic-gate 	if (sec_hdr_len < sizeof (sp_sec_hdr))
467c478bd9Sstevel@tonic-gate 		return (0);
477c478bd9Sstevel@tonic-gate 	(void) memcpy(sec_hdr, sp_sec_hdr, sizeof (sp_sec_hdr));
487c478bd9Sstevel@tonic-gate 	return (sizeof (sp_sec_hdr));
497c478bd9Sstevel@tonic-gate }
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate /*
527c478bd9Sstevel@tonic-gate  * function to return segment header for simulated SPD fruid
537c478bd9Sstevel@tonic-gate  *
547c478bd9Sstevel@tonic-gate  * parameters:
557c478bd9Sstevel@tonic-gate  *	seg_hdr		buffer to receive segment header
567c478bd9Sstevel@tonic-gate  *	seg_hdr_len	size of buffer seg_hdr
577c478bd9Sstevel@tonic-gate  * return value:
587c478bd9Sstevel@tonic-gate  *	size of returned data (0 if seg_hdr_len too small)
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate size_t
get_sp_seg_hdr(void * seg_hdr,size_t seg_hdr_len)617c478bd9Sstevel@tonic-gate get_sp_seg_hdr(void *seg_hdr, size_t seg_hdr_len)
627c478bd9Sstevel@tonic-gate {
637c478bd9Sstevel@tonic-gate 	if (seg_hdr_len < sizeof (sp_seg_hdr))
647c478bd9Sstevel@tonic-gate 		return (0);
657c478bd9Sstevel@tonic-gate 	(void) memcpy(seg_hdr, sp_seg_hdr, sizeof (sp_seg_hdr));
667c478bd9Sstevel@tonic-gate 	return (sizeof (sp_seg_hdr));
677c478bd9Sstevel@tonic-gate }
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
707c478bd9Sstevel@tonic-gate  * Function to convert SPD data into SPD fruid segment.
717c478bd9Sstevel@tonic-gate  * The segment comprises two tagged records: DIMM_Capacity and SPD_R.
727c478bd9Sstevel@tonic-gate  *
737c478bd9Sstevel@tonic-gate  * DIMM_Capacity is a text string showing the total usable size of the
747c478bd9Sstevel@tonic-gate  * DIMM (i.e. not including error correction bits). This record is derived
757c478bd9Sstevel@tonic-gate  * from module row density and number of rows.
767c478bd9Sstevel@tonic-gate  *
777c478bd9Sstevel@tonic-gate  * SPD_R contains the entire SPD data area from the DIMM. It is slightly
787c478bd9Sstevel@tonic-gate  * massaged to make it easier to display:
797c478bd9Sstevel@tonic-gate  * bytes  0 -  63 are presented as is
807c478bd9Sstevel@tonic-gate  * bytes 64 -  71 (JEDEC code) are compressed into 2 bytes, matching the
817c478bd9Sstevel@tonic-gate  *		  format used in ManR
827c478bd9Sstevel@tonic-gate  * bytes 72 -  92 are copied as is (to bytes 66 - 86)
837c478bd9Sstevel@tonic-gate  * byte  93	  year of manufacture is expanded to a 2 byte (big endian)
847c478bd9Sstevel@tonic-gate  *		  field which includes the century (to bytes 87 - 88)
857c478bd9Sstevel@tonic-gate  * bytes 94 - 127 are copied as is (to bytes 89 - 122)
867c478bd9Sstevel@tonic-gate  *
877c478bd9Sstevel@tonic-gate  * parameters:
887c478bd9Sstevel@tonic-gate  *	spd_data	pointer to SPD data
897c478bd9Sstevel@tonic-gate  *	spd_data_len	length of supplied SPD data
907c478bd9Sstevel@tonic-gate  *	sp_seg_ptr	pointer to receive address of converted data
917c478bd9Sstevel@tonic-gate  *	sp_seg_len	pointer for size of converted data
927c478bd9Sstevel@tonic-gate  * return value:
937c478bd9Sstevel@tonic-gate  *	0	- success
947c478bd9Sstevel@tonic-gate  *	NZ	- error code
957c478bd9Sstevel@tonic-gate  */
967c478bd9Sstevel@tonic-gate int
cvrt_dim_data(const char * spd_data,size_t spd_data_len,uchar_t ** sp_seg_ptr,size_t * sp_seg_len)977c478bd9Sstevel@tonic-gate cvrt_dim_data(const char *spd_data, size_t spd_data_len, uchar_t **sp_seg_ptr,
987c478bd9Sstevel@tonic-gate     size_t *sp_seg_len)
997c478bd9Sstevel@tonic-gate {
1007c478bd9Sstevel@tonic-gate 	int		c;
1017c478bd9Sstevel@tonic-gate 	ushort_t	year;
1027c478bd9Sstevel@tonic-gate 	int		capacity;
1037c478bd9Sstevel@tonic-gate 	spd_data_t	*spd;
1047c478bd9Sstevel@tonic-gate 	uint32_t	sum;
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	if (spd_data_len < sizeof (spd_data_t))
1077c478bd9Sstevel@tonic-gate 		return (EINVAL);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 	spd = (spd_data_t *)spd_data;
1107c478bd9Sstevel@tonic-gate 	*sp_seg_ptr = malloc(sizeof (sp_seg_body));
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	if (*sp_seg_ptr == NULL)
1137c478bd9Sstevel@tonic-gate 		return (ENOMEM);
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 	/* set up template for SP seg */
1167c478bd9Sstevel@tonic-gate 	(void) memcpy(*sp_seg_ptr, sp_seg_body, sizeof (sp_seg_body));
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	year = spd->manu_year;
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 	if (year < 80)
1217c478bd9Sstevel@tonic-gate 		year += 2000;
1227c478bd9Sstevel@tonic-gate 	else
1237c478bd9Sstevel@tonic-gate 		year += 1900;
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 	/*
1267c478bd9Sstevel@tonic-gate 	 * move first 64 bytes of SPD data into SPD-R record
1277c478bd9Sstevel@tonic-gate 	 */
1287c478bd9Sstevel@tonic-gate 	(void) memcpy(*sp_seg_ptr + SPD_R_OFF, spd_data, 64);
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	/*
1317c478bd9Sstevel@tonic-gate 	 * re-write full data width as big endian
1327c478bd9Sstevel@tonic-gate 	 */
1337c478bd9Sstevel@tonic-gate 	(*sp_seg_ptr + SPD_R_OFF + DATA_WIDTH)[0] = spd->ms_data_width;
1347c478bd9Sstevel@tonic-gate 	(*sp_seg_ptr + SPD_R_OFF + DATA_WIDTH)[1] = spd->ls_data_width;
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	/*
1377c478bd9Sstevel@tonic-gate 	 * construct Sun compressed encoding for JEDEC code
1387c478bd9Sstevel@tonic-gate 	 */
1397c478bd9Sstevel@tonic-gate 	for (c = 0; c < sizeof (spd->jedec) - 1; c++) {
140*161c3e5aSps 		if (spd->jedec[c] != 0x7F)
1417c478bd9Sstevel@tonic-gate 			break;
1427c478bd9Sstevel@tonic-gate 	}
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 	(*sp_seg_ptr)[SPD_R_OFF + MANUF_ID] = (uchar_t)c;
1457c478bd9Sstevel@tonic-gate 	(*sp_seg_ptr)[SPD_R_OFF + MANUF_ID + 1] = (uchar_t)spd->jedec[c];
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	/*
1487c478bd9Sstevel@tonic-gate 	 * move other fields in place
1497c478bd9Sstevel@tonic-gate 	 */
1507c478bd9Sstevel@tonic-gate 	(void) memcpy(*sp_seg_ptr + SPD_R_OFF + MANUF_LOC,
1517c478bd9Sstevel@tonic-gate 	    &spd->manu_loc, MANUF_YEAR - MANUF_LOC);
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	(*sp_seg_ptr + SPD_R_OFF + MANUF_YEAR)[0] = (uchar_t)(year >> 8);
1547c478bd9Sstevel@tonic-gate 	(*sp_seg_ptr + SPD_R_OFF + MANUF_YEAR)[1] = (uchar_t)year;
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	(void) memcpy(*sp_seg_ptr + SPD_R_OFF + MANUF_WEEK,
1577c478bd9Sstevel@tonic-gate 	    &spd->manu_week, SPD_R_LEN - MANUF_WEEK);
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	/*
1607c478bd9Sstevel@tonic-gate 	 * calculate the capacity and insert into capacity record
1617c478bd9Sstevel@tonic-gate 	 */
1627c478bd9Sstevel@tonic-gate 	if ((spd->spd_rev >> 4) > 1) {
1637c478bd9Sstevel@tonic-gate 		(void) snprintf((char *)(*sp_seg_ptr + DIMM_CAP_OFF), 8,
1647c478bd9Sstevel@tonic-gate 		    "ver %x.%x", spd->spd_rev >> 4, spd->spd_rev & 0x0f);
1657c478bd9Sstevel@tonic-gate 	} else if ((spd->memory_type != SPDMEM_SDRAM) &&
1667c478bd9Sstevel@tonic-gate 	    (spd->memory_type != SPDMEM_SDRAM_DDR) &&
1677c478bd9Sstevel@tonic-gate 	    (spd->memory_type != SPDMEM_DDR2_SDRAM)) {
1687c478bd9Sstevel@tonic-gate 		/*
1697c478bd9Sstevel@tonic-gate 		 * can't handle this memory type
1707c478bd9Sstevel@tonic-gate 		 */
1717c478bd9Sstevel@tonic-gate 		((char *)(*sp_seg_ptr))[DIMM_CAP_OFF] = '\0';
1727c478bd9Sstevel@tonic-gate 	} else if ((((spd->ms_data_width << 8) | spd->ls_data_width) == 72) &&
1737c478bd9Sstevel@tonic-gate 	    ((spd->n_rows & 0xf0) == 0) && ((spd->n_cols & 0xf0) == 0)) {
1747c478bd9Sstevel@tonic-gate 		/*
1757c478bd9Sstevel@tonic-gate 		 * OK it's 72-bits wide with equal width banks
1767c478bd9Sstevel@tonic-gate 		 */
1777c478bd9Sstevel@tonic-gate 		char m_or_g = 'G';
1787c478bd9Sstevel@tonic-gate 		capacity = spd->mod_row_density;
1797c478bd9Sstevel@tonic-gate 		if (((spd->memory_type == SPDMEM_DDR2_SDRAM) &&
1807c478bd9Sstevel@tonic-gate 		    (capacity > 16)) ||
1817c478bd9Sstevel@tonic-gate 		    (capacity > 4)) {
1827c478bd9Sstevel@tonic-gate 			capacity *= 4;
1837c478bd9Sstevel@tonic-gate 			m_or_g = 'M';
1847c478bd9Sstevel@tonic-gate 		}
1857c478bd9Sstevel@tonic-gate 		c = spd->n_mod_rows;
1867c478bd9Sstevel@tonic-gate 		if (spd->memory_type == SPDMEM_DDR2_SDRAM) {
1877c478bd9Sstevel@tonic-gate 			c &= 7;
1887c478bd9Sstevel@tonic-gate 			c++;
1897c478bd9Sstevel@tonic-gate 		}
1907c478bd9Sstevel@tonic-gate 		capacity *= c;
1917c478bd9Sstevel@tonic-gate 		if ((m_or_g == 'M') && (capacity >= 1024)) {
1927c478bd9Sstevel@tonic-gate 			capacity /= 1024;
1937c478bd9Sstevel@tonic-gate 			m_or_g = 'G';
1947c478bd9Sstevel@tonic-gate 		}
1957c478bd9Sstevel@tonic-gate 		(void) snprintf((char *)(*sp_seg_ptr + DIMM_CAP_OFF), 8,
1967c478bd9Sstevel@tonic-gate 		    "%d %cB", capacity, m_or_g);
1977c478bd9Sstevel@tonic-gate 	} else {
1987c478bd9Sstevel@tonic-gate 		((char *)(*sp_seg_ptr))[DIMM_CAP_OFF] = '\0';
1997c478bd9Sstevel@tonic-gate 	}
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	/*
2027c478bd9Sstevel@tonic-gate 	 * finally, set the checksum
2037c478bd9Sstevel@tonic-gate 	 */
2047c478bd9Sstevel@tonic-gate 	sum = compute_crc32(*sp_seg_ptr, sizeof (sp_seg_body) - 5);
2057c478bd9Sstevel@tonic-gate 	for (c = 0; c < 4; c++) {
2067c478bd9Sstevel@tonic-gate 		(*sp_seg_ptr + sizeof (sp_seg_body) - 4)[c] =
2077c478bd9Sstevel@tonic-gate 		    ((char *)(&sum))[c];
2087c478bd9Sstevel@tonic-gate 	}
2097c478bd9Sstevel@tonic-gate 	*sp_seg_len = sizeof (sp_seg_body);
2107c478bd9Sstevel@tonic-gate 	return (0);
2117c478bd9Sstevel@tonic-gate }
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate /*
2147c478bd9Sstevel@tonic-gate  * get_spd_data - reads raw data from container
2157c478bd9Sstevel@tonic-gate  * parameters:
2167c478bd9Sstevel@tonic-gate  *	fd		file descriptor for SPD device
2177c478bd9Sstevel@tonic-gate  *	ctr_offset	container offset
2187c478bd9Sstevel@tonic-gate  *	ctr_len		container size
2197c478bd9Sstevel@tonic-gate  *	spd_data	buffer to receive SPD data (length ctr_len)
2207c478bd9Sstevel@tonic-gate  * return value:
2217c478bd9Sstevel@tonic-gate  *	0	- success
2227c478bd9Sstevel@tonic-gate  *	NZ	- error code
2237c478bd9Sstevel@tonic-gate  */
2247c478bd9Sstevel@tonic-gate int
get_spd_data(int fd,char * spd_data,size_t ctr_len,off_t ctr_offset)2257c478bd9Sstevel@tonic-gate get_spd_data(int fd, char *spd_data, size_t ctr_len, off_t ctr_offset)
2267c478bd9Sstevel@tonic-gate {
2277c478bd9Sstevel@tonic-gate 	if (ctr_len < sizeof (spd_data_t))
2287c478bd9Sstevel@tonic-gate 		return (EINVAL);
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	(void) memset(spd_data, 0, ctr_len);
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	if (pread(fd, spd_data, sizeof (spd_data_t), ctr_offset) !=
2337c478bd9Sstevel@tonic-gate 	    sizeof (spd_data_t))
2347c478bd9Sstevel@tonic-gate 		return (EIO);
2357c478bd9Sstevel@tonic-gate 	return (0);
2367c478bd9Sstevel@tonic-gate }
237