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
85struct bios32_entry bios32_entry;
86struct smbios_entry smbios_entry;
87
88/*
89 * Initialize the BIOS32 interface.
90 */
91void
92bios32_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 */
194int
195bios32_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 */
242int
243smbios_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
294char *
295smbios_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