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