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