xref: /illumos-gate/usr/src/uts/i86pc/os/smb_dev.c (revision 5400c85432c0a689ecefb8c19b8f9861809ca093)
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