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