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