gptboot.c revision 96424
1/* 2 * Copyright (c) 1998 Robert Nordier 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are freely 6 * permitted provided that the above copyright notice and this 7 * paragraph and the following disclaimer are duplicated in all 8 * such forms. 9 * 10 * This software is provided "AS IS" and without any express or 11 * implied warranties, including, without limitation, the implied 12 * warranties of merchantability and fitness for a particular 13 * purpose. 14 */ 15 16/* 17 * $FreeBSD: head/sys/boot/i386/gptboot/gptboot.c 96424 2002-05-11 21:39:59Z peter $ 18 */ 19 20#include <sys/param.h> 21#include <sys/reboot.h> 22#include <sys/diskslice.h> 23#include <sys/disklabel.h> 24#include <sys/dirent.h> 25#include <machine/bootinfo.h> 26#include <machine/elf.h> 27 28#include <ufs/ffs/fs.h> 29#include <ufs/ufs/dinode.h> 30 31#include <stdarg.h> 32 33#include <a.out.h> 34 35#include <btxv86.h> 36 37#include "boot2.h" 38#include "lib.h" 39 40#define IO_KEYBOARD 1 41#define IO_SERIAL 2 42 43#define SECOND 18 /* Circa that many ticks in a second. */ 44 45#define RBX_ASKNAME 0x0 /* -a */ 46#define RBX_SINGLE 0x1 /* -s */ 47#define RBX_DFLTROOT 0x5 /* -r */ 48#define RBX_KDB 0x6 /* -d */ 49#define RBX_CONFIG 0xa /* -c */ 50#define RBX_VERBOSE 0xb /* -v */ 51#define RBX_SERIAL 0xc /* -h */ 52#define RBX_CDROM 0xd /* -C */ 53#define RBX_GDB 0xf /* -g */ 54#define RBX_MUTE 0x10 /* -m */ 55#define RBX_PAUSE 0x12 /* -p */ 56#define RBX_DUAL 0x1d /* -D */ 57#define RBX_PROBEKBD 0x1e /* -P */ 58#define RBX_NOINTR 0x1f /* -n */ 59 60#define RBX_MASK 0x2005ffff 61 62#define PATH_CONFIG "/boot.config" 63#define PATH_BOOT3 "/boot/loader" 64#define PATH_KERNEL "/kernel" 65 66#define ARGS 0x900 67#define NOPT 14 68#define NDEV 5 69#define MEM_BASE 0x12 70#define MEM_EXT 0x15 71#define V86_CY(x) ((x) & 1) 72#define V86_ZR(x) ((x) & 0x40) 73 74/* 75 * We use 4k `virtual' blocks for filesystem data, whatever the actual 76 * filesystem block size. FFS blocks are always a multiple of 4k. 77 */ 78#define VBLKSIZE 4096 79#define VBLKMASK (VBLKSIZE - 1) 80#define DBPERVBLK (VBLKSIZE / DEV_BSIZE) 81#define IPERVBLK (VBLKSIZE / sizeof(struct dinode)) 82#define INDIRPERVBLK (VBLKSIZE / sizeof(ufs_daddr_t)) 83#define INO_TO_VBA(fs, x) (fsbtodb(fs, ino_to_fsba(fs, x)) + \ 84 (ino_to_fsbo(fs, x) / IPERVBLK) * DBPERVBLK) 85#define INO_TO_VBO(fs, x) (ino_to_fsbo(fs, x) % IPERVBLK) 86#define FS_TO_VBA(fs, fsb, off) (fsbtodb(fs, fsb) + \ 87 ((off) / VBLKSIZE) * DBPERVBLK) 88#define FS_TO_VBO(fs, fsb, off) ((off) & VBLKMASK) 89 90#define DRV_HARD 0x80 91#define DRV_MASK 0x7f 92 93#define TYPE_AD 0 94#define TYPE_WD 1 95#define TYPE_WFD 2 96#define TYPE_FD 3 97#define TYPE_DA 4 98 99/* Buffers that must not span a 64k boundary. */ 100static struct dmadat { 101 char blkbuf[VBLKSIZE]; /* filesystem blocks */ 102 ufs_daddr_t indbuf[VBLKSIZE / sizeof(ufs_daddr_t)]; /* indir blocks */ 103 char sbbuf[SBSIZE]; /* superblock */ 104 char secbuf[DEV_BSIZE]; /* for MBR/disklabel */ 105} *dmadat; 106 107extern uint32_t _end; 108 109static const char optstr[NOPT] = "DhaCcdgmnPprsv"; 110static const unsigned char flags[NOPT] = { 111 RBX_DUAL, 112 RBX_SERIAL, 113 RBX_ASKNAME, 114 RBX_CDROM, 115 RBX_CONFIG, 116 RBX_KDB, 117 RBX_GDB, 118 RBX_MUTE, 119 RBX_NOINTR, 120 RBX_PROBEKBD, 121 RBX_PAUSE, 122 RBX_DFLTROOT, 123 RBX_SINGLE, 124 RBX_VERBOSE 125}; 126 127static const char *const dev_nm[] = {"ad", "wd", " ", "fd", "da"}; 128static const unsigned dev_maj[] = {30, 0, 1, 2, 4}; 129 130static struct dsk { 131 unsigned drive; 132 unsigned type; 133 unsigned unit; 134 unsigned slice; 135 unsigned part; 136 unsigned start; 137 int init; 138 int meta; 139} dsk; 140static char cmd[512]; 141static char kname[1024]; 142static uint32_t opts; 143static struct bootinfo bootinfo; 144static int ls; 145static uint32_t fs_off; 146static uint8_t ioctrl = IO_KEYBOARD; 147 148void exit(int); 149static void load(const char *); 150static int parse(char *); 151static ino_t lookup(const char *); 152static int xfsread(ino_t, void *, size_t); 153static ssize_t fsread(ino_t, void *, size_t); 154static int dskread(void *, unsigned, unsigned); 155static int printf(const char *,...); 156static int putchar(int); 157static void *memcpy(void *, const void *, size_t); 158static uint32_t memsize(int); 159static int drvread(void *, unsigned, unsigned); 160static int keyhit(unsigned); 161static int xputc(int); 162static int xgetc(int); 163static int getc(int); 164 165static inline void 166readfile(const char *fname, void *buf, size_t size) 167{ 168 ino_t ino; 169 170 if ((ino = lookup(fname))) 171 fsread(ino, buf, size); 172} 173 174static inline int 175strcmp(const char *s1, const char *s2) 176{ 177 for (; *s1 == *s2 && *s1; s1++, s2++); 178 return (u_char)*s1 - (u_char)*s2; 179} 180 181static inline int 182fsfind(const char *name, ino_t * ino) 183{ 184 char buf[DEV_BSIZE]; 185 struct dirent *d; 186 char *s; 187 ssize_t n; 188 189 fs_off = 0; 190 while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0) 191 for (s = buf; s < buf + DEV_BSIZE;) { 192 d = (void *)s; 193 if (ls) 194 printf("%s ", d->d_name); 195 else if (!strcmp(name, d->d_name)) { 196 *ino = d->d_fileno; 197 return d->d_type; 198 } 199 s += d->d_reclen; 200 } 201 if (n != -1 && ls) 202 putchar('\n'); 203 return 0; 204} 205 206static inline int 207getchar(void) 208{ 209 int c; 210 211 c = xgetc(0); 212 if (c == '\r') 213 c = '\n'; 214 return c; 215} 216 217static inline void 218getstr(char *str, int size) 219{ 220 char *s; 221 int c; 222 223 s = str; 224 do { 225 switch (c = getchar()) { 226 case 0: 227 break; 228 case '\b': 229 case '\177': 230 if (s > str) { 231 s--; 232 putchar('\b'); 233 putchar(' '); 234 } else 235 c = 0; 236 break; 237 case '\n': 238 *s = 0; 239 break; 240 default: 241 if (s - str < size - 1) 242 *s++ = c; 243 } 244 if (c) 245 putchar(c); 246 } while (c != '\n'); 247} 248 249static inline uint32_t 250drvinfo(int drive) 251{ 252 v86.addr = 0x13; 253 v86.eax = 0x800; 254 v86.edx = DRV_HARD + drive; 255 v86int(); 256 if (V86_CY(v86.efl)) 257 return 0x4f010f; 258 return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | 259 (v86.edx & 0xff00) | (v86.ecx & 0x3f); 260} 261 262static inline void 263putc(int c) 264{ 265 v86.addr = 0x10; 266 v86.eax = 0xe00 | (c & 0xff); 267 v86.ebx = 0x7; 268 v86int(); 269} 270 271int 272main(void) 273{ 274 int autoboot, i; 275 276 dmadat = (void *)(roundup2(__base + _end, 0x10000) - __base); 277 v86.ctl = V86_FLAGS; 278 dsk.drive = *(uint8_t *)PTOV(ARGS); 279 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 280 dsk.unit = dsk.drive & DRV_MASK; 281 dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; 282 bootinfo.bi_version = BOOTINFO_VERSION; 283 bootinfo.bi_size = sizeof(bootinfo); 284 bootinfo.bi_basemem = memsize(MEM_BASE); 285 bootinfo.bi_extmem = memsize(MEM_EXT); 286 bootinfo.bi_memsizes_valid++; 287 for (i = 0; i < N_BIOS_GEOM; i++) 288 bootinfo.bi_bios_geom[i] = drvinfo(i); 289 290 /* Process configuration file */ 291 292 autoboot = 1; 293 readfile(PATH_CONFIG, cmd, sizeof(cmd)); 294 if (*cmd) { 295 printf("%s: %s", PATH_CONFIG, cmd); 296 if (parse(cmd)) 297 autoboot = 0; 298 /* Do not process this command twice */ 299 *cmd = 0; 300 } 301 302 /* 303 * Try to exec stage 3 boot loader. If interrupted by a keypress, 304 * or in case of failure, try to load a kernel directly instead. 305 */ 306 307 if (autoboot && !*kname) { 308 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 309 if (!keyhit(3*SECOND)) { 310 load(kname); 311 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 312 } 313 } 314 315 /* Present the user with the boot2 prompt. */ 316 317 for (;;) { 318 printf(" \n>> FreeBSD/i386 BOOT\n" 319 "Default: %u:%s(%u,%c)%s\n" 320 "boot: ", 321 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 322 'a' + dsk.part, kname); 323 if (ioctrl & IO_SERIAL) 324 sio_flush(); 325 if (!autoboot || keyhit(5*SECOND)) 326 getstr(cmd, sizeof(cmd)); 327 else 328 putchar('\n'); 329 autoboot = 0; 330 if (parse(cmd)) 331 putchar('\a'); 332 else 333 load(kname); 334 } 335} 336 337/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 338void 339exit(int x) 340{ 341} 342 343static void 344load(const char *fname) 345{ 346 union { 347 struct exec ex; 348 Elf32_Ehdr eh; 349 } hdr; 350 Elf32_Phdr ep[2]; 351 Elf32_Shdr es[2]; 352 caddr_t p; 353 ino_t ino; 354 uint32_t addr, x; 355 int fmt, i, j; 356 357 if (!(ino = lookup(fname))) { 358 if (!ls) 359 printf("No %s\n", fname); 360 return; 361 } 362 if (xfsread(ino, &hdr, sizeof(hdr))) 363 return; 364 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 365 fmt = 0; 366 else if (IS_ELF(hdr.eh)) 367 fmt = 1; 368 else { 369 printf("Invalid %s\n", "format"); 370 return; 371 } 372 if (fmt == 0) { 373 addr = hdr.ex.a_entry & 0xffffff; 374 p = PTOV(addr); 375 fs_off = PAGE_SIZE; 376 if (xfsread(ino, p, hdr.ex.a_text)) 377 return; 378 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 379 if (xfsread(ino, p, hdr.ex.a_data)) 380 return; 381 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 382 bootinfo.bi_symtab = VTOP(p); 383 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 384 p += sizeof(hdr.ex.a_syms); 385 if (hdr.ex.a_syms) { 386 if (xfsread(ino, p, hdr.ex.a_syms)) 387 return; 388 p += hdr.ex.a_syms; 389 if (xfsread(ino, p, sizeof(int))) 390 return; 391 x = *(uint32_t *)p; 392 p += sizeof(int); 393 x -= sizeof(int); 394 if (xfsread(ino, p, x)) 395 return; 396 p += x; 397 } 398 } else { 399 fs_off = hdr.eh.e_phoff; 400 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 401 if (xfsread(ino, ep + j, sizeof(ep[0]))) 402 return; 403 if (ep[j].p_type == PT_LOAD) 404 j++; 405 } 406 for (i = 0; i < 2; i++) { 407 p = PTOV(ep[i].p_paddr & 0xffffff); 408 fs_off = ep[i].p_offset; 409 if (xfsread(ino, p, ep[i].p_filesz)) 410 return; 411 } 412 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 413 bootinfo.bi_symtab = VTOP(p); 414 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 415 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 416 (hdr.eh.e_shstrndx + 1); 417 if (xfsread(ino, &es, sizeof(es))) 418 return; 419 for (i = 0; i < 2; i++) { 420 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 421 p += sizeof(es[i].sh_size); 422 fs_off = es[i].sh_offset; 423 if (xfsread(ino, p, es[i].sh_size)) 424 return; 425 p += es[i].sh_size; 426 } 427 } 428 addr = hdr.eh.e_entry & 0xffffff; 429 } 430 bootinfo.bi_esymtab = VTOP(p); 431 bootinfo.bi_kernelname = VTOP(fname); 432 bootinfo.bi_bios_dev = dsk.drive; 433 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 434 MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part), 435 0, 0, 0, VTOP(&bootinfo)); 436} 437 438static int 439parse(char *arg) 440{ 441 char *p, *q; 442 int drv, c, i; 443 444 while ((c = *arg++)) { 445 if (c == ' ' || c == '\t' || c == '\n') 446 continue; 447 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 448 if (*p) 449 *p++ = 0; 450 if (c == '-') { 451 while ((c = *arg++)) { 452 for (i = 0; c != optstr[i]; i++) 453 if (i == NOPT - 1) 454 return -1; 455 opts ^= 1 << flags[i]; 456 } 457 if (opts & 1 << RBX_PROBEKBD) { 458 i = *(uint8_t *)PTOV(0x496) & 0x10; 459 /* printf("Keyboard: %s\n", i ? "yes" : "no"); XXX GCC31 size */ 460 if (!i) 461 opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL; 462 opts &= ~(1 << RBX_PROBEKBD); 463 } 464 ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) : 465 opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD; 466 if (ioctrl & IO_SERIAL) 467 sio_init(); 468 } else { 469 for (q = arg--; *q && *q != '('; q++); 470 if (*q) { 471 drv = -1; 472 if (arg[1] == ':') { 473 if (*arg < '0' || *arg > '9') 474 return -1; 475 drv = *arg - '0'; 476 arg += 2; 477 } 478 if (q - arg != 2) 479 return -1; 480 for (i = 0; arg[0] != dev_nm[i][0] || 481 arg[1] != dev_nm[i][1]; i++) 482 if (i == NDEV - 1) 483 return -1; 484 dsk.type = i; 485 arg += 3; 486 if (arg[1] != ',' || *arg < '0' || *arg > '9') 487 return -1; 488 dsk.unit = *arg - '0'; 489 arg += 2; 490 dsk.slice = WHOLE_DISK_SLICE; 491 if (arg[1] == ',') { 492 if (*arg < '0' || *arg > '0' + NDOSPART) 493 return -1; 494 if ((dsk.slice = *arg - '0')) 495 dsk.slice++; 496 arg += 2; 497 } 498 if (arg[1] != ')' || *arg < 'a' || *arg > 'p') 499 return -1; 500 dsk.part = *arg - 'a'; 501 arg += 2; 502 if (drv == -1) 503 drv = dsk.unit; 504 dsk.drive = (dsk.type == TYPE_WD || 505 dsk.type == TYPE_AD || 506 dsk.type == TYPE_DA ? DRV_HARD : 0) + drv; 507 dsk.meta = 0; 508 fsread(0, NULL, 0); 509 } 510 if ((i = p - arg - !*(p - 1))) { 511 if (i >= sizeof(kname)) 512 return -1; 513 memcpy(kname, arg, i + 1); 514 } 515 } 516 arg = p; 517 } 518 return 0; 519} 520 521static ino_t 522lookup(const char *path) 523{ 524 char name[MAXNAMLEN + 1]; 525 const char *s; 526 ino_t ino; 527 ssize_t n; 528 int dt; 529 530 ino = ROOTINO; 531 dt = DT_DIR; 532 for (;;) { 533 if (*path == '/') 534 path++; 535 if (!*path) 536 break; 537 for (s = path; *s && *s != '/'; s++); 538 if ((n = s - path) > MAXNAMLEN) 539 return 0; 540 ls = *path == '?' && n == 1 && !*s; 541 memcpy(name, path, n); 542 name[n] = 0; 543 if ((dt = fsfind(name, &ino)) <= 0) 544 break; 545 path = s; 546 } 547 return dt == DT_REG ? ino : 0; 548} 549static int 550xfsread(ino_t inode, void *buf, size_t nbyte) 551{ 552 if (fsread(inode, buf, nbyte) != nbyte) { 553 printf("Invalid %s\n", "format"); 554 return -1; 555 } 556 return 0; 557} 558 559static ssize_t 560fsread(ino_t inode, void *buf, size_t nbyte) 561{ 562 static struct dinode din; 563 static ino_t inomap; 564 static daddr_t blkmap, indmap; 565 char *blkbuf; 566 ufs_daddr_t *indbuf; 567 struct fs *fs; 568 char *s; 569 ufs_daddr_t lbn, addr; 570 daddr_t vbaddr; 571 size_t n, nb, off, vboff; 572 573 blkbuf = dmadat->blkbuf; 574 indbuf = dmadat->indbuf; 575 fs = (struct fs *)dmadat->sbbuf; 576 if (!dsk.meta) { 577 inomap = 0; 578 if (dskread(fs, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE)) 579 return -1; 580 if (fs->fs_magic != FS_MAGIC) { 581 printf("Not ufs\n"); 582 return -1; 583 } 584 dsk.meta++; 585 } 586 if (!inode) 587 return 0; 588 if (inomap != inode) { 589 if (dskread(blkbuf, INO_TO_VBA(fs, inode), DBPERVBLK)) 590 return -1; 591 din = ((struct dinode *)blkbuf)[INO_TO_VBO(fs, inode)]; 592 inomap = inode; 593 fs_off = 0; 594 blkmap = indmap = 0; 595 } 596 s = buf; 597 if (nbyte > (n = din.di_size - fs_off)) 598 nbyte = n; 599 nb = nbyte; 600 while (nb) { 601 lbn = lblkno(fs, fs_off); 602 off = blkoff(fs, fs_off); 603 if (lbn < NDADDR) 604 addr = din.di_db[lbn]; 605 else { 606 vbaddr = FS_TO_VBA(fs, din.di_ib[0], sizeof(indbuf[0]) * 607 ((lbn - NDADDR) % NINDIR(fs))); 608 if (indmap != vbaddr) { 609 if (dskread(indbuf, vbaddr, DBPERVBLK)) 610 return -1; 611 indmap = vbaddr; 612 } 613 addr = indbuf[(lbn - NDADDR) % INDIRPERVBLK]; 614 } 615 vbaddr = FS_TO_VBA(fs, addr, off); 616 vboff = FS_TO_VBO(fs, addr, off); 617 n = dblksize(fs, &din, lbn) - (off & ~VBLKMASK); 618 if (n > VBLKSIZE) 619 n = VBLKSIZE; 620 if (blkmap != vbaddr) { 621 if (dskread(blkbuf, vbaddr, n >> DEV_BSHIFT)) 622 return -1; 623 blkmap = vbaddr; 624 } 625 n -= vboff; 626 if (n > nb) 627 n = nb; 628 memcpy(s, blkbuf + vboff, n); 629 s += n; 630 fs_off += n; 631 nb -= n; 632 } 633 return nbyte; 634} 635 636static int 637dskread(void *buf, unsigned lba, unsigned nblk) 638{ 639 struct dos_partition *dp; 640 struct disklabel *d; 641 char *sec; 642 unsigned sl, i; 643 644 if (!dsk.meta) { 645 sec = dmadat->secbuf; 646 dsk.start = 0; 647 if (drvread(sec, DOSBBSECTOR, 1)) 648 return -1; 649 dp = (void *)(sec + DOSPARTOFF); 650 sl = dsk.slice; 651 if (sl < BASE_SLICE) { 652 for (i = 0; i < NDOSPART; i++) 653 if (dp[i].dp_typ == DOSPTYP_386BSD && 654 (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { 655 sl = BASE_SLICE + i; 656 if (dp[i].dp_flag & 0x80 || 657 dsk.slice == COMPATIBILITY_SLICE) 658 break; 659 } 660 if (dsk.slice == WHOLE_DISK_SLICE) 661 dsk.slice = sl; 662 } 663 if (sl != WHOLE_DISK_SLICE) { 664 if (sl != COMPATIBILITY_SLICE) 665 dp += sl - BASE_SLICE; 666 if (dp->dp_typ != DOSPTYP_386BSD) { 667 printf("Invalid %s\n", "slice"); 668 return -1; 669 } 670 dsk.start = dp->dp_start; 671 } 672 if (drvread(sec, dsk.start + LABELSECTOR, 1)) 673 return -1; 674 d = (void *)(sec + LABELOFFSET); 675 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { 676 if (dsk.part != RAW_PART) { 677 printf("Invalid %s\n", "label"); 678 return -1; 679 } 680 } else { 681 if (!dsk.init) { 682 if (d->d_type == DTYPE_SCSI) 683 dsk.type = TYPE_DA; 684 dsk.init++; 685 } 686 if (dsk.part >= d->d_npartitions || 687 !d->d_partitions[dsk.part].p_size) { 688 printf("Invalid %s\n", "partition"); 689 return -1; 690 } 691 dsk.start = d->d_partitions[dsk.part].p_offset; 692 } 693 } 694 return drvread(buf, dsk.start + lba, nblk); 695} 696 697static int 698printf(const char *fmt,...) 699{ 700 static const char digits[16] = "0123456789abcdef"; 701 va_list ap; 702 char buf[10]; 703 char *s; 704 unsigned r, u; 705 int c; 706 707 va_start(ap, fmt); 708 while ((c = *fmt++)) { 709 if (c == '%') { 710 c = *fmt++; 711 switch (c) { 712 case 'c': 713 putchar(va_arg(ap, int)); 714 continue; 715 case 's': 716 for (s = va_arg(ap, char *); *s; s++) 717 putchar(*s); 718 continue; 719 case 'u': 720 case 'x': 721 r = c == 'u' ? 10U : 16U; 722 u = va_arg(ap, unsigned); 723 s = buf; 724 do 725 *s++ = digits[u % r]; 726 while (u /= r); 727 while (--s >= buf) 728 putchar(*s); 729 continue; 730 } 731 } 732 putchar(c); 733 } 734 va_end(ap); 735 return 0; 736} 737 738static int 739putchar(int c) 740{ 741 if (c == '\n') 742 xputc('\r'); 743 return xputc(c); 744} 745 746static void * 747memcpy(void *dst, const void *src, size_t size) 748{ 749 const char *s; 750 char *d; 751 752 for (d = dst, s = src; size; size--) 753 *d++ = *s++; 754 return dst; 755} 756 757static uint32_t 758memsize(int type) 759{ 760 v86.addr = type; 761 v86.eax = 0x8800; 762 v86int(); 763 return v86.eax; 764} 765 766static int 767drvread(void *buf, unsigned lba, unsigned nblk) 768{ 769 static unsigned c = 0x2d5c7c2f; 770 771 printf("%c\b", c = c << 8 | c >> 24); 772 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 773 v86.addr = XREADORG; /* call to xread in boot1 */ 774 v86.es = VTOPSEG(buf); 775 v86.eax = lba; 776 v86.ebx = VTOPOFF(buf); 777 v86.ecx = lba >> 16; 778 v86.edx = nblk << 8 | dsk.drive; 779 v86int(); 780 v86.ctl = V86_FLAGS; 781 if (V86_CY(v86.efl)) { 782 printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff, 783 lba); 784 return -1; 785 } 786 return 0; 787} 788 789static int 790keyhit(unsigned ticks) 791{ 792 uint32_t t0, t1; 793 794 if (opts & 1 << RBX_NOINTR) 795 return 0; 796 t0 = 0; 797 for (;;) { 798 if (xgetc(1)) 799 return 1; 800 t1 = *(uint32_t *)PTOV(0x46c); 801 if (!t0) 802 t0 = t1; 803 if (t1 < t0 || t1 >= t0 + ticks) 804 return 0; 805 } 806} 807 808static int 809xputc(int c) 810{ 811 if (ioctrl & IO_KEYBOARD) 812 putc(c); 813 if (ioctrl & IO_SERIAL) 814 sio_putc(c); 815 return c; 816} 817 818static int 819xgetc(int fn) 820{ 821 if (opts & 1 << RBX_NOINTR) 822 return 0; 823 for (;;) { 824 if (ioctrl & IO_KEYBOARD && getc(1)) 825 return fn ? 1 : getc(0); 826 if (ioctrl & IO_SERIAL && sio_ischar()) 827 return fn ? 1 : sio_getc(); 828 if (fn) 829 return 0; 830 } 831} 832 833static int 834getc(int fn) 835{ 836 v86.addr = 0x16; 837 v86.eax = fn << 8; 838 v86int(); 839 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); 840} 841