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 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 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 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 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