From c4aec13535e56f9b28426e8a4cc096dab9d9fd9e Mon Sep 17 00:00:00 2001 From: rnordier Date: Mon, 12 Oct 1998 21:16:26 +0000 Subject: [PATCH] New boot blocks: support for /boot/loader; a.out & ELF; cyl > 1023; multiple 0xa5 slices; etc. --- sys/boot/i386/boot2/Makefile | 51 +++ sys/boot/i386/boot2/boot1.m4 | 62 +++ sys/boot/i386/boot2/boot1.s | 250 ++++++++++++ sys/boot/i386/boot2/boot2.c | 731 +++++++++++++++++++++++++++++++++++ 4 files changed, 1094 insertions(+) create mode 100644 sys/boot/i386/boot2/Makefile create mode 100644 sys/boot/i386/boot2/boot1.m4 create mode 100644 sys/boot/i386/boot2/boot1.s create mode 100644 sys/boot/i386/boot2/boot2.c diff --git a/sys/boot/i386/boot2/Makefile b/sys/boot/i386/boot2/Makefile new file mode 100644 index 000000000000..b52493f6866f --- /dev/null +++ b/sys/boot/i386/boot2/Makefile @@ -0,0 +1,51 @@ +# $Id:$ + +M4?= m4 + +.if exists(${.OBJDIR}/../btx) +BTX= ${.OBJDIR}/../btx +.else +BTX= ${.CURDIR}/../btx +.endif + +ORG1= 0x7c00 +ORG2= 0x1000 + +CFLAGS= -elf -I${BTX}/lib -I. -fno-builtin \ + -O2 -malign-functions=0 -malign-jumps=0 -malign-loops=0 -mrtd \ + -Wall -Waggregate-return -Wbad-function-cast -Wcast-align \ + -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \ + -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings + +LDFLAGS=-nostdlib -static -N + +all: boot1 boot2 + +boot1: boot1.out + /usr/libexec/elf/objcopy -S -O binary boot1.out ${.TARGET} + +boot1.out: boot1.o + ${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o + +boot1.o: boot1.m4 boot1.s + ${M4} boot1.m4 boot1.s | ${AS} ${AFLAGS} -o ${.TARGET} + +boot2: boot2.ldr boot2.bin + btxld -v -E ${ORG2} -f bin -b ${BTX}/btx/btx -l boot2.ldr \ + -o ${.TARGET} -P 1 boot2.bin + @ls -l boot2 | awk '{ x = 7680 - $$5; print x " bytes free"; \ + if (x < 0) exit 1 }' + +boot2.ldr: + dd if=/dev/zero of=${.TARGET} bs=512 count=1 2>/dev/null + +boot2.bin: boot2.out + /usr/libexec/elf/objcopy -S -O binary boot2.out ${.TARGET} + +boot2.out: boot2.o + ${LD} ${LDFLAGS} -Ttext ${ORG2} -o ${.TARGET} \ + ${BTX}/lib/crt0.o boot2.o + +clean: + rm -f boot1 boot1.out boot1.o boot2 boot2.ldr boot2.bin \ + boot2.out boot2.o diff --git a/sys/boot/i386/boot2/boot1.m4 b/sys/boot/i386/boot2/boot1.m4 new file mode 100644 index 000000000000..fc221a1854c1 --- /dev/null +++ b/sys/boot/i386/boot2/boot1.m4 @@ -0,0 +1,62 @@ +# +# 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. +# + +# $Id:$ + +define(_al,0x0)dnl +define(_cl,0x1)dnl +define(_dl,0x2)dnl +define(_bl,0x3)dnl +define(_ah,0x4)dnl +define(_ch,0x5)dnl +define(_dh,0x6)dnl +define(_bh,0x7)dnl + +define(_ax,0x0)dnl +define(_cx,0x1)dnl +define(_dx,0x2)dnl +define(_bx,0x3)dnl +define(_sp,0x4)dnl +define(_bp,0x5)dnl +define(_si,0x6)dnl +define(_di,0x7)dnl + +define(_bx_si,0x0)dnl +define(_bx_di,0x1)dnl +define(_bp_si,0x2)dnl +define(_bp_di,0x3)dnl +define(_si_,0x4)dnl +define(_di_,0x5)dnl +define(_bp_,0x6)dnl +define(_bx_,0x7)dnl + +define(o16,`.byte 0x66')dnl + +define(addbr1,`.byte 0x0; .byte 0x40 | ($1 << 0x3) | $3; .byte $2')dnl +define(addwr1,`.byte 0x1; .byte 0x40 | ($1 << 0x3) | $3; .byte $2')dnl +define(subbr1,`.byte 0x28; .byte 0x40 | ($1 << 0x3) | $3; .byte $2')dnl +define(cmpbi1,`.byte 0x80; .byte 0x78 | $3; .byte $2; .byte $1')dnl +define(cmpwir,`.byte 0x81; .byte 0xf8 | $2; .word $1')dnl +define(movbr1,`.byte 0x88; .byte 0x40 | ($1 << 0x3) | $3; .byte $2')dnl +define(movwr1,`.byte 0x89; .byte 0x40 | ($1 << 0x3) | $3; .byte $2')dnl +define(movwrm,`.byte 0x89; .byte 0x6 | ($1 << 0x3); .word $2')dnl +define(movb1r,`.byte 0x8a; .byte 0x40 | ($3 << 0x3) | $2; .byte $1')dnl +define(movw1r,`.byte 0x8b; .byte 0x40 | ($3 << 0x3) | $2; .byte $1')dnl +define(movwir,`.byte 0xb8 | $2; .word $1')dnl +define(lesw1r,`.byte 0xc4; .byte 0x40 | ($3 << 0x3) | $2; .byte $1')dnl +define(movbi1,`.byte 0xc6; .byte 0x40 | $3; .byte $2; .byte $1')dnl +define(callwi,`.byte 0xe8; .word $1 - . - 0x2')dnl +define(jmpnwi,`.byte 0xe9; .word $1 - . - 0x2')dnl +define(tstbi0,`.byte 0xf6; .byte $2; .byte $1')dnl diff --git a/sys/boot/i386/boot2/boot1.s b/sys/boot/i386/boot2/boot1.s new file mode 100644 index 000000000000..b84ddf341078 --- /dev/null +++ b/sys/boot/i386/boot2/boot1.s @@ -0,0 +1,250 @@ +# +# 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. +# + +# $Id:$ + + .set MEM_REL,0x600 # Relocation address + .set MEM_ARG,0x800 # Arguments + .set MEM_PKT,0x810 # Disk packet + .set MEM_ORG,0x7c00 # Origin + .set MEM_BUF,0x8c00 # Load area + .set MEM_BTX,0x9000 # BTX start + .set MEM_JMP,0x9010 # BTX entry point + .set MEM_USR,0xa000 # Client start + + .set PRT_OFF,0x1be # Partition offset + .set PRT_NUM,0x4 # Partitions + .set PRT_BSD,0xa5 # Partition type + + .set SIZ_PAG,0x1000 # Page size + .set SIZ_SEC,0x200 # Sector size + + .globl start + .globl xread + +start: jmp main # Start recognizably + + .org 0x4,0x90 + +xread: pushl %cs # Address + popl %ds # data +xread.1: movwir(MEM_PKT,_si) # Packet + movbr1(_al,0x2,_si_) # Blocks to read + o16 # Transfer + movwr1(_bx,0x4,_si_) # buffer + o16 # LBA + movwr1(_cx,0x8,_si_) # address + callwi(read) # Read from disk + lret # To caller + +main: cld # String ops inc + xorl %eax,%eax # Zero + movl %ax,%es # Address + movl %ax,%ds # data + movl %ax,%ss # Set up + movwir(start,_sp) # stack + movl %esp,%esi # Source + movwir(MEM_REL,_di) # Destination + movwir(0x100,_cx) # Word count + rep # Copy + movsl # code + movb $0x10,%cl # Words to clear + rep # Zero + stosl # them + movbi1(0x10,-0x10,_di_) # Set packet size + cmpb $0x80,%dl # Hard drive? + jb main.4 # No + movwir(part4,_si) # Read master + movb $0x1,%al # boot + callwi(nread) # record + xorl %eax,%eax # Pass number +main.1: movwir(MEM_BUF+PRT_OFF,_si) # Partition table + movb $0x1,%dh # Partition +main.2: cmpbi1(PRT_BSD,0x4,_si_) # Our partition type? + jne main.3 # No + tstbi0(0x80,_si_) # Active? + jnz main.5 # Yes + testb %al,%al # Second pass? + jnz main.5 # Yes +main.3: addl $0x10,%esi # Next entry + incb %dh # Partition + cmpb $0x1+PRT_NUM,%dh # Done? + jb main.2 # No + incl %eax # Pass + cmpb $0x2,%al # Done? + jb main.1 # No + movwir(msg_part,_si) # Message + jmp error # Error +main.4: xorl %edx,%edx # Partition:drive + movwir(part4,_si) # Partition pointer +main.5: movwrm(_dx,MEM_ARG) # Save args + movb $0x10,%al # Sector count + callwi(nread) # Read disk + movwir(MEM_BTX,_bx) # BTX + movw1r(0xa,_bx_,_si) # Point past + addl %ebx,%esi # it + movwir(MEM_USR+SIZ_PAG,_di) # Client page 1 + movwir(MEM_BTX+0xd*SIZ_SEC,_cx) # Byte + subl %esi,%ecx # count + rep # Relocate + movsb # client + subl %edi,%ecx # Byte count + xorb %al,%al # Zero + rep # assumed + stosb # bss + callwi(seta20) # Enable A20 + jmpnwi(start+MEM_JMP-MEM_ORG) # Start BTX + +# Enable A20 + +seta20: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20 # Yes + movb $0xd1,%al # Command: Write + outb %al,$0x64 # output port +seta20.1: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.1 # Yes + movb $0xdf,%al # Enable + outb %al,$0x60 # A20 + ret # To caller + +# Read from disk + +nread: xorw %bx,%bx # Transfer + movb $MEM_BUF>>0x8,%bh # buffer + o16 # LBA + movw1r(0x8,_si_,_cx) # address + pushl %cs # Read from + callwi(xread.1) # disk + jnc return # If success + movwir(msg_read,_si) # Message + +# Error exit + +error: callwi(putstr) # Display message + movwir(msg_boot,_si) # Display + callwi(putstr) # prompt + xorb %ah,%ah # BIOS: Get + int $0x16 # keypress + int $0x19 # BIOS: Reboot + +# Display string + +putstr.0: movwir(0x7,_bx) # Page:attribute + movb $0xe,%ah # BIOS: Display + int $0x10 # character +putstr: lodsb # Get char + testb %al,%al # End of string? + jne putstr.0 # No + +return: ret # Generic return + +# Read from disk + +read: testb %dh,%dh # Try for extensions? + jz read.3 # No + movwir(0x55aa,_bx) # Magic + pushl %edx # Save + movb $0x41,%ah # BIOS: Check + int $0x13 # extensions present + popl %edx # Restore + jc read.3 # If error + cmpwir(0xaa55,_bx) # Magic? + jne read.3 # No + testb $0x1,%cl # Packet interface? + jz read.3 # No + movb $0x42,%ah # BIOS: Extended + int $0x13 # read + ret # To caller + +read.1: movb $0x1,%ah # Invalid + stc # parameter +read.2: ret # To caller + +read.3: pushl %edx # Save + movb $0x8,%ah # BIOS: Get drive + int $0x13 # parameters + movb %dh,%ch # Max head number + popl %edx # Restore + jc read.2 # If error + andb $0x3f,%cl # Sectors per track + jz read.1 # If zero + o16 # Get + movw1r(0x8,_si_,_ax) # LBA + pushl %edx # Save + movzbw %cl,%bx # Divide by + xorw %dx,%dx # sectors + divw %bx,%ax # per track + movb %ch,%bl # Max head number + movb %dl,%ch # Sector number + incl %ebx # Divide by + xorb %dl,%dl # number of + divw %bx,%ax # heads + movb %dl,%bh # Head number + popl %edx # Restore + o16 # Cylinder number + cmpl $0x3ff,%eax # supportable? + ja read.1 # No + xchgb %al,%ah # Set up cylinder + rorb $0x2,%al # number + orb %ch,%al # Merge + incl %eax # sector + xchgl %eax,%ecx # number + movb %bh,%dh # Head number + subb %ah,%al # Sectors this track + movb1r(0x2,_si_,_ah) # Blocks to read + cmpb %ah,%al # To read + jb read.4 # this + movb %ah,%al # track +read.4: movwir(0x5,_bp) # Try count +read.5: lesw1r(0x4,_si_,_bx) # Transfer buffer + pushl %eax # Save + movb $0x2,%ah # BIOS: Conventional + int $0x13 # read + popl %ebx # Restore + jnc read.6 # If success + decl %ebp # Retry? + jz read.7 # No + xorb %ah,%ah # BIOS: Reset + int $0x13 # disk system + movl %ebx,%eax # Block count + jmp read.5 # Continue +read.6: movzbw %bl,%ax # Sectors read + o16 # Adjust + addwr1(_ax,0x8,_si_) # LBA, + shlb %bl # buffer + addbr1(_bl,0x5,_si_) # pointer, + subbr1(_al,0x2,_si_) # block count + ja read.3 # If not done +read.7: ret # To caller + +# Messages + +msg_read: .asciz "Read error" +msg_part: .asciz "No bootable partition" +msg_boot: .asciz "\r\nHit return to reboot: " + + .org PRT_OFF,0x90 + +# Partition table + + .fill 0x30,0x1,0x0 +part4: .byte 0x80, 0x00, 0x01, 0x00 + .byte 0xa5, 0xff, 0xff, 0xff + .byte 0x00, 0x00, 0x00, 0x00 + .byte 0x50, 0xc3, 0x00, 0x00 + + .word 0xaa55 # Magic number diff --git a/sys/boot/i386/boot2/boot2.c b/sys/boot/i386/boot2/boot2.c new file mode 100644 index 000000000000..25dd42a55662 --- /dev/null +++ b/sys/boot/i386/boot2/boot2.c @@ -0,0 +1,731 @@ +/* + * 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. + */ + +/* + * $Id:$ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#define RBX_ASKNAME 0x0 /* -a */ +#define RBX_SINGLE 0x1 /* -s */ +#define RBX_DFLTROOT 0x5 /* -r */ +#define RBX_KDB 0x6 /* -d */ +#define RBX_CONFIG 0xa /* -c */ +#define RBX_VERBOSE 0xb /* -v */ +#define RBX_CDROM 0xd /* -C */ +#define RBX_GDB 0xf /* -g */ + +#define PATH_CONFIG "/boot.config" +#define PATH_BOOT3 "/boot/loader" +#define PATH_KERNEL "/kernel" +#define PATH_HELP "boot.help" + +#define ARGS 0x800 +#define NOPT 8 +#define BSIZEMAX 8192 +#define NDEV 3 +#define MEM_BASE 0x12 +#define MEM_EXT 0x15 +#define V86_CY(x) ((x) & 1) +#define V86_ZR(x) ((x) & 0x40) + +extern uint32_t _end; + +static const char optstr[NOPT] = "aCcdgrsv"; +static const unsigned char flags[NOPT] = { + RBX_ASKNAME, + RBX_CDROM, + RBX_CONFIG, + RBX_KDB, + RBX_GDB, + RBX_DFLTROOT, + RBX_SINGLE, + RBX_VERBOSE +}; + +static const char *const dev_nm[] = {"fd", "wd", "da"}; +static const uint8_t dev_bios[] = {0, 0x80, 0x82}; +static const uint8_t dev_bsd[] = {2, 0, 4}; + +static struct dsk { + unsigned drive; + unsigned type; + unsigned unit; + unsigned slice; + unsigned part; + int meta; +} dsk; +static char cmd[512]; +static char kname[1024]; +static char help[2048]; +static uint32_t opts; +static struct bootinfo bootinfo; +static int ls; +static uint32_t fs_off; + +void exit(int); +static void load(const char *); +static int parse(char *); +static void readfile(const char *, void *, size_t); +static ino_t lookup(const char *); +static int fsfind(const char *, ino_t *); +static ssize_t fsread(ino_t, void *, size_t); +static int dskread(void *, unsigned, unsigned); +static int printf(const char *,...); +static void getstr(char *, int); +static int putchar(int); +static int getchar(void); +static void *memcpy(void *, const void *, size_t); +static int strcmp(const char *, const char *); +static void *malloc(size_t); +static uint32_t memsize(int); +static uint32_t drvinfo(int); +static int drvread(void *, unsigned, unsigned); +static int keyhit(unsigned); +static int putch(int); +static int getch(void); + +int +main(void) +{ + int autoboot, helpon, i; + + dsk.drive = *(uint8_t *)PTOV(ARGS); + for (dsk.type = NDEV - 1; + (i = dsk.drive - dev_bios[dsk.type]) < 0; + dsk.type--); + dsk.unit = i; + dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; + bootinfo.bi_version = BOOTINFO_VERSION; + bootinfo.bi_size = sizeof(bootinfo); + bootinfo.bi_basemem = memsize(MEM_BASE); + bootinfo.bi_extmem = memsize(MEM_EXT); + bootinfo.bi_memsizes_valid++; + for (i = 0; i < N_BIOS_GEOM; i++) + bootinfo.bi_bios_geom[i] = drvinfo(i); + autoboot = 2; + helpon = 1; + readfile(PATH_CONFIG, cmd, sizeof(cmd)); + if (parse(cmd)) + autoboot = 0; + else if (!*kname) { + if (autoboot == 2) { + memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); + if (!keyhit(0x37)) { + load(kname); + autoboot = 1; + } + } + if (autoboot == 1) + memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); + } + readfile(PATH_HELP, help, sizeof(help)); + for (;;) { + printf(" \n>> FreeBSD/i386 BOOT\n" + "Default: %u:%s(%u,%c)%s\n" + "%s" + "boot: ", + dsk.drive & 0x7f, dev_nm[dsk.type], dsk.unit, + 'a' + dsk.part, kname, helpon ? help : ""); + if (!autoboot || keyhit(0x5a)) + getstr(cmd, sizeof(cmd)); + autoboot = helpon = 0; + if (parse(cmd)) + helpon = 1; + else + load(kname); + } +} + +void +exit(int x) +{ +} + +static void +load(const char *fname) +{ + union { + struct exec ex; + Elf32_Ehdr eh; + } hdr; + Elf32_Phdr ep[2]; + Elf32_Shdr es[2]; + caddr_t p; + ino_t ino; + uint32_t addr, x; + int fmt, i, j; + + if (!(ino = lookup(fname)) && !ls) { + printf("No `%s'\n", fname); + return; + } + if (fsread(ino, &hdr, sizeof(hdr)) != sizeof(hdr)) + return; + if (N_GETMAGIC(hdr.ex) == ZMAGIC) + fmt = 0; + else if (IS_ELF(hdr.eh)) + fmt = 1; + else { + printf("Invalid %s\n", "format"); + return; + } + if (fmt == 0) { + addr = hdr.ex.a_entry & 0xffffff; + p = PTOV(addr); + printf("%s=0x%x ", "text", (unsigned)hdr.ex.a_text); + fs_off = PAGE_SIZE; + if (fsread(ino, p, hdr.ex.a_text) != hdr.ex.a_text) + return; + p += roundup2(hdr.ex.a_text, PAGE_SIZE); + printf("%s=0x%x ", "data", (unsigned)hdr.ex.a_data); + if (fsread(ino, p, hdr.ex.a_data) != hdr.ex.a_data) + return; + p += hdr.ex.a_data; + printf("%s=0x%x ", "bss", (unsigned)hdr.ex.a_bss); + p += roundup2(hdr.ex.a_bss, PAGE_SIZE); + bootinfo.bi_symtab = VTOP(p); + memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); + p += sizeof(hdr.ex.a_syms); + printf("symbols=["); + printf("+0x%x", (unsigned)hdr.ex.a_syms); + if (hdr.ex.a_syms) { + if (fsread(ino, p, hdr.ex.a_syms) != hdr.ex.a_syms) + return; + p += hdr.ex.a_syms; + if (fsread(ino, p, sizeof(int)) != sizeof(int)) + return; + x = *(uint32_t *)p; + p += sizeof(int); + x -= sizeof(int); + printf("+0x%x", x); + if (fsread(ino, p, x) != x) + return; + p += x; + } + } else { + fs_off = hdr.eh.e_phoff; + for (j = i = 0; i < hdr.eh.e_phoff && j < 2; i++) { + if (fsread(ino, ep + j, sizeof(ep[0])) != 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); + printf("%s=0x%x ", !i ? "text" : "data", ep[i].p_filesz); + fs_off = ep[i].p_offset; + if (fsread(ino, p, ep[i].p_filesz) != ep[i].p_filesz) + return; + } + printf("%s=0x%x ", "bss", ep[1].p_memsz - ep[1].p_filesz); + p += roundup2(ep[1].p_memsz, PAGE_SIZE); + bootinfo.bi_symtab = VTOP(p); + printf("symbols=["); + 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 (fsread(ino, &es, sizeof(es)) != sizeof(es)) + return; + for (i = 0; i < 2; i++) { + memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); + p += sizeof(es[i].sh_size); + printf("+0x%x", es[i].sh_size); + fs_off = es[i].sh_offset; + if (fsread(ino, p, es[i].sh_size) != es[i].sh_size) + return; + p += es[i].sh_size; + } + } + addr = hdr.eh.e_entry & 0xffffff; + } + bootinfo.bi_esymtab = VTOP(p); + printf("]\nentry=0x%x\n", addr); + bootinfo.bi_kernelname = VTOP(fname); + __exec((caddr_t)addr, RB_BOOTINFO | opts, + MAKEBOOTDEV(dev_bsd[dsk.type], 0, dsk.slice, dsk.unit, + dsk.part), 0, 0, 0, VTOP(&bootinfo)); +} + +static int +parse(char *arg) +{ + char *p, *q; + int drv, c, i; + + while ((c = *arg++)) { + if (c == ' ') + continue; + for (p = arg; *p && *p != '\n' && *p != ' '; p++); + if (*p) + *p++ = 0; + if (c == '-') + while ((c = *arg++)) { + for (i = 0; c != optstr[i]; i++) + if (i == NOPT - 1) + return -1; + opts |= 1 << flags[i]; + } + else { + for (q = arg--; *q && *q != '('; q++); + if (*q) { + drv = -1; + if (arg[1] == ':') { + if (*arg < '0' || *arg > '9') + return -1; + drv = *arg - '0'; + 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; + if (arg[1] != ',' || *arg < '0' || *arg > '9') + return -1; + dsk.unit = *arg - '0'; + arg += 2; + if (arg[1] == ',') { + if (*arg < '0' || *arg > '4') + return -1; + if ((dsk.slice = *arg - '0')) + dsk.slice++; + arg += 2; + } + if (arg[1] != ')' || *arg < 'a' || *arg > 'p') + return -1; + dsk.part = *arg - 'a'; + arg += 2; + if (drv == -1) + dsk.drive = dev_bios[dsk.type] + dsk.unit; + else + dsk.drive = (dsk.type ? 0x80 : 0) + drv; + dsk.meta = 0; + } + if ((i = p - arg - !*(p - 1))) { + if (i >= sizeof(kname)) + return -1; + memcpy(kname, arg, i + 1); + } + } + arg = p; + } + return 0; +} + +static void +readfile(const char *fname, void *buf, size_t size) +{ + ino_t ino; + + if ((ino = lookup(fname))) + fsread(ino, buf, size); +} + +static ino_t +lookup(const char *path) +{ + char name[MAXNAMLEN + 1]; + const char *s; + ino_t ino; + ssize_t n; + int dt; + + ino = ROOTINO; + dt = DT_DIR; + for (;;) { + if (*path == '/') + path++; + if (!*path) + break; + for (s = path; *s && *s != '/'; s++); + if ((n = s - path) > MAXNAMLEN) + return 0; + ls = *path == '?' && n == 1 && !*s; + memcpy(name, path, n); + name[n] = 0; + if ((dt = fsfind(name, &ino)) <= 0) + break; + path = s; + } + return dt == DT_REG ? ino : 0; +} + +static int +fsfind(const char *name, ino_t * ino) +{ + char buf[DEV_BSIZE]; + struct dirent *d; + char *s; + ssize_t n; + + fs_off = 0; + while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0) + for (s = buf; s < buf + DEV_BSIZE;) { + d = (void *)s; + if (ls) + printf("%s ", d->d_name); + else if (!strcmp(name, d->d_name)) { + *ino = d->d_fileno; + return d->d_type; + } + s += d->d_reclen; + } + if (n != -1 && ls) + putchar('\n'); + return 0; +} + +static ssize_t +fsread(ino_t inode, void *buf, size_t nbyte) +{ + static struct fs fs; + static struct dinode din; + static char *blkbuf; + static ufs_daddr_t *indbuf; + static ino_t inomap; + static ufs_daddr_t blkmap, indmap; + static unsigned fsblks; + char *s; + ufs_daddr_t lbn, addr; + size_t n, nb, off; + + if (!dsk.meta) { + if (!blkbuf) + blkbuf = malloc(BSIZEMAX); + inomap = 0; + if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE)) + return -1; + memcpy(&fs, blkbuf, sizeof(fs)); + if (fs.fs_magic != FS_MAGIC) { + printf("Not ufs\n"); + return -1; + } + fsblks = fs.fs_bsize >> DEV_BSHIFT; + dsk.meta = 1; + } + if (inomap != inode) { + if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)), + fsblks)) + return -1; + din = ((struct dinode *)blkbuf)[inode % INOPB(&fs)]; + inomap = inode; + fs_off = 0; + blkmap = indmap = 0; + } + s = buf; + if (nbyte > (n = din.di_size - fs_off)) + nbyte = n; + nb = nbyte; + while (nb) { + lbn = lblkno(&fs, fs_off); + if (lbn < NDADDR) + addr = din.di_db[lbn]; + else { + if (indmap != din.di_ib[0]) { + if (!indbuf) + indbuf = malloc(BSIZEMAX); + if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]), + fsblks)) + return -1; + indmap = din.di_ib[0]; + } + addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)]; + } + n = dblksize(&fs, &din, lbn); + if (blkmap != addr) { + if (dskread(blkbuf, fsbtodb(&fs, addr), n >> DEV_BSHIFT)) + return -1; + blkmap = addr; + } + off = blkoff(&fs, fs_off); + n -= off; + if (n > nb) + n = nb; + memcpy(s, blkbuf + off, n); + s += n; + fs_off += n; + nb -= n; + } + return nbyte; +} + +static int +dskread(void *buf, unsigned lba, unsigned nblk) +{ + static char *sec; + static unsigned hidden; + struct dos_partition *dp; + struct disklabel *d; + unsigned sl, i; + + if (!dsk.meta) { + if (!sec) + sec = malloc(DEV_BSIZE); + hidden = 0; + sl = dsk.slice; + if (sl != WHOLE_DISK_SLICE) { + if (drvread(sec, DOSBBSECTOR, 1)) + return -1; + dp = (void *)(sec + DOSPARTOFF); + if (sl == COMPATIBILITY_SLICE) + for (i = 0; i < NDOSPART; i++) + if (dp[i].dp_typ == DOSPTYP_386BSD && + (dp[i].dp_flag & 0x80 || + sl == COMPATIBILITY_SLICE)) + sl = BASE_SLICE + i; + if (sl != COMPATIBILITY_SLICE) + dp += sl - BASE_SLICE; + if (dp->dp_typ != DOSPTYP_386BSD) { + printf("Invalid %s\n", "slice"); + return -1; + } + hidden = dp->dp_start; + } + if (dsk.part != RAW_PART) { + if (drvread(sec, hidden + LABELSECTOR, 1)) + return -1; + d = (void *)(sec + LABELOFFSET); + if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { + printf("Invalid %s\n", "label"); + return -1; + } + if (dsk.part >= d->d_npartitions) { + printf("Invalid %s\n", "partition"); + return -1; + } + hidden = d->d_partitions[dsk.part].p_offset; + } + } + return drvread(buf, hidden + lba, nblk); +} + +static int +printf(const char *fmt,...) +{ + static const char digits[16] = "0123456789abcdef"; + va_list ap; + char buf[10]; + char *s; + unsigned r, 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': + case 'x': + r = c == 'u' ? 10U : 16U; + u = va_arg(ap, unsigned); + s = buf; + do + *s++ = digits[u % r]; + while (u /= r); + while (--s >= buf) + putchar(*s); + continue; + } + } + putchar(c); + } + va_end(ap); + return 0; +} + +static void +getstr(char *str, int size) +{ + char *s; + int c; + + s = str; + do { + switch (c = getchar()) { + case '\b': + if (s > str) + s--; + break; + case '\n': + *s = 0; + break; + default: + if (s - str < size - 1) + *s++ = c; + } + putchar(c); + } while (c != '\n'); +} + +static int +putchar(int c) +{ + if (c == '\n') + putch('\r'); + return putch(c); +} + +static int +getchar(void) +{ + int c; + + c = getch(); + if (c == '\r') + c = '\n'; + return c; +} + +static void * +memcpy(void *dst, const void *src, size_t size) +{ + const char *s; + char *d; + + for (d = dst, s = src; size; size--) + *d++ = *s++; + return dst; +} + +static int +strcmp(const char *s1, const char *s2) +{ + for (; *s1 == *s2 && *s1; s1++, s2++); + return (u_char)*s1 - (u_char)*s2; +} + +static void * +malloc(size_t size) +{ + static uint32_t next; + void *p; + + if (!next) + next = roundup2(__base + _end, 0x10000) - __base; + p = (void *)next; + next += size; + return p; +} + +static uint32_t +memsize(int type) +{ + v86.ctl = V86_FLAGS; + v86.addr = type; + v86.eax = 0x8800; + v86int(); + return v86.eax; +} + +static uint32_t +drvinfo(int drive) +{ + v86.addr = 0x13; + v86.eax = 0x800; + v86.edx = 0x80 + drive; + v86int(); + if (V86_CY(v86.efl)) + return 0x4f010f; + return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | + (v86.edx & 0xff00) | (v86.ecx & 0x3f); +} + +static int +drvread(void *buf, unsigned lba, unsigned nblk) +{ + v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; + v86.addr = 0x604; + v86.eax = nblk; + v86.ebx = VTOPSEG(buf) << 16 | VTOPOFF(buf); + v86.ecx = lba; + v86.edx = dsk.drive; + v86int(); + v86.ctl = V86_FLAGS; + if (V86_CY(v86.efl)) { + printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff, + lba); + return -1; + } + return 0; +} + +static int +keyhit(unsigned ticks) +{ + uint32_t x; + + x = 0; + for (;;) { + v86.addr = 0x16; + v86.eax = 0x100; + v86int(); + if (!V86_ZR(v86.efl)) + return 1; + v86.addr = 0x1a; + v86.eax = 0; + v86.edx = 0; + v86int(); + if (!x) + x = v86.edx; + else if (v86.edx < x || v86.edx > x + ticks) + return 0; + } +} + +static int +putch(int c) +{ + v86.addr = 0x10; + v86.eax = 0xe00 | (c & 0xff); + v86.ebx = 0x7; + v86int(); + return c; +} + +static int +getch(void) +{ + v86.addr = 0x16; + v86.eax = 0; + v86int(); + return v86.eax & 0xff; +}