Changeset View
Changeset View
Standalone View
Standalone View
head/sys/boot/pc98/boot2/boot2.c
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* Copyright (c) 2008-2009 TAKAHASHI Yoshihiro | |||||
* Copyright (c) 1998 Robert Nordier | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms are freely | |||||
* permitted provided that the above copyright notice and this | |||||
* paragraph and the following disclaimer are duplicated in all | |||||
* such forms. | |||||
* | |||||
* This software is provided "AS IS" and without any express or | |||||
* implied warranties, including, without limitation, the implied | |||||
* warranties of merchantability and fitness for a particular | |||||
* purpose. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <sys/param.h> | |||||
#include <sys/disklabel.h> | |||||
#include <sys/diskpc98.h> | |||||
#include <sys/dirent.h> | |||||
#include <sys/reboot.h> | |||||
#include <machine/bootinfo.h> | |||||
#include <machine/cpufunc.h> | |||||
#include <machine/elf.h> | |||||
#include <stdarg.h> | |||||
#include <a.out.h> | |||||
#include <btxv86.h> | |||||
#include "boot2.h" | |||||
#include "lib.h" | |||||
#include "paths.h" | |||||
#include "rbx.h" | |||||
/* Define to 0 to omit serial support */ | |||||
#ifndef SERIAL | |||||
#define SERIAL 0 | |||||
#endif | |||||
#define IO_KEYBOARD 1 | |||||
#define IO_SERIAL 2 | |||||
#if SERIAL | |||||
#define DO_KBD (ioctrl & IO_KEYBOARD) | |||||
#define DO_SIO (ioctrl & IO_SERIAL) | |||||
#else | |||||
#define DO_KBD (1) | |||||
#define DO_SIO (0) | |||||
#endif | |||||
#define SECOND 1 /* Circa that many ticks in a second. */ | |||||
#define ARGS 0x900 | |||||
#define NOPT 14 | |||||
#define NDEV 3 | |||||
#define DRV_DISK 0xf0 | |||||
#define DRV_UNIT 0x0f | |||||
#define TYPE_AD 0 | |||||
#define TYPE_DA 1 | |||||
#define TYPE_FD 2 | |||||
extern uint32_t _end; | |||||
static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ | |||||
static const unsigned char flags[NOPT] = { | |||||
RBX_DUAL, | |||||
RBX_SERIAL, | |||||
RBX_ASKNAME, | |||||
RBX_CDROM, | |||||
RBX_CONFIG, | |||||
RBX_KDB, | |||||
RBX_GDB, | |||||
RBX_MUTE, | |||||
RBX_NOINTR, | |||||
RBX_PAUSE, | |||||
RBX_QUIET, | |||||
RBX_DFLTROOT, | |||||
RBX_SINGLE, | |||||
RBX_VERBOSE | |||||
}; | |||||
static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; | |||||
static const unsigned char dev_maj[NDEV] = {30, 4, 2}; | |||||
static const unsigned char dev_daua[NDEV] = {0x80, 0xa0, 0x90}; | |||||
static struct dsk { | |||||
unsigned daua; | |||||
unsigned type; | |||||
unsigned disk; | |||||
unsigned unit; | |||||
unsigned head; | |||||
unsigned sec; | |||||
uint8_t slice; | |||||
uint8_t part; | |||||
unsigned start; | |||||
} dsk; | |||||
static char cmd[512], cmddup[512], knamebuf[1024]; | |||||
static const char *kname; | |||||
uint32_t opts; | |||||
static struct bootinfo bootinfo; | |||||
#if SERIAL | |||||
static int comspeed = SIOSPD; | |||||
static uint8_t ioctrl = IO_KEYBOARD; | |||||
#endif | |||||
int main(void); | |||||
void exit(int); | |||||
static void load(void); | |||||
static int parse(void); | |||||
static int dskread(void *, unsigned, unsigned); | |||||
static void printf(const char *,...); | |||||
static void putchar(int); | |||||
static int drvread(void *, unsigned); | |||||
static int keyhit(unsigned); | |||||
static int xputc(int); | |||||
static int xgetc(int); | |||||
static inline int getc(int); | |||||
static void memcpy(void *, const void *, int); | |||||
static void | |||||
memcpy(void *dst, const void *src, int len) | |||||
{ | |||||
const char *s = src; | |||||
char *d = dst; | |||||
while (len--) | |||||
*d++ = *s++; | |||||
} | |||||
static inline int | |||||
strcmp(const char *s1, const char *s2) | |||||
{ | |||||
for (; *s1 == *s2 && *s1; s1++, s2++); | |||||
return (unsigned char)*s1 - (unsigned char)*s2; | |||||
} | |||||
#define UFS_SMALL_CGBASE | |||||
#include "ufsread.c" | |||||
static inline int | |||||
xfsread(ufs_ino_t inode, void *buf, size_t nbyte) | |||||
{ | |||||
if ((size_t)fsread(inode, buf, nbyte) != nbyte) { | |||||
printf("Invalid %s\n", "format"); | |||||
return -1; | |||||
} | |||||
return 0; | |||||
} | |||||
static inline void | |||||
getstr(void) | |||||
{ | |||||
char *s; | |||||
int c; | |||||
s = cmd; | |||||
for (;;) { | |||||
switch (c = xgetc(0)) { | |||||
case 0: | |||||
break; | |||||
case '\177': | |||||
case '\b': | |||||
if (s > cmd) { | |||||
s--; | |||||
printf("\b \b"); | |||||
} | |||||
break; | |||||
case '\n': | |||||
case '\r': | |||||
*s = 0; | |||||
return; | |||||
default: | |||||
if (s - cmd < sizeof(cmd) - 1) | |||||
*s++ = c; | |||||
putchar(c); | |||||
} | |||||
} | |||||
} | |||||
static inline void | |||||
putc(int c) | |||||
{ | |||||
v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; | |||||
v86.addr = PUTCORG; /* call to putc in boot1 */ | |||||
v86.eax = c; | |||||
v86int(); | |||||
v86.ctl = V86_FLAGS; | |||||
} | |||||
static inline int | |||||
is_scsi_hd(void) | |||||
{ | |||||
if ((*(u_char *)PTOV(0x482) >> dsk.unit) & 0x01) | |||||
return 1; | |||||
return 0; | |||||
} | |||||
static inline void | |||||
fix_sector_size(void) | |||||
{ | |||||
u_char *p; | |||||
p = (u_char *)PTOV(0x460 + dsk.unit * 4); /* SCSI equipment parameter */ | |||||
if ((p[0] & 0x1f) == 7) { /* SCSI MO */ | |||||
if (!(p[3] & 0x30)) { /* 256B / sector */ | |||||
p[3] |= 0x10; /* forced set 512B / sector */ | |||||
p[3 + 0xa1000] |= 0x10; | |||||
} | |||||
} | |||||
} | |||||
static inline uint32_t | |||||
get_diskinfo(void) | |||||
{ | |||||
if (dsk.disk == 0x30) { /* 1440KB FD */ | |||||
/* 80 cylinders, 2 heads, 18 sectors */ | |||||
return (80 << 16) | (2 << 8) | 18; | |||||
} else if (dsk.disk == 0x90) { /* 1200KB FD */ | |||||
/* 80 cylinders, 2 heads, 15 sectors */ | |||||
return (80 << 16) | (2 << 8) | 15; | |||||
} else if (dsk.disk == 0x80 || is_scsi_hd()) { /* IDE or SCSI HDD */ | |||||
v86.addr = 0x1b; | |||||
v86.eax = 0x8400 | dsk.daua; | |||||
v86int(); | |||||
return (v86.ecx << 16) | v86.edx; | |||||
} | |||||
/* SCSI MO or CD */ | |||||
fix_sector_size(); /* SCSI MO */ | |||||
/* other SCSI devices */ | |||||
return (65535 << 16) | (8 << 8) | 32; | |||||
} | |||||
static void | |||||
set_dsk(void) | |||||
{ | |||||
uint32_t di; | |||||
di = get_diskinfo(); | |||||
dsk.head = (di >> 8) & 0xff; | |||||
dsk.sec = di & 0xff; | |||||
dsk.start = 0; | |||||
} | |||||
#ifdef GET_BIOSGEOM | |||||
static uint32_t | |||||
bd_getbigeom(int bunit) | |||||
{ | |||||
int hds = 0; | |||||
int unit = 0x80; /* IDE HDD */ | |||||
u_int addr = 0x55d; | |||||
while (unit < 0xa7) { | |||||
if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f))) | |||||
if (hds++ == bunit) | |||||
break; | |||||
if (unit >= 0xA0) { | |||||
int media = ((unsigned *)PTOV(0x460))[unit & 0x0F] & 0x1F; | |||||
if (media == 7 && hds++ == bunit) /* SCSI MO */ | |||||
return(0xFFFE0820); /* C:65535 H:8 S:32 */ | |||||
} | |||||
if (++unit == 0x84) { | |||||
unit = 0xA0; /* SCSI HDD */ | |||||
addr = 0x482; | |||||
} | |||||
} | |||||
if (unit == 0xa7) | |||||
return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ | |||||
v86.addr = 0x1b; | |||||
v86.eax = 0x8400 | unit; | |||||
v86int(); | |||||
if (V86_CY(v86.efl)) | |||||
return 0x4F020F; /* 1200KB FD C:80 H:2 S:15 */ | |||||
return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff); | |||||
} | |||||
#endif | |||||
static int | |||||
check_slice(void) | |||||
{ | |||||
struct pc98_partition *dp; | |||||
char *sec; | |||||
unsigned i, cyl; | |||||
sec = dmadat->secbuf; | |||||
cyl = *(uint16_t *)PTOV(ARGS); | |||||
set_dsk(); | |||||
if (dsk.type == TYPE_FD) | |||||
return (WHOLE_DISK_SLICE); | |||||
if (drvread(sec, PC98_BBSECTOR)) | |||||
return (WHOLE_DISK_SLICE); /* Read error */ | |||||
dp = (void *)(sec + PC98_PARTOFF); | |||||
for (i = 0; i < PC98_NPARTS; i++) { | |||||
if (dp[i].dp_mid == DOSMID_386BSD) { | |||||
if (dp[i].dp_scyl <= cyl && cyl <= dp[i].dp_ecyl) | |||||
return (BASE_SLICE + i); | |||||
} | |||||
} | |||||
return (WHOLE_DISK_SLICE); | |||||
} | |||||
int | |||||
main(void) | |||||
{ | |||||
#ifdef GET_BIOSGEOM | |||||
int i; | |||||
#endif | |||||
uint8_t autoboot; | |||||
ufs_ino_t ino; | |||||
size_t nbyte; | |||||
dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); | |||||
v86.ctl = V86_FLAGS; | |||||
v86.efl = PSL_RESERVED_DEFAULT | PSL_I; | |||||
dsk.daua = *(uint8_t *)PTOV(0x584); | |||||
dsk.disk = dsk.daua & DRV_DISK; | |||||
dsk.unit = dsk.daua & DRV_UNIT; | |||||
if (dsk.disk == 0x80) | |||||
dsk.type = TYPE_AD; | |||||
else if (dsk.disk == 0xa0) | |||||
dsk.type = TYPE_DA; | |||||
else /* if (dsk.disk == 0x30 || dsk.disk == 0x90) */ | |||||
dsk.type = TYPE_FD; | |||||
dsk.slice = check_slice(); | |||||
#ifdef GET_BIOSGEOM | |||||
for (i = 0; i < N_BIOS_GEOM; i++) | |||||
bootinfo.bi_bios_geom[i] = bd_getbigeom(i); | |||||
#endif | |||||
bootinfo.bi_version = BOOTINFO_VERSION; | |||||
bootinfo.bi_size = sizeof(bootinfo); | |||||
/* Process configuration file */ | |||||
autoboot = 1; | |||||
if ((ino = lookup(PATH_CONFIG)) || | |||||
(ino = lookup(PATH_DOTCONFIG))) { | |||||
nbyte = fsread(ino, cmd, sizeof(cmd) - 1); | |||||
cmd[nbyte] = '\0'; | |||||
} | |||||
if (*cmd) { | |||||
memcpy(cmddup, cmd, sizeof(cmd)); | |||||
if (parse()) | |||||
autoboot = 0; | |||||
if (!OPT_CHECK(RBX_QUIET)) | |||||
printf("%s: %s", PATH_CONFIG, cmddup); | |||||
/* Do not process this command twice */ | |||||
*cmd = 0; | |||||
} | |||||
/* | |||||
* Try to exec stage 3 boot loader. If interrupted by a keypress, | |||||
* or in case of failure, try to load a kernel directly instead. | |||||
*/ | |||||
if (!kname) { | |||||
kname = PATH_LOADER; | |||||
if (autoboot && !keyhit(3*SECOND)) { | |||||
load(); | |||||
kname = PATH_KERNEL; | |||||
} | |||||
} | |||||
/* Present the user with the boot2 prompt. */ | |||||
for (;;) { | |||||
if (!autoboot || !OPT_CHECK(RBX_QUIET)) | |||||
printf("\nFreeBSD/pc98 boot\n" | |||||
"Default: %u:%s(%u,%c)%s\n" | |||||
"boot: ", | |||||
dsk.unit, dev_nm[dsk.type], dsk.unit, | |||||
'a' + dsk.part, kname); | |||||
if (DO_SIO) | |||||
sio_flush(); | |||||
if (!autoboot || keyhit(3*SECOND)) | |||||
getstr(); | |||||
else if (!autoboot || !OPT_CHECK(RBX_QUIET)) | |||||
putchar('\n'); | |||||
autoboot = 0; | |||||
if (parse()) | |||||
putchar('\a'); | |||||
else | |||||
load(); | |||||
} | |||||
} | |||||
/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ | |||||
void | |||||
exit(int x) | |||||
{ | |||||
} | |||||
static void | |||||
load(void) | |||||
{ | |||||
union { | |||||
struct exec ex; | |||||
Elf32_Ehdr eh; | |||||
} hdr; | |||||
static Elf32_Phdr ep[2]; | |||||
static Elf32_Shdr es[2]; | |||||
caddr_t p; | |||||
ufs_ino_t ino; | |||||
uint32_t addr; | |||||
int k; | |||||
uint8_t i, j; | |||||
if (!(ino = lookup(kname))) { | |||||
if (!ls) | |||||
printf("No %s\n", kname); | |||||
return; | |||||
} | |||||
if (xfsread(ino, &hdr, sizeof(hdr))) | |||||
return; | |||||
if (N_GETMAGIC(hdr.ex) == ZMAGIC) { | |||||
addr = hdr.ex.a_entry & 0xffffff; | |||||
p = PTOV(addr); | |||||
fs_off = PAGE_SIZE; | |||||
if (xfsread(ino, p, hdr.ex.a_text)) | |||||
return; | |||||
p += roundup2(hdr.ex.a_text, PAGE_SIZE); | |||||
if (xfsread(ino, p, hdr.ex.a_data)) | |||||
return; | |||||
} else if (IS_ELF(hdr.eh)) { | |||||
fs_off = hdr.eh.e_phoff; | |||||
for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) { | |||||
if (xfsread(ino, ep + j, sizeof(ep[0]))) | |||||
return; | |||||
if (ep[j].p_type == PT_LOAD) | |||||
j++; | |||||
} | |||||
for (i = 0; i < 2; i++) { | |||||
p = PTOV(ep[i].p_paddr & 0xffffff); | |||||
fs_off = ep[i].p_offset; | |||||
if (xfsread(ino, p, ep[i].p_filesz)) | |||||
return; | |||||
} | |||||
p += roundup2(ep[1].p_memsz, PAGE_SIZE); | |||||
bootinfo.bi_symtab = VTOP(p); | |||||
if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { | |||||
fs_off = hdr.eh.e_shoff + sizeof(es[0]) * | |||||
(hdr.eh.e_shstrndx + 1); | |||||
if (xfsread(ino, &es, sizeof(es))) | |||||
return; | |||||
for (i = 0; i < 2; i++) { | |||||
*(Elf32_Word *)p = es[i].sh_size; | |||||
p += sizeof(es[i].sh_size); | |||||
fs_off = es[i].sh_offset; | |||||
if (xfsread(ino, p, es[i].sh_size)) | |||||
return; | |||||
p += es[i].sh_size; | |||||
} | |||||
} | |||||
addr = hdr.eh.e_entry & 0xffffff; | |||||
bootinfo.bi_esymtab = VTOP(p); | |||||
} else { | |||||
printf("Invalid %s\n", "format"); | |||||
return; | |||||
} | |||||
bootinfo.bi_kernelname = VTOP(kname); | |||||
bootinfo.bi_bios_dev = dsk.daua; | |||||
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), | |||||
MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part), | |||||
0, 0, 0, VTOP(&bootinfo)); | |||||
} | |||||
static int | |||||
parse() | |||||
{ | |||||
char *arg = cmd; | |||||
char *ep, *p, *q; | |||||
const char *cp; | |||||
unsigned int drv; | |||||
int c, i, j; | |||||
size_t k; | |||||
while ((c = *arg++)) { | |||||
if (c == ' ' || c == '\t' || c == '\n') | |||||
continue; | |||||
for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); | |||||
ep = p; | |||||
if (*p) | |||||
*p++ = 0; | |||||
if (c == '-') { | |||||
while ((c = *arg++)) { | |||||
if (c == 'P') { | |||||
if (*(uint8_t *)PTOV(0x481) & 0x48) { | |||||
cp = "yes"; | |||||
} else { | |||||
opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); | |||||
cp = "no"; | |||||
} | |||||
printf("Keyboard: %s\n", cp); | |||||
continue; | |||||
#if SERIAL | |||||
} else if (c == 'S') { | |||||
j = 0; | |||||
while ((unsigned int)(i = *arg++ - '0') <= 9) | |||||
j = j * 10 + i; | |||||
if (j > 0 && i == -'0') { | |||||
comspeed = j; | |||||
break; | |||||
} | |||||
/* Fall through to error below ('S' not in optstr[]). */ | |||||
#endif | |||||
} | |||||
for (i = 0; c != optstr[i]; i++) | |||||
if (i == NOPT - 1) | |||||
return -1; | |||||
opts ^= OPT_SET(flags[i]); | |||||
} | |||||
#if SERIAL | |||||
ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : | |||||
OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; | |||||
if (DO_SIO) { | |||||
if (sio_init(115200 / comspeed) != 0) | |||||
ioctrl &= ~IO_SERIAL; | |||||
} | |||||
#endif | |||||
} else { | |||||
for (q = arg--; *q && *q != '('; q++); | |||||
if (*q) { | |||||
drv = -1; | |||||
if (arg[1] == ':') { | |||||
drv = *arg - '0'; | |||||
if (drv > 9) | |||||
return (-1); | |||||
arg += 2; | |||||
} | |||||
if (q - arg != 2) | |||||
return -1; | |||||
for (i = 0; arg[0] != dev_nm[i][0] || | |||||
arg[1] != dev_nm[i][1]; i++) | |||||
if (i == NDEV - 1) | |||||
return -1; | |||||
dsk.type = i; | |||||
arg += 3; | |||||
dsk.unit = *arg - '0'; | |||||
if (arg[1] != ',' || dsk.unit > 9) | |||||
return -1; | |||||
arg += 2; | |||||
dsk.slice = WHOLE_DISK_SLICE; | |||||
if (arg[1] == ',') { | |||||
dsk.slice = *arg - '0' + 1; | |||||
if (dsk.slice > PC98_NPARTS + 1) | |||||
return -1; | |||||
arg += 2; | |||||
} | |||||
if (arg[1] != ')') | |||||
return -1; | |||||
dsk.part = *arg - 'a'; | |||||
if (dsk.part > 7) | |||||
return (-1); | |||||
arg += 2; | |||||
if (drv == -1) | |||||
drv = dsk.unit; | |||||
dsk.disk = dev_daua[dsk.type]; | |||||
dsk.daua = dsk.disk | dsk.unit; | |||||
dsk_meta = 0; | |||||
} | |||||
k = ep - arg; | |||||
if (k > 0) { | |||||
if (k >= sizeof(knamebuf)) | |||||
return -1; | |||||
memcpy(knamebuf, arg, k + 1); | |||||
kname = knamebuf; | |||||
} | |||||
} | |||||
arg = p; | |||||
} | |||||
return 0; | |||||
} | |||||
static int | |||||
dskread(void *buf, unsigned lba, unsigned nblk) | |||||
{ | |||||
struct pc98_partition *dp; | |||||
struct disklabel *d; | |||||
char *sec; | |||||
unsigned i; | |||||
uint8_t sl; | |||||
u_char *p; | |||||
const char *reason; | |||||
if (!dsk_meta) { | |||||
sec = dmadat->secbuf; | |||||
set_dsk(); | |||||
if (dsk.type == TYPE_FD) | |||||
goto unsliced; | |||||
if (drvread(sec, PC98_BBSECTOR)) | |||||
return -1; | |||||
dp = (void *)(sec + PC98_PARTOFF); | |||||
sl = dsk.slice; | |||||
if (sl < BASE_SLICE) { | |||||
for (i = 0; i < PC98_NPARTS; i++) | |||||
if (dp[i].dp_mid == DOSMID_386BSD) { | |||||
sl = BASE_SLICE + i; | |||||
break; | |||||
} | |||||
dsk.slice = sl; | |||||
} | |||||
if (sl != WHOLE_DISK_SLICE) { | |||||
dp += sl - BASE_SLICE; | |||||
if (dp->dp_mid != DOSMID_386BSD) { | |||||
reason = "slice"; | |||||
goto error; | |||||
} | |||||
dsk.start = dp->dp_scyl * dsk.head * dsk.sec + | |||||
dp->dp_shd * dsk.sec + dp->dp_ssect; | |||||
} | |||||
if (drvread(sec, dsk.start + LABELSECTOR)) | |||||
return -1; | |||||
d = (void *)(sec + LABELOFFSET); | |||||
if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { | |||||
if (dsk.part != RAW_PART) { | |||||
reason = "label"; | |||||
goto error; | |||||
} | |||||
} else { | |||||
if (dsk.part >= d->d_npartitions || | |||||
!d->d_partitions[dsk.part].p_size) { | |||||
reason = "partition"; | |||||
goto error; | |||||
} | |||||
dsk.start += d->d_partitions[dsk.part].p_offset; | |||||
dsk.start -= d->d_partitions[RAW_PART].p_offset; | |||||
} | |||||
unsliced: ; | |||||
} | |||||
for (p = buf; nblk; p += 512, lba++, nblk--) { | |||||
if ((i = drvread(p, dsk.start + lba))) | |||||
return i; | |||||
} | |||||
return 0; | |||||
error: | |||||
printf("Invalid %s\n", reason); | |||||
return -1; | |||||
} | |||||
static void | |||||
printf(const char *fmt,...) | |||||
{ | |||||
va_list ap; | |||||
static char buf[10]; | |||||
char *s; | |||||
unsigned u; | |||||
int c; | |||||
va_start(ap, fmt); | |||||
while ((c = *fmt++)) { | |||||
if (c == '%') { | |||||
c = *fmt++; | |||||
switch (c) { | |||||
case 'c': | |||||
putchar(va_arg(ap, int)); | |||||
continue; | |||||
case 's': | |||||
for (s = va_arg(ap, char *); *s; s++) | |||||
putchar(*s); | |||||
continue; | |||||
case 'u': | |||||
u = va_arg(ap, unsigned); | |||||
s = buf; | |||||
do | |||||
*s++ = '0' + u % 10U; | |||||
while (u /= 10U); | |||||
while (--s >= buf) | |||||
putchar(*s); | |||||
continue; | |||||
} | |||||
} | |||||
putchar(c); | |||||
} | |||||
va_end(ap); | |||||
return; | |||||
} | |||||
static void | |||||
putchar(int c) | |||||
{ | |||||
if (c == '\n') | |||||
xputc('\r'); | |||||
xputc(c); | |||||
} | |||||
static int | |||||
drvread(void *buf, unsigned lba) | |||||
{ | |||||
static unsigned c = 0x2d5c7c2f; | |||||
unsigned bpc, x, cyl, head, sec; | |||||
bpc = dsk.sec * dsk.head; | |||||
cyl = lba / bpc; | |||||
x = lba % bpc; | |||||
head = x / dsk.sec; | |||||
sec = x % dsk.sec; | |||||
if (!OPT_CHECK(RBX_QUIET)) { | |||||
xputc(c = c << 8 | c >> 24); | |||||
xputc('\b'); | |||||
} | |||||
v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; | |||||
v86.addr = READORG; /* call to read in boot1 */ | |||||
v86.ecx = cyl; | |||||
v86.edx = (head << 8) | sec; | |||||
v86.edi = lba; | |||||
v86.ebx = 512; | |||||
v86.es = VTOPSEG(buf); | |||||
v86.ebp = VTOPOFF(buf); | |||||
v86int(); | |||||
v86.ctl = V86_FLAGS; | |||||
if (V86_CY(v86.efl)) { | |||||
printf("error %u c/h/s %u/%u/%u lba %u\n", v86.eax >> 8 & 0xff, | |||||
cyl, head, sec, lba); | |||||
return -1; | |||||
} | |||||
return 0; | |||||
} | |||||
static inline void | |||||
delay(void) | |||||
{ | |||||
int i; | |||||
i = 800; | |||||
do { | |||||
outb(0x5f, 0); /* about 600ns */ | |||||
} while (--i >= 0); | |||||
} | |||||
static int | |||||
keyhit(unsigned sec) | |||||
{ | |||||
unsigned i; | |||||
if (OPT_CHECK(RBX_NOINTR)) | |||||
return 0; | |||||
for (i = 0; i < sec * 1000; i++) { | |||||
if (xgetc(1)) | |||||
return 1; | |||||
delay(); | |||||
} | |||||
return 0; | |||||
} | |||||
static int | |||||
xputc(int c) | |||||
{ | |||||
if (DO_KBD) | |||||
putc(c); | |||||
if (DO_SIO) | |||||
sio_putc(c); | |||||
return c; | |||||
} | |||||
static int | |||||
getc(int fn) | |||||
{ | |||||
v86.addr = 0x18; | |||||
v86.eax = fn << 8; | |||||
v86int(); | |||||
if (fn) | |||||
return (v86.ebx >> 8) & 0x01; | |||||
else | |||||
return v86.eax & 0xff; | |||||
} | |||||
static int | |||||
xgetc(int fn) | |||||
{ | |||||
if (OPT_CHECK(RBX_NOINTR)) | |||||
return 0; | |||||
for (;;) { | |||||
if (DO_KBD && getc(1)) | |||||
return fn ? 1 : getc(0); | |||||
if (DO_SIO && sio_ischar()) | |||||
return fn ? 1 : sio_getc(); | |||||
if (fn) | |||||
return 0; | |||||
} | |||||
} |