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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <stdarg.h> 30 #include <string.h> 31 #include <strings.h> 32 #include <libnvpair.h> 33 #include <sys/types.h> 34 #include <libipmi.h> 35 #include <fm/topo_mod.h> 36 #include <ctype.h> 37 #include "chip.h" 38 39 #define BUFSZ 128 40 #define JEDEC_TBL_SZ 5 41 42 /* 43 * The following table maps DIMM manufacturer names to a JEDEC ID as sourced 44 * from JEDEC publication JEP106W. This is (obviously) a sparse table which 45 * only contains entries for manufacturers whose DIMM's have been qualified 46 * for use on Sun platforms. 47 */ 48 static const char *jedec_tbl[JEDEC_TBL_SZ][2] = 49 { 50 { "HYUNDAI ELECTRONICS", "00AD" }, 51 { "INFINEON", "00C1" }, 52 { "MICRON TECHNOLOGY", "002C" }, 53 { "QIMONDA", "7F51" }, 54 { "SAMSUNG", "00CE" }, 55 }; 56 57 static int 58 ipmi_serial_lookup(topo_mod_t *mod, char *ipmi_tag, char *buf) 59 { 60 char *fru_data; 61 int i, found_id = 0, serial_len; 62 ipmi_handle_t *hdl; 63 ipmi_sdr_fru_locator_t *fru_loc; 64 ipmi_fru_prod_info_t prod_info; 65 66 topo_mod_dprintf(mod, "ipmi_serial_lookup() called\n"); 67 if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) { 68 topo_mod_dprintf(mod, "Failed to get IPMI handle\n"); 69 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 70 } 71 72 topo_mod_dprintf(mod, "Looking up FRU data for %s ...\n", ipmi_tag); 73 if ((fru_loc = ipmi_sdr_lookup_fru(hdl, (const char *)ipmi_tag)) 74 == NULL) { 75 topo_mod_dprintf(mod, "Failed to lookup %s (%s)\n", ipmi_tag, 76 ipmi_errmsg(hdl)); 77 topo_mod_ipmi_rele(mod); 78 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 79 } 80 81 82 topo_mod_dprintf(mod, "Reading FRU data ...\n"); 83 if (ipmi_fru_read(hdl, fru_loc, &fru_data) < 0) { 84 topo_mod_dprintf(mod, "Failed to read FRU data (%s)\n", 85 ipmi_errmsg(hdl)); 86 topo_mod_ipmi_rele(mod); 87 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 88 } 89 90 topo_mod_dprintf(mod, "Parsing product info area ...\n"); 91 if (ipmi_fru_parse_product(hdl, fru_data, &prod_info) < 0) { 92 topo_mod_dprintf(mod, "Failed to read FRU product info (%s)\n", 93 ipmi_errmsg(hdl)); 94 free(fru_data); 95 topo_mod_ipmi_rele(mod); 96 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 97 } 98 free(fru_data); 99 topo_mod_ipmi_rele(mod); 100 101 topo_mod_dprintf(mod, "FRU Product Serial: %s\n", 102 prod_info.ifpi_product_serial); 103 topo_mod_dprintf(mod, "Manufacturer Name: \"%s\"\n", 104 prod_info.ifpi_manuf_name); 105 106 serial_len = strnlen(prod_info.ifpi_product_serial, FRU_INFO_MAXLEN); 107 108 /* 109 * Newer ILOM software that has the fix for CR 6607996 will have 110 * an 18-character serial number that has been synthesized using 111 * the recipe from the Sun SPD JEDEC DIMM specification. If we 112 * find an 18-character then we'll simply use it, as-is, and 113 * return. 114 */ 115 if (serial_len == 18) { 116 (void) memcpy(buf, prod_info.ifpi_product_serial, 18); 117 *(buf+18) = '\0'; 118 return (0); 119 } 120 /* 121 * Older ILOM software that DOESN'T have the fix for CR 6607996 will 122 * only provide the 8 character manufacturer serial number. 123 * 124 * However, if for some reason the product info area doesn't have the 125 * serial information or if the serial isn't 8 characters (we may 126 * encounter SP's that don't populate the serial field or are buggy and 127 * populate it with garbage), then we'll stop right now and just set the 128 * buf to an empty string. 129 */ 130 if (serial_len != 8) { 131 *buf = '\0'; 132 return (0); 133 } 134 135 /* 136 * What follows is a very crude adaptation of the recipe from the 137 * Sun SPD JEDEC DIMM specification for synthesizing globally unique 138 * serial numbers from the 8 character manufacturer serial number. 139 * 140 * The Sun serial number takes the following form: 141 * 142 * jjjjllyywwssssssss 143 * 144 * The components are: 145 * 146 * yyyy: JEDEC ID in hex (2 byte manufacture ID, 2 byte continuation 147 * code). 148 * 149 * ll: The memory module's manufacturing location. 150 * 151 * yyww: The module's manufacturing date (2-digit year/2-digit week) 152 * 153 * ssssssss: The 8 character maufacturer serial number 154 */ 155 /* 156 * First we need to normalize the manufacturer name we pulled out of 157 * the FRU product info area. Our normalization algorithm is fairly 158 * simple: 159 * - convert all alpha chars to uppercase 160 * - convert non-alphanumeric characters to a single space 161 * 162 * We use the normalized name to lookup the JEDEC ID from a static 163 * table. If the FRU area didn't have a manufacturer name or if the ID 164 * lookup fails we'll set jjjj to 0000. 165 */ 166 for (i = 0; prod_info.ifpi_manuf_name[i]; i++) { 167 prod_info.ifpi_manuf_name[i] = 168 toupper(prod_info.ifpi_manuf_name[i]); 169 if (!isalpha(prod_info.ifpi_manuf_name[i]) && 170 !isdigit(prod_info.ifpi_manuf_name[i])) 171 prod_info.ifpi_manuf_name[i] = (char)0x20; 172 } 173 topo_mod_dprintf(mod, "Normalized Manufacturer Name \"%s\"\n", 174 prod_info.ifpi_manuf_name); 175 176 for (i = 0; i < JEDEC_TBL_SZ; i++) 177 if (strcmp(prod_info.ifpi_manuf_name, jedec_tbl[i][0]) == 0) { 178 found_id = 1; 179 break; 180 } 181 182 if (found_id) 183 (void) memcpy(buf, jedec_tbl[i][1], 4); 184 else 185 (void) memcpy(buf, (char *)("0000"), 4); 186 187 /* 188 * The manufacturing location and date is not available via IPMI on 189 * Sun platforms, so we simply set these six digits to zeros. 190 */ 191 (void) memcpy((buf+4), (char *)("000000"), 6); 192 193 /* 194 * Finally, we just copy the 8 character product serial straight over 195 * and then NULL terminate the string. 196 */ 197 (void) memcpy((buf+10), prod_info.ifpi_product_serial, 8); 198 *(buf+18) = '\0'; 199 200 return (0); 201 } 202 203 /* ARGSUSED */ 204 int 205 get_dimm_serial(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 206 nvlist_t *in, nvlist_t **out) 207 { 208 char **entity_refs, fru_serial[FRU_INFO_MAXLEN]; 209 int err, rv = 0, i; 210 uint_t nelems; 211 boolean_t found_serial = B_FALSE; 212 213 if (topo_prop_get_string_array(node, TOPO_PGROUP_IPMI, "entity_ref", 214 &entity_refs, &nelems, &err) != 0) { 215 topo_mod_dprintf(mod, "%s: Failed to lookup entity_ref property" 216 " (%s)", __func__, topo_strerror(err)); 217 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 218 } 219 220 for (i = 0; i < nelems; i++) { 221 if (ipmi_serial_lookup(mod, entity_refs[i], fru_serial) == 0) { 222 found_serial = B_TRUE; 223 break; 224 } else 225 topo_mod_dprintf(mod, "Failed to lookup serial for " 226 "%s\n", entity_refs[i]); 227 } 228 if (! found_serial) 229 (void) strcpy(fru_serial, ""); 230 231 if (store_prop_val(mod, fru_serial, "serial", out) != 0) { 232 topo_mod_dprintf(mod, "Failed to set serial\n"); 233 /* topo errno already set */ 234 rv = -1; 235 } 236 for (i = 0; i < nelems; i++) 237 topo_mod_strfree(mod, entity_refs[i]); 238 topo_mod_free(mod, entity_refs, (nelems * sizeof (char *))); 239 240 return (rv); 241 } 242