xref: /titanic_41/usr/src/lib/libsmbios/common/smb_lib.c (revision 967d0e8a751eaccea6022668cd46d532ebea8f89)
184ab085aSmws /*
284ab085aSmws  * CDDL HEADER START
384ab085aSmws  *
484ab085aSmws  * The contents of this file are subject to the terms of the
584ab085aSmws  * Common Development and Distribution License, Version 1.0 only
684ab085aSmws  * (the "License").  You may not use this file except in compliance
784ab085aSmws  * with the License.
884ab085aSmws  *
984ab085aSmws  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1084ab085aSmws  * or http://www.opensolaris.org/os/licensing.
1184ab085aSmws  * See the License for the specific language governing permissions
1284ab085aSmws  * and limitations under the License.
1384ab085aSmws  *
1484ab085aSmws  * When distributing Covered Code, include this CDDL HEADER in each
1584ab085aSmws  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1684ab085aSmws  * If applicable, add the following below this CDDL HEADER, with the
1784ab085aSmws  * fields enclosed by brackets "[]" replaced with your own identifying
1884ab085aSmws  * information: Portions Copyright [yyyy] [name of copyright owner]
1984ab085aSmws  *
2084ab085aSmws  * CDDL HEADER END
2184ab085aSmws  */
2284ab085aSmws 
2384ab085aSmws /*
24e4586ebfSmws  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2584ab085aSmws  * Use is subject to license terms.
2684ab085aSmws  */
2784ab085aSmws 
2884ab085aSmws #include <sys/types.h>
2984ab085aSmws #include <sys/smbios_impl.h>
3084ab085aSmws #include <sys/sysmacros.h>
3184ab085aSmws #include <sys/stat.h>
3284ab085aSmws #include <sys/mman.h>
3384ab085aSmws 
34e4586ebfSmws #include <alloca.h>
3584ab085aSmws #include <limits.h>
3684ab085aSmws #include <unistd.h>
3784ab085aSmws #include <strings.h>
3884ab085aSmws #include <stdlib.h>
3984ab085aSmws #include <errno.h>
4084ab085aSmws #include <fcntl.h>
41*967d0e8aSToomas Soome #include <libdevinfo.h>
4284ab085aSmws 
4384ab085aSmws #pragma init(smb_init)
4484ab085aSmws static void
smb_init(void)4584ab085aSmws smb_init(void)
4684ab085aSmws {
4784ab085aSmws 	_smb_debug = getenv("SMB_DEBUG") != NULL;
4884ab085aSmws }
4984ab085aSmws 
5084ab085aSmws static smbios_hdl_t *
smb_fileopen(int fd,int version,int flags,int * errp)5184ab085aSmws smb_fileopen(int fd, int version, int flags, int *errp)
5284ab085aSmws {
53e4586ebfSmws 	smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN);
54*967d0e8aSToomas Soome 	smbios_entry_point_t ep_type;
5584ab085aSmws 	smbios_hdl_t *shp = NULL;
56*967d0e8aSToomas Soome 	uint32_t smbe_stlen;
57*967d0e8aSToomas Soome 	off64_t smbe_staddr;
58e4586ebfSmws 	ssize_t n, elen;
5984ab085aSmws 	void *stbuf;
6084ab085aSmws 
61e4586ebfSmws 	if ((n = pread64(fd, ep, sizeof (*ep), 0)) != sizeof (*ep))
6284ab085aSmws 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR));
6384ab085aSmws 
64*967d0e8aSToomas Soome 	if (strncmp(ep->ep21.smbe_eanchor, SMB_ENTRY_EANCHOR,
65*967d0e8aSToomas Soome 	    SMB_ENTRY_EANCHORLEN) == 0) {
66*967d0e8aSToomas Soome 		ep_type = SMBIOS_ENTRY_POINT_21;
67*967d0e8aSToomas Soome 		elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN);
68*967d0e8aSToomas Soome 	} else if (strncmp(ep->ep30.smbe_eanchor, SMB3_ENTRY_EANCHOR,
69*967d0e8aSToomas Soome 	    SMB3_ENTRY_EANCHORLEN) == 0) {
70*967d0e8aSToomas Soome 		ep_type = SMBIOS_ENTRY_POINT_30;
71*967d0e8aSToomas Soome 		elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN);
72*967d0e8aSToomas Soome 	} else {
7384ab085aSmws 		return (smb_open_error(shp, errp, ESMB_HEADER));
74*967d0e8aSToomas Soome 	}
75e4586ebfSmws 
76e4586ebfSmws 	if ((n = pread64(fd, ep, elen, 0)) != elen)
77e4586ebfSmws 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR));
78e4586ebfSmws 
79*967d0e8aSToomas Soome 	if (ep_type == SMBIOS_ENTRY_POINT_21) {
80*967d0e8aSToomas Soome 		smbe_stlen = ep->ep21.smbe_stlen;
81*967d0e8aSToomas Soome 		smbe_staddr = (off64_t)ep->ep21.smbe_staddr;
82*967d0e8aSToomas Soome 	} else {
83*967d0e8aSToomas Soome 		smbe_stlen = ep->ep30.smbe_stlen;
84*967d0e8aSToomas Soome 		smbe_staddr = (off64_t)ep->ep30.smbe_staddr;
85*967d0e8aSToomas Soome 	}
86*967d0e8aSToomas Soome 	stbuf = smb_alloc(smbe_stlen);
87*967d0e8aSToomas Soome 
88*967d0e8aSToomas Soome 	if (stbuf == NULL)
8984ab085aSmws 		return (smb_open_error(shp, errp, ESMB_NOMEM));
9084ab085aSmws 
91*967d0e8aSToomas Soome 	if ((n = pread64(fd, stbuf, smbe_stlen, smbe_staddr)) != smbe_stlen) {
92*967d0e8aSToomas Soome 		smb_free(stbuf, smbe_stlen);
9384ab085aSmws 		return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOSTAB));
9484ab085aSmws 	}
9584ab085aSmws 
96*967d0e8aSToomas Soome 	shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, errp);
9784ab085aSmws 
9884ab085aSmws 	if (shp != NULL)
9984ab085aSmws 		shp->sh_flags |= SMB_FL_BUFALLOC;
10084ab085aSmws 	else
101*967d0e8aSToomas Soome 		smb_free(stbuf, smbe_stlen);
10284ab085aSmws 
10384ab085aSmws 	return (shp);
10484ab085aSmws }
10584ab085aSmws 
10684ab085aSmws static smbios_hdl_t *
smb_biosopen(int fd,int version,int flags,int * errp)10784ab085aSmws smb_biosopen(int fd, int version, int flags, int *errp)
10884ab085aSmws {
109e4586ebfSmws 	smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN);
110*967d0e8aSToomas Soome 	smbios_entry_point_t ep_type;
11184ab085aSmws 	smbios_hdl_t *shp = NULL;
112*967d0e8aSToomas Soome 	size_t elen, pgsize, pgmask, pgoff;
11384ab085aSmws 	void *stbuf, *bios, *p, *q;
114*967d0e8aSToomas Soome 	di_node_t root;
115*967d0e8aSToomas Soome 	int64_t *val64;
116*967d0e8aSToomas Soome 	uint32_t smbe_stlen;
117*967d0e8aSToomas Soome 	off64_t smbe_staddr;
11884ab085aSmws 
119*967d0e8aSToomas Soome 	bios = MAP_FAILED;
120*967d0e8aSToomas Soome 	if ((root = di_init("/", DINFOPROP)) != DI_NODE_NIL) {
121*967d0e8aSToomas Soome 		if (di_prop_lookup_int64(DDI_DEV_T_ANY, root,
122*967d0e8aSToomas Soome 		    "smbios-address", &val64) == 1) {
123*967d0e8aSToomas Soome 			bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1,
124*967d0e8aSToomas Soome 			    PROT_READ, MAP_SHARED, fd, (off_t)*val64);
125*967d0e8aSToomas Soome 		}
126*967d0e8aSToomas Soome 		di_fini(root);
127*967d0e8aSToomas Soome 	}
128*967d0e8aSToomas Soome 	if (bios == MAP_FAILED) {
12984ab085aSmws 		bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1,
13084ab085aSmws 		    PROT_READ, MAP_SHARED, fd, (uint32_t)SMB_RANGE_START);
131*967d0e8aSToomas Soome 	}
13284ab085aSmws 
13384ab085aSmws 	if (bios == MAP_FAILED)
13484ab085aSmws 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
13584ab085aSmws 
13684ab085aSmws 	q = (void *)((uintptr_t)bios + SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
13784ab085aSmws 
138*967d0e8aSToomas Soome 	for (p = bios; p < q; p = (void *)((uintptr_t)p + SMB_SCAN_STEP)) {
139*967d0e8aSToomas Soome 		if (strncmp(p, SMB_ENTRY_EANCHOR, SMB_ENTRY_EANCHORLEN) == 0) {
140*967d0e8aSToomas Soome 			ep_type = SMBIOS_ENTRY_POINT_21;
141*967d0e8aSToomas Soome 			elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN);
14284ab085aSmws 			break;
14384ab085aSmws 		}
144*967d0e8aSToomas Soome 		if (strncmp(p, SMB3_ENTRY_EANCHOR,
145*967d0e8aSToomas Soome 		    SMB3_ENTRY_EANCHORLEN) == 0) {
146*967d0e8aSToomas Soome 			ep_type = SMBIOS_ENTRY_POINT_30;
147*967d0e8aSToomas Soome 			elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN);
148*967d0e8aSToomas Soome 			break;
149*967d0e8aSToomas Soome 		}
150*967d0e8aSToomas Soome 	}
15184ab085aSmws 
15284ab085aSmws 	if (p >= q) {
15384ab085aSmws 		(void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
15484ab085aSmws 		return (smb_open_error(NULL, errp, ESMB_NOTFOUND));
15584ab085aSmws 	}
15684ab085aSmws 
157e4586ebfSmws 	bcopy(p, ep, sizeof (smbios_entry_t));
158*967d0e8aSToomas Soome 	bcopy(p, ep, elen);
15984ab085aSmws 	(void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
16084ab085aSmws 
161*967d0e8aSToomas Soome 	switch (ep_type) {
162*967d0e8aSToomas Soome 	case SMBIOS_ENTRY_POINT_21:
163*967d0e8aSToomas Soome 		smbe_stlen = ep->ep21.smbe_stlen;
164*967d0e8aSToomas Soome 		smbe_staddr = ep->ep21.smbe_staddr;
165*967d0e8aSToomas Soome 		break;
166*967d0e8aSToomas Soome 	case SMBIOS_ENTRY_POINT_30:
167*967d0e8aSToomas Soome 		smbe_stlen = ep->ep30.smbe_stlen;
168*967d0e8aSToomas Soome 		smbe_staddr = ep->ep30.smbe_staddr;
169*967d0e8aSToomas Soome 		break;
170*967d0e8aSToomas Soome 	default:
171*967d0e8aSToomas Soome 		return (smb_open_error(NULL, errp, ESMB_VERSION));
172*967d0e8aSToomas Soome 	}
173*967d0e8aSToomas Soome 
17484ab085aSmws 	pgsize = getpagesize();
17584ab085aSmws 	pgmask = ~(pgsize - 1);
176*967d0e8aSToomas Soome 	pgoff = smbe_staddr & ~pgmask;
17784ab085aSmws 
178*967d0e8aSToomas Soome 	bios = mmap(NULL, smbe_stlen + pgoff,
179*967d0e8aSToomas Soome 	    PROT_READ, MAP_SHARED, fd, smbe_staddr & pgmask);
18084ab085aSmws 
18184ab085aSmws 	if (bios == MAP_FAILED)
18284ab085aSmws 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
18384ab085aSmws 
184*967d0e8aSToomas Soome 	if ((stbuf = smb_alloc(smbe_stlen)) == NULL) {
185*967d0e8aSToomas Soome 		(void) munmap(bios, smbe_stlen + pgoff);
18684ab085aSmws 		return (smb_open_error(shp, errp, ESMB_NOMEM));
18784ab085aSmws 	}
18884ab085aSmws 
189*967d0e8aSToomas Soome 	bcopy((char *)bios + pgoff, stbuf, smbe_stlen);
190*967d0e8aSToomas Soome 	(void) munmap(bios, smbe_stlen + pgoff);
191*967d0e8aSToomas Soome 	shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, errp);
19284ab085aSmws 
19384ab085aSmws 	if (shp != NULL)
19484ab085aSmws 		shp->sh_flags |= SMB_FL_BUFALLOC;
19584ab085aSmws 	else
196*967d0e8aSToomas Soome 		smb_free(stbuf, smbe_stlen);
19784ab085aSmws 
19884ab085aSmws 	return (shp);
19984ab085aSmws }
20084ab085aSmws 
20184ab085aSmws smbios_hdl_t *
smbios_fdopen(int fd,int version,int flags,int * errp)20284ab085aSmws smbios_fdopen(int fd, int version, int flags, int *errp)
20384ab085aSmws {
20484ab085aSmws 	struct stat64 st1, st2;
20584ab085aSmws 
20684ab085aSmws 	if (stat64(SMB_BIOS_DEVICE, &st1) == 0 && fstat64(fd, &st2) == 0 &&
20784ab085aSmws 	    S_ISCHR(st2.st_mode) && st1.st_rdev == st2.st_rdev)
20884ab085aSmws 		return (smb_biosopen(fd, version, flags, errp));
20984ab085aSmws 	else
21084ab085aSmws 		return (smb_fileopen(fd, version, flags, errp));
21184ab085aSmws }
21284ab085aSmws 
21384ab085aSmws smbios_hdl_t *
smbios_open(const char * file,int version,int flags,int * errp)21484ab085aSmws smbios_open(const char *file, int version, int flags, int *errp)
21584ab085aSmws {
21684ab085aSmws 	smbios_hdl_t *shp;
21784ab085aSmws 	int fd;
21884ab085aSmws 
21984ab085aSmws 	if ((fd = open64(file ? file : SMB_SMBIOS_DEVICE, O_RDONLY)) == -1) {
22084ab085aSmws 		if ((errno == ENOENT || errno == ENXIO) &&
22184ab085aSmws 		    (file == NULL || strcmp(file, SMB_SMBIOS_DEVICE) == 0))
22284ab085aSmws 			errno = ESMB_NOTFOUND;
22384ab085aSmws 		return (smb_open_error(NULL, errp, errno));
22484ab085aSmws 	}
22584ab085aSmws 
22684ab085aSmws 	shp = smbios_fdopen(fd, version, flags, errp);
22784ab085aSmws 	(void) close(fd);
22884ab085aSmws 	return (shp);
22984ab085aSmws }
23084ab085aSmws 
23184ab085aSmws static int
smbios_xwrite(smbios_hdl_t * shp,int fd,const void * buf,size_t buflen)23284ab085aSmws smbios_xwrite(smbios_hdl_t *shp, int fd, const void *buf, size_t buflen)
23384ab085aSmws {
23484ab085aSmws 	ssize_t resid = buflen;
23584ab085aSmws 	ssize_t len;
23684ab085aSmws 
23784ab085aSmws 	while (resid != 0) {
23884ab085aSmws 		if ((len = write(fd, buf, resid)) <= 0)
23984ab085aSmws 			return (smb_set_errno(shp, errno));
24084ab085aSmws 		resid -= len;
24184ab085aSmws 		buf = (uchar_t *)buf + len;
24284ab085aSmws 	}
24384ab085aSmws 
24484ab085aSmws 	return (0);
24584ab085aSmws }
24684ab085aSmws 
24784ab085aSmws int
smbios_write(smbios_hdl_t * shp,int fd)24884ab085aSmws smbios_write(smbios_hdl_t *shp, int fd)
24984ab085aSmws {
25084ab085aSmws 	smbios_entry_t ep;
25184ab085aSmws 	off64_t off = lseek64(fd, 0, SEEK_CUR) + P2ROUNDUP(sizeof (ep), 16);
25284ab085aSmws 
25384ab085aSmws 	if (off > UINT32_MAX)
25484ab085aSmws 		return (smb_set_errno(shp, EOVERFLOW));
25584ab085aSmws 
25684ab085aSmws 	bcopy(&shp->sh_ent, &ep, sizeof (ep));
257*967d0e8aSToomas Soome 	if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_21)
258*967d0e8aSToomas Soome 		ep.ep21.smbe_staddr = (uint32_t)off;
259*967d0e8aSToomas Soome 	else if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_30)
260*967d0e8aSToomas Soome 		ep.ep30.smbe_staddr = (uint64_t)off;
261*967d0e8aSToomas Soome 	else
262*967d0e8aSToomas Soome 		return (-1);
263*967d0e8aSToomas Soome 
26484ab085aSmws 	smbios_checksum(shp, &ep);
26584ab085aSmws 
26684ab085aSmws 	if (smbios_xwrite(shp, fd, &ep, sizeof (ep)) == -1 ||
26784ab085aSmws 	    lseek64(fd, off, SEEK_SET) != off ||
26884ab085aSmws 	    smbios_xwrite(shp, fd, shp->sh_buf, shp->sh_buflen) == -1)
26984ab085aSmws 		return (-1);
27084ab085aSmws 
27184ab085aSmws 	return (0);
27284ab085aSmws }
273