1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/smbios_impl.h> 30 #include <sys/sysmacros.h> 31 #include <sys/stat.h> 32 #include <sys/mman.h> 33 34 #include <alloca.h> 35 #include <limits.h> 36 #include <unistd.h> 37 #include <strings.h> 38 #include <stdlib.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <libdevinfo.h> 42 43 #pragma init(smb_init) 44 static void 45 smb_init(void) 46 { 47 _smb_debug = getenv("SMB_DEBUG") != NULL; 48 } 49 50 static smbios_hdl_t * 51 smb_fileopen(int fd, int version, int flags, int *errp) 52 { 53 smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN); 54 smbios_entry_point_t ep_type; 55 smbios_hdl_t *shp = NULL; 56 uint32_t smbe_stlen; 57 off64_t smbe_staddr; 58 ssize_t n, elen; 59 void *stbuf; 60 61 if ((n = pread64(fd, ep, sizeof (*ep), 0)) != sizeof (*ep)) 62 return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR)); 63 64 if (strncmp(ep->ep21.smbe_eanchor, SMB_ENTRY_EANCHOR, 65 SMB_ENTRY_EANCHORLEN) == 0) { 66 ep_type = SMBIOS_ENTRY_POINT_21; 67 elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN); 68 } else if (strncmp(ep->ep30.smbe_eanchor, SMB3_ENTRY_EANCHOR, 69 SMB3_ENTRY_EANCHORLEN) == 0) { 70 ep_type = SMBIOS_ENTRY_POINT_30; 71 elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN); 72 } else { 73 return (smb_open_error(shp, errp, ESMB_HEADER)); 74 } 75 76 if ((n = pread64(fd, ep, elen, 0)) != elen) 77 return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOHDR)); 78 79 if (ep_type == SMBIOS_ENTRY_POINT_21) { 80 smbe_stlen = ep->ep21.smbe_stlen; 81 smbe_staddr = (off64_t)ep->ep21.smbe_staddr; 82 } else { 83 smbe_stlen = ep->ep30.smbe_stlen; 84 smbe_staddr = (off64_t)ep->ep30.smbe_staddr; 85 } 86 stbuf = smb_alloc(smbe_stlen); 87 88 if (stbuf == NULL) 89 return (smb_open_error(shp, errp, ESMB_NOMEM)); 90 91 if ((n = pread64(fd, stbuf, smbe_stlen, smbe_staddr)) != smbe_stlen) { 92 smb_free(stbuf, smbe_stlen); 93 return (smb_open_error(shp, errp, n < 0 ? errno : ESMB_NOSTAB)); 94 } 95 96 shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, errp); 97 98 if (shp != NULL) 99 shp->sh_flags |= SMB_FL_BUFALLOC; 100 else 101 smb_free(stbuf, smbe_stlen); 102 103 return (shp); 104 } 105 106 static smbios_hdl_t * 107 smb_biosopen(int fd, int version, int flags, int *errp) 108 { 109 smbios_entry_t *ep = alloca(SMB_ENTRY_MAXLEN); 110 smbios_entry_point_t ep_type; 111 smbios_hdl_t *shp = NULL; 112 size_t elen, pgsize, pgmask, pgoff; 113 void *stbuf, *bios, *p, *q; 114 di_node_t root; 115 int64_t *val64; 116 uint32_t smbe_stlen; 117 off64_t smbe_staddr; 118 119 bios = MAP_FAILED; 120 if ((root = di_init("/", DINFOPROP)) != DI_NODE_NIL) { 121 if (di_prop_lookup_int64(DDI_DEV_T_ANY, root, 122 "smbios-address", &val64) == 1) { 123 bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1, 124 PROT_READ, MAP_SHARED, fd, (off_t)*val64); 125 } 126 di_fini(root); 127 } 128 if (bios == MAP_FAILED) { 129 bios = mmap(NULL, SMB_RANGE_LIMIT - SMB_RANGE_START + 1, 130 PROT_READ, MAP_SHARED, fd, (uint32_t)SMB_RANGE_START); 131 } 132 133 if (bios == MAP_FAILED) 134 return (smb_open_error(shp, errp, ESMB_MAPDEV)); 135 136 q = (void *)((uintptr_t)bios + SMB_RANGE_LIMIT - SMB_RANGE_START + 1); 137 138 for (p = bios; p < q; p = (void *)((uintptr_t)p + SMB_SCAN_STEP)) { 139 if (strncmp(p, SMB_ENTRY_EANCHOR, SMB_ENTRY_EANCHORLEN) == 0) { 140 ep_type = SMBIOS_ENTRY_POINT_21; 141 elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN); 142 break; 143 } 144 if (strncmp(p, SMB3_ENTRY_EANCHOR, 145 SMB3_ENTRY_EANCHORLEN) == 0) { 146 ep_type = SMBIOS_ENTRY_POINT_30; 147 elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN); 148 break; 149 } 150 } 151 152 if (p >= q) { 153 (void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1); 154 return (smb_open_error(NULL, errp, ESMB_NOTFOUND)); 155 } 156 157 bcopy(p, ep, sizeof (smbios_entry_t)); 158 bcopy(p, ep, elen); 159 (void) munmap(bios, SMB_RANGE_LIMIT - SMB_RANGE_START + 1); 160 161 switch (ep_type) { 162 case SMBIOS_ENTRY_POINT_21: 163 smbe_stlen = ep->ep21.smbe_stlen; 164 smbe_staddr = ep->ep21.smbe_staddr; 165 break; 166 case SMBIOS_ENTRY_POINT_30: 167 smbe_stlen = ep->ep30.smbe_stlen; 168 smbe_staddr = ep->ep30.smbe_staddr; 169 break; 170 default: 171 return (smb_open_error(NULL, errp, ESMB_VERSION)); 172 } 173 174 pgsize = getpagesize(); 175 pgmask = ~(pgsize - 1); 176 pgoff = smbe_staddr & ~pgmask; 177 178 bios = mmap(NULL, smbe_stlen + pgoff, 179 PROT_READ, MAP_SHARED, fd, smbe_staddr & pgmask); 180 181 if (bios == MAP_FAILED) 182 return (smb_open_error(shp, errp, ESMB_MAPDEV)); 183 184 if ((stbuf = smb_alloc(smbe_stlen)) == NULL) { 185 (void) munmap(bios, smbe_stlen + pgoff); 186 return (smb_open_error(shp, errp, ESMB_NOMEM)); 187 } 188 189 bcopy((char *)bios + pgoff, stbuf, smbe_stlen); 190 (void) munmap(bios, smbe_stlen + pgoff); 191 shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, errp); 192 193 if (shp != NULL) 194 shp->sh_flags |= SMB_FL_BUFALLOC; 195 else 196 smb_free(stbuf, smbe_stlen); 197 198 return (shp); 199 } 200 201 smbios_hdl_t * 202 smbios_fdopen(int fd, int version, int flags, int *errp) 203 { 204 struct stat64 st1, st2; 205 206 if (stat64(SMB_BIOS_DEVICE, &st1) == 0 && fstat64(fd, &st2) == 0 && 207 S_ISCHR(st2.st_mode) && st1.st_rdev == st2.st_rdev) 208 return (smb_biosopen(fd, version, flags, errp)); 209 else 210 return (smb_fileopen(fd, version, flags, errp)); 211 } 212 213 smbios_hdl_t * 214 smbios_open(const char *file, int version, int flags, int *errp) 215 { 216 smbios_hdl_t *shp; 217 int fd; 218 219 if ((fd = open64(file ? file : SMB_SMBIOS_DEVICE, O_RDONLY)) == -1) { 220 if ((errno == ENOENT || errno == ENXIO) && 221 (file == NULL || strcmp(file, SMB_SMBIOS_DEVICE) == 0)) 222 errno = ESMB_NOTFOUND; 223 return (smb_open_error(NULL, errp, errno)); 224 } 225 226 shp = smbios_fdopen(fd, version, flags, errp); 227 (void) close(fd); 228 return (shp); 229 } 230 231 static int 232 smbios_xwrite(smbios_hdl_t *shp, int fd, const void *buf, size_t buflen) 233 { 234 ssize_t resid = buflen; 235 ssize_t len; 236 237 while (resid != 0) { 238 if ((len = write(fd, buf, resid)) <= 0) 239 return (smb_set_errno(shp, errno)); 240 resid -= len; 241 buf = (uchar_t *)buf + len; 242 } 243 244 return (0); 245 } 246 247 int 248 smbios_write(smbios_hdl_t *shp, int fd) 249 { 250 smbios_entry_t ep; 251 off64_t off = lseek64(fd, 0, SEEK_CUR) + P2ROUNDUP(sizeof (ep), 16); 252 253 if (off > UINT32_MAX) 254 return (smb_set_errno(shp, EOVERFLOW)); 255 256 bcopy(&shp->sh_ent, &ep, sizeof (ep)); 257 if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_21) 258 ep.ep21.smbe_staddr = (uint32_t)off; 259 else if (shp->sh_ent_type == SMBIOS_ENTRY_POINT_30) 260 ep.ep30.smbe_staddr = (uint64_t)off; 261 else 262 return (-1); 263 264 smbios_checksum(shp, &ep); 265 266 if (smbios_xwrite(shp, fd, &ep, sizeof (ep)) == -1 || 267 lseek64(fd, off, SEEK_SET) != off || 268 smbios_xwrite(shp, fd, shp->sh_buf, shp->sh_buflen) == -1) 269 return (-1); 270 271 return (0); 272 } 273