xref: /illumos-gate/usr/src/cmd/fm/schemes/mem/mem_read.c (revision 2a8bcb4e)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Retrieval of the DIMM serial number from data encoded in the SPD and
29  * SEEPROM formats.
30  */
31 
32 #include <mem_spd.h>
33 #include <mem_seeprom.h>
34 #include <mem.h>
35 
36 #include <fm/fmd_fmri.h>
37 
38 #include <stdio.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #include <strings.h>
43 #include <sys/byteorder.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 
47 #define	BUFSIZ_SPD	256
48 #define	BUFSIZ_SEEPROM	8192
49 
50 /*
51  * SEEPROMs are composed of self-describing containers.  The first container,
52  * starting at offset 0, is r/w.  The second container, starting at 0x1800, is
53  * r/o, and contains identification information supplied by the manufacturer.
54  */
55 #define	SEEPROM_OFFSET_RO	0x1800
56 
57 static int
mem_get_spd_serid(const char * buf,size_t bufsz,char * serid,size_t seridsz)58 mem_get_spd_serid(const char *buf, size_t bufsz, char *serid, size_t seridsz)
59 {
60 	static const char hex_digits[] = "0123456789ABCDEF";
61 	spd_data_t *spd = (spd_data_t *)buf;
62 	char *c;
63 	int i;
64 
65 	if (bufsz < sizeof (spd_data_t))
66 		return (fmd_fmri_set_errno(EINVAL));
67 
68 	if (seridsz < sizeof (spd->asmb_serial_no) * 2 + 1)
69 		return (fmd_fmri_set_errno(EINVAL));
70 
71 	for (c = serid, i = 0; i < sizeof (spd->asmb_serial_no); i++) {
72 		*c++ = hex_digits[spd->asmb_serial_no[i] >> 4];
73 		*c++ = hex_digits[spd->asmb_serial_no[i] & 0xf];
74 	}
75 	*c = '\0';
76 
77 	return (0);
78 }
79 
80 static void *
seeprom_seg_lookup(const char * buf,size_t bufsz,char * segname,size_t * segszp)81 seeprom_seg_lookup(const char *buf, size_t bufsz, char *segname, size_t *segszp)
82 {
83 	seeprom_container_t *sc;
84 	seeprom_seg_t *segp, seg;
85 	int sidx;
86 
87 	if (strlen(segname) != sizeof (seg.sees_name))
88 		return (NULL);
89 
90 	sc = (seeprom_container_t *)(buf + SEEPROM_OFFSET_RO);
91 
92 	/* Validate sc size then dereference it */
93 	if (bufsz < SEEPROM_OFFSET_RO + sizeof (seeprom_container_t) ||
94 	    bufsz < SEEPROM_OFFSET_RO + sizeof (seeprom_container_t) +
95 	    sc->seec_contsz)
96 		return (NULL);
97 
98 	if (sc->seec_tag == 0 || sc->seec_contsz == 0 ||
99 	    sc->seec_nsegs == 0)
100 		return (NULL);
101 
102 	for (sidx = 0; sidx < sc->seec_nsegs; sidx++) {
103 		/* LINTED - pointer alignment */
104 		segp = ((seeprom_seg_t *)(sc + 1)) + sidx;
105 
106 		bcopy(segp, &seg, sizeof (seeprom_seg_t));
107 		seg.sees_segoff = ntohs(seg.sees_segoff);
108 		seg.sees_seglen = ntohs(seg.sees_seglen);
109 
110 		if (bufsz < seg.sees_segoff + seg.sees_seglen)
111 			return (NULL);
112 
113 		if (strncmp(segname, seg.sees_name,
114 		    sizeof (seg.sees_name)) == 0) {
115 			*segszp = seg.sees_seglen;
116 			return ((void *)(buf + seg.sees_segoff));
117 		}
118 
119 	}
120 
121 	return (NULL);
122 }
123 
124 static int
mem_get_seeprom_serid(const char * buf,size_t bufsz,char * serid,size_t seridsz)125 mem_get_seeprom_serid(const char *buf, size_t bufsz, char *serid,
126     size_t seridsz)
127 {
128 	seeprom_seg_sd_t *sd;
129 	size_t segsz;
130 
131 	if (seridsz < sizeof (sd->seesd_sun_sno) + 1)
132 		return (fmd_fmri_set_errno(EINVAL));
133 
134 	if ((sd = seeprom_seg_lookup(buf, bufsz, "SD", &segsz)) == NULL)
135 		return (fmd_fmri_set_errno(EINVAL));
136 
137 	if (segsz < sizeof (seeprom_seg_sd_t))
138 		return (fmd_fmri_set_errno(EINVAL));
139 
140 	bcopy(sd->seesd_sun_sno, serid, sizeof (sd->seesd_sun_sno));
141 	serid[sizeof (sd->seesd_sun_sno)] = '\0';
142 
143 	return (0);
144 }
145 
146 int
mem_get_serid(const char * device,char * serid,size_t seridsz)147 mem_get_serid(const char *device, char *serid, size_t seridsz)
148 {
149 	char buf[8192];
150 	int fd;
151 	ssize_t sz;
152 
153 	if ((fd = open(device, O_RDONLY)) < 0)
154 		return (-1); /* errno is set for us */
155 
156 	if ((sz = read(fd, buf, sizeof (buf))) < 0) {
157 		int err = errno;
158 		(void) close(fd);
159 		return (fmd_fmri_set_errno(err));
160 	}
161 
162 	(void) close(fd);
163 
164 	switch (sz) {
165 	case BUFSIZ_SPD:
166 		return (mem_get_spd_serid(buf, BUFSIZ_SPD, serid, seridsz));
167 	case BUFSIZ_SEEPROM:
168 		return (mem_get_seeprom_serid(buf, BUFSIZ_SEEPROM, serid,
169 		    seridsz));
170 	default:
171 		return (fmd_fmri_set_errno(EINVAL));
172 	}
173 }
174