1437Smws /*
2437Smws  * CDDL HEADER START
3437Smws  *
4437Smws  * The contents of this file are subject to the terms of the
5437Smws  * Common Development and Distribution License, Version 1.0 only
6437Smws  * (the "License").  You may not use this file except in compliance
7437Smws  * with the License.
8437Smws  *
9437Smws  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10437Smws  * or http://www.opensolaris.org/os/licensing.
11437Smws  * See the License for the specific language governing permissions
12437Smws  * and limitations under the License.
13437Smws  *
14437Smws  * When distributing Covered Code, include this CDDL HEADER in each
15437Smws  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16437Smws  * If applicable, add the following below this CDDL HEADER, with the
17437Smws  * fields enclosed by brackets "[]" replaced with your own identifying
18437Smws  * information: Portions Copyright [yyyy] [name of copyright owner]
19437Smws  *
20437Smws  * CDDL HEADER END
21437Smws  */
22437Smws 
23437Smws /*
24*1222Smws  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25437Smws  * Use is subject to license terms.
26437Smws  */
27437Smws 
28437Smws #pragma ident	"%Z%%M%	%I%	%E% SMI"
29437Smws 
30437Smws #include <sys/types.h>
31437Smws #include <sys/smbios_impl.h>
32437Smws #include <sys/sysmacros.h>
33437Smws #include <sys/stat.h>
34437Smws #include <sys/mman.h>
35437Smws 
36*1222Smws #include <alloca.h>
37437Smws #include <limits.h>
38437Smws #include <unistd.h>
39437Smws #include <strings.h>
40437Smws #include <stdlib.h>
41437Smws #include <errno.h>
42437Smws #include <fcntl.h>
43437Smws 
44437Smws #pragma init(smb_init)
45437Smws static void
46437Smws smb_init(void)
47437Smws {
48437Smws 	_smb_debug = getenv("SMB_DEBUG") != NULL;
49437Smws }
50437Smws 
51437Smws static smbios_hdl_t *
52437Smws smb_fileopen(int fd, int version, int flags, int *errp)
53437Smws {
54*1222Smws 	smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN);
55437Smws 	smbios_hdl_t *shp = NULL;
56*1222Smws 	ssize_t n, elen;
57437Smws 	void *stbuf;
58437Smws 
59*1222Smws 	if ((n = pread64(fd, ep, sizeof (*ep), 0)) != sizeof (*ep))
60437Smws 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR));
61437Smws 
62*1222Smws 	if (strncmp(ep->smbe_eanchor, SMB_ENTRY_EANCHOR, SMB_ENTRY_EANCHORLEN))
63437Smws 		return (smb_open_error(shp, errp, ESMB_HEADER));
64437Smws 
65*1222Smws 	elen = MIN(ep->smbe_elen, SMB_ENTRY_MAXLEN);
66*1222Smws 
67*1222Smws 	if ((n = pread64(fd, ep, elen, 0)) != elen)
68*1222Smws 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR));
69*1222Smws 
70*1222Smws 	if ((stbuf = smb_alloc(ep->smbe_stlen)) == NULL)
71437Smws 		return (smb_open_error(shp, errp, ESMB_NOMEM));
72437Smws 
73*1222Smws 	if ((n = pread64(fd, stbuf, ep->smbe_stlen,
74*1222Smws 	    (off64_t)ep->smbe_staddr)) != ep->smbe_stlen) {
75*1222Smws 		smb_free(stbuf, ep->smbe_stlen);
76437Smws 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOSTAB));
77437Smws 	}
78437Smws 
79*1222Smws 	shp = smbios_bufopen(ep, stbuf, ep->smbe_stlen, version, flags, errp);
80437Smws 
81437Smws 	if (shp != NULL)
82437Smws 		shp->sh_flags |= SMB_FL_BUFALLOC;
83437Smws 	else
84*1222Smws 		smb_free(stbuf, ep->smbe_stlen);
85437Smws 
86437Smws 	return (shp);
87437Smws }
88437Smws 
89437Smws static smbios_hdl_t *
90437Smws smb_biosopen(int fd, int version, int flags, int *errp)
91437Smws {
92*1222Smws 	smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN);
93437Smws 	smbios_hdl_t *shp = NULL;
94437Smws 	size_t pgsize, pgmask, pgoff;
95437Smws 	void *stbuf, *bios, *p, *q;
96437Smws 
97437Smws 	bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1,
98437Smws 	    PROT_READ, MAP_SHARED, fd, (uint32_t)SMB_RANGE_START);
99437Smws 
100437Smws 	if (bios == MAP_FAILED)
101437Smws 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
102437Smws 
103437Smws 	q = (void *)((uintptr_t)bios + SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
104437Smws 
105437Smws 	for (p = bios; p < q; p = (void *)((uintptr_t)p + 16)) {
106437Smws 		if (strncmp(p, SMB_ENTRY_EANCHOR, SMB_ENTRY_EANCHORLEN) == 0)
107437Smws 			break;
108437Smws 	}
109437Smws 
110437Smws 	if (p >= q) {
111437Smws 		(void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
112437Smws 		return (smb_open_error(NULL, errp, ESMB_NOTFOUND));
113437Smws 	}
114437Smws 
115*1222Smws 	bcopy(p, ep, sizeof (smbios_entry_t));
116*1222Smws 	ep->smbe_elen = MIN(ep->smbe_elen, SMB_ENTRY_MAXLEN);
117*1222Smws 	bcopy(p, ep, ep->smbe_elen);
118437Smws 	(void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
119437Smws 
120437Smws 	pgsize = getpagesize();
121437Smws 	pgmask = ~(pgsize - 1);
122*1222Smws 	pgoff = ep->smbe_staddr & ~pgmask;
123437Smws 
124*1222Smws 	bios = mmap(NULL, ep->smbe_stlen + pgoff,
125*1222Smws 	    PROT_READ, MAP_SHARED, fd, ep->smbe_staddr & pgmask);
126437Smws 
127437Smws 	if (bios == MAP_FAILED)
128437Smws 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
129437Smws 
130*1222Smws 	if ((stbuf = smb_alloc(ep->smbe_stlen)) == NULL) {
131*1222Smws 		(void) munmap(bios, ep->smbe_stlen + pgoff);
132437Smws 		return (smb_open_error(shp, errp, ESMB_NOMEM));
133437Smws 	}
134437Smws 
135*1222Smws 	bcopy((char *)bios + pgoff, stbuf, ep->smbe_stlen);
136*1222Smws 	(void) munmap(bios, ep->smbe_stlen + pgoff);
137*1222Smws 	shp = smbios_bufopen(ep, stbuf, ep->smbe_stlen, version, flags, errp);
138437Smws 
139437Smws 	if (shp != NULL)
140437Smws 		shp->sh_flags |= SMB_FL_BUFALLOC;
141437Smws 	else
142*1222Smws 		smb_free(stbuf, ep->smbe_stlen);
143437Smws 
144437Smws 	return (shp);
145437Smws }
146437Smws 
147437Smws smbios_hdl_t *
148437Smws smbios_fdopen(int fd, int version, int flags, int *errp)
149437Smws {
150437Smws 	struct stat64 st1, st2;
151437Smws 
152437Smws 	if (stat64(SMB_BIOS_DEVICE, &st1) == 0 && fstat64(fd, &st2) == 0 &&
153437Smws 	    S_ISCHR(st2.st_mode) && st1.st_rdev == st2.st_rdev)
154437Smws 		return (smb_biosopen(fd, version, flags, errp));
155437Smws 	else
156437Smws 		return (smb_fileopen(fd, version, flags, errp));
157437Smws }
158437Smws 
159437Smws smbios_hdl_t *
160437Smws smbios_open(const char *file, int version, int flags, int *errp)
161437Smws {
162437Smws 	smbios_hdl_t *shp;
163437Smws 	int fd;
164437Smws 
165437Smws 	if ((fd = open64(file ? file : SMB_SMBIOS_DEVICE, O_RDONLY)) == -1) {
166437Smws 		if ((errno == ENOENT || errno == ENXIO) &&
167437Smws 		    (file == NULL || strcmp(file, SMB_SMBIOS_DEVICE) == 0))
168437Smws 			errno = ESMB_NOTFOUND;
169437Smws 		return (smb_open_error(NULL, errp, errno));
170437Smws 	}
171437Smws 
172437Smws 	shp = smbios_fdopen(fd, version, flags, errp);
173437Smws 	(void) close(fd);
174437Smws 	return (shp);
175437Smws }
176437Smws 
177437Smws static int
178437Smws smbios_xwrite(smbios_hdl_t *shp, int fd, const void *buf, size_t buflen)
179437Smws {
180437Smws 	ssize_t resid = buflen;
181437Smws 	ssize_t len;
182437Smws 
183437Smws 	while (resid != 0) {
184437Smws 		if ((len = write(fd, buf, resid)) <= 0)
185437Smws 			return (smb_set_errno(shp, errno));
186437Smws 		resid -= len;
187437Smws 		buf = (uchar_t *)buf + len;
188437Smws 	}
189437Smws 
190437Smws 	return (0);
191437Smws }
192437Smws 
193437Smws int
194437Smws smbios_write(smbios_hdl_t *shp, int fd)
195437Smws {
196437Smws 	smbios_entry_t ep;
197437Smws 	off64_t off = lseek64(fd, 0, SEEK_CUR) + P2ROUNDUP(sizeof (ep), 16);
198437Smws 
199437Smws 	if (off > UINT32_MAX)
200437Smws 		return (smb_set_errno(shp, EOVERFLOW));
201437Smws 
202437Smws 	bcopy(&shp->sh_ent, &ep, sizeof (ep));
203437Smws 	ep.smbe_staddr = (uint32_t)off;
204437Smws 	smbios_checksum(shp, &ep);
205437Smws 
206437Smws 	if (smbios_xwrite(shp, fd, &ep, sizeof (ep)) == -1 ||
207437Smws 	    lseek64(fd, off, SEEK_SET) != off ||
208437Smws 	    smbios_xwrite(shp, fd, shp->sh_buf, shp->sh_buflen) == -1)
209437Smws 		return (-1);
210437Smws 
211437Smws 	return (0);
212437Smws }
213