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 * Copyright (c) 2018, Joyent, Inc. 27 * Copyright 2026 Edgecast Cloud LLC. 28 */ 29 30 /* 31 * Platform-Specific SMBIOS Subroutines 32 * 33 * The routines in this file form part of <sys/smbios_impl.h> and combine with 34 * the usr/src/common/smbios code to form an in-kernel SMBIOS decoding service. 35 * The SMBIOS entry point is locating by scanning a range of physical memory 36 * assigned to BIOS as described in Section 2 of the DMTF SMBIOS specification. 37 */ 38 39 #include <sys/smbios_impl.h> 40 #include <sys/sysmacros.h> 41 #include <sys/errno.h> 42 #include <sys/psm.h> 43 #include <sys/smp_impldefs.h> 44 45 smbios_hdl_t *ksmbios; 46 int ksmbios_flags; 47 48 smbios_hdl_t * 49 smb_open_error(smbios_hdl_t *shp, int *errp, int err) 50 { 51 if (shp != NULL) 52 smbios_close(shp); 53 54 if (errp != NULL) 55 *errp = err; 56 57 if (ksmbios == NULL) 58 cmn_err(CE_CONT, "?SMBIOS not loaded (%s)", smbios_errmsg(err)); 59 60 return (NULL); 61 } 62 63 smbios_hdl_t * 64 smbios_open(const char *file, int version, int flags, int *errp) 65 { 66 smbios_hdl_t *shp = NULL; 67 smbios_entry_t *ep; 68 caddr_t stbuf, bios, p, q; 69 caddr_t smb2, smb3; 70 uint64_t startaddr, epaddr; 71 size_t bioslen, eplen; 72 uint_t smbe_stlen; 73 smbios_entry_point_t ep_type; 74 uint8_t smbe_major, smbe_minor; 75 int err; 76 77 if (file != NULL || (flags & ~SMB_O_MASK)) 78 return (smb_open_error(shp, errp, ESMB_INVAL)); 79 80 if ((startaddr = ddi_prop_get_int64(DDI_DEV_T_ANY, ddi_root_node(), 81 DDI_PROP_DONTPASS, "smbios-address", 0)) == 0) { 82 startaddr = SMB_RANGE_START; 83 bioslen = SMB_RANGE_LIMIT - SMB_RANGE_START + 1; 84 } else { 85 /* 86 * We have smbios address from boot loader. Map only 87 * the entry point. 88 */ 89 bioslen = sizeof (smbios_entry_t); 90 } 91 92 bios = psm_map_phys_new(startaddr, bioslen, PSM_PROT_READ); 93 94 if (bios == NULL) 95 return (smb_open_error(shp, errp, ESMB_MAPDEV)); 96 97 /* 98 * Entry point structure must fit into the mapped range. 99 */ 100 p = bios; 101 q = bios + bioslen - sizeof (smbios_entry_t); 102 smb2 = smb3 = NULL; 103 while (p <= q) { 104 if (smb2 != NULL && smb3 != NULL) 105 break; 106 107 if (smb3 == NULL && strncmp(p, SMB3_ENTRY_EANCHOR, 108 SMB3_ENTRY_EANCHORLEN) == 0) { 109 smb3 = p; 110 } else if (smb2 == NULL && strncmp(p, SMB_ENTRY_EANCHOR, 111 SMB_ENTRY_EANCHORLEN) == 0) { 112 smb2 = p; 113 } 114 115 p += SMB_SCAN_STEP; 116 } 117 118 if (smb2 == NULL && smb3 == NULL) { 119 psm_unmap_phys(bios, bioslen); 120 return (smb_open_error(shp, errp, ESMB_NOTFOUND)); 121 } 122 123 /* 124 * While they're not supposed to (as per the SMBIOS 3.2 spec), some 125 * vendors end up having a newer version in one of the two entry points 126 * than the other. If we found multiple tables then we will prefer the 127 * one with the newer version. If they're equivalent, we prefer the 128 * 32-bit version. If only one is present, then we use that. 129 */ 130 ep = smb_alloc(SMB_ENTRY_MAXLEN); 131 if (smb2 != NULL && smb3 != NULL) { 132 uint8_t smb2maj, smb2min, smb3maj, smb3min; 133 134 bcopy(smb2, ep, sizeof (smbios_entry_t)); 135 smb2maj = ep->ep21.smbe_major; 136 smb2min = ep->ep21.smbe_minor; 137 bcopy(smb3, ep, sizeof (smbios_entry_t)); 138 smb3maj = ep->ep30.smbe_major; 139 smb3min = ep->ep30.smbe_minor; 140 141 if (smb3maj > smb2maj || 142 (smb3maj == smb2maj && smb3min > smb2min)) { 143 ep_type = SMBIOS_ENTRY_POINT_30; 144 p = smb3; 145 } else { 146 ep_type = SMBIOS_ENTRY_POINT_21; 147 p = smb2; 148 } 149 } else if (smb3 != NULL) { 150 ep_type = SMBIOS_ENTRY_POINT_30; 151 p = smb3; 152 } else { 153 ep_type = SMBIOS_ENTRY_POINT_21; 154 p = smb2; 155 } 156 bcopy(p, ep, sizeof (smbios_entry_t)); 157 if (ep_type == SMBIOS_ENTRY_POINT_21) 158 eplen = ep->ep21.smbe_elen; 159 else 160 eplen = ep->ep30.smbe_elen; 161 eplen = MIN(eplen, SMB_ENTRY_MAXLEN); 162 epaddr = startaddr + (p - bios); 163 psm_unmap_phys(bios, bioslen); 164 165 /* 166 * If entry point is larger than our structure, remap it. 167 */ 168 if (eplen > sizeof (smbios_entry_t)) { 169 bios = psm_map_phys_new(epaddr, eplen, PSM_PROT_READ); 170 if (bios == NULL) { 171 smb_free(ep, SMB_ENTRY_MAXLEN); 172 return (smb_open_error(shp, errp, ESMB_MAPDEV)); 173 } 174 bcopy(bios, ep, eplen); 175 psm_unmap_phys(bios, eplen); 176 } 177 178 switch (ep_type) { 179 case SMBIOS_ENTRY_POINT_21: 180 smbe_major = ep->ep21.smbe_major; 181 smbe_minor = ep->ep21.smbe_minor; 182 smbe_stlen = ep->ep21.smbe_stlen; 183 bios = psm_map_phys(ep->ep21.smbe_staddr, smbe_stlen, 184 PSM_PROT_READ); 185 break; 186 case SMBIOS_ENTRY_POINT_30: 187 smbe_major = ep->ep30.smbe_major; 188 smbe_minor = ep->ep30.smbe_minor; 189 smbe_stlen = ep->ep30.smbe_stlen; 190 bios = psm_map_phys_new(ep->ep30.smbe_staddr, smbe_stlen, 191 PSM_PROT_READ); 192 break; 193 default: 194 smb_free(ep, SMB_ENTRY_MAXLEN); 195 return (smb_open_error(shp, errp, ESMB_VERSION)); 196 } 197 198 if (bios == NULL) { 199 smb_free(ep, SMB_ENTRY_MAXLEN); 200 return (smb_open_error(shp, errp, ESMB_MAPDEV)); 201 } 202 203 stbuf = smb_alloc(smbe_stlen); 204 bcopy(bios, stbuf, smbe_stlen); 205 psm_unmap_phys(bios, smbe_stlen); 206 shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, &err); 207 208 if (shp == NULL) { 209 smb_free(stbuf, smbe_stlen); 210 smb_free(ep, SMB_ENTRY_MAXLEN); 211 return (smb_open_error(shp, errp, err)); 212 } 213 214 if (ksmbios == NULL) { 215 cmn_err(CE_CONT, "?SMBIOS v%u.%u loaded (%u bytes)", 216 smbe_major, smbe_minor, smbe_stlen); 217 if (shp->sh_flags & SMB_FL_TRUNC) 218 cmn_err(CE_CONT, "?SMBIOS table is truncated"); 219 } 220 221 shp->sh_flags |= SMB_FL_BUFALLOC; 222 smb_free(ep, SMB_ENTRY_MAXLEN); 223 224 return (shp); 225 } 226 227 smbios_hdl_t * 228 smbios_fdopen(int fd __unused, int version __unused, int flags __unused, 229 int *errp) 230 { 231 return (smb_open_error(NULL, errp, ENOTSUP)); 232 } 233 234 int 235 smbios_write(smbios_hdl_t *shp, int fd __unused) 236 { 237 return (smb_set_errno(shp, ENOTSUP)); 238 } 239