1*437Smws /*
2*437Smws  * CDDL HEADER START
3*437Smws  *
4*437Smws  * The contents of this file are subject to the terms of the
5*437Smws  * Common Development and Distribution License, Version 1.0 only
6*437Smws  * (the "License").  You may not use this file except in compliance
7*437Smws  * with the License.
8*437Smws  *
9*437Smws  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*437Smws  * or http://www.opensolaris.org/os/licensing.
11*437Smws  * See the License for the specific language governing permissions
12*437Smws  * and limitations under the License.
13*437Smws  *
14*437Smws  * When distributing Covered Code, include this CDDL HEADER in each
15*437Smws  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*437Smws  * If applicable, add the following below this CDDL HEADER, with the
17*437Smws  * fields enclosed by brackets "[]" replaced with your own identifying
18*437Smws  * information: Portions Copyright [yyyy] [name of copyright owner]
19*437Smws  *
20*437Smws  * CDDL HEADER END
21*437Smws  */
22*437Smws 
23*437Smws /*
24*437Smws  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25*437Smws  * Use is subject to license terms.
26*437Smws  */
27*437Smws 
28*437Smws #pragma ident	"%Z%%M%	%I%	%E% SMI"
29*437Smws 
30*437Smws #include <sys/types.h>
31*437Smws #include <sys/smbios_impl.h>
32*437Smws #include <sys/sysmacros.h>
33*437Smws #include <sys/stat.h>
34*437Smws #include <sys/mman.h>
35*437Smws 
36*437Smws #include <limits.h>
37*437Smws #include <unistd.h>
38*437Smws #include <strings.h>
39*437Smws #include <stdlib.h>
40*437Smws #include <errno.h>
41*437Smws #include <fcntl.h>
42*437Smws 
43*437Smws #pragma init(smb_init)
44*437Smws static void
45*437Smws smb_init(void)
46*437Smws {
47*437Smws 	_smb_debug = getenv("SMB_DEBUG") != NULL;
48*437Smws }
49*437Smws 
50*437Smws static smbios_hdl_t *
51*437Smws smb_fileopen(int fd, int version, int flags, int *errp)
52*437Smws {
53*437Smws 	smbios_hdl_t *shp = NULL;
54*437Smws 	smbios_entry_t ep;
55*437Smws 	void *stbuf;
56*437Smws 	ssize_t n;
57*437Smws 
58*437Smws 	if ((n = pread64(fd, &ep, sizeof (ep), 0)) != sizeof (ep))
59*437Smws 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR));
60*437Smws 
61*437Smws 	if (strncmp(ep.smbe_eanchor, SMB_ENTRY_EANCHOR, SMB_ENTRY_EANCHORLEN))
62*437Smws 		return (smb_open_error(shp, errp, ESMB_HEADER));
63*437Smws 
64*437Smws 	if ((stbuf = smb_alloc(ep.smbe_stlen)) == NULL)
65*437Smws 		return (smb_open_error(shp, errp, ESMB_NOMEM));
66*437Smws 
67*437Smws 	if ((n = pread64(fd, stbuf, ep.smbe_stlen,
68*437Smws 	    (off64_t)ep.smbe_staddr)) != ep.smbe_stlen) {
69*437Smws 		smb_free(stbuf, ep.smbe_stlen);
70*437Smws 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOSTAB));
71*437Smws 	}
72*437Smws 
73*437Smws 	shp = smbios_bufopen(&ep, stbuf, ep.smbe_stlen, version, flags, errp);
74*437Smws 
75*437Smws 	if (shp != NULL)
76*437Smws 		shp->sh_flags |= SMB_FL_BUFALLOC;
77*437Smws 	else
78*437Smws 		smb_free(stbuf, ep.smbe_stlen);
79*437Smws 
80*437Smws 	return (shp);
81*437Smws }
82*437Smws 
83*437Smws static smbios_hdl_t *
84*437Smws smb_biosopen(int fd, int version, int flags, int *errp)
85*437Smws {
86*437Smws 	smbios_hdl_t *shp = NULL;
87*437Smws 	size_t pgsize, pgmask, pgoff;
88*437Smws 	void *stbuf, *bios, *p, *q;
89*437Smws 	smbios_entry_t ep;
90*437Smws 
91*437Smws 	bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1,
92*437Smws 	    PROT_READ, MAP_SHARED, fd, (uint32_t)SMB_RANGE_START);
93*437Smws 
94*437Smws 	if (bios == MAP_FAILED)
95*437Smws 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
96*437Smws 
97*437Smws 	q = (void *)((uintptr_t)bios + SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
98*437Smws 
99*437Smws 	for (p = bios; p < q; p = (void *)((uintptr_t)p + 16)) {
100*437Smws 		if (strncmp(p, SMB_ENTRY_EANCHOR, SMB_ENTRY_EANCHORLEN) == 0)
101*437Smws 			break;
102*437Smws 	}
103*437Smws 
104*437Smws 	if (p >= q) {
105*437Smws 		(void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
106*437Smws 		return (smb_open_error(NULL, errp, ESMB_NOTFOUND));
107*437Smws 	}
108*437Smws 
109*437Smws 	bcopy(p, &ep, sizeof (smbios_entry_t));
110*437Smws 	(void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
111*437Smws 
112*437Smws 	pgsize = getpagesize();
113*437Smws 	pgmask = ~(pgsize - 1);
114*437Smws 	pgoff = ep.smbe_staddr & ~pgmask;
115*437Smws 
116*437Smws 	bios = mmap(NULL, ep.smbe_stlen + pgoff,
117*437Smws 	    PROT_READ, MAP_SHARED, fd, ep.smbe_staddr & pgmask);
118*437Smws 
119*437Smws 	if (bios == MAP_FAILED)
120*437Smws 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
121*437Smws 
122*437Smws 	if ((stbuf = smb_alloc(ep.smbe_stlen)) == NULL) {
123*437Smws 		(void) munmap(bios, ep.smbe_stlen + pgoff);
124*437Smws 		return (smb_open_error(shp, errp, ESMB_NOMEM));
125*437Smws 	}
126*437Smws 
127*437Smws 	bcopy((char *)bios + pgoff, stbuf, ep.smbe_stlen);
128*437Smws 	(void) munmap(bios, ep.smbe_stlen + pgoff);
129*437Smws 	shp = smbios_bufopen(&ep, stbuf, ep.smbe_stlen, version, flags, errp);
130*437Smws 
131*437Smws 	if (shp != NULL)
132*437Smws 		shp->sh_flags |= SMB_FL_BUFALLOC;
133*437Smws 	else
134*437Smws 		smb_free(stbuf, ep.smbe_stlen);
135*437Smws 
136*437Smws 	return (shp);
137*437Smws }
138*437Smws 
139*437Smws smbios_hdl_t *
140*437Smws smbios_fdopen(int fd, int version, int flags, int *errp)
141*437Smws {
142*437Smws 	struct stat64 st1, st2;
143*437Smws 
144*437Smws 	if (stat64(SMB_BIOS_DEVICE, &st1) == 0 && fstat64(fd, &st2) == 0 &&
145*437Smws 	    S_ISCHR(st2.st_mode) && st1.st_rdev == st2.st_rdev)
146*437Smws 		return (smb_biosopen(fd, version, flags, errp));
147*437Smws 	else
148*437Smws 		return (smb_fileopen(fd, version, flags, errp));
149*437Smws }
150*437Smws 
151*437Smws smbios_hdl_t *
152*437Smws smbios_open(const char *file, int version, int flags, int *errp)
153*437Smws {
154*437Smws 	smbios_hdl_t *shp;
155*437Smws 	int fd;
156*437Smws 
157*437Smws 	if ((fd = open64(file ? file : SMB_SMBIOS_DEVICE, O_RDONLY)) == -1) {
158*437Smws 		if ((errno == ENOENT || errno == ENXIO) &&
159*437Smws 		    (file == NULL || strcmp(file, SMB_SMBIOS_DEVICE) == 0))
160*437Smws 			errno = ESMB_NOTFOUND;
161*437Smws 		return (smb_open_error(NULL, errp, errno));
162*437Smws 	}
163*437Smws 
164*437Smws 	shp = smbios_fdopen(fd, version, flags, errp);
165*437Smws 	(void) close(fd);
166*437Smws 	return (shp);
167*437Smws }
168*437Smws 
169*437Smws static int
170*437Smws smbios_xwrite(smbios_hdl_t *shp, int fd, const void *buf, size_t buflen)
171*437Smws {
172*437Smws 	ssize_t resid = buflen;
173*437Smws 	ssize_t len;
174*437Smws 
175*437Smws 	while (resid != 0) {
176*437Smws 		if ((len = write(fd, buf, resid)) <= 0)
177*437Smws 			return (smb_set_errno(shp, errno));
178*437Smws 		resid -= len;
179*437Smws 		buf = (uchar_t *)buf + len;
180*437Smws 	}
181*437Smws 
182*437Smws 	return (0);
183*437Smws }
184*437Smws 
185*437Smws int
186*437Smws smbios_write(smbios_hdl_t *shp, int fd)
187*437Smws {
188*437Smws 	smbios_entry_t ep;
189*437Smws 	off64_t off = lseek64(fd, 0, SEEK_CUR) + P2ROUNDUP(sizeof (ep), 16);
190*437Smws 
191*437Smws 	if (off > UINT32_MAX)
192*437Smws 		return (smb_set_errno(shp, EOVERFLOW));
193*437Smws 
194*437Smws 	bcopy(&shp->sh_ent, &ep, sizeof (ep));
195*437Smws 	ep.smbe_staddr = (uint32_t)off;
196*437Smws 	smbios_checksum(shp, &ep);
197*437Smws 
198*437Smws 	if (smbios_xwrite(shp, fd, &ep, sizeof (ep)) == -1 ||
199*437Smws 	    lseek64(fd, off, SEEK_SET) != off ||
200*437Smws 	    smbios_xwrite(shp, fd, shp->sh_buf, shp->sh_buflen) == -1)
201*437Smws 		return (-1);
202*437Smws 
203*437Smws 	return (0);
204*437Smws }
205