12cb5535aSrobj /*
22cb5535aSrobj * CDDL HEADER START
32cb5535aSrobj *
42cb5535aSrobj * The contents of this file are subject to the terms of the
52cb5535aSrobj * Common Development and Distribution License (the "License").
62cb5535aSrobj * You may not use this file except in compliance with the License.
72cb5535aSrobj *
82cb5535aSrobj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92cb5535aSrobj * or http://www.opensolaris.org/os/licensing.
102cb5535aSrobj * See the License for the specific language governing permissions
112cb5535aSrobj * and limitations under the License.
122cb5535aSrobj *
132cb5535aSrobj * When distributing Covered Code, include this CDDL HEADER in each
142cb5535aSrobj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152cb5535aSrobj * If applicable, add the following below this CDDL HEADER, with the
162cb5535aSrobj * fields enclosed by brackets "[]" replaced with your own identifying
172cb5535aSrobj * information: Portions Copyright [yyyy] [name of copyright owner]
182cb5535aSrobj *
192cb5535aSrobj * CDDL HEADER END
202cb5535aSrobj */
212cb5535aSrobj
222cb5535aSrobj /*
23c3c82186SSrihari Venkatesan * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
242cb5535aSrobj * Use is subject to license terms.
252cb5535aSrobj */
26*e2336878SRob Johnston /*
27*e2336878SRob Johnston * Copyright 2019 Joyent, Inc.
28*e2336878SRob Johnston */
292cb5535aSrobj
302cb5535aSrobj #include <stdio.h>
312cb5535aSrobj #include <stdlib.h>
322cb5535aSrobj #include <stdarg.h>
332cb5535aSrobj #include <string.h>
342cb5535aSrobj #include <strings.h>
352cb5535aSrobj #include <libnvpair.h>
362cb5535aSrobj #include <sys/types.h>
372cb5535aSrobj #include <libipmi.h>
382cb5535aSrobj #include <fm/topo_mod.h>
392cb5535aSrobj #include <ctype.h>
402cb5535aSrobj #include "chip.h"
412cb5535aSrobj
422cb5535aSrobj #define BUFSZ 128
4388045cffSRobert Johnston #define JEDEC_TBL_SZ 5
442cb5535aSrobj
452cb5535aSrobj /*
462cb5535aSrobj * The following table maps DIMM manufacturer names to a JEDEC ID as sourced
472cb5535aSrobj * from JEDEC publication JEP106W. This is (obviously) a sparse table which
482cb5535aSrobj * only contains entries for manufacturers whose DIMM's have been qualified
492cb5535aSrobj * for use on Sun platforms.
502cb5535aSrobj */
512cb5535aSrobj static const char *jedec_tbl[JEDEC_TBL_SZ][2] =
522cb5535aSrobj {
5388045cffSRobert Johnston { "HYUNDAI ELECTRONICS", "00AD" },
542cb5535aSrobj { "INFINEON", "00C1" },
552cb5535aSrobj { "MICRON TECHNOLOGY", "002C" },
562cb5535aSrobj { "QIMONDA", "7F51" },
572cb5535aSrobj { "SAMSUNG", "00CE" },
582cb5535aSrobj };
592cb5535aSrobj
602cb5535aSrobj static int
ipmi_serial_lookup(topo_mod_t * mod,char * ipmi_tag,char * buf)612cb5535aSrobj ipmi_serial_lookup(topo_mod_t *mod, char *ipmi_tag, char *buf)
622cb5535aSrobj {
632cb5535aSrobj char *fru_data;
642cb5535aSrobj int i, found_id = 0, serial_len;
652cb5535aSrobj ipmi_handle_t *hdl;
662cb5535aSrobj ipmi_sdr_fru_locator_t *fru_loc;
672cb5535aSrobj ipmi_fru_prod_info_t prod_info;
682cb5535aSrobj
692cb5535aSrobj topo_mod_dprintf(mod, "ipmi_serial_lookup() called\n");
700b1b4412SEric Schrock if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
712cb5535aSrobj topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
722cb5535aSrobj return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
732cb5535aSrobj }
742cb5535aSrobj
752cb5535aSrobj topo_mod_dprintf(mod, "Looking up FRU data for %s ...\n", ipmi_tag);
762cb5535aSrobj if ((fru_loc = ipmi_sdr_lookup_fru(hdl, (const char *)ipmi_tag))
772cb5535aSrobj == NULL) {
782cb5535aSrobj topo_mod_dprintf(mod, "Failed to lookup %s (%s)\n", ipmi_tag,
792cb5535aSrobj ipmi_errmsg(hdl));
800b1b4412SEric Schrock topo_mod_ipmi_rele(mod);
812cb5535aSrobj return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
822cb5535aSrobj }
832cb5535aSrobj
842cb5535aSrobj
852cb5535aSrobj topo_mod_dprintf(mod, "Reading FRU data ...\n");
862cb5535aSrobj if (ipmi_fru_read(hdl, fru_loc, &fru_data) < 0) {
872cb5535aSrobj topo_mod_dprintf(mod, "Failed to read FRU data (%s)\n",
882cb5535aSrobj ipmi_errmsg(hdl));
890b1b4412SEric Schrock topo_mod_ipmi_rele(mod);
902cb5535aSrobj return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
912cb5535aSrobj }
922cb5535aSrobj
932cb5535aSrobj topo_mod_dprintf(mod, "Parsing product info area ...\n");
942cb5535aSrobj if (ipmi_fru_parse_product(hdl, fru_data, &prod_info) < 0) {
952cb5535aSrobj topo_mod_dprintf(mod, "Failed to read FRU product info (%s)\n",
962cb5535aSrobj ipmi_errmsg(hdl));
972cb5535aSrobj free(fru_data);
980b1b4412SEric Schrock topo_mod_ipmi_rele(mod);
992cb5535aSrobj return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1002cb5535aSrobj }
1012cb5535aSrobj free(fru_data);
1020b1b4412SEric Schrock topo_mod_ipmi_rele(mod);
1032cb5535aSrobj
1042cb5535aSrobj topo_mod_dprintf(mod, "FRU Product Serial: %s\n",
1052cb5535aSrobj prod_info.ifpi_product_serial);
1062cb5535aSrobj topo_mod_dprintf(mod, "Manufacturer Name: \"%s\"\n",
1072cb5535aSrobj prod_info.ifpi_manuf_name);
1082cb5535aSrobj
1092cb5535aSrobj serial_len = strnlen(prod_info.ifpi_product_serial, FRU_INFO_MAXLEN);
1102cb5535aSrobj
1112cb5535aSrobj /*
1122cb5535aSrobj * Newer ILOM software that has the fix for CR 6607996 will have
1132cb5535aSrobj * an 18-character serial number that has been synthesized using
1142cb5535aSrobj * the recipe from the Sun SPD JEDEC DIMM specification. If we
1152cb5535aSrobj * find an 18-character then we'll simply use it, as-is, and
1162cb5535aSrobj * return.
1172cb5535aSrobj */
1182cb5535aSrobj if (serial_len == 18) {
1192cb5535aSrobj (void) memcpy(buf, prod_info.ifpi_product_serial, 18);
1202cb5535aSrobj *(buf+18) = '\0';
1212cb5535aSrobj return (0);
1222cb5535aSrobj }
1232cb5535aSrobj /*
1242cb5535aSrobj * Older ILOM software that DOESN'T have the fix for CR 6607996 will
1252cb5535aSrobj * only provide the 8 character manufacturer serial number.
1262cb5535aSrobj *
1272cb5535aSrobj * However, if for some reason the product info area doesn't have the
1282cb5535aSrobj * serial information or if the serial isn't 8 characters (we may
1292cb5535aSrobj * encounter SP's that don't populate the serial field or are buggy and
1302cb5535aSrobj * populate it with garbage), then we'll stop right now and just set the
1312cb5535aSrobj * buf to an empty string.
1322cb5535aSrobj */
1332cb5535aSrobj if (serial_len != 8) {
1342cb5535aSrobj *buf = '\0';
1352cb5535aSrobj return (0);
1362cb5535aSrobj }
1372cb5535aSrobj
1382cb5535aSrobj /*
1392cb5535aSrobj * What follows is a very crude adaptation of the recipe from the
1402cb5535aSrobj * Sun SPD JEDEC DIMM specification for synthesizing globally unique
1412cb5535aSrobj * serial numbers from the 8 character manufacturer serial number.
1422cb5535aSrobj *
1432cb5535aSrobj * The Sun serial number takes the following form:
1442cb5535aSrobj *
1452cb5535aSrobj * jjjjllyywwssssssss
1462cb5535aSrobj *
1472cb5535aSrobj * The components are:
1482cb5535aSrobj *
1492cb5535aSrobj * yyyy: JEDEC ID in hex (2 byte manufacture ID, 2 byte continuation
150*e2336878SRob Johnston * code).
1512cb5535aSrobj *
1522cb5535aSrobj * ll: The memory module's manufacturing location.
1532cb5535aSrobj *
1542cb5535aSrobj * yyww: The module's manufacturing date (2-digit year/2-digit week)
1552cb5535aSrobj *
1562cb5535aSrobj * ssssssss: The 8 character maufacturer serial number
1572cb5535aSrobj */
1582cb5535aSrobj /*
1592cb5535aSrobj * First we need to normalize the manufacturer name we pulled out of
1602cb5535aSrobj * the FRU product info area. Our normalization algorithm is fairly
1612cb5535aSrobj * simple:
1622cb5535aSrobj * - convert all alpha chars to uppercase
1632cb5535aSrobj * - convert non-alphanumeric characters to a single space
1642cb5535aSrobj *
1652cb5535aSrobj * We use the normalized name to lookup the JEDEC ID from a static
1662cb5535aSrobj * table. If the FRU area didn't have a manufacturer name or if the ID
1672cb5535aSrobj * lookup fails we'll set jjjj to 0000.
1682cb5535aSrobj */
1692cb5535aSrobj for (i = 0; prod_info.ifpi_manuf_name[i]; i++) {
1702cb5535aSrobj prod_info.ifpi_manuf_name[i] =
1712cb5535aSrobj toupper(prod_info.ifpi_manuf_name[i]);
1722cb5535aSrobj if (!isalpha(prod_info.ifpi_manuf_name[i]) &&
1732cb5535aSrobj !isdigit(prod_info.ifpi_manuf_name[i]))
1742cb5535aSrobj prod_info.ifpi_manuf_name[i] = (char)0x20;
1752cb5535aSrobj }
1762cb5535aSrobj topo_mod_dprintf(mod, "Normalized Manufacturer Name \"%s\"\n",
1772cb5535aSrobj prod_info.ifpi_manuf_name);
1782cb5535aSrobj
1792cb5535aSrobj for (i = 0; i < JEDEC_TBL_SZ; i++)
1802cb5535aSrobj if (strcmp(prod_info.ifpi_manuf_name, jedec_tbl[i][0]) == 0) {
1812cb5535aSrobj found_id = 1;
1822cb5535aSrobj break;
1832cb5535aSrobj }
1842cb5535aSrobj
1852cb5535aSrobj if (found_id)
1862cb5535aSrobj (void) memcpy(buf, jedec_tbl[i][1], 4);
1872cb5535aSrobj else
1882cb5535aSrobj (void) memcpy(buf, (char *)("0000"), 4);
1892cb5535aSrobj
1902cb5535aSrobj /*
1912cb5535aSrobj * The manufacturing location and date is not available via IPMI on
1922cb5535aSrobj * Sun platforms, so we simply set these six digits to zeros.
1932cb5535aSrobj */
1942cb5535aSrobj (void) memcpy((buf+4), (char *)("000000"), 6);
1952cb5535aSrobj
1962cb5535aSrobj /*
1972cb5535aSrobj * Finally, we just copy the 8 character product serial straight over
1982cb5535aSrobj * and then NULL terminate the string.
1992cb5535aSrobj */
2002cb5535aSrobj (void) memcpy((buf+10), prod_info.ifpi_product_serial, 8);
2012cb5535aSrobj *(buf+18) = '\0';
2022cb5535aSrobj
2032cb5535aSrobj return (0);
2042cb5535aSrobj }
2052cb5535aSrobj
2062cb5535aSrobj /* ARGSUSED */
2072cb5535aSrobj int
get_dimm_serial(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)2082cb5535aSrobj get_dimm_serial(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
2092cb5535aSrobj nvlist_t *in, nvlist_t **out)
2102cb5535aSrobj {
21188045cffSRobert Johnston char **entity_refs, fru_serial[FRU_INFO_MAXLEN];
21288045cffSRobert Johnston int err, rv = 0, i;
21388045cffSRobert Johnston uint_t nelems;
21488045cffSRobert Johnston boolean_t found_serial = B_FALSE;
21588045cffSRobert Johnston
21688045cffSRobert Johnston if (topo_prop_get_string_array(node, TOPO_PGROUP_IPMI, "entity_ref",
21788045cffSRobert Johnston &entity_refs, &nelems, &err) != 0) {
21888045cffSRobert Johnston topo_mod_dprintf(mod, "%s: Failed to lookup entity_ref property"
21988045cffSRobert Johnston " (%s)", __func__, topo_strerror(err));
2202cb5535aSrobj return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
2212cb5535aSrobj }
2222cb5535aSrobj
22388045cffSRobert Johnston for (i = 0; i < nelems; i++) {
22488045cffSRobert Johnston if (ipmi_serial_lookup(mod, entity_refs[i], fru_serial) == 0) {
22588045cffSRobert Johnston found_serial = B_TRUE;
22688045cffSRobert Johnston break;
22788045cffSRobert Johnston } else
22888045cffSRobert Johnston topo_mod_dprintf(mod, "Failed to lookup serial for "
22988045cffSRobert Johnston "%s\n", entity_refs[i]);
230c3c82186SSrihari Venkatesan }
23188045cffSRobert Johnston if (! found_serial)
232c3c82186SSrihari Venkatesan (void) strcpy(fru_serial, "");
233c3c82186SSrihari Venkatesan
234c3c82186SSrihari Venkatesan if (store_prop_val(mod, fru_serial, "serial", out) != 0) {
235c3c82186SSrihari Venkatesan topo_mod_dprintf(mod, "Failed to set serial\n");
236c3c82186SSrihari Venkatesan /* topo errno already set */
23788045cffSRobert Johnston rv = -1;
238c3c82186SSrihari Venkatesan }
239*e2336878SRob Johnston topo_mod_strfreev(mod, entity_refs, nelems);
24088045cffSRobert Johnston
24188045cffSRobert Johnston return (rv);
242c3c82186SSrihari Venkatesan }
243