/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Retrieval of the DIMM serial number from data encoded in the SPD and * SEEPROM formats. */ #include #include #include #include #include #include #include #include #include #include #include #include #define BUFSIZ_SPD 256 #define BUFSIZ_SEEPROM 8192 /* * SEEPROMs are composed of self-describing containers. The first container, * starting at offset 0, is r/w. The second container, starting at 0x1800, is * r/o, and contains identification information supplied by the manufacturer. */ #define SEEPROM_OFFSET_RO 0x1800 static int mem_get_spd_serid(const char *buf, size_t bufsz, char *serid, size_t seridsz) { static const char hex_digits[] = "0123456789ABCDEF"; spd_data_t *spd = (spd_data_t *)buf; char *c; int i; if (bufsz < sizeof (spd_data_t)) return (fmd_fmri_set_errno(EINVAL)); if (seridsz < sizeof (spd->asmb_serial_no) * 2 + 1) return (fmd_fmri_set_errno(EINVAL)); for (c = serid, i = 0; i < sizeof (spd->asmb_serial_no); i++) { *c++ = hex_digits[spd->asmb_serial_no[i] >> 4]; *c++ = hex_digits[spd->asmb_serial_no[i] & 0xf]; } *c = '\0'; return (0); } static void * seeprom_seg_lookup(const char *buf, size_t bufsz, char *segname, size_t *segszp) { seeprom_container_t *sc; seeprom_seg_t *segp, seg; int sidx; if (strlen(segname) != sizeof (seg.sees_name)) return (NULL); sc = (seeprom_container_t *)(buf + SEEPROM_OFFSET_RO); /* Validate sc size then dereference it */ if (bufsz < SEEPROM_OFFSET_RO + sizeof (seeprom_container_t) || bufsz < SEEPROM_OFFSET_RO + sizeof (seeprom_container_t) + sc->seec_contsz) return (NULL); if (sc->seec_tag == 0 || sc->seec_contsz == 0 || sc->seec_nsegs == 0) return (NULL); for (sidx = 0; sidx < sc->seec_nsegs; sidx++) { /* LINTED - pointer alignment */ segp = ((seeprom_seg_t *)(sc + 1)) + sidx; bcopy(segp, &seg, sizeof (seeprom_seg_t)); seg.sees_segoff = ntohs(seg.sees_segoff); seg.sees_seglen = ntohs(seg.sees_seglen); if (bufsz < seg.sees_segoff + seg.sees_seglen) return (NULL); if (strncmp(segname, seg.sees_name, sizeof (seg.sees_name)) == 0) { *segszp = seg.sees_seglen; return ((void *)(buf + seg.sees_segoff)); } } return (NULL); } static int mem_get_seeprom_serid(const char *buf, size_t bufsz, char *serid, size_t seridsz) { seeprom_seg_sd_t *sd; size_t segsz; if (seridsz < sizeof (sd->seesd_sun_sno) + 1) return (fmd_fmri_set_errno(EINVAL)); if ((sd = seeprom_seg_lookup(buf, bufsz, "SD", &segsz)) == NULL) return (fmd_fmri_set_errno(EINVAL)); if (segsz < sizeof (seeprom_seg_sd_t)) return (fmd_fmri_set_errno(EINVAL)); bcopy(sd->seesd_sun_sno, serid, sizeof (sd->seesd_sun_sno)); serid[sizeof (sd->seesd_sun_sno)] = '\0'; return (0); } int mem_get_serid(const char *device, char *serid, size_t seridsz) { char buf[8192]; int fd; ssize_t sz; if ((fd = open(device, O_RDONLY)) < 0) return (-1); /* errno is set for us */ if ((sz = read(fd, buf, sizeof (buf))) < 0) { int err = errno; (void) close(fd); return (fmd_fmri_set_errno(err)); } (void) close(fd); switch (sz) { case BUFSIZ_SPD: return (mem_get_spd_serid(buf, BUFSIZ_SPD, serid, seridsz)); case BUFSIZ_SEEPROM: return (mem_get_seeprom_serid(buf, BUFSIZ_SEEPROM, serid, seridsz)); default: return (fmd_fmri_set_errno(EINVAL)); } }