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