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