x86bios.c revision 205297
1/*- 2 * Copyright (c) 2009 Alex Keda <admin@lissyara.su> 3 * Copyright (c) 2009-2010 Jung-uk Kim <jkim@FreeBSD.org> 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 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/compat/x86bios/x86bios.c 205297 2010-03-18 20:15:34Z jkim $"); 30 31#include "opt_x86bios.h" 32 33#include <sys/param.h> 34#include <sys/bus.h> 35#include <sys/kernel.h> 36#include <sys/lock.h> 37#include <sys/malloc.h> 38#include <sys/module.h> 39#include <sys/mutex.h> 40#include <sys/proc.h> 41#include <sys/sysctl.h> 42 43#include <contrib/x86emu/x86emu.h> 44#include <contrib/x86emu/x86emu_regs.h> 45#include <compat/x86bios/x86bios.h> 46 47#include <dev/pci/pcireg.h> 48#include <dev/pci/pcivar.h> 49 50#include <machine/cpufunc.h> 51 52#include <vm/vm.h> 53#include <vm/pmap.h> 54 55#define X86BIOS_PAGE_SIZE 0x00001000 /* 4K */ 56 57#define X86BIOS_IVT_SIZE 0x00000500 /* 1K + 256 (BDA) */ 58#define X86BIOS_SEG_SIZE 0x00010000 /* 64K */ 59#define X86BIOS_MEM_SIZE 0x00100000 /* 1M */ 60 61#define X86BIOS_IVT_BASE 0x00000000 62#define X86BIOS_RAM_BASE 0x00001000 63#define X86BIOS_ROM_BASE 0x000a0000 /* XXX EBDA? */ 64 65#define X86BIOS_ROM_SIZE (X86BIOS_MEM_SIZE - X86BIOS_ROM_BASE) 66 67#define X86BIOS_PAGES (X86BIOS_MEM_SIZE / X86BIOS_PAGE_SIZE) 68 69#define X86BIOS_R_DS _pad1 70#define X86BIOS_R_SS _pad2 71 72static struct x86emu x86bios_emu; 73 74static struct mtx x86bios_lock; 75 76static void *x86bios_ivt; 77static void *x86bios_rom; 78static void *x86bios_seg; 79 80static vm_offset_t *x86bios_map; 81 82static vm_paddr_t x86bios_seg_phys; 83 84static int x86bios_fault; 85static uint32_t x86bios_fault_addr; 86static uint32_t x86bios_fault_inst; 87 88SYSCTL_NODE(_debug, OID_AUTO, x86bios, CTLFLAG_RD, NULL, "x86bios debugging"); 89static int x86bios_trace_call; 90TUNABLE_INT("debug.x86bios.call", &x86bios_trace_call); 91SYSCTL_INT(_debug_x86bios, OID_AUTO, call, CTLFLAG_RW, &x86bios_trace_call, 0, 92 "Trace far function calls"); 93static int x86bios_trace_int; 94TUNABLE_INT("debug.x86bios.int", &x86bios_trace_int); 95SYSCTL_INT(_debug_x86bios, OID_AUTO, int, CTLFLAG_RW, &x86bios_trace_int, 0, 96 "Trace software interrupt handlers"); 97 98static void 99x86bios_set_fault(struct x86emu *emu, uint32_t addr) 100{ 101 102 x86bios_fault = 1; 103 x86bios_fault_addr = addr; 104 x86bios_fault_inst = (emu->x86.R_CS << 4) + emu->x86.R_IP; 105} 106 107static void * 108x86bios_get_pages(uint32_t offset, size_t size) 109{ 110 int i; 111 112 if (offset + size > X86BIOS_MEM_SIZE) 113 return (NULL); 114 115 i = offset / X86BIOS_PAGE_SIZE; 116 if (x86bios_map[i] != 0) 117 return ((void *)(x86bios_map[i] + offset - 118 i * X86BIOS_PAGE_SIZE)); 119 120 return (NULL); 121} 122 123static void 124x86bios_set_pages(vm_offset_t va, vm_paddr_t pa, size_t size) 125{ 126 int i, j; 127 128 for (i = pa / X86BIOS_PAGE_SIZE, j = 0; 129 j < howmany(size, X86BIOS_PAGE_SIZE); i++, j++) 130 x86bios_map[i] = va + j * X86BIOS_PAGE_SIZE; 131} 132 133static uint8_t 134x86bios_emu_rdb(struct x86emu *emu, uint32_t addr) 135{ 136 uint8_t *va; 137 138 va = x86bios_get_pages(addr, sizeof(*va)); 139 if (va == NULL) { 140 x86bios_set_fault(emu, addr); 141 x86emu_halt_sys(emu); 142 } 143 144 return (*va); 145} 146 147static uint16_t 148x86bios_emu_rdw(struct x86emu *emu, uint32_t addr) 149{ 150 uint16_t *va; 151 152 va = x86bios_get_pages(addr, sizeof(*va)); 153 if (va == NULL) { 154 x86bios_set_fault(emu, addr); 155 x86emu_halt_sys(emu); 156 } 157 158 return (le16toh(*va)); 159} 160 161static uint32_t 162x86bios_emu_rdl(struct x86emu *emu, uint32_t addr) 163{ 164 uint32_t *va; 165 166 va = x86bios_get_pages(addr, sizeof(*va)); 167 if (va == NULL) { 168 x86bios_set_fault(emu, addr); 169 x86emu_halt_sys(emu); 170 } 171 172 return (le32toh(*va)); 173} 174 175static void 176x86bios_emu_wrb(struct x86emu *emu, uint32_t addr, uint8_t val) 177{ 178 uint8_t *va; 179 180 va = x86bios_get_pages(addr, sizeof(*va)); 181 if (va == NULL) { 182 x86bios_set_fault(emu, addr); 183 x86emu_halt_sys(emu); 184 } 185 186 *va = val; 187} 188 189static void 190x86bios_emu_wrw(struct x86emu *emu, uint32_t addr, uint16_t val) 191{ 192 uint16_t *va; 193 194 va = x86bios_get_pages(addr, sizeof(*va)); 195 if (va == NULL) { 196 x86bios_set_fault(emu, addr); 197 x86emu_halt_sys(emu); 198 } 199 200 *va = htole16(val); 201} 202 203static void 204x86bios_emu_wrl(struct x86emu *emu, uint32_t addr, uint32_t val) 205{ 206 uint32_t *va; 207 208 va = x86bios_get_pages(addr, sizeof(*va)); 209 if (va == NULL) { 210 x86bios_set_fault(emu, addr); 211 x86emu_halt_sys(emu); 212 } 213 214 *va = htole32(val); 215} 216 217static uint8_t 218x86bios_emu_inb(struct x86emu *emu, uint16_t port) 219{ 220 221 if (port == 0xb2) /* APM scratch register */ 222 return (0); 223 if (port >= 0x80 && port < 0x88) /* POST status register */ 224 return (0); 225 226 return (inb(port)); 227} 228 229static uint16_t 230x86bios_emu_inw(struct x86emu *emu, uint16_t port) 231{ 232 233 if (port >= 0x80 && port < 0x88) /* POST status register */ 234 return (0); 235 236 return (inw(port)); 237} 238 239static uint32_t 240x86bios_emu_inl(struct x86emu *emu, uint16_t port) 241{ 242 243 if (port >= 0x80 && port < 0x88) /* POST status register */ 244 return (0); 245 246 return (inl(port)); 247} 248 249static void 250x86bios_emu_outb(struct x86emu *emu, uint16_t port, uint8_t val) 251{ 252 253 if (port == 0xb2) /* APM scratch register */ 254 return; 255 if (port >= 0x80 && port < 0x88) /* POST status register */ 256 return; 257 258 outb(port, val); 259} 260 261static void 262x86bios_emu_outw(struct x86emu *emu, uint16_t port, uint16_t val) 263{ 264 265 if (port >= 0x80 && port < 0x88) /* POST status register */ 266 return; 267 268 outw(port, val); 269} 270 271static void 272x86bios_emu_outl(struct x86emu *emu, uint16_t port, uint32_t val) 273{ 274 275 if (port >= 0x80 && port < 0x88) /* POST status register */ 276 return; 277 278 outl(port, val); 279} 280 281static void 282x86bios_emu_get_intr(struct x86emu *emu, int intno) 283{ 284 uint16_t *sp; 285 uint32_t iv; 286 287 emu->x86.R_SP -= 6; 288 289 sp = (uint16_t *)((vm_offset_t)x86bios_seg + emu->x86.R_SP); 290 sp[0] = htole16(emu->x86.R_IP); 291 sp[1] = htole16(emu->x86.R_CS); 292 sp[2] = htole16(emu->x86.R_FLG); 293 294 iv = x86bios_get_intr(intno); 295 emu->x86.R_IP = iv & 0x000f; 296 emu->x86.R_CS = (iv >> 12) & 0xffff; 297 emu->x86.R_FLG &= ~(F_IF | F_TF); 298} 299 300void * 301x86bios_alloc(uint32_t *offset, size_t size) 302{ 303 void *vaddr; 304 305 if (offset == NULL || size == 0) 306 return (NULL); 307 308 vaddr = contigmalloc(size, M_DEVBUF, M_NOWAIT, X86BIOS_RAM_BASE, 309 X86BIOS_ROM_BASE, X86BIOS_PAGE_SIZE, 0); 310 if (vaddr != NULL) { 311 *offset = vtophys(vaddr); 312 x86bios_set_pages((vm_offset_t)vaddr, *offset, size); 313 } 314 315 return (vaddr); 316} 317 318void 319x86bios_free(void *addr, size_t size) 320{ 321 vm_paddr_t paddr; 322 323 if (addr == NULL || size == 0) 324 return; 325 326 paddr = vtophys(addr); 327 if (paddr < X86BIOS_RAM_BASE || paddr >= X86BIOS_ROM_BASE || 328 paddr % X86BIOS_PAGE_SIZE != 0) 329 return; 330 331 bzero(x86bios_map + paddr / X86BIOS_PAGE_SIZE, 332 sizeof(*x86bios_map) * howmany(size, X86BIOS_PAGE_SIZE)); 333 contigfree(addr, size, M_DEVBUF); 334} 335 336void 337x86bios_init_regs(struct x86regs *regs) 338{ 339 340 bzero(regs, sizeof(*regs)); 341 regs->X86BIOS_R_DS = regs->X86BIOS_R_SS = x86bios_seg_phys >> 4; 342} 343 344void 345x86bios_call(struct x86regs *regs, uint16_t seg, uint16_t off) 346{ 347 348 if (x86bios_map == NULL) 349 return; 350 351 if (x86bios_trace_call) 352 printf("Calling 0x%05x (ax=0x%04x bx=0x%04x " 353 "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n", 354 (seg << 4) + off, regs->R_AX, regs->R_BX, regs->R_CX, 355 regs->R_DX, regs->R_ES, regs->R_DI); 356 357 mtx_lock_spin(&x86bios_lock); 358 memcpy(&x86bios_emu.x86, regs, sizeof(*regs)); 359 x86bios_fault = 0; 360 x86emu_exec_call(&x86bios_emu, seg, off); 361 memcpy(regs, &x86bios_emu.x86, sizeof(*regs)); 362 mtx_unlock_spin(&x86bios_lock); 363 364 if (x86bios_trace_call) { 365 printf("Exiting 0x%05x (ax=0x%04x bx=0x%04x " 366 "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n", 367 (seg << 4) + off, regs->R_AX, regs->R_BX, regs->R_CX, 368 regs->R_DX, regs->R_ES, regs->R_DI); 369 if (x86bios_fault) 370 printf("Page fault at 0x%05x from 0x%05x.\n", 371 x86bios_fault_addr, x86bios_fault_inst); 372 } 373} 374 375uint32_t 376x86bios_get_intr(int intno) 377{ 378 uint32_t *iv; 379 380 iv = (uint32_t *)((vm_offset_t)x86bios_ivt + intno * 4); 381 382 return (le32toh(*iv)); 383} 384 385void 386x86bios_intr(struct x86regs *regs, int intno) 387{ 388 389 if (intno < 0 || intno > 255) 390 return; 391 392 if (x86bios_map == NULL) 393 return; 394 395 if (x86bios_trace_int) 396 printf("Calling int 0x%x (ax=0x%04x bx=0x%04x " 397 "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n", 398 intno, regs->R_AX, regs->R_BX, regs->R_CX, 399 regs->R_DX, regs->R_ES, regs->R_DI); 400 401 mtx_lock_spin(&x86bios_lock); 402 memcpy(&x86bios_emu.x86, regs, sizeof(*regs)); 403 x86bios_fault = 0; 404 x86emu_exec_intr(&x86bios_emu, intno); 405 memcpy(regs, &x86bios_emu.x86, sizeof(*regs)); 406 mtx_unlock_spin(&x86bios_lock); 407 408 if (x86bios_trace_int) { 409 printf("Exiting int 0x%x (ax=0x%04x bx=0x%04x " 410 "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n", 411 intno, regs->R_AX, regs->R_BX, regs->R_CX, 412 regs->R_DX, regs->R_ES, regs->R_DI); 413 if (x86bios_fault) 414 printf("Page fault at 0x%05x from 0x%05x.\n", 415 x86bios_fault_addr, x86bios_fault_inst); 416 } 417} 418 419void * 420x86bios_offset(uint32_t offset) 421{ 422 423 return (x86bios_get_pages(offset, 1)); 424} 425 426void * 427x86bios_get_orm(uint32_t offset) 428{ 429 uint8_t *p; 430 431 /* Does the shadow ROM contain BIOS POST code for x86? */ 432 p = x86bios_offset(offset); 433 if (p == NULL || p[0] != 0x55 || p[1] != 0xaa || p[3] != 0xe9) 434 return (NULL); 435 436 return (p); 437} 438 439int 440x86bios_match_device(uint32_t offset, device_t dev) 441{ 442 uint8_t *p; 443 uint16_t device, vendor; 444 uint8_t class, progif, subclass; 445 446 /* Does the shadow ROM contain BIOS POST code for x86? */ 447 p = x86bios_get_orm(offset); 448 if (p == NULL) 449 return (0); 450 451 /* Does it contain PCI data structure? */ 452 p += le16toh(*(uint16_t *)(p + 0x18)); 453 if (bcmp(p, "PCIR", 4) != 0 || 454 le16toh(*(uint16_t *)(p + 0x0a)) < 0x18 || *(p + 0x14) != 0) 455 return (0); 456 457 /* Does it match the vendor, device, and classcode? */ 458 vendor = le16toh(*(uint16_t *)(p + 0x04)); 459 device = le16toh(*(uint16_t *)(p + 0x06)); 460 progif = *(p + 0x0d); 461 subclass = *(p + 0x0e); 462 class = *(p + 0x0f); 463 if (vendor != pci_get_vendor(dev) || device != pci_get_device(dev) || 464 class != pci_get_class(dev) || subclass != pci_get_subclass(dev) || 465 progif != pci_get_progif(dev)) 466 return (0); 467 468 return (1); 469} 470 471static __inline int 472x86bios_map_mem(void) 473{ 474 475 x86bios_ivt = pmap_mapbios(X86BIOS_IVT_BASE, X86BIOS_IVT_SIZE); 476 if (x86bios_ivt == NULL) 477 return (1); 478 x86bios_rom = pmap_mapdev(X86BIOS_ROM_BASE, X86BIOS_ROM_SIZE); 479 if (x86bios_rom == NULL) { 480 pmap_unmapdev((vm_offset_t)x86bios_ivt, X86BIOS_IVT_SIZE); 481 return (1); 482 } 483 x86bios_seg = contigmalloc(X86BIOS_SEG_SIZE, M_DEVBUF, M_WAITOK, 484 X86BIOS_RAM_BASE, X86BIOS_ROM_BASE, X86BIOS_PAGE_SIZE, 0); 485 x86bios_seg_phys = vtophys(x86bios_seg); 486 487 return (0); 488} 489 490static __inline void 491x86bios_unmap_mem(void) 492{ 493 494 pmap_unmapdev((vm_offset_t)x86bios_ivt, X86BIOS_IVT_SIZE); 495 pmap_unmapdev((vm_offset_t)x86bios_rom, X86BIOS_ROM_SIZE); 496 contigfree(x86bios_seg, X86BIOS_SEG_SIZE, M_DEVBUF); 497} 498 499static void 500x86bios_init(void *arg __unused) 501{ 502 int i; 503 504 mtx_init(&x86bios_lock, "x86bios lock", NULL, MTX_SPIN); 505 506 if (x86bios_map_mem() != 0) 507 return; 508 509 x86bios_map = malloc(sizeof(*x86bios_map) * X86BIOS_PAGES, M_DEVBUF, 510 M_WAITOK | M_ZERO); 511 x86bios_set_pages((vm_offset_t)x86bios_ivt, X86BIOS_IVT_BASE, 512 X86BIOS_IVT_SIZE); 513 x86bios_set_pages((vm_offset_t)x86bios_rom, X86BIOS_ROM_BASE, 514 X86BIOS_ROM_SIZE); 515 x86bios_set_pages((vm_offset_t)x86bios_seg, x86bios_seg_phys, 516 X86BIOS_SEG_SIZE); 517 518 bzero(&x86bios_emu, sizeof(x86bios_emu)); 519 520 x86bios_emu.emu_rdb = x86bios_emu_rdb; 521 x86bios_emu.emu_rdw = x86bios_emu_rdw; 522 x86bios_emu.emu_rdl = x86bios_emu_rdl; 523 x86bios_emu.emu_wrb = x86bios_emu_wrb; 524 x86bios_emu.emu_wrw = x86bios_emu_wrw; 525 x86bios_emu.emu_wrl = x86bios_emu_wrl; 526 527 x86bios_emu.emu_inb = x86bios_emu_inb; 528 x86bios_emu.emu_inw = x86bios_emu_inw; 529 x86bios_emu.emu_inl = x86bios_emu_inl; 530 x86bios_emu.emu_outb = x86bios_emu_outb; 531 x86bios_emu.emu_outw = x86bios_emu_outw; 532 x86bios_emu.emu_outl = x86bios_emu_outl; 533 534 for (i = 0; i < 256; i++) 535 x86bios_emu._x86emu_intrTab[i] = x86bios_emu_get_intr; 536} 537 538static void 539x86bios_uninit(void *arg __unused) 540{ 541 vm_offset_t *map = x86bios_map; 542 543 mtx_lock_spin(&x86bios_lock); 544 if (x86bios_map != NULL) { 545 free(x86bios_map, M_DEVBUF); 546 x86bios_map = NULL; 547 } 548 mtx_unlock_spin(&x86bios_lock); 549 550 if (map != NULL) 551 x86bios_unmap_mem(); 552 553 mtx_destroy(&x86bios_lock); 554} 555 556static int 557x86bios_modevent(module_t mod __unused, int type, void *data __unused) 558{ 559 560 switch (type) { 561 case MOD_LOAD: 562 x86bios_init(NULL); 563 break; 564 case MOD_UNLOAD: 565 x86bios_uninit(NULL); 566 break; 567 default: 568 return (ENOTSUP); 569 } 570 571 return (0); 572} 573 574static moduledata_t x86bios_mod = { 575 "x86bios", 576 x86bios_modevent, 577 NULL, 578}; 579 580DECLARE_MODULE(x86bios, x86bios_mod, SI_SUB_CPU, SI_ORDER_ANY); 581MODULE_VERSION(x86bios, 1); 582