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.
26*a60349c8SRobert Mustacchi * Copyright (c) 2018, Joyent, Inc.
2784ab085aSmws */
2884ab085aSmws
2984ab085aSmws #include <sys/types.h>
3084ab085aSmws #include <sys/smbios_impl.h>
3184ab085aSmws #include <sys/sysmacros.h>
3284ab085aSmws #include <sys/stat.h>
3384ab085aSmws #include <sys/mman.h>
3484ab085aSmws
35e4586ebfSmws #include <alloca.h>
3684ab085aSmws #include <limits.h>
3784ab085aSmws #include <unistd.h>
3884ab085aSmws #include <strings.h>
3984ab085aSmws #include <stdlib.h>
4084ab085aSmws #include <errno.h>
4184ab085aSmws #include <fcntl.h>
42af349cd6SToomas Soome #include <libdevinfo.h>
4384ab085aSmws
4484ab085aSmws #pragma init(smb_init)
4584ab085aSmws static void
smb_init(void)4684ab085aSmws smb_init(void)
4784ab085aSmws {
4884ab085aSmws _smb_debug = getenv("SMB_DEBUG") != NULL;
4984ab085aSmws }
5084ab085aSmws
5184ab085aSmws static smbios_hdl_t *
smb_fileopen(int fd,int version,int flags,int * errp)5284ab085aSmws smb_fileopen(int fd, int version, int flags, int *errp)
5384ab085aSmws {
54e4586ebfSmws smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN);
55af349cd6SToomas Soome smbios_entry_point_t ep_type;
5684ab085aSmws smbios_hdl_t *shp = NULL;
57af349cd6SToomas Soome uint32_t smbe_stlen;
58af349cd6SToomas Soome off64_t smbe_staddr;
59e4586ebfSmws ssize_t n, elen;
6084ab085aSmws void *stbuf;
6184ab085aSmws
62e4586ebfSmws if ((n = pread64(fd, ep, sizeof (*ep), 0)) != sizeof (*ep))
6384ab085aSmws return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR));
6484ab085aSmws
65af349cd6SToomas Soome if (strncmp(ep->ep21.smbe_eanchor, SMB_ENTRY_EANCHOR,
66af349cd6SToomas Soome SMB_ENTRY_EANCHORLEN) == 0) {
67af349cd6SToomas Soome ep_type = SMBIOS_ENTRY_POINT_21;
68af349cd6SToomas Soome elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN);
69af349cd6SToomas Soome } else if (strncmp(ep->ep30.smbe_eanchor, SMB3_ENTRY_EANCHOR,
70af349cd6SToomas Soome SMB3_ENTRY_EANCHORLEN) == 0) {
71af349cd6SToomas Soome ep_type = SMBIOS_ENTRY_POINT_30;
72af349cd6SToomas Soome elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN);
73af349cd6SToomas Soome } else {
7484ab085aSmws return (smb_open_error(shp, errp, ESMB_HEADER));
75af349cd6SToomas Soome }
76e4586ebfSmws
77e4586ebfSmws if ((n = pread64(fd, ep, elen, 0)) != elen)
78e4586ebfSmws return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR));
79e4586ebfSmws
80af349cd6SToomas Soome if (ep_type == SMBIOS_ENTRY_POINT_21) {
81af349cd6SToomas Soome smbe_stlen = ep->ep21.smbe_stlen;
82af349cd6SToomas Soome smbe_staddr = (off64_t)ep->ep21.smbe_staddr;
83af349cd6SToomas Soome } else {
84af349cd6SToomas Soome smbe_stlen = ep->ep30.smbe_stlen;
85af349cd6SToomas Soome smbe_staddr = (off64_t)ep->ep30.smbe_staddr;
86af349cd6SToomas Soome }
87af349cd6SToomas Soome stbuf = smb_alloc(smbe_stlen);
88af349cd6SToomas Soome
89af349cd6SToomas Soome if (stbuf == NULL)
9084ab085aSmws return (smb_open_error(shp, errp, ESMB_NOMEM));
9184ab085aSmws
92af349cd6SToomas Soome if ((n = pread64(fd, stbuf, smbe_stlen, smbe_staddr)) != smbe_stlen) {
93af349cd6SToomas Soome smb_free(stbuf, smbe_stlen);
9484ab085aSmws return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOSTAB));
9584ab085aSmws }
9684ab085aSmws
97af349cd6SToomas Soome shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, errp);
9884ab085aSmws
9984ab085aSmws if (shp != NULL)
10084ab085aSmws shp->sh_flags |= SMB_FL_BUFALLOC;
10184ab085aSmws else
102af349cd6SToomas Soome smb_free(stbuf, smbe_stlen);
10384ab085aSmws
10484ab085aSmws return (shp);
10584ab085aSmws }
10684ab085aSmws
10784ab085aSmws static smbios_hdl_t *
smb_biosopen(int fd,int version,int flags,int * errp)10884ab085aSmws smb_biosopen(int fd, int version, int flags, int *errp)
10984ab085aSmws {
110e4586ebfSmws smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN);
111af349cd6SToomas Soome smbios_entry_point_t ep_type;
11284ab085aSmws smbios_hdl_t *shp = NULL;
113*a60349c8SRobert Mustacchi size_t pgsize, pgmask, pgoff;
11484ab085aSmws void *stbuf, *bios, *p, *q;
115*a60349c8SRobert Mustacchi void *smb2, *smb3;
116af349cd6SToomas Soome di_node_t root;
117af349cd6SToomas Soome int64_t *val64;
118af349cd6SToomas Soome uint32_t smbe_stlen;
119af349cd6SToomas Soome off64_t smbe_staddr;
12084ab085aSmws
121af349cd6SToomas Soome bios = MAP_FAILED;
122af349cd6SToomas Soome if ((root = di_init("/", DINFOPROP)) != DI_NODE_NIL) {
123af349cd6SToomas Soome if (di_prop_lookup_int64(DDI_DEV_T_ANY, root,
124af349cd6SToomas Soome "smbios-address", &val64) == 1) {
125af349cd6SToomas Soome bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1,
126af349cd6SToomas Soome PROT_READ, MAP_SHARED, fd, (off_t)*val64);
127af349cd6SToomas Soome }
128af349cd6SToomas Soome di_fini(root);
129af349cd6SToomas Soome }
130af349cd6SToomas Soome if (bios == MAP_FAILED) {
13184ab085aSmws bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1,
13284ab085aSmws PROT_READ, MAP_SHARED, fd, (uint32_t)SMB_RANGE_START);
133af349cd6SToomas Soome }
13484ab085aSmws
13584ab085aSmws if (bios == MAP_FAILED)
13684ab085aSmws return (smb_open_error(shp, errp, ESMB_MAPDEV));
13784ab085aSmws
13884ab085aSmws q = (void *)((uintptr_t)bios + SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
13984ab085aSmws
140*a60349c8SRobert Mustacchi smb2 = smb3 = NULL;
141af349cd6SToomas Soome for (p = bios; p < q; p = (void *)((uintptr_t)p + SMB_SCAN_STEP)) {
142*a60349c8SRobert Mustacchi if (smb2 != NULL && smb3 != NULL)
14384ab085aSmws break;
144*a60349c8SRobert Mustacchi if (smb3 == NULL && strncmp(p, SMB3_ENTRY_EANCHOR,
145af349cd6SToomas Soome SMB3_ENTRY_EANCHORLEN) == 0) {
146*a60349c8SRobert Mustacchi smb3 = p;
147*a60349c8SRobert Mustacchi } else if (smb2 == NULL && strncmp(p, SMB_ENTRY_EANCHOR,
148*a60349c8SRobert Mustacchi SMB_ENTRY_EANCHORLEN) == 0) {
149*a60349c8SRobert Mustacchi smb2 = p;
150af349cd6SToomas Soome }
151af349cd6SToomas Soome }
15284ab085aSmws
153*a60349c8SRobert Mustacchi /*
154*a60349c8SRobert Mustacchi * While they're not supposed to (as per the SMBIOS 3.2 spec), some
155*a60349c8SRobert Mustacchi * vendors end up having a newer version in one of the two entry points
156*a60349c8SRobert Mustacchi * than the other. If we found multiple tables then we will prefer the
157*a60349c8SRobert Mustacchi * one with the newer version. If they're equivalent, we prefer the
158*a60349c8SRobert Mustacchi * 32-bit version. If only one is present, then we use that.
159*a60349c8SRobert Mustacchi */
160*a60349c8SRobert Mustacchi if (smb2 != NULL && smb3 != NULL) {
161*a60349c8SRobert Mustacchi uint8_t smb2maj, smb2min, smb3maj, smb3min;
162*a60349c8SRobert Mustacchi
163*a60349c8SRobert Mustacchi bcopy(smb2, ep, sizeof (smbios_entry_t));
164*a60349c8SRobert Mustacchi smb2maj = ep->ep21.smbe_major;
165*a60349c8SRobert Mustacchi smb2min = ep->ep21.smbe_minor;
166*a60349c8SRobert Mustacchi bcopy(smb3, ep, sizeof (smbios_entry_t));
167*a60349c8SRobert Mustacchi smb3maj = ep->ep30.smbe_major;
168*a60349c8SRobert Mustacchi smb3min = ep->ep30.smbe_minor;
169*a60349c8SRobert Mustacchi
170*a60349c8SRobert Mustacchi if (smb3maj > smb2maj ||
171*a60349c8SRobert Mustacchi (smb3maj == smb2maj && smb3min > smb2min)) {
172*a60349c8SRobert Mustacchi ep_type = SMBIOS_ENTRY_POINT_30;
173*a60349c8SRobert Mustacchi p = smb3;
174*a60349c8SRobert Mustacchi } else {
175*a60349c8SRobert Mustacchi ep_type = SMBIOS_ENTRY_POINT_21;
176*a60349c8SRobert Mustacchi p = smb2;
177*a60349c8SRobert Mustacchi }
178*a60349c8SRobert Mustacchi } else if (smb3 != NULL) {
179*a60349c8SRobert Mustacchi ep_type = SMBIOS_ENTRY_POINT_30;
180*a60349c8SRobert Mustacchi p = smb3;
181*a60349c8SRobert Mustacchi } else if (smb2 != NULL) {
182*a60349c8SRobert Mustacchi ep_type = SMBIOS_ENTRY_POINT_21;
183*a60349c8SRobert Mustacchi p = smb2;
184*a60349c8SRobert Mustacchi } else {
18584ab085aSmws (void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
18684ab085aSmws return (smb_open_error(NULL, errp, ESMB_NOTFOUND));
18784ab085aSmws }
18884ab085aSmws
189e4586ebfSmws bcopy(p, ep, sizeof (smbios_entry_t));
19084ab085aSmws
191af349cd6SToomas Soome switch (ep_type) {
192af349cd6SToomas Soome case SMBIOS_ENTRY_POINT_21:
193*a60349c8SRobert Mustacchi ep->ep21.smbe_elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN);
194*a60349c8SRobert Mustacchi bcopy(p, ep, ep->ep21.smbe_elen);
195af349cd6SToomas Soome smbe_stlen = ep->ep21.smbe_stlen;
196af349cd6SToomas Soome smbe_staddr = ep->ep21.smbe_staddr;
197af349cd6SToomas Soome break;
198af349cd6SToomas Soome case SMBIOS_ENTRY_POINT_30:
199*a60349c8SRobert Mustacchi ep->ep30.smbe_elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN);
200*a60349c8SRobert Mustacchi bcopy(p, ep, ep->ep30.smbe_elen);
201af349cd6SToomas Soome smbe_stlen = ep->ep30.smbe_stlen;
202af349cd6SToomas Soome smbe_staddr = ep->ep30.smbe_staddr;
203af349cd6SToomas Soome break;
204af349cd6SToomas Soome default:
205*a60349c8SRobert Mustacchi (void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
206af349cd6SToomas Soome return (smb_open_error(NULL, errp, ESMB_VERSION));
207af349cd6SToomas Soome }
208*a60349c8SRobert Mustacchi (void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1);
209af349cd6SToomas Soome
21084ab085aSmws pgsize = getpagesize();
21184ab085aSmws pgmask = ~(pgsize - 1);
212af349cd6SToomas Soome pgoff = smbe_staddr & ~pgmask;
21384ab085aSmws
214af349cd6SToomas Soome bios = mmap(NULL, smbe_stlen + pgoff,
215af349cd6SToomas Soome PROT_READ, MAP_SHARED, fd, smbe_staddr & pgmask);
21684ab085aSmws
21784ab085aSmws if (bios == MAP_FAILED)
21884ab085aSmws return (smb_open_error(shp, errp, ESMB_MAPDEV));
21984ab085aSmws
220af349cd6SToomas Soome if ((stbuf = smb_alloc(smbe_stlen)) == NULL) {
221af349cd6SToomas Soome (void) munmap(bios, smbe_stlen + pgoff);
22284ab085aSmws return (smb_open_error(shp, errp, ESMB_NOMEM));
22384ab085aSmws }
22484ab085aSmws
225af349cd6SToomas Soome bcopy((char *)bios + pgoff, stbuf, smbe_stlen);
226af349cd6SToomas Soome (void) munmap(bios, smbe_stlen + pgoff);
227af349cd6SToomas Soome shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, errp);
22884ab085aSmws
22984ab085aSmws if (shp != NULL)
23084ab085aSmws shp->sh_flags |= SMB_FL_BUFALLOC;
23184ab085aSmws else
232af349cd6SToomas Soome smb_free(stbuf, smbe_stlen);
23384ab085aSmws
23484ab085aSmws return (shp);
23584ab085aSmws }
23684ab085aSmws
23784ab085aSmws smbios_hdl_t *
smbios_fdopen(int fd,int version,int flags,int * errp)23884ab085aSmws smbios_fdopen(int fd, int version, int flags, int *errp)
23984ab085aSmws {
24084ab085aSmws struct stat64 st1, st2;
24184ab085aSmws
24284ab085aSmws if (stat64(SMB_BIOS_DEVICE, &st1) == 0 && fstat64(fd, &st2) == 0 &&
24384ab085aSmws S_ISCHR(st2.st_mode) && st1.st_rdev == st2.st_rdev)
24484ab085aSmws return (smb_biosopen(fd, version, flags, errp));
24584ab085aSmws else
24684ab085aSmws return (smb_fileopen(fd, version, flags, errp));
24784ab085aSmws }
24884ab085aSmws
24984ab085aSmws smbios_hdl_t *
smbios_open(const char * file,int version,int flags,int * errp)25084ab085aSmws smbios_open(const char *file, int version, int flags, int *errp)
25184ab085aSmws {
25284ab085aSmws smbios_hdl_t *shp;
25384ab085aSmws int fd;
25484ab085aSmws
25584ab085aSmws if ((fd = open64(file ? file : SMB_SMBIOS_DEVICE, O_RDONLY)) == -1) {
25684ab085aSmws if ((errno == ENOENT || errno == ENXIO) &&
25784ab085aSmws (file == NULL || strcmp(file, SMB_SMBIOS_DEVICE) == 0))
25884ab085aSmws errno = ESMB_NOTFOUND;
25984ab085aSmws return (smb_open_error(NULL, errp, errno));
26084ab085aSmws }
26184ab085aSmws
26284ab085aSmws shp = smbios_fdopen(fd, version, flags, errp);
26384ab085aSmws (void) close(fd);
26484ab085aSmws return (shp);
26584ab085aSmws }
26684ab085aSmws
26784ab085aSmws static int
smbios_xwrite(smbios_hdl_t * shp,int fd,const void * buf,size_t buflen)26884ab085aSmws smbios_xwrite(smbios_hdl_t *shp, int fd, const void *buf, size_t buflen)
26984ab085aSmws {
27084ab085aSmws ssize_t resid = buflen;
27184ab085aSmws ssize_t len;
27284ab085aSmws
27384ab085aSmws while (resid != 0) {
27484ab085aSmws if ((len = write(fd, buf, resid)) <= 0)
27584ab085aSmws return (smb_set_errno(shp, errno));
27684ab085aSmws resid -= len;
27784ab085aSmws buf = (uchar_t *)buf + len;
27884ab085aSmws }
27984ab085aSmws
28084ab085aSmws return (0);
28184ab085aSmws }
28284ab085aSmws
28384ab085aSmws int
smbios_write(smbios_hdl_t * shp,int fd)28484ab085aSmws smbios_write(smbios_hdl_t *shp, int fd)
28584ab085aSmws {
28684ab085aSmws smbios_entry_t ep;
28784ab085aSmws off64_t off = lseek64(fd, 0, SEEK_CUR) + P2ROUNDUP(sizeof (ep), 16);
28884ab085aSmws
28984ab085aSmws if (off > UINT32_MAX)
29084ab085aSmws return (smb_set_errno(shp, EOVERFLOW));
29184ab085aSmws
29284ab085aSmws bcopy(&shp->sh_ent, &ep, sizeof (ep));
293af349cd6SToomas Soome if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_21)
294af349cd6SToomas Soome ep.ep21.smbe_staddr = (uint32_t)off;
295af349cd6SToomas Soome else if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_30)
296af349cd6SToomas Soome ep.ep30.smbe_staddr = (uint64_t)off;
297af349cd6SToomas Soome else
298af349cd6SToomas Soome return (-1);
299af349cd6SToomas Soome
30084ab085aSmws smbios_checksum(shp, &ep);
30184ab085aSmws
30284ab085aSmws if (smbios_xwrite(shp, fd, &ep, sizeof (ep)) == -1 ||
30384ab085aSmws lseek64(fd, off, SEEK_SET) != off ||
30484ab085aSmws smbios_xwrite(shp, fd, shp->sh_buf, shp->sh_buflen) == -1)
30584ab085aSmws return (-1);
30684ab085aSmws
30784ab085aSmws return (0);
30884ab085aSmws }
309