xref: /illumos-gate/usr/src/lib/libsmbios/common/smb_lib.c (revision ce326879a41b052db3abafb44e551f9d9c40cdba)
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 /*
24  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/smbios_impl.h>
30 #include <sys/sysmacros.h>
31 #include <sys/stat.h>
32 #include <sys/mman.h>
33 
34 #include <alloca.h>
35 #include <limits.h>
36 #include <unistd.h>
37 #include <strings.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <libdevinfo.h>
42 
43 #pragma init(smb_init)
44 static void
45 smb_init(void)
46 {
47 	_smb_debug = getenv("SMB_DEBUG") != NULL;
48 }
49 
50 static smbios_hdl_t *
51 smb_fileopen(int fd, int version, int flags, int *errp)
52 {
53 	smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN);
54 	smbios_entry_point_t ep_type;
55 	smbios_hdl_t *shp = NULL;
56 	uint32_t smbe_stlen;
57 	off64_t smbe_staddr;
58 	ssize_t n, elen;
59 	void *stbuf;
60 
61 	if ((n = pread64(fd, ep, sizeof (*ep), 0)) != sizeof (*ep))
62 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR));
63 
64 	if (strncmp(ep->ep21.smbe_eanchor, SMB_ENTRY_EANCHOR,
65 	    SMB_ENTRY_EANCHORLEN) == 0) {
66 		ep_type = SMBIOS_ENTRY_POINT_21;
67 		elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN);
68 	} else if (strncmp(ep->ep30.smbe_eanchor, SMB3_ENTRY_EANCHOR,
69 	    SMB3_ENTRY_EANCHORLEN) == 0) {
70 		ep_type = SMBIOS_ENTRY_POINT_30;
71 		elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN);
72 	} else {
73 		return (smb_open_error(shp, errp, ESMB_HEADER));
74 	}
75 
76 	if ((n = pread64(fd, ep, elen, 0)) != elen)
77 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR));
78 
79 	if (ep_type == SMBIOS_ENTRY_POINT_21) {
80 		smbe_stlen = ep->ep21.smbe_stlen;
81 		smbe_staddr = (off64_t)ep->ep21.smbe_staddr;
82 	} else {
83 		smbe_stlen = ep->ep30.smbe_stlen;
84 		smbe_staddr = (off64_t)ep->ep30.smbe_staddr;
85 	}
86 	stbuf = smb_alloc(smbe_stlen);
87 
88 	if (stbuf == NULL)
89 		return (smb_open_error(shp, errp, ESMB_NOMEM));
90 
91 	if ((n = pread64(fd, stbuf, smbe_stlen, smbe_staddr)) != smbe_stlen) {
92 		smb_free(stbuf, smbe_stlen);
93 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOSTAB));
94 	}
95 
96 	shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, errp);
97 
98 	if (shp != NULL)
99 		shp->sh_flags |= SMB_FL_BUFALLOC;
100 	else
101 		smb_free(stbuf, smbe_stlen);
102 
103 	return (shp);
104 }
105 
106 static smbios_hdl_t *
107 smb_biosopen(int fd, int version, int flags, int *errp)
108 {
109 	smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN);
110 	smbios_entry_point_t ep_type;
111 	smbios_hdl_t *shp = NULL;
112 	size_t elen, pgsize, pgmask, pgoff;
113 	void *stbuf, *bios, *p, *q;
114 	di_node_t root;
115 	int64_t *val64;
116 	uint32_t smbe_stlen;
117 	off64_t smbe_staddr;
118 
119 	bios = MAP_FAILED;
120 	if ((root = di_init("/", DINFOPROP)) != DI_NODE_NIL) {
121 		if (di_prop_lookup_int64(DDI_DEV_T_ANY, root,
122 		    "smbios-address", &val64) == 1) {
123 			bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1,
124 			    PROT_READ, MAP_SHARED, fd, (off_t)*val64);
125 		}
126 		di_fini(root);
127 	}
128 	if (bios == MAP_FAILED) {
129 		bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1,
130 		    PROT_READ, MAP_SHARED, fd, (uint32_t)SMB_RANGE_START);
131 	}
132 
133 	if (bios == MAP_FAILED)
134 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
135 
136 	q = (void *)((uintptr_t)bios + SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
137 
138 	for (p = bios; p < q; p = (void *)((uintptr_t)p + SMB_SCAN_STEP)) {
139 		if (strncmp(p, SMB_ENTRY_EANCHOR, SMB_ENTRY_EANCHORLEN) == 0) {
140 			ep_type = SMBIOS_ENTRY_POINT_21;
141 			elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN);
142 			break;
143 		}
144 		if (strncmp(p, SMB3_ENTRY_EANCHOR,
145 		    SMB3_ENTRY_EANCHORLEN) == 0) {
146 			ep_type = SMBIOS_ENTRY_POINT_30;
147 			elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN);
148 			break;
149 		}
150 	}
151 
152 	if (p >= q) {
153 		(void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
154 		return (smb_open_error(NULL, errp, ESMB_NOTFOUND));
155 	}
156 
157 	bcopy(p, ep, sizeof (smbios_entry_t));
158 	bcopy(p, ep, elen);
159 	(void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
160 
161 	switch (ep_type) {
162 	case SMBIOS_ENTRY_POINT_21:
163 		smbe_stlen = ep->ep21.smbe_stlen;
164 		smbe_staddr = ep->ep21.smbe_staddr;
165 		break;
166 	case SMBIOS_ENTRY_POINT_30:
167 		smbe_stlen = ep->ep30.smbe_stlen;
168 		smbe_staddr = ep->ep30.smbe_staddr;
169 		break;
170 	default:
171 		return (smb_open_error(NULL, errp, ESMB_VERSION));
172 	}
173 
174 	pgsize = getpagesize();
175 	pgmask = ~(pgsize - 1);
176 	pgoff = smbe_staddr & ~pgmask;
177 
178 	bios = mmap(NULL, smbe_stlen + pgoff,
179 	    PROT_READ, MAP_SHARED, fd, smbe_staddr & pgmask);
180 
181 	if (bios == MAP_FAILED)
182 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
183 
184 	if ((stbuf = smb_alloc(smbe_stlen)) == NULL) {
185 		(void) munmap(bios, smbe_stlen + pgoff);
186 		return (smb_open_error(shp, errp, ESMB_NOMEM));
187 	}
188 
189 	bcopy((char *)bios + pgoff, stbuf, smbe_stlen);
190 	(void) munmap(bios, smbe_stlen + pgoff);
191 	shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, errp);
192 
193 	if (shp != NULL)
194 		shp->sh_flags |= SMB_FL_BUFALLOC;
195 	else
196 		smb_free(stbuf, smbe_stlen);
197 
198 	return (shp);
199 }
200 
201 smbios_hdl_t *
202 smbios_fdopen(int fd, int version, int flags, int *errp)
203 {
204 	struct stat64 st1, st2;
205 
206 	if (stat64(SMB_BIOS_DEVICE, &st1) == 0 && fstat64(fd, &st2) == 0 &&
207 	    S_ISCHR(st2.st_mode) && st1.st_rdev == st2.st_rdev)
208 		return (smb_biosopen(fd, version, flags, errp));
209 	else
210 		return (smb_fileopen(fd, version, flags, errp));
211 }
212 
213 smbios_hdl_t *
214 smbios_open(const char *file, int version, int flags, int *errp)
215 {
216 	smbios_hdl_t *shp;
217 	int fd;
218 
219 	if ((fd = open64(file ? file : SMB_SMBIOS_DEVICE, O_RDONLY)) == -1) {
220 		if ((errno == ENOENT || errno == ENXIO) &&
221 		    (file == NULL || strcmp(file, SMB_SMBIOS_DEVICE) == 0))
222 			errno = ESMB_NOTFOUND;
223 		return (smb_open_error(NULL, errp, errno));
224 	}
225 
226 	shp = smbios_fdopen(fd, version, flags, errp);
227 	(void) close(fd);
228 	return (shp);
229 }
230 
231 static int
232 smbios_xwrite(smbios_hdl_t *shp, int fd, const void *buf, size_t buflen)
233 {
234 	ssize_t resid = buflen;
235 	ssize_t len;
236 
237 	while (resid != 0) {
238 		if ((len = write(fd, buf, resid)) <= 0)
239 			return (smb_set_errno(shp, errno));
240 		resid -= len;
241 		buf = (uchar_t *)buf + len;
242 	}
243 
244 	return (0);
245 }
246 
247 int
248 smbios_write(smbios_hdl_t *shp, int fd)
249 {
250 	smbios_entry_t ep;
251 	off64_t off = lseek64(fd, 0, SEEK_CUR) + P2ROUNDUP(sizeof (ep), 16);
252 
253 	if (off > UINT32_MAX)
254 		return (smb_set_errno(shp, EOVERFLOW));
255 
256 	bcopy(&shp->sh_ent, &ep, sizeof (ep));
257 	if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_21)
258 		ep.ep21.smbe_staddr = (uint32_t)off;
259 	else if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_30)
260 		ep.ep30.smbe_staddr = (uint64_t)off;
261 	else
262 		return (-1);
263 
264 	smbios_checksum(shp, &ep);
265 
266 	if (smbios_xwrite(shp, fd, &ep, sizeof (ep)) == -1 ||
267 	    lseek64(fd, off, SEEK_SET) != off ||
268 	    smbios_xwrite(shp, fd, shp->sh_buf, shp->sh_buflen) == -1)
269 		return (-1);
270 
271 	return (0);
272 }
273