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 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include "fru_access_impl.h"
28 
29 static uchar_t			sp_sec_hdr[] = SP_SEC_HDR;
30 static uchar_t			sp_seg_hdr[] = SP_SEG_HDR;
31 static uchar_t			sp_seg_body[] = SP_DATA;
32 
33 /*
34  * function to return section header for simulated SPD fruid
35  *
36  * parameters:
37  *	sec_hdr		buffer to receive section header
38  *	sec_hdr_len	size of buffer sec_hdr
39  * return value:
40  *	size of returned data (0 if sec_hdr_len too small)
41  */
42 size_t
get_sp_sec_hdr(void * sec_hdr,size_t sec_hdr_len)43 get_sp_sec_hdr(void *sec_hdr, size_t sec_hdr_len)
44 {
45 	if (sec_hdr_len < sizeof (sp_sec_hdr))
46 		return (0);
47 	(void) memcpy(sec_hdr, sp_sec_hdr, sizeof (sp_sec_hdr));
48 	return (sizeof (sp_sec_hdr));
49 }
50 
51 /*
52  * function to return segment header for simulated SPD fruid
53  *
54  * parameters:
55  *	seg_hdr		buffer to receive segment header
56  *	seg_hdr_len	size of buffer seg_hdr
57  * return value:
58  *	size of returned data (0 if seg_hdr_len too small)
59  */
60 size_t
get_sp_seg_hdr(void * seg_hdr,size_t seg_hdr_len)61 get_sp_seg_hdr(void *seg_hdr, size_t seg_hdr_len)
62 {
63 	if (seg_hdr_len < sizeof (sp_seg_hdr))
64 		return (0);
65 	(void) memcpy(seg_hdr, sp_seg_hdr, sizeof (sp_seg_hdr));
66 	return (sizeof (sp_seg_hdr));
67 }
68 
69 /*
70  * Function to convert SPD data into SPD fruid segment.
71  * The segment comprises two tagged records: DIMM_Capacity and SPD_R.
72  *
73  * DIMM_Capacity is a text string showing the total usable size of the
74  * DIMM (i.e. not including error correction bits). This record is derived
75  * from module row density and number of rows.
76  *
77  * SPD_R contains the entire SPD data area from the DIMM. It is slightly
78  * massaged to make it easier to display:
79  * bytes  0 -  63 are presented as is
80  * bytes 64 -  71 (JEDEC code) are compressed into 2 bytes, matching the
81  *		  format used in ManR
82  * bytes 72 -  92 are copied as is (to bytes 66 - 86)
83  * byte  93	  year of manufacture is expanded to a 2 byte (big endian)
84  *		  field which includes the century (to bytes 87 - 88)
85  * bytes 94 - 127 are copied as is (to bytes 89 - 122)
86  *
87  * parameters:
88  *	spd_data	pointer to SPD data
89  *	spd_data_len	length of supplied SPD data
90  *	sp_seg_ptr	pointer to receive address of converted data
91  *	sp_seg_len	pointer for size of converted data
92  * return value:
93  *	0	- success
94  *	NZ	- error code
95  */
96 int
cvrt_dim_data(const char * spd_data,size_t spd_data_len,uchar_t ** sp_seg_ptr,size_t * sp_seg_len)97 cvrt_dim_data(const char *spd_data, size_t spd_data_len, uchar_t **sp_seg_ptr,
98     size_t *sp_seg_len)
99 {
100 	int		c;
101 	ushort_t	year;
102 	int		capacity;
103 	spd_data_t	*spd;
104 	uint32_t	sum;
105 
106 	if (spd_data_len < sizeof (spd_data_t))
107 		return (EINVAL);
108 
109 	spd = (spd_data_t *)spd_data;
110 	*sp_seg_ptr = malloc(sizeof (sp_seg_body));
111 
112 	if (*sp_seg_ptr == NULL)
113 		return (ENOMEM);
114 
115 	/* set up template for SP seg */
116 	(void) memcpy(*sp_seg_ptr, sp_seg_body, sizeof (sp_seg_body));
117 
118 	year = spd->manu_year;
119 
120 	if (year < 80)
121 		year += 2000;
122 	else
123 		year += 1900;
124 
125 	/*
126 	 * move first 64 bytes of SPD data into SPD-R record
127 	 */
128 	(void) memcpy(*sp_seg_ptr + SPD_R_OFF, spd_data, 64);
129 
130 	/*
131 	 * re-write full data width as big endian
132 	 */
133 	(*sp_seg_ptr + SPD_R_OFF + DATA_WIDTH)[0] = spd->ms_data_width;
134 	(*sp_seg_ptr + SPD_R_OFF + DATA_WIDTH)[1] = spd->ls_data_width;
135 
136 	/*
137 	 * construct Sun compressed encoding for JEDEC code
138 	 */
139 	for (c = 0; c < sizeof (spd->jedec) - 1; c++) {
140 		if (spd->jedec[c] != 0x7F)
141 			break;
142 	}
143 
144 	(*sp_seg_ptr)[SPD_R_OFF + MANUF_ID] = (uchar_t)c;
145 	(*sp_seg_ptr)[SPD_R_OFF + MANUF_ID + 1] = (uchar_t)spd->jedec[c];
146 
147 	/*
148 	 * move other fields in place
149 	 */
150 	(void) memcpy(*sp_seg_ptr + SPD_R_OFF + MANUF_LOC,
151 	    &spd->manu_loc, MANUF_YEAR - MANUF_LOC);
152 
153 	(*sp_seg_ptr + SPD_R_OFF + MANUF_YEAR)[0] = (uchar_t)(year >> 8);
154 	(*sp_seg_ptr + SPD_R_OFF + MANUF_YEAR)[1] = (uchar_t)year;
155 
156 	(void) memcpy(*sp_seg_ptr + SPD_R_OFF + MANUF_WEEK,
157 	    &spd->manu_week, SPD_R_LEN - MANUF_WEEK);
158 
159 	/*
160 	 * calculate the capacity and insert into capacity record
161 	 */
162 	if ((spd->spd_rev >> 4) > 1) {
163 		(void) snprintf((char *)(*sp_seg_ptr + DIMM_CAP_OFF), 8,
164 		    "ver %x.%x", spd->spd_rev >> 4, spd->spd_rev & 0x0f);
165 	} else if ((spd->memory_type != SPDMEM_SDRAM) &&
166 	    (spd->memory_type != SPDMEM_SDRAM_DDR) &&
167 	    (spd->memory_type != SPDMEM_DDR2_SDRAM)) {
168 		/*
169 		 * can't handle this memory type
170 		 */
171 		((char *)(*sp_seg_ptr))[DIMM_CAP_OFF] = '\0';
172 	} else if ((((spd->ms_data_width << 8) | spd->ls_data_width) == 72) &&
173 	    ((spd->n_rows & 0xf0) == 0) && ((spd->n_cols & 0xf0) == 0)) {
174 		/*
175 		 * OK it's 72-bits wide with equal width banks
176 		 */
177 		char m_or_g = 'G';
178 		capacity = spd->mod_row_density;
179 		if (((spd->memory_type == SPDMEM_DDR2_SDRAM) &&
180 		    (capacity > 16)) ||
181 		    (capacity > 4)) {
182 			capacity *= 4;
183 			m_or_g = 'M';
184 		}
185 		c = spd->n_mod_rows;
186 		if (spd->memory_type == SPDMEM_DDR2_SDRAM) {
187 			c &= 7;
188 			c++;
189 		}
190 		capacity *= c;
191 		if ((m_or_g == 'M') && (capacity >= 1024)) {
192 			capacity /= 1024;
193 			m_or_g = 'G';
194 		}
195 		(void) snprintf((char *)(*sp_seg_ptr + DIMM_CAP_OFF), 8,
196 		    "%d %cB", capacity, m_or_g);
197 	} else {
198 		((char *)(*sp_seg_ptr))[DIMM_CAP_OFF] = '\0';
199 	}
200 
201 	/*
202 	 * finally, set the checksum
203 	 */
204 	sum = compute_crc32(*sp_seg_ptr, sizeof (sp_seg_body) - 5);
205 	for (c = 0; c < 4; c++) {
206 		(*sp_seg_ptr + sizeof (sp_seg_body) - 4)[c] =
207 		    ((char *)(&sum))[c];
208 	}
209 	*sp_seg_len = sizeof (sp_seg_body);
210 	return (0);
211 }
212 
213 /*
214  * get_spd_data - reads raw data from container
215  * parameters:
216  *	fd		file descriptor for SPD device
217  *	ctr_offset	container offset
218  *	ctr_len		container size
219  *	spd_data	buffer to receive SPD data (length ctr_len)
220  * return value:
221  *	0	- success
222  *	NZ	- error code
223  */
224 int
get_spd_data(int fd,char * spd_data,size_t ctr_len,off_t ctr_offset)225 get_spd_data(int fd, char *spd_data, size_t ctr_len, off_t ctr_offset)
226 {
227 	if (ctr_len < sizeof (spd_data_t))
228 		return (EINVAL);
229 
230 	(void) memset(spd_data, 0, ctr_len);
231 
232 	if (pread(fd, spd_data, sizeof (spd_data_t), ctr_offset) !=
233 	    sizeof (spd_data_t))
234 		return (EIO);
235 	return (0);
236 }
237