xref: /illumos-gate/usr/src/uts/i86pc/os/smb_dev.c (revision cadd68ea0014761eda6a293664086dfa80686d85)
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 *
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 *
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 	}
87 
88 	bios = psm_map_phys(startaddr, bioslen, PSM_PROT_READ);
89 
90 	if (bios == NULL)
91 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
92 
93 	/*
94 	 * In case we did map one page, make sure we will not cross
95 	 * the end of the page.
96 	 */
97 	p = bios + startoff;
98 	q = bios + bioslen - startoff;
99 	while (p < q) {
100 		err = strncmp(p, SMB3_ENTRY_EANCHOR, SMB3_ENTRY_EANCHORLEN);
101 		if (err == 0) {
102 			ep_type = SMBIOS_ENTRY_POINT_30;
103 			break;
104 		}
105 
106 		if (strncmp(p, SMB_ENTRY_EANCHOR, SMB_ENTRY_EANCHORLEN) == 0)
107 			break;
108 		p += SMB_SCAN_STEP;
109 	}
110 
111 	if (p >= q) {
112 		psm_unmap_phys(bios, bioslen);
113 		return (smb_open_error(shp, errp, ESMB_NOTFOUND));
114 	}
115 
116 	ep = smb_alloc(SMB_ENTRY_MAXLEN);
117 	bcopy(p, ep, sizeof (smbios_entry_t));
118 	if (ep_type == SMBIOS_ENTRY_POINT_21) {
119 		ep->ep21.smbe_elen = MIN(ep->ep21.smbe_elen, SMB_ENTRY_MAXLEN);
120 		bcopy(p, ep, ep->ep21.smbe_elen);
121 	} else if (ep_type == SMBIOS_ENTRY_POINT_30) {
122 		ep->ep30.smbe_elen = MIN(ep->ep30.smbe_elen, SMB_ENTRY_MAXLEN);
123 		bcopy(p, ep, ep->ep30.smbe_elen);
124 	}
125 
126 	psm_unmap_phys(bios, bioslen);
127 	switch (ep_type) {
128 	case SMBIOS_ENTRY_POINT_21:
129 		smbe_major = ep->ep21.smbe_major;
130 		smbe_minor = ep->ep21.smbe_minor;
131 		smbe_stlen = ep->ep21.smbe_stlen;
132 		bios = psm_map_phys(ep->ep21.smbe_staddr, smbe_stlen,
133 		    PSM_PROT_READ);
134 		break;
135 	case SMBIOS_ENTRY_POINT_30:
136 		smbe_major = ep->ep30.smbe_major;
137 		smbe_minor = ep->ep30.smbe_minor;
138 		smbe_stlen = ep->ep30.smbe_stlen;
139 		bios = psm_map_phys_new(ep->ep30.smbe_staddr, smbe_stlen,
140 		    PSM_PROT_READ);
141 		break;
142 	default:
143 		smb_free(ep, SMB_ENTRY_MAXLEN);
144 		return (smb_open_error(shp, errp, ESMB_VERSION));
145 	}
146 
147 	if (bios == NULL) {
148 		smb_free(ep, SMB_ENTRY_MAXLEN);
149 		return (smb_open_error(shp, errp, ESMB_MAPDEV));
150 	}
151 
152 	stbuf = smb_alloc(smbe_stlen);
153 	bcopy(bios, stbuf, smbe_stlen);
154 	psm_unmap_phys(bios, smbe_stlen);
155 	shp = smbios_bufopen(ep, stbuf, smbe_stlen, version, flags, &err);
156 
157 	if (shp == NULL) {
158 		smb_free(stbuf, smbe_stlen);
159 		smb_free(ep, SMB_ENTRY_MAXLEN);
160 		return (smb_open_error(shp, errp, err));
161 	}
162 
163 	if (ksmbios == NULL) {
164 		cmn_err(CE_CONT, "?SMBIOS v%u.%u loaded (%u bytes)",
165 		    smbe_major, smbe_minor, smbe_stlen);
166 		if (shp->sh_flags & SMB_FL_TRUNC)
167 			cmn_err(CE_CONT, "?SMBIOS table is truncated");
168 	}
169 
170 	shp->sh_flags |= SMB_FL_BUFALLOC;
171 	smb_free(ep, SMB_ENTRY_MAXLEN);
172 
173 	return (shp);
174 }
175 
176 /*ARGSUSED*/
177 smbios_hdl_t *
178 smbios_fdopen(int fd, int version, int flags, int *errp)
179 {
180 	return (smb_open_error(NULL, errp, ENOTSUP));
181 }
182 
183 /*ARGSUSED*/
184 int
185 smbios_write(smbios_hdl_t *shp, int fd)
186 {
187 	return (smb_set_errno(shp, ENOTSUP));
188 }
189