bios.c revision 49197
1/*-
2 * Copyright (c) 1997 Michael Smith
3 * Copyright (c) 1998 Jonathan Lemon
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *      $Id: bios.c,v 1.12 1999/03/16 21:11:28 msmith Exp $
28 */
29
30/*
31 * Code for dealing with the BIOS in x86 PC systems.
32 */
33
34#include <sys/param.h>
35#include <sys/proc.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <vm/vm.h>
40#include <vm/pmap.h>
41#include <machine/md_var.h>
42#include <machine/segments.h>
43#include <machine/stdarg.h>
44#include <machine/tss.h>
45#include <machine/vmparam.h>
46#include <machine/pc/bios.h>
47
48#define BIOS_START	0xe0000
49#define BIOS_SIZE	0x20000
50
51/* exported lookup results */
52struct bios32_SDentry		PCIbios = {entry : 0};
53static struct SMBIOS_table	*SMBIOStable = 0;
54static struct DMI_table		*DMItable = 0;
55
56static u_int		bios32_SDCI = 0;
57
58static void		bios32_init(void *junk);
59
60/* start fairly early */
61SYSINIT(bios32, SI_SUB_CPU, SI_ORDER_ANY, bios32_init, NULL);
62
63/*
64 * bios32_init
65 *
66 * Locate various bios32 entities.
67 */
68static void
69bios32_init(void *junk)
70{
71    u_long			sigaddr;
72    struct bios32_SDheader	*sdh;
73    struct SMBIOS_table		*sbt;
74    struct DMI_table		*dmit;
75    u_int8_t			ck, *cv;
76    int				i;
77
78
79    /*
80     * BIOS32 Service Directory
81     */
82
83    /* look for the signature */
84    if ((sigaddr = bios_sigsearch(0, "_32_", 4, 16, 0)) != 0) {
85
86	/* get a virtual pointer to the structure */
87	sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
88	for (cv = (u_int8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) {
89	    ck += cv[i];
90	}
91	/* If checksum is OK, enable use of the entrypoint */
92	if ((ck == 0) && (sdh->entry < (BIOS_START + BIOS_SIZE))) {
93	    bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry);
94	    if (bootverbose) {
95		printf("Found BIOS32 Service Directory header at %p\n", sdh);
96		printf("Entry = 0x%x (%x)  Rev = %d  Len = %d\n",
97		       sdh->entry, bios32_SDCI, sdh->revision, sdh->len);
98	    }
99	    /* See if there's a PCI BIOS entrypoint here */
100	    PCIbios.ident.id = 0x49435024;	/* PCI systems should have this */
101	    if (!bios32_SDlookup(&PCIbios) && bootverbose)
102		printf("PCI BIOS entry at 0x%x\n", PCIbios.entry);
103	} else {
104	    printf("Bad BIOS32 Service Directory!\n");
105	}
106    }
107
108    /*
109     * System Management BIOS
110     */
111    /* look for the SMBIOS signature */
112    if ((sigaddr = bios_sigsearch(0, "_SM_", 4, 16, 0)) != 0) {
113
114	/* get a virtual pointer to the structure */
115	sbt = (struct SMBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
116	for (cv = (u_int8_t *)sbt, ck = 0, i = 0; i < sbt->len; i++) {
117	    ck += cv[i];
118	}
119	/* if checksum is OK, we have action */
120	if (ck == 0) {
121	    SMBIOStable = sbt;		/* save reference */
122	    DMItable = &sbt->dmi;	/* contained within */
123	    if (bootverbose) {
124		printf("SMIBIOS header at %p\n", sbt);
125		printf("Version %d.%d\n", sbt->major, sbt->minor);
126		printf("Table at 0x%x, %d entries, %d bytes, largest entry %d bytes\n",
127		       sbt->dmi.st_base, (int)sbt->dmi.st_entries, (int)sbt->dmi.st_size,
128		       (int)sbt->st_maxsize);
129	    }
130	} else {
131	    printf("Bad SMBIOS table checksum!\n");
132	}
133
134    }
135
136    /* look for the DMI signature */
137    if ((sigaddr = bios_sigsearch(0, "_DMI_", 5, 16, 0)) != 0) {
138
139	/* get a virtual pointer to the structure */
140	dmit = (struct DMI_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
141	for (cv = (u_int8_t *)dmit, ck = 0, i = 0; i < 15; i++) {
142	    ck += cv[i];
143	}
144	/* if checksum is OK, we have action */
145	if (ck == 0) {
146	    DMItable = dmit;		/* save reference */
147	    if (bootverbose) {
148		printf("DMI header at %p\n", dmit);
149		printf("Version %d.%d\n", (dmit->bcd_revision >> 4),
150		       (dmit->bcd_revision & 0x0f));
151		printf("Table at 0x%x, %d entries, %d bytes\n",
152		       dmit->st_base, (int)dmit->st_entries,
153		       (int)dmit->st_size);
154	    }
155	} else {
156	    printf("Bad DMI table checksum!\n");
157	}
158    }
159    if (bootverbose) {
160	    /* look for other know signatures */
161	    printf("Other BIOS signatures found:\n");
162	    printf("ACPI: %08x\n", bios_sigsearch(0, "RST PTR", 8, 16, 0));
163	    printf("$PnP: %08x\n", bios_sigsearch(0, "$PnP", 4, 16, 0));
164    }
165}
166
167/*
168 * bios32_SDlookup
169 *
170 * Query the BIOS32 Service Directory for the service named in (ent),
171 * returns nonzero if the lookup fails.  The caller must fill in
172 * (ent->ident), the remainder are populated on a successful lookup.
173 */
174int
175bios32_SDlookup(struct bios32_SDentry *ent)
176{
177	struct bios_regs args;
178
179	if (bios32_SDCI == 0)
180		return (1);
181
182	args.eax = ent->ident.id;		/* set up arguments */
183	args.ebx = args.ecx = args.edx = 0;
184	bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL));
185	if ((args.eax & 0xff) == 0) {		/* success? */
186		ent->base = args.ebx;
187		ent->len = args.ecx;
188		ent->entry = args.edx;
189		return (0);			/* all OK */
190	}
191	return (1);				/* failed */
192}
193
194
195/*
196 * bios_sigsearch
197 *
198 * Search some or all of the BIOS region for a signature string.
199 *
200 * (start)	Optional offset returned from this function
201 *		(for searching for multiple matches), or NULL
202 *		to start the search from the base of the BIOS.
203 *		Note that this will be a _physical_ address in
204 *		the range 0xe0000 - 0xfffff.
205 * (sig)	is a pointer to the byte(s) of the signature.
206 * (siglen)	number of bytes in the signature.
207 * (paralen)	signature paragraph (alignment) size.
208 * (sigofs)	offset of the signature within the paragraph.
209 *
210 * Returns the _physical_ address of the found signature, 0 if the
211 * signature was not found.
212 */
213
214u_int32_t
215bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs)
216{
217    u_char	*sp, *end;
218
219    /* compute the starting address */
220    if ((start >= BIOS_START) && (start <= (BIOS_START + BIOS_SIZE))) {
221	sp = (char *)BIOS_PADDRTOVADDR(start);
222    } else if (start == 0) {
223	sp = (char *)BIOS_PADDRTOVADDR(BIOS_START);
224    } else {
225	return 0;				/* bogus start address */
226    }
227
228    /* compute the end address */
229    end = (u_char *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_SIZE);
230
231    /* loop searching */
232    while ((sp + sigofs + siglen) < end) {
233
234	/* compare here */
235	if (!bcmp(sp + sigofs, sig, siglen)) {
236	    /* convert back to physical address */
237	    return((u_int32_t)BIOS_VADDRTOPADDR(sp));
238	}
239	sp += paralen;
240    }
241    return(0);
242}
243
244/*
245 * do not staticize, used by bioscall.s
246 */
247union {
248	struct {
249		u_short	offset;
250		u_short	segment;
251	} vec16;
252	struct {
253		u_int	offset;
254		u_short	segment;
255	} vec32;
256} bioscall_vector;			/* bios jump vector */
257
258void
259set_bios_selectors(struct bios_segments *seg, int flags)
260{
261	static u_int curgen = 1;
262	struct soft_segment_descriptor ssd = {
263		0,			/* segment base address (overwritten) */
264		0,			/* length (overwritten) */
265		SDT_MEMERA,		/* segment type (overwritten) */
266		0,			/* priority level */
267		1,			/* descriptor present */
268		0, 0,
269		1,			/* descriptor size (overwritten) */
270		0			/* granularity == byte units */
271	};
272
273	if (seg->generation == curgen)
274		return;
275	if (++curgen == 0)
276		curgen = 1;
277	seg->generation = curgen;
278
279	ssd.ssd_base = seg->code32.base;
280	ssd.ssd_limit = seg->code32.limit;
281	ssdtosd(&ssd, &gdt[GBIOSCODE32_SEL].sd);
282
283	ssd.ssd_def32 = 0;
284	if (flags & BIOSCODE_FLAG) {
285		ssd.ssd_base = seg->code16.base;
286		ssd.ssd_limit = seg->code16.limit;
287		ssdtosd(&ssd, &gdt[GBIOSCODE16_SEL].sd);
288	}
289
290	ssd.ssd_type = SDT_MEMRWA;
291	if (flags & BIOSDATA_FLAG) {
292		ssd.ssd_base = seg->data.base;
293		ssd.ssd_limit = seg->data.limit;
294		ssdtosd(&ssd, &gdt[GBIOSDATA_SEL].sd);
295	}
296
297	if (flags & BIOSUTIL_FLAG) {
298		ssd.ssd_base = seg->util.base;
299		ssd.ssd_limit = seg->util.limit;
300		ssdtosd(&ssd, &gdt[GBIOSUTIL_SEL].sd);
301	}
302
303	if (flags & BIOSARGS_FLAG) {
304		ssd.ssd_base = seg->args.base;
305		ssd.ssd_limit = seg->args.limit;
306		ssdtosd(&ssd, &gdt[GBIOSARGS_SEL].sd);
307	}
308}
309
310/*
311 * for pointers, we don't know how much space is supposed to be allocated,
312 * so we assume a minimum size of 256 bytes.  If more than this is needed,
313 * then this can be revisited, such as adding a length specifier.
314 */
315#define	ASSUMED_ARGSIZE		256
316
317extern int vm86pa;
318
319/*
320 * this routine is really greedy with selectors, and uses 5:
321 *
322 * 32-bit code selector:	to return to kernel
323 * 16-bit code selector:	for running code
324 *        data selector:	for 16-bit data
325 *        util selector:	extra utility selector
326 *        args selector:	to handle pointers
327 *
328 * the util selector is set from the util16 entry in bios16_args, if a
329 * "U" specifier is seen.
330 *
331 * See <machine/pc/bios.h> for description of format specifiers
332 */
333int
334bios16(struct bios_args *args, char *fmt, ...)
335{
336	char *p, *stack, *stack_top;
337	va_list ap;
338	int flags = BIOSCODE_FLAG | BIOSDATA_FLAG;
339	u_int i, arg_start, arg_end;
340	u_int *pte, *ptd;
341
342	arg_start = 0xffffffff;
343	arg_end = 0;
344
345	stack = (caddr_t)PAGE_SIZE;
346	va_start(ap, fmt);
347	for (p = fmt; p && *p; p++) {
348		switch (*p) {
349		case 'p':			/* 32-bit pointer */
350			i = va_arg(ap, u_int);
351			arg_start = min(arg_start, i);
352			arg_end = max(arg_end, i + ASSUMED_ARGSIZE);
353			flags |= BIOSARGS_FLAG;
354			stack -= 4;
355			break;
356
357		case 'i':			/* 32-bit integer */
358			i = va_arg(ap, u_int);
359			stack -= 4;
360			break;
361
362		case 'U':			/* 16-bit selector */
363			flags |= BIOSUTIL_FLAG;
364			/* FALL THROUGH */
365		case 'D':			/* 16-bit selector */
366		case 'C':			/* 16-bit selector */
367		case 's':			/* 16-bit integer */
368			i = va_arg(ap, u_short);
369			stack -= 2;
370			break;
371
372		default:
373			return (EINVAL);
374		}
375	}
376
377	if (flags & BIOSARGS_FLAG) {
378		if (arg_end - arg_start > ctob(16))
379			return (EACCES);
380		args->seg.args.base = arg_start;
381		args->seg.args.limit = arg_end - arg_start;
382	}
383
384	args->seg.code32.base = (u_int)&bios16_call & PG_FRAME;
385	args->seg.code32.limit = 0xffff;
386
387	ptd = (u_int *)rcr3();
388#ifdef SMP
389	if (ptd == my_idlePTD)
390#else
391	if (ptd == IdlePTD)
392#endif
393	{
394		/*
395		 * no page table, so create one and install it.
396		 */
397		pte = (u_int *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
398		ptd = (u_int *)((u_int)ptd + KERNBASE);
399		*ptd = vtophys(pte) | PG_RW | PG_V;
400	} else {
401		/*
402		 * this is a user-level page table
403		 */
404		pte = (u_int *)&PTmap;
405	}
406	/*
407	 * install pointer to page 0.  we don't need to flush the tlb,
408	 * since there should not be a previous mapping for page 0.
409	 */
410	*pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V;
411
412	stack_top = stack;
413	va_start(ap, fmt);
414	for (p = fmt; p && *p; p++) {
415		switch (*p) {
416		case 'p':			/* 32-bit pointer */
417			i = va_arg(ap, u_int);
418			*(u_int *)stack = (i - arg_start) |
419			    (GSEL(GBIOSARGS_SEL, SEL_KPL) << 16);
420			stack += 4;
421			break;
422
423		case 'i':			/* 32-bit integer */
424			i = va_arg(ap, u_int);
425			*(u_int *)stack = i;
426			stack += 4;
427			break;
428
429		case 'U':			/* 16-bit selector */
430			i = va_arg(ap, u_short);
431			*(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL);
432			stack += 2;
433			break;
434
435		case 'D':			/* 16-bit selector */
436			i = va_arg(ap, u_short);
437			*(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL);
438			stack += 2;
439			break;
440
441		case 'C':			/* 16-bit selector */
442			i = va_arg(ap, u_short);
443			*(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL);
444			stack += 2;
445			break;
446
447		case 's':			/* 16-bit integer */
448			i = va_arg(ap, u_short);
449			*(u_short *)stack = i;
450			stack += 2;
451			break;
452
453		default:
454			return (EINVAL);
455		}
456	}
457
458	args->seg.generation = 0;			/* reload selectors */
459	set_bios_selectors(&args->seg, flags);
460	bioscall_vector.vec16.offset = (u_short)args->entry;
461	bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL);
462
463	i = bios16_call(&args->r, stack_top);
464
465	if (pte == (u_int *)&PTmap) {
466		*pte = 0;			/* remove entry */
467	} else {
468		*ptd = 0;			/* remove page table */
469		free(pte, M_TEMP);		/* ... and free it */
470	}
471
472
473	/*
474	 * XXX only needs to be invlpg(0) but that doesn't work on the 386
475	 */
476	invltlb();
477
478	return (i);
479}
480