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 2008 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 <stdio.h> 30 #include <stdlib.h> 31 #include <stdarg.h> 32 #include <string.h> 33 #include <strings.h> 34 #include <libnvpair.h> 35 #include <sys/types.h> 36 #include <libipmi.h> 37 #include <fm/topo_mod.h> 38 #include <ctype.h> 39 #include "chip.h" 40 41 #define BUFSZ 128 42 #define JEDEC_TBL_SZ 4 43 44 /* 45 * The following table maps DIMM manufacturer names to a JEDEC ID as sourced 46 * from JEDEC publication JEP106W. This is (obviously) a sparse table which 47 * only contains entries for manufacturers whose DIMM's have been qualified 48 * for use on Sun platforms. 49 */ 50 static const char *jedec_tbl[JEDEC_TBL_SZ][2] = 51 { 52 { "INFINEON", "00C1" }, 53 { "MICRON TECHNOLOGY", "002C" }, 54 { "QIMONDA", "7F51" }, 55 { "SAMSUNG", "00CE" }, 56 }; 57 58 static int 59 ipmi_serial_lookup(topo_mod_t *mod, char *ipmi_tag, char *buf) 60 { 61 char *fru_data; 62 int i, found_id = 0, serial_len; 63 ipmi_handle_t *hdl; 64 ipmi_sdr_fru_locator_t *fru_loc; 65 ipmi_fru_prod_info_t prod_info; 66 67 topo_mod_dprintf(mod, "ipmi_serial_lookup() called\n"); 68 if ((hdl = topo_mod_ipmi(mod)) == NULL) { 69 topo_mod_dprintf(mod, "Failed to get IPMI handle\n"); 70 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 71 } 72 73 topo_mod_dprintf(mod, "Looking up FRU data for %s ...\n", ipmi_tag); 74 if ((fru_loc = ipmi_sdr_lookup_fru(hdl, (const char *)ipmi_tag)) 75 == NULL) { 76 topo_mod_dprintf(mod, "Failed to lookup %s (%s)\n", ipmi_tag, 77 ipmi_errmsg(hdl)); 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 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 87 } 88 89 topo_mod_dprintf(mod, "Parsing product info area ...\n"); 90 if (ipmi_fru_parse_product(hdl, fru_data, &prod_info) < 0) { 91 topo_mod_dprintf(mod, "Failed to read FRU product info (%s)\n", 92 ipmi_errmsg(hdl)); 93 free(fru_data); 94 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 95 } 96 free(fru_data); 97 98 topo_mod_dprintf(mod, "FRU Product Serial: %s\n", 99 prod_info.ifpi_product_serial); 100 topo_mod_dprintf(mod, "Manufacturer Name: \"%s\"\n", 101 prod_info.ifpi_manuf_name); 102 103 serial_len = strnlen(prod_info.ifpi_product_serial, FRU_INFO_MAXLEN); 104 105 /* 106 * Newer ILOM software that has the fix for CR 6607996 will have 107 * an 18-character serial number that has been synthesized using 108 * the recipe from the Sun SPD JEDEC DIMM specification. If we 109 * find an 18-character then we'll simply use it, as-is, and 110 * return. 111 */ 112 if (serial_len == 18) { 113 (void) memcpy(buf, prod_info.ifpi_product_serial, 18); 114 *(buf+18) = '\0'; 115 return (0); 116 } 117 /* 118 * Older ILOM software that DOESN'T have the fix for CR 6607996 will 119 * only provide the 8 character manufacturer serial number. 120 * 121 * However, if for some reason the product info area doesn't have the 122 * serial information or if the serial isn't 8 characters (we may 123 * encounter SP's that don't populate the serial field or are buggy and 124 * populate it with garbage), then we'll stop right now and just set the 125 * buf to an empty string. 126 */ 127 if (serial_len != 8) { 128 *buf = '\0'; 129 return (0); 130 } 131 132 /* 133 * What follows is a very crude adaptation of the recipe from the 134 * Sun SPD JEDEC DIMM specification for synthesizing globally unique 135 * serial numbers from the 8 character manufacturer serial number. 136 * 137 * The Sun serial number takes the following form: 138 * 139 * jjjjllyywwssssssss 140 * 141 * The components are: 142 * 143 * yyyy: JEDEC ID in hex (2 byte manufacture ID, 2 byte continuation 144 * code). 145 * 146 * ll: The memory module's manufacturing location. 147 * 148 * yyww: The module's manufacturing date (2-digit year/2-digit week) 149 * 150 * ssssssss: The 8 character maufacturer serial number 151 */ 152 /* 153 * First we need to normalize the manufacturer name we pulled out of 154 * the FRU product info area. Our normalization algorithm is fairly 155 * simple: 156 * - convert all alpha chars to uppercase 157 * - convert non-alphanumeric characters to a single space 158 * 159 * We use the normalized name to lookup the JEDEC ID from a static 160 * table. If the FRU area didn't have a manufacturer name or if the ID 161 * lookup fails we'll set jjjj to 0000. 162 */ 163 for (i = 0; prod_info.ifpi_manuf_name[i]; i++) { 164 prod_info.ifpi_manuf_name[i] = 165 toupper(prod_info.ifpi_manuf_name[i]); 166 if (!isalpha(prod_info.ifpi_manuf_name[i]) && 167 !isdigit(prod_info.ifpi_manuf_name[i])) 168 prod_info.ifpi_manuf_name[i] = (char)0x20; 169 } 170 topo_mod_dprintf(mod, "Normalized Manufacturer Name \"%s\"\n", 171 prod_info.ifpi_manuf_name); 172 173 for (i = 0; i < JEDEC_TBL_SZ; i++) 174 if (strcmp(prod_info.ifpi_manuf_name, jedec_tbl[i][0]) == 0) { 175 found_id = 1; 176 break; 177 } 178 179 if (found_id) 180 (void) memcpy(buf, jedec_tbl[i][1], 4); 181 else 182 (void) memcpy(buf, (char *)("0000"), 4); 183 184 /* 185 * The manufacturing location and date is not available via IPMI on 186 * Sun platforms, so we simply set these six digits to zeros. 187 */ 188 (void) memcpy((buf+4), (char *)("000000"), 6); 189 190 /* 191 * Finally, we just copy the 8 character product serial straight over 192 * and then NULL terminate the string. 193 */ 194 (void) memcpy((buf+10), prod_info.ifpi_product_serial, 8); 195 *(buf+18) = '\0'; 196 197 return (0); 198 } 199 200 /* ARGSUSED */ 201 int 202 get_dimm_serial(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 203 nvlist_t *in, nvlist_t **out) 204 { 205 char *fmtstr, ipmi_tag[BUFSZ], fru_serial[FRU_INFO_MAXLEN]; 206 tnode_t *chip; 207 int ret; 208 uint32_t offset; 209 nvlist_t *args; 210 211 topo_mod_dprintf(mod, "get_dimm_serial() called\n"); 212 if ((ret = nvlist_lookup_nvlist(in, "args", &args)) != 0) { 213 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 214 strerror(ret)); 215 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 216 } 217 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 218 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 219 strerror(ret)); 220 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 221 } 222 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 223 /* topo errno set */ 224 topo_mod_dprintf(mod, "Failed to retrieve format arg\n"); 225 return (-1); 226 } 227 228 chip = topo_node_parent(topo_node_parent(node)); 229 230 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 231 (void) snprintf(ipmi_tag, BUFSZ, fmtstr, topo_node_instance(chip), 232 (topo_node_instance(node) + offset)); 233 234 if (ipmi_serial_lookup(mod, ipmi_tag, fru_serial) != 0) { 235 topo_mod_dprintf(mod, "Failed to lookup serial for %s\n", 236 ipmi_tag); 237 (void) strcpy(fru_serial, ""); 238 } 239 240 if (store_prop_val(mod, fru_serial, "serial", out) != 0) { 241 topo_mod_dprintf(mod, "Failed to set serial\n"); 242 /* topo errno already set */ 243 return (-1); 244 } 245 return (0); 246 } 247