17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 237c478bd9Sstevel@tonic-gate * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include "fru_access_impl.h" 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate static uchar_t sp_sec_hdr[] = SP_SEC_HDR; 327c478bd9Sstevel@tonic-gate static uchar_t sp_seg_hdr[] = SP_SEG_HDR; 337c478bd9Sstevel@tonic-gate static uchar_t sp_seg_body[] = SP_DATA; 347c478bd9Sstevel@tonic-gate 357c478bd9Sstevel@tonic-gate /* 367c478bd9Sstevel@tonic-gate * function to return section header for simulated SPD fruid 377c478bd9Sstevel@tonic-gate * 387c478bd9Sstevel@tonic-gate * parameters: 397c478bd9Sstevel@tonic-gate * sec_hdr buffer to receive section header 407c478bd9Sstevel@tonic-gate * sec_hdr_len size of buffer sec_hdr 417c478bd9Sstevel@tonic-gate * return value: 427c478bd9Sstevel@tonic-gate * size of returned data (0 if sec_hdr_len too small) 437c478bd9Sstevel@tonic-gate */ 447c478bd9Sstevel@tonic-gate size_t 457c478bd9Sstevel@tonic-gate get_sp_sec_hdr(void *sec_hdr, size_t sec_hdr_len) 467c478bd9Sstevel@tonic-gate { 477c478bd9Sstevel@tonic-gate if (sec_hdr_len < sizeof (sp_sec_hdr)) 487c478bd9Sstevel@tonic-gate return (0); 497c478bd9Sstevel@tonic-gate (void) memcpy(sec_hdr, sp_sec_hdr, sizeof (sp_sec_hdr)); 507c478bd9Sstevel@tonic-gate return (sizeof (sp_sec_hdr)); 517c478bd9Sstevel@tonic-gate } 527c478bd9Sstevel@tonic-gate 537c478bd9Sstevel@tonic-gate /* 547c478bd9Sstevel@tonic-gate * function to return segment header for simulated SPD fruid 557c478bd9Sstevel@tonic-gate * 567c478bd9Sstevel@tonic-gate * parameters: 577c478bd9Sstevel@tonic-gate * seg_hdr buffer to receive segment header 587c478bd9Sstevel@tonic-gate * seg_hdr_len size of buffer seg_hdr 597c478bd9Sstevel@tonic-gate * return value: 607c478bd9Sstevel@tonic-gate * size of returned data (0 if seg_hdr_len too small) 617c478bd9Sstevel@tonic-gate */ 627c478bd9Sstevel@tonic-gate size_t 637c478bd9Sstevel@tonic-gate get_sp_seg_hdr(void *seg_hdr, size_t seg_hdr_len) 647c478bd9Sstevel@tonic-gate { 657c478bd9Sstevel@tonic-gate if (seg_hdr_len < sizeof (sp_seg_hdr)) 667c478bd9Sstevel@tonic-gate return (0); 677c478bd9Sstevel@tonic-gate (void) memcpy(seg_hdr, sp_seg_hdr, sizeof (sp_seg_hdr)); 687c478bd9Sstevel@tonic-gate return (sizeof (sp_seg_hdr)); 697c478bd9Sstevel@tonic-gate } 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate /* 727c478bd9Sstevel@tonic-gate * Function to convert SPD data into SPD fruid segment. 737c478bd9Sstevel@tonic-gate * The segment comprises two tagged records: DIMM_Capacity and SPD_R. 747c478bd9Sstevel@tonic-gate * 757c478bd9Sstevel@tonic-gate * DIMM_Capacity is a text string showing the total usable size of the 767c478bd9Sstevel@tonic-gate * DIMM (i.e. not including error correction bits). This record is derived 777c478bd9Sstevel@tonic-gate * from module row density and number of rows. 787c478bd9Sstevel@tonic-gate * 797c478bd9Sstevel@tonic-gate * SPD_R contains the entire SPD data area from the DIMM. It is slightly 807c478bd9Sstevel@tonic-gate * massaged to make it easier to display: 817c478bd9Sstevel@tonic-gate * bytes 0 - 63 are presented as is 827c478bd9Sstevel@tonic-gate * bytes 64 - 71 (JEDEC code) are compressed into 2 bytes, matching the 837c478bd9Sstevel@tonic-gate * format used in ManR 847c478bd9Sstevel@tonic-gate * bytes 72 - 92 are copied as is (to bytes 66 - 86) 857c478bd9Sstevel@tonic-gate * byte 93 year of manufacture is expanded to a 2 byte (big endian) 867c478bd9Sstevel@tonic-gate * field which includes the century (to bytes 87 - 88) 877c478bd9Sstevel@tonic-gate * bytes 94 - 127 are copied as is (to bytes 89 - 122) 887c478bd9Sstevel@tonic-gate * 897c478bd9Sstevel@tonic-gate * parameters: 907c478bd9Sstevel@tonic-gate * spd_data pointer to SPD data 917c478bd9Sstevel@tonic-gate * spd_data_len length of supplied SPD data 927c478bd9Sstevel@tonic-gate * sp_seg_ptr pointer to receive address of converted data 937c478bd9Sstevel@tonic-gate * sp_seg_len pointer for size of converted data 947c478bd9Sstevel@tonic-gate * return value: 957c478bd9Sstevel@tonic-gate * 0 - success 967c478bd9Sstevel@tonic-gate * NZ - error code 977c478bd9Sstevel@tonic-gate */ 987c478bd9Sstevel@tonic-gate int 997c478bd9Sstevel@tonic-gate cvrt_dim_data(const char *spd_data, size_t spd_data_len, uchar_t **sp_seg_ptr, 1007c478bd9Sstevel@tonic-gate size_t *sp_seg_len) 1017c478bd9Sstevel@tonic-gate { 1027c478bd9Sstevel@tonic-gate int c; 1037c478bd9Sstevel@tonic-gate ushort_t year; 1047c478bd9Sstevel@tonic-gate int capacity; 1057c478bd9Sstevel@tonic-gate spd_data_t *spd; 1067c478bd9Sstevel@tonic-gate uint32_t sum; 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate if (spd_data_len < sizeof (spd_data_t)) 1097c478bd9Sstevel@tonic-gate return (EINVAL); 1107c478bd9Sstevel@tonic-gate 1117c478bd9Sstevel@tonic-gate spd = (spd_data_t *)spd_data; 1127c478bd9Sstevel@tonic-gate *sp_seg_ptr = malloc(sizeof (sp_seg_body)); 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate if (*sp_seg_ptr == NULL) 1157c478bd9Sstevel@tonic-gate return (ENOMEM); 1167c478bd9Sstevel@tonic-gate 1177c478bd9Sstevel@tonic-gate /* set up template for SP seg */ 1187c478bd9Sstevel@tonic-gate (void) memcpy(*sp_seg_ptr, sp_seg_body, sizeof (sp_seg_body)); 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate year = spd->manu_year; 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate if (year < 80) 1237c478bd9Sstevel@tonic-gate year += 2000; 1247c478bd9Sstevel@tonic-gate else 1257c478bd9Sstevel@tonic-gate year += 1900; 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate /* 1287c478bd9Sstevel@tonic-gate * move first 64 bytes of SPD data into SPD-R record 1297c478bd9Sstevel@tonic-gate */ 1307c478bd9Sstevel@tonic-gate (void) memcpy(*sp_seg_ptr + SPD_R_OFF, spd_data, 64); 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate /* 1337c478bd9Sstevel@tonic-gate * re-write full data width as big endian 1347c478bd9Sstevel@tonic-gate */ 1357c478bd9Sstevel@tonic-gate (*sp_seg_ptr + SPD_R_OFF + DATA_WIDTH)[0] = spd->ms_data_width; 1367c478bd9Sstevel@tonic-gate (*sp_seg_ptr + SPD_R_OFF + DATA_WIDTH)[1] = spd->ls_data_width; 1377c478bd9Sstevel@tonic-gate 1387c478bd9Sstevel@tonic-gate /* 1397c478bd9Sstevel@tonic-gate * construct Sun compressed encoding for JEDEC code 1407c478bd9Sstevel@tonic-gate */ 1417c478bd9Sstevel@tonic-gate for (c = 0; c < sizeof (spd->jedec) - 1; c++) { 142*161c3e5aSps if (spd->jedec[c] != 0x7F) 1437c478bd9Sstevel@tonic-gate break; 1447c478bd9Sstevel@tonic-gate } 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate (*sp_seg_ptr)[SPD_R_OFF + MANUF_ID] = (uchar_t)c; 1477c478bd9Sstevel@tonic-gate (*sp_seg_ptr)[SPD_R_OFF + MANUF_ID + 1] = (uchar_t)spd->jedec[c]; 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate /* 1507c478bd9Sstevel@tonic-gate * move other fields in place 1517c478bd9Sstevel@tonic-gate */ 1527c478bd9Sstevel@tonic-gate (void) memcpy(*sp_seg_ptr + SPD_R_OFF + MANUF_LOC, 1537c478bd9Sstevel@tonic-gate &spd->manu_loc, MANUF_YEAR - MANUF_LOC); 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate (*sp_seg_ptr + SPD_R_OFF + MANUF_YEAR)[0] = (uchar_t)(year >> 8); 1567c478bd9Sstevel@tonic-gate (*sp_seg_ptr + SPD_R_OFF + MANUF_YEAR)[1] = (uchar_t)year; 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate (void) memcpy(*sp_seg_ptr + SPD_R_OFF + MANUF_WEEK, 1597c478bd9Sstevel@tonic-gate &spd->manu_week, SPD_R_LEN - MANUF_WEEK); 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate /* 1627c478bd9Sstevel@tonic-gate * calculate the capacity and insert into capacity record 1637c478bd9Sstevel@tonic-gate */ 1647c478bd9Sstevel@tonic-gate if ((spd->spd_rev >> 4) > 1) { 1657c478bd9Sstevel@tonic-gate (void) snprintf((char *)(*sp_seg_ptr + DIMM_CAP_OFF), 8, 1667c478bd9Sstevel@tonic-gate "ver %x.%x", spd->spd_rev >> 4, spd->spd_rev & 0x0f); 1677c478bd9Sstevel@tonic-gate } else if ((spd->memory_type != SPDMEM_SDRAM) && 1687c478bd9Sstevel@tonic-gate (spd->memory_type != SPDMEM_SDRAM_DDR) && 1697c478bd9Sstevel@tonic-gate (spd->memory_type != SPDMEM_DDR2_SDRAM)) { 1707c478bd9Sstevel@tonic-gate /* 1717c478bd9Sstevel@tonic-gate * can't handle this memory type 1727c478bd9Sstevel@tonic-gate */ 1737c478bd9Sstevel@tonic-gate ((char *)(*sp_seg_ptr))[DIMM_CAP_OFF] = '\0'; 1747c478bd9Sstevel@tonic-gate } else if ((((spd->ms_data_width << 8) | spd->ls_data_width) == 72) && 1757c478bd9Sstevel@tonic-gate ((spd->n_rows & 0xf0) == 0) && ((spd->n_cols & 0xf0) == 0)) { 1767c478bd9Sstevel@tonic-gate /* 1777c478bd9Sstevel@tonic-gate * OK it's 72-bits wide with equal width banks 1787c478bd9Sstevel@tonic-gate */ 1797c478bd9Sstevel@tonic-gate char m_or_g = 'G'; 1807c478bd9Sstevel@tonic-gate capacity = spd->mod_row_density; 1817c478bd9Sstevel@tonic-gate if (((spd->memory_type == SPDMEM_DDR2_SDRAM) && 1827c478bd9Sstevel@tonic-gate (capacity > 16)) || 1837c478bd9Sstevel@tonic-gate (capacity > 4)) { 1847c478bd9Sstevel@tonic-gate capacity *= 4; 1857c478bd9Sstevel@tonic-gate m_or_g = 'M'; 1867c478bd9Sstevel@tonic-gate } 1877c478bd9Sstevel@tonic-gate c = spd->n_mod_rows; 1887c478bd9Sstevel@tonic-gate if (spd->memory_type == SPDMEM_DDR2_SDRAM) { 1897c478bd9Sstevel@tonic-gate c &= 7; 1907c478bd9Sstevel@tonic-gate c++; 1917c478bd9Sstevel@tonic-gate } 1927c478bd9Sstevel@tonic-gate capacity *= c; 1937c478bd9Sstevel@tonic-gate if ((m_or_g == 'M') && (capacity >= 1024)) { 1947c478bd9Sstevel@tonic-gate capacity /= 1024; 1957c478bd9Sstevel@tonic-gate m_or_g = 'G'; 1967c478bd9Sstevel@tonic-gate } 1977c478bd9Sstevel@tonic-gate (void) snprintf((char *)(*sp_seg_ptr + DIMM_CAP_OFF), 8, 1987c478bd9Sstevel@tonic-gate "%d %cB", capacity, m_or_g); 1997c478bd9Sstevel@tonic-gate } else { 2007c478bd9Sstevel@tonic-gate ((char *)(*sp_seg_ptr))[DIMM_CAP_OFF] = '\0'; 2017c478bd9Sstevel@tonic-gate } 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate /* 2047c478bd9Sstevel@tonic-gate * finally, set the checksum 2057c478bd9Sstevel@tonic-gate */ 2067c478bd9Sstevel@tonic-gate sum = compute_crc32(*sp_seg_ptr, sizeof (sp_seg_body) - 5); 2077c478bd9Sstevel@tonic-gate for (c = 0; c < 4; c++) { 2087c478bd9Sstevel@tonic-gate (*sp_seg_ptr + sizeof (sp_seg_body) - 4)[c] = 2097c478bd9Sstevel@tonic-gate ((char *)(&sum))[c]; 2107c478bd9Sstevel@tonic-gate } 2117c478bd9Sstevel@tonic-gate *sp_seg_len = sizeof (sp_seg_body); 2127c478bd9Sstevel@tonic-gate return (0); 2137c478bd9Sstevel@tonic-gate } 2147c478bd9Sstevel@tonic-gate 2157c478bd9Sstevel@tonic-gate /* 2167c478bd9Sstevel@tonic-gate * get_spd_data - reads raw data from container 2177c478bd9Sstevel@tonic-gate * parameters: 2187c478bd9Sstevel@tonic-gate * fd file descriptor for SPD device 2197c478bd9Sstevel@tonic-gate * ctr_offset container offset 2207c478bd9Sstevel@tonic-gate * ctr_len container size 2217c478bd9Sstevel@tonic-gate * spd_data buffer to receive SPD data (length ctr_len) 2227c478bd9Sstevel@tonic-gate * return value: 2237c478bd9Sstevel@tonic-gate * 0 - success 2247c478bd9Sstevel@tonic-gate * NZ - error code 2257c478bd9Sstevel@tonic-gate */ 2267c478bd9Sstevel@tonic-gate int 2277c478bd9Sstevel@tonic-gate get_spd_data(int fd, char *spd_data, size_t ctr_len, off_t ctr_offset) 2287c478bd9Sstevel@tonic-gate { 2297c478bd9Sstevel@tonic-gate if (ctr_len < sizeof (spd_data_t)) 2307c478bd9Sstevel@tonic-gate return (EINVAL); 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate (void) memset(spd_data, 0, ctr_len); 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate if (pread(fd, spd_data, sizeof (spd_data_t), ctr_offset) != 2357c478bd9Sstevel@tonic-gate sizeof (spd_data_t)) 2367c478bd9Sstevel@tonic-gate return (EIO); 2377c478bd9Sstevel@tonic-gate return (0); 2387c478bd9Sstevel@tonic-gate } 239