1 | /* $NetBSD: bios32.c,v 1.21 2015/11/22 13:41:24 maxv Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1999 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, |
9 | * NASA Ames Research Center. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * Copyright (c) 1999, by UCHIYAMA Yasushi |
35 | * All rights reserved. |
36 | * |
37 | * Redistribution and use in source and binary forms, with or without |
38 | * modification, are permitted provided that the following conditions |
39 | * are met: |
40 | * 1. Redistributions of source code must retain the above copyright |
41 | * notice, this list of conditions and the following disclaimer. |
42 | * 2. The name of the developer may NOT be used to endorse or promote products |
43 | * derived from this software without specific prior written permission. |
44 | * |
45 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
46 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
48 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
49 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
50 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
51 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
52 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
53 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
54 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
55 | * SUCH DAMAGE. |
56 | */ |
57 | |
58 | /* |
59 | * Basic interface to BIOS32 services. |
60 | */ |
61 | |
62 | #include <sys/cdefs.h> |
63 | __KERNEL_RCSID(0, "$NetBSD: bios32.c,v 1.21 2015/11/22 13:41:24 maxv Exp $" ); |
64 | |
65 | #include <sys/param.h> |
66 | #include <sys/systm.h> |
67 | #include <sys/device.h> |
68 | |
69 | #include <dev/isa/isareg.h> |
70 | #include <machine/isa_machdep.h> |
71 | |
72 | #include <machine/segments.h> |
73 | #include <machine/bios32.h> |
74 | #include <x86/smbiosvar.h> |
75 | |
76 | #include <uvm/uvm.h> |
77 | |
78 | #include "ipmi.h" |
79 | #include "opt_xen.h" |
80 | |
81 | #define BIOS32_START 0xe0000 |
82 | #define BIOS32_SIZE 0x20000 |
83 | #define BIOS32_END (BIOS32_START + BIOS32_SIZE - 0x10) |
84 | |
85 | struct bios32_entry bios32_entry; |
86 | struct smbios_entry smbios_entry; |
87 | |
88 | /* |
89 | * Initialize the BIOS32 interface. |
90 | */ |
91 | void |
92 | bios32_init(void) |
93 | { |
94 | #if 0 /* XXXfvdl need to set up compatibility segment for this */ |
95 | paddr_t entry = 0; |
96 | void *p; |
97 | unsigned char cksum; |
98 | int i; |
99 | |
100 | for (p = (void *)ISA_HOLE_VADDR(BIOS32_START); |
101 | p < (void *)ISA_HOLE_VADDR(BIOS32_END); |
102 | p += 16) { |
103 | if (*(int *)p != BIOS32_MAKESIG('_', '3', '2', '_')) |
104 | continue; |
105 | |
106 | cksum = 0; |
107 | for (i = 0; i < 16; i++) |
108 | cksum += *(unsigned char *)(p + i); |
109 | if (cksum != 0) |
110 | continue; |
111 | |
112 | if (*(p + 9) != 1) |
113 | continue; |
114 | |
115 | entry = *(uint32_t *)(p + 4); |
116 | |
117 | aprint_verbose("BIOS32 rev. %d found at 0x%lx\n" , |
118 | *(p + 8), entry); |
119 | |
120 | if (entry < BIOS32_START || |
121 | entry >= BIOS32_END) { |
122 | aprint_error("BIOS32 entry point outside " |
123 | "allowable range\n" ); |
124 | entry = 0; |
125 | } |
126 | break; |
127 | } |
128 | |
129 | if (entry != 0) { |
130 | bios32_entry.offset = (void *)ISA_HOLE_VADDR(entry); |
131 | bios32_entry.segment = GSEL(GCODE_SEL, SEL_KPL); |
132 | } |
133 | #endif |
134 | uint8_t *p; |
135 | int i; |
136 | |
137 | /* see if we have SMBIOS extentions */ |
138 | for (p = ISA_HOLE_VADDR(SMBIOS_START); |
139 | p < (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END); p+= 16) { |
140 | struct smbhdr * sh = (struct smbhdr *)p; |
141 | uint8_t chksum; |
142 | vaddr_t eva; |
143 | paddr_t pa, end; |
144 | |
145 | if (sh->sig != BIOS32_MAKESIG('_', 'S', 'M', '_')) |
146 | continue; |
147 | i = sh->len; |
148 | for (chksum = 0; i--; ) |
149 | chksum += p[i]; |
150 | if (chksum != 0) |
151 | continue; |
152 | p += 0x10; |
153 | if (p[0] != '_' && p[1] != 'D' && p[2] != 'M' && |
154 | p[3] != 'I' && p[4] != '_') |
155 | continue; |
156 | for (chksum = 0, i = 0xf; i--; ) |
157 | chksum += p[i]; |
158 | if (chksum != 0) |
159 | continue; |
160 | |
161 | pa = trunc_page(sh->addr); |
162 | end = round_page(sh->addr + sh->size); |
163 | eva = uvm_km_alloc(kernel_map, end - pa, 0, UVM_KMF_VAONLY); |
164 | if (eva == 0) |
165 | break; |
166 | |
167 | smbios_entry.addr = (uint8_t *)(eva + |
168 | (sh->addr & PGOFSET)); |
169 | smbios_entry.len = sh->size; |
170 | smbios_entry.mjr = sh->majrev; |
171 | smbios_entry.min = sh->minrev; |
172 | smbios_entry.count = sh->count; |
173 | |
174 | for (; pa < end; pa+= NBPG, eva+= NBPG) |
175 | #ifdef XEN |
176 | pmap_kenter_ma(eva, pa, VM_PROT_READ, 0); |
177 | #else |
178 | pmap_kenter_pa(eva, pa, VM_PROT_READ, 0); |
179 | #endif |
180 | pmap_update(pmap_kernel()); |
181 | |
182 | aprint_debug("SMBIOS rev. %d.%d @ 0x%lx (%d entries)\n" , |
183 | sh->majrev, sh->minrev, (u_long)sh->addr, |
184 | sh->count); |
185 | |
186 | break; |
187 | } |
188 | } |
189 | |
190 | /* |
191 | * Call BIOS32 to locate the specified BIOS32 service, and fill |
192 | * in the entry point information. |
193 | */ |
194 | int |
195 | bios32_service(uint32_t service, bios32_entry_t e, bios32_entry_info_t ei) |
196 | { |
197 | uint32_t eax, ebx, ecx, edx; |
198 | paddr_t entry; |
199 | |
200 | if (bios32_entry.offset == 0) |
201 | return (0); /* BIOS32 not present */ |
202 | |
203 | __asm volatile("lcall *(%%rdi)" |
204 | : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) |
205 | : "0" (service), "1" (0), "D" (&bios32_entry)); |
206 | |
207 | if ((eax & 0xff) != 0) |
208 | return (0); /* service not found */ |
209 | |
210 | entry = ebx + edx; |
211 | |
212 | if (entry < BIOS32_START || entry >= BIOS32_END) { |
213 | aprint_error( |
214 | "bios32: entry point for service %c%c%c%c is outside " |
215 | "allowable range\n" , |
216 | service & 0xff, |
217 | (service >> 8) & 0xff, |
218 | (service >> 16) & 0xff, |
219 | (service >> 24) & 0xff); |
220 | return (0); |
221 | } |
222 | |
223 | e->offset = (void *)ISA_HOLE_VADDR(entry); |
224 | e->segment = GSEL(GCODE_SEL, SEL_KPL); |
225 | |
226 | ei->bei_base = ebx; |
227 | ei->bei_size = ecx; |
228 | ei->bei_entry = entry; |
229 | |
230 | return (1); |
231 | } |
232 | |
233 | /* |
234 | * smbios_find_table() takes a caller supplied smbios struct type and |
235 | * a pointer to a handle (struct smbtable) returning one if the structure |
236 | * is sucessfully located and zero otherwise. Callers should take care |
237 | * to initilize the cookie field of the smbtable structure to zero before |
238 | * the first invocation of this function. |
239 | * Multiple tables of the same type can be located by repeadtly calling |
240 | * smbios_find_table with the same arguments. |
241 | */ |
242 | int |
243 | smbios_find_table(uint8_t type, struct smbtable *st) |
244 | { |
245 | uint8_t *va, *end; |
246 | struct smbtblhdr *hdr; |
247 | int ret = 0, tcount = 1; |
248 | |
249 | va = smbios_entry.addr; |
250 | end = va + smbios_entry.len; |
251 | |
252 | /* |
253 | * The cookie field of the smtable structure is used to locate |
254 | * multiple instances of a table of an arbitrary type. Following the |
255 | * sucessful location of a table, the type is encoded as bits 0:7 of |
256 | * the cookie value, the offset in terms of the number of structures |
257 | * preceding that referenced by the handle is encoded in bits 15:31. |
258 | */ |
259 | if ((st->cookie & 0xfff) == type && st->cookie >> 16) { |
260 | if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) { |
261 | hdr = st->hdr; |
262 | if (hdr->type == type) { |
263 | va = (uint8_t *)hdr + hdr->size; |
264 | for (; va + 1 < end; va++) |
265 | if (*va == 0 && *(va + 1) == 0) |
266 | break; |
267 | va+= 2; |
268 | tcount = st->cookie >> 16; |
269 | } |
270 | } |
271 | } |
272 | for (; va + sizeof(struct smbtblhdr) < end && tcount <= |
273 | smbios_entry.count; tcount++) { |
274 | hdr = (struct smbtblhdr *)va; |
275 | if (hdr->type == type) { |
276 | ret = 1; |
277 | st->hdr = hdr; |
278 | st->tblhdr = va + sizeof(struct smbtblhdr); |
279 | st->cookie = (tcount + 1) << 16 | type; |
280 | break; |
281 | } |
282 | if (hdr->type == SMBIOS_TYPE_EOT) |
283 | break; |
284 | va+= hdr->size; |
285 | for (; va + 1 < end; va++) |
286 | if (*va == 0 && *(va + 1) == 0) |
287 | break; |
288 | va+=2; |
289 | } |
290 | |
291 | return ret; |
292 | } |
293 | |
294 | char * |
295 | smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len) |
296 | { |
297 | uint8_t *va, *end; |
298 | char *ret = NULL; |
299 | int i; |
300 | |
301 | va = (uint8_t *)st->hdr + st->hdr->size; |
302 | end = smbios_entry.addr + smbios_entry.len; |
303 | for (i = 1; va < end && i < indx && *va; i++) |
304 | while (*va++) |
305 | ; |
306 | if (i == indx) { |
307 | if (va + len < end) { |
308 | ret = dest; |
309 | memcpy(ret, va, len); |
310 | ret[len - 1] = '\0'; |
311 | } |
312 | } |
313 | |
314 | return ret; |
315 | } |
316 | |