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*174bc649SRobert 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> 42c325726fSToomas Soome #include <libdevinfo.h> 4384ab085aSmws 4484ab085aSmws #pragma init(smb_init) 4584ab085aSmws static void 4684ab085aSmws smb_init(void) 4784ab085aSmws { 4884ab085aSmws _smb_debug = getenv("SMB_DEBUG") != NULL; 4984ab085aSmws } 5084ab085aSmws 5184ab085aSmws static smbios_hdl_t * 5284ab085aSmws smb_fileopen(int fd, int version, int flags, int *errp) 5384ab085aSmws { 54e4586ebfSmws smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN); 55c325726fSToomas Soome smbios_entry_point_t ep_type; 5684ab085aSmws smbios_hdl_t *shp = NULL; 57c325726fSToomas Soome uint32_t smbe_stlen; 58c325726fSToomas 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 65c325726fSToomas Soome if (strncmp(ep->ep21.smbe_eanchor, SMB_ENTRY_EANCHOR, 66c325726fSToomas Soome SMB_ENTRY_EANCHORLEN) == 0) { 67c325726fSToomas Soome ep_type = SMBIOS_ENTRY_POINT_21; 68c325726fSToomas Soome elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN); 69c325726fSToomas Soome } else if (strncmp(ep->ep30.smbe_eanchor, SMB3_ENTRY_EANCHOR, 70c325726fSToomas Soome SMB3_ENTRY_EANCHORLEN) == 0) { 71c325726fSToomas Soome ep_type = SMBIOS_ENTRY_POINT_30; 72c325726fSToomas Soome elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN); 73c325726fSToomas Soome } else { 7484ab085aSmws return (smb_open_error(shp, errp, ESMB_HEADER)); 75c325726fSToomas Soome } 76e4586ebfSmws 77e4586ebfSmws if ((n = pread64(fd, ep, elen, 0)) != elen) 78e4586ebfSmws return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR)); 79e4586ebfSmws 80c325726fSToomas Soome if (ep_type == SMBIOS_ENTRY_POINT_21) { 81c325726fSToomas Soome smbe_stlen = ep->ep21.smbe_stlen; 82c325726fSToomas Soome smbe_staddr = (off64_t)ep->ep21.smbe_staddr; 83c325726fSToomas Soome } else { 84c325726fSToomas Soome smbe_stlen = ep->ep30.smbe_stlen; 85c325726fSToomas Soome smbe_staddr = (off64_t)ep->ep30.smbe_staddr; 86c325726fSToomas Soome } 87c325726fSToomas Soome stbuf = smb_alloc(smbe_stlen); 88c325726fSToomas Soome 89c325726fSToomas Soome if (stbuf == NULL) 9084ab085aSmws return (smb_open_error(shp, errp, ESMB_NOMEM)); 9184ab085aSmws 92c325726fSToomas Soome if ((n = pread64(fd, stbuf, smbe_stlen, smbe_staddr)) != smbe_stlen) { 93c325726fSToomas Soome smb_free(stbuf, smbe_stlen); 9484ab085aSmws return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOSTAB)); 9584ab085aSmws } 9684ab085aSmws 97c325726fSToomas 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 102c325726fSToomas Soome smb_free(stbuf, smbe_stlen); 10384ab085aSmws 10484ab085aSmws return (shp); 10584ab085aSmws } 10684ab085aSmws 10784ab085aSmws static smbios_hdl_t * 10884ab085aSmws smb_biosopen(int fd, int version, int flags, int *errp) 10984ab085aSmws { 110e4586ebfSmws smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN); 111c325726fSToomas Soome smbios_entry_point_t ep_type; 11284ab085aSmws smbios_hdl_t *shp = NULL; 113*174bc649SRobert Mustacchi size_t pgsize, pgmask, pgoff; 11484ab085aSmws void *stbuf, *bios, *p, *q; 115*174bc649SRobert Mustacchi void *smb2, *smb3; 116c325726fSToomas Soome di_node_t root; 117c325726fSToomas Soome int64_t *val64; 118c325726fSToomas Soome uint32_t smbe_stlen; 119c325726fSToomas Soome off64_t smbe_staddr; 12084ab085aSmws 121c325726fSToomas Soome bios = MAP_FAILED; 122c325726fSToomas Soome if ((root = di_init("/", DINFOPROP)) != DI_NODE_NIL) { 123c325726fSToomas Soome if (di_prop_lookup_int64(DDI_DEV_T_ANY, root, 124c325726fSToomas Soome "smbios-address", &val64) == 1) { 125c325726fSToomas Soome bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1, 126c325726fSToomas Soome PROT_READ, MAP_SHARED, fd, (off_t)*val64); 127c325726fSToomas Soome } 128c325726fSToomas Soome di_fini(root); 129c325726fSToomas Soome } 130c325726fSToomas 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); 133c325726fSToomas 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*174bc649SRobert Mustacchi smb2 = smb3 = NULL; 141c325726fSToomas Soome for (p = bios; p < q; p = (void *)((uintptr_t)p + SMB_SCAN_STEP)) { 142*174bc649SRobert Mustacchi if (smb2 != NULL && smb3 != NULL) 14384ab085aSmws break; 144*174bc649SRobert Mustacchi if (smb3 == NULL && strncmp(p, SMB3_ENTRY_EANCHOR, 145c325726fSToomas Soome SMB3_ENTRY_EANCHORLEN) == 0) { 146*174bc649SRobert Mustacchi smb3 = p; 147*174bc649SRobert Mustacchi } else if (smb2 == NULL && strncmp(p, SMB_ENTRY_EANCHOR, 148*174bc649SRobert Mustacchi SMB_ENTRY_EANCHORLEN) == 0) { 149*174bc649SRobert Mustacchi smb2 = p; 150c325726fSToomas Soome } 151c325726fSToomas Soome } 15284ab085aSmws 153*174bc649SRobert Mustacchi /* 154*174bc649SRobert Mustacchi * While they're not supposed to (as per the SMBIOS 3.2 spec), some 155*174bc649SRobert Mustacchi * vendors end up having a newer version in one of the two entry points 156*174bc649SRobert Mustacchi * than the other. If we found multiple tables then we will prefer the 157*174bc649SRobert Mustacchi * one with the newer version. If they're equivalent, we prefer the 158*174bc649SRobert Mustacchi * 32-bit version. If only one is present, then we use that. 159*174bc649SRobert Mustacchi */ 160*174bc649SRobert Mustacchi if (smb2 != NULL && smb3 != NULL) { 161*174bc649SRobert Mustacchi uint8_t smb2maj, smb2min, smb3maj, smb3min; 162*174bc649SRobert Mustacchi 163*174bc649SRobert Mustacchi bcopy(smb2, ep, sizeof (smbios_entry_t)); 164*174bc649SRobert Mustacchi smb2maj = ep->ep21.smbe_major; 165*174bc649SRobert Mustacchi smb2min = ep->ep21.smbe_minor; 166*174bc649SRobert Mustacchi bcopy(smb3, ep, sizeof (smbios_entry_t)); 167*174bc649SRobert Mustacchi smb3maj = ep->ep30.smbe_major; 168*174bc649SRobert Mustacchi smb3min = ep->ep30.smbe_minor; 169*174bc649SRobert Mustacchi 170*174bc649SRobert Mustacchi if (smb3maj > smb2maj || 171*174bc649SRobert Mustacchi (smb3maj == smb2maj && smb3min > smb2min)) { 172*174bc649SRobert Mustacchi ep_type = SMBIOS_ENTRY_POINT_30; 173*174bc649SRobert Mustacchi p = smb3; 174*174bc649SRobert Mustacchi } else { 175*174bc649SRobert Mustacchi ep_type = SMBIOS_ENTRY_POINT_21; 176*174bc649SRobert Mustacchi p = smb2; 177*174bc649SRobert Mustacchi } 178*174bc649SRobert Mustacchi } else if (smb3 != NULL) { 179*174bc649SRobert Mustacchi ep_type = SMBIOS_ENTRY_POINT_30; 180*174bc649SRobert Mustacchi p = smb3; 181*174bc649SRobert Mustacchi } else if (smb2 != NULL) { 182*174bc649SRobert Mustacchi ep_type = SMBIOS_ENTRY_POINT_21; 183*174bc649SRobert Mustacchi p = smb2; 184*174bc649SRobert 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 191c325726fSToomas Soome switch (ep_type) { 192c325726fSToomas Soome case SMBIOS_ENTRY_POINT_21: 193*174bc649SRobert Mustacchi ep->ep21.smbe_elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN); 194*174bc649SRobert Mustacchi bcopy(p, ep, ep->ep21.smbe_elen); 195c325726fSToomas Soome smbe_stlen = ep->ep21.smbe_stlen; 196c325726fSToomas Soome smbe_staddr = ep->ep21.smbe_staddr; 197c325726fSToomas Soome break; 198c325726fSToomas Soome case SMBIOS_ENTRY_POINT_30: 199*174bc649SRobert Mustacchi ep->ep30.smbe_elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN); 200*174bc649SRobert Mustacchi bcopy(p, ep, ep->ep30.smbe_elen); 201c325726fSToomas Soome smbe_stlen = ep->ep30.smbe_stlen; 202c325726fSToomas Soome smbe_staddr = ep->ep30.smbe_staddr; 203c325726fSToomas Soome break; 204c325726fSToomas Soome default: 205*174bc649SRobert Mustacchi (void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1); 206c325726fSToomas Soome return (smb_open_error(NULL, errp, ESMB_VERSION)); 207c325726fSToomas Soome } 208*174bc649SRobert Mustacchi (void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1); 209c325726fSToomas Soome 21084ab085aSmws pgsize = getpagesize(); 21184ab085aSmws pgmask = ~(pgsize - 1); 212c325726fSToomas Soome pgoff = smbe_staddr & ~pgmask; 21384ab085aSmws 214c325726fSToomas Soome bios = mmap(NULL, smbe_stlen + pgoff, 215c325726fSToomas 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 220c325726fSToomas Soome if ((stbuf = smb_alloc(smbe_stlen)) == NULL) { 221c325726fSToomas Soome (void) munmap(bios, smbe_stlen + pgoff); 22284ab085aSmws return (smb_open_error(shp, errp, ESMB_NOMEM)); 22384ab085aSmws } 22484ab085aSmws 225c325726fSToomas Soome bcopy((char *)bios + pgoff, stbuf, smbe_stlen); 226c325726fSToomas Soome (void) munmap(bios, smbe_stlen + pgoff); 227c325726fSToomas 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 232c325726fSToomas Soome smb_free(stbuf, smbe_stlen); 23384ab085aSmws 23484ab085aSmws return (shp); 23584ab085aSmws } 23684ab085aSmws 23784ab085aSmws smbios_hdl_t * 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 * 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 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 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)); 293c325726fSToomas Soome if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_21) 294c325726fSToomas Soome ep.ep21.smbe_staddr = (uint32_t)off; 295c325726fSToomas Soome else if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_30) 296c325726fSToomas Soome ep.ep30.smbe_staddr = (uint64_t)off; 297c325726fSToomas Soome else 298c325726fSToomas Soome return (-1); 299c325726fSToomas 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