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] != 0x7F) 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