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