diff options
Diffstat (limited to 'sys/boot/i386/boot2')
-rw-r--r-- | sys/boot/i386/boot2/Makefile | 123 | ||||
-rw-r--r-- | sys/boot/i386/boot2/boot1.S | 365 | ||||
-rw-r--r-- | sys/boot/i386/boot2/boot1.s | 365 | ||||
-rw-r--r-- | sys/boot/i386/boot2/boot2.c | 645 | ||||
-rw-r--r-- | sys/boot/i386/boot2/lib.h | 24 | ||||
-rw-r--r-- | sys/boot/i386/boot2/sio.S | 80 | ||||
-rw-r--r-- | sys/boot/i386/boot2/sio.s | 80 |
7 files changed, 1682 insertions, 0 deletions
diff --git a/sys/boot/i386/boot2/Makefile b/sys/boot/i386/boot2/Makefile new file mode 100644 index 0000000..aa8c0cd --- /dev/null +++ b/sys/boot/i386/boot2/Makefile @@ -0,0 +1,123 @@ +# $FreeBSD$ + +PROG= boot2 +NOMAN= +STRIP= +BINDIR?= /boot +BINMODE= 444 +CLEANFILES= boot boot1 boot1.out boot1.o \ + boot2.ldr boot2.bin boot2.ld boot2.out boot2.o boot2.h \ + boot2.s sio.o + +NM?= nm + +# A value of 0x80 enables LBA support. +B1FLAGS= 0x80 + +BOOT_COMCONSOLE_PORT?= 0x3f8 +BOOT_COMCONSOLE_SPEED?= 9600 +B2SIOFMT?= 0x3 + +.if exists(${.OBJDIR}/../btx) +BTX= ${.OBJDIR}/../btx +.else +BTX= ${.CURDIR}/../btx +.endif + +REL1= 0x700 +ORG1= 0x7c00 +ORG2= 0x2000 + +# Decide Level of UFS support. UFS1_AND_UFS2 doesn't fit. + +# BOOT2_UFS?= UFS2_ONLY +BOOT2_UFS?= UFS1_AND_UFS2 +# BOOT2_UFS?= UFS1_ONLY + +CFLAGS= -elf -ffreestanding -Os -fno-builtin \ + -fno-guess-branch-probability -fomit-frame-pointer\ + -mno-align-long-strings \ + -mrtd \ + -D${BOOT2_UFS} \ + -I${.CURDIR}/../../common \ + -I${.CURDIR}/../btx/lib -I. \ + -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 --gc-sections + +all: boot1 boot2 boot + +boot: boot1 boot2 + cat boot1 boot2 > boot + +boot1: boot1.out + objcopy -S -O binary boot1.out ${.TARGET} + +boot1.out: boot1.o + ${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o + +boot1.o: boot1.s + ${CPP} ${CFLAGS} ${.IMPSRC} | \ + ${AS} ${AFLAGS} --defsym FLAGS=${B1FLAGS} -o ${.TARGET} + +boot2.o: boot2.c ${.CURDIR}/../../common/ufsread.c + ${CC} ${CFLAGS} -S -o boot2.s.tmp ${.IMPSRC} + sed -e '/align/d' -e '/nop/d' < boot2.s.tmp > boot2.s + rm -f boot2.s.tmp + ${AS} ${AFLAGS} -o boot2.o boot2.s + +boot2.h: boot1.out + ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \ + { x = $$1 - ORG1; \ + printf("#define XREADORG %#x\n", REL1 + x) }' \ + ORG1=`printf "%d" ${ORG1}` \ + REL1=`printf "%d" ${REL1}` > boot2.h + +boot2: boot2.ldr boot2.bin ${BTX}/btx/btx + btxld -v -E ${ORG2} -f bin -b ${BTX}/btx/btx -l boot2.ldr \ + -o boot2.ld -P 1 boot2.bin + @ls -l boot2.ld | awk '{ x = 7680 - $$5; \ + print x " bytes available"; if (x < 0) exit 1 }' + dd if=boot2.ld of=${.TARGET} obs=7680 conv=osync 2>/dev/null + +boot2.ldr: + dd if=/dev/zero of=${.TARGET} bs=276 count=1 2>/dev/null + +boot2.bin: boot2.out + objcopy -S -O binary boot2.out ${.TARGET} + +boot2.out: boot2.o sio.o + ${LD} ${LDFLAGS} -Ttext ${ORG2} -o ${.TARGET} \ + ${BTX}/lib/crt0.o ${.ALLSRC} + +boot2.o: boot2.h + +sio.o: sio.s + ${AS} ${AFLAGS} --defsym SIOPRT=${BOOT_COMCONSOLE_PORT} \ + --defsym SIOFMT=${B2SIOFMT} \ + --defsym SIOSPD=${BOOT_COMCONSOLE_SPEED} \ + ${.IMPSRC} -o ${.TARGET} + +install: + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + boot ${DESTDIR}${BINDIR}/boot + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + boot1 ${DESTDIR}${BINDIR}/boot1 + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + boot2 ${DESTDIR}${BINDIR}/boot2 + +.include <bsd.prog.mk> + +.if defined(REALLY_AMD64) +boot2.o: machine + +beforedepend ${OBJS}: machine + +machine: + ln -sf ${.CURDIR}/../../../i386/include machine + +CLEANFILES+= machine +.endif + diff --git a/sys/boot/i386/boot2/boot1.S b/sys/boot/i386/boot2/boot1.S new file mode 100644 index 0000000..f7d9df6 --- /dev/null +++ b/sys/boot/i386/boot2/boot1.S @@ -0,0 +1,365 @@ +// +// 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. +// + +// $FreeBSD$ + +// Memory Locations + .set MEM_REL,0x700 // Relocation address + .set MEM_ARG,0x900 // Arguments + .set MEM_ORG,0x7c00 // Origin + .set MEM_BUF,0x8cec // Load area + .set MEM_BTX,0x9000 // BTX start + .set MEM_JMP,0x9010 // BTX entry point + .set MEM_USR,0xa000 // Client start + .set BDA_BOOT,0x472 // Boot howto flag + +// Partition Constants + .set PRT_OFF,0x1be // Partition offset + .set PRT_NUM,0x4 // Partitions + .set PRT_BSD,0xa5 // Partition type + +// Flag Bits + .set FL_PACKET,0x80 // Packet mode + +// Misc. Constants + .set SIZ_PAG,0x1000 // Page size + .set SIZ_SEC,0x200 // Sector size + + .set NSECT,0x10 + .globl start + .globl xread + .code16 + +start: jmp main // Start recognizably + +// This is the start of a standard BIOS Parameter Block (BPB). Most bootable +// FAT disks have this at the start of their MBR. While normal BIOS's will +// work fine without this section, IBM's El Torito emulation "fixes" up the +// BPB by writing into the memory copy of the MBR. Rather than have data +// written into our xread routine, we'll define a BPB to work around it. +// The data marked with (T) indicates a field required for a ThinkPad to +// recognize the disk and (W) indicates fields written from IBM BIOS code. +// The use of the BPB is based on what OpenBSD and NetBSD implemented in +// their boot code but the required fields were determined by trial and error. +// +// Note: If additional space is needed in boot1, one solution would be to +// move the "prompt" message data (below) to replace the OEM ID. + + .org 0x03, 0x00 +oemid: .space 0x08, 0x00 // OEM ID + + .org 0x0b, 0x00 +bpb: .word 512 // sector size (T) + .byte 0 // sectors/clustor + .word 0 // reserved sectors + .byte 0 // number of FATs + .word 0 // root entries + .word 0 // small sectors + .byte 0 // media type (W) + .word 0 // sectors/fat + .word 18 // sectors per track (T) + .word 2 // number of heads (T) + .long 0 // hidden sectors (W) + .long 0 // large sectors + + .org 0x24, 0x00 +ebpb: .byte 0 // BIOS physical drive number (W) + + .org 0x25,0x90 +// +// Trampoline used by boot2 to call read to read data from the disk via +// the BIOS. Call with: +// +// %cx:%ax - long - LBA to read in +// %es:(%bx) - caddr_t - buffer to read data into +// %dl - byte - drive to read from +// %dh - byte - num sectors to read +// + +xread: push %ss // Address + pop %ds // data +// +// Setup an EDD disk packet and pass it to read +// +xread.1: // Starting + pushl $0x0 // absolute + push %cx // block + push %ax // number + push %es // Address of + push %bx // transfer buffer + xor %ax,%ax // Number of + movb %dh,%al // blocks to + push %ax // transfer + push $0x10 // Size of packet + mov %sp,%bp // Packet pointer + callw read // Read from disk + lea 0x10(%bp),%sp // Clear stack + lret // To far caller +// +// Load the rest of boot2 and BTX up, copy the parts to the right locations, +// and start it all up. +// + +// +// Setup the segment registers to flat addressing (segment 0) and setup the +// stack to end just below the start of our code. +// +main: cld // String ops inc + xor %cx,%cx // Zero + mov %cx,%es // Address + mov %cx,%ds // data + mov %cx,%ss // Set up + mov $start,%sp // stack +// +// Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets +// %cx == 0x100. +// + mov %sp,%si // Source + mov $MEM_REL,%di // Destination + incb %ch // Word count + rep // Copy + movsw // code +// +// If we are on a hard drive, then load the MBR and look for the first +// FreeBSD slice. We use the fake partition entry below that points to +// the MBR when we call nread. The first pass looks for the first active +// FreeBSD slice. The second pass looks for the first non-active FreeBSD +// slice if the first one fails. +// + mov $part4,%si // Partition + cmpb $0x80,%dl // Hard drive? + jb main.4 // No + movb $0x1,%dh // Block count + callw nread // Read MBR + mov $0x1,%cx // Two passes +main.1: mov $MEM_BUF+PRT_OFF,%si // Partition table + movb $0x1,%dh // Partition +main.2: cmpb $PRT_BSD,0x4(%si) // Our partition type? + jne main.3 // No + jcxz main.5 // If second pass + testb $0x80,(%si) // Active? + jnz main.5 // Yes +main.3: add $0x10,%si // Next entry + incb %dh // Partition + cmpb $0x1+PRT_NUM,%dh // In table? + jb main.2 // Yes + dec %cx // Do two + jcxz main.1 // passes +// +// If we get here, we didn't find any FreeBSD slices at all, so print an +// error message and die. +// + mov $msg_part,%si // Message + jmp error // Error +// +// Floppies use partition 0 of drive 0. +// +main.4: xor %dx,%dx // Partition:drive +// +// Ok, we have a slice and drive in %dx now, so use that to locate and load +// boot2. %si references the start of the slice we are looking for, so go +// ahead and load up the first 16 sectors (boot1 + boot2) from that. When +// we read it in, we conveniently use 0x8cec as our transfer buffer. Thus, +// boot1 ends up at 0x8cec, and boot2 starts at 0x8cec + 0x200 = 0x8eec. +// The first part of boot2 is the disklabel, which is 0x114 bytes long. +// The second part is BTX, which is thus loaded into 0x9000, which is where +// it also runs from. The boot2.bin binary starts right after the end of +// BTX, so we have to figure out where the start of it is and then move the +// binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, but +// when we use btxld to create boot2, we use an entry point of 0x2000. That +// entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000. +// +main.5: mov %dx,MEM_ARG // Save args + movb $NSECT,%dh // Sector count + callw nread // Read disk + mov $MEM_BTX,%bx // BTX + mov 0xa(%bx),%si // Get BTX length and set + add %bx,%si // %si to start of boot2.bin + mov $MEM_USR+SIZ_PAG*2,%di // Client page 2 + mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx // Byte + sub %si,%cx // count + rep // Relocate + movsb // client + sub %di,%cx // Byte count + xorb %al,%al // Zero assumed bss from + rep // the end of boot2.bin + stosb // up to 0x10000 + callw seta20 // Enable A20 + jmp start+MEM_JMP-MEM_ORG // Start BTX +// +// Enable A20 so we can access memory above 1 meg. +// +seta20: cli // Disable interrupts +seta20.1: inb $0x64,%al // Get status + testb $0x2,%al // Busy? + jnz seta20.1 // Yes + movb $0xd1,%al // Command: Write + outb %al,$0x64 // output port +seta20.2: inb $0x64,%al // Get status + testb $0x2,%al // Busy? + jnz seta20.2 // Yes + movb $0xdf,%al // Enable + outb %al,$0x60 // A20 + sti // Enable interrupts + retw // To caller +// +// Trampoline used to call read from within boot1. +// +nread: mov $MEM_BUF,%bx // Transfer buffer + mov 0x8(%si),%ax // Get + mov 0xa(%si),%cx // LBA + push %cs // Read from + callw xread.1 // disk + jnc return // If success, return + mov $msg_read,%si // Otherwise, set the error + // message and fall through to + // the error routine +// +// Print out the error message pointed to by %ds:(%si) followed +// by a prompt, wait for a keypress, and then reboot the machine. +// +error: callw putstr // Display message + mov $prompt,%si // Display + callw putstr // prompt + xorb %ah,%ah // BIOS: Get + int $0x16 // keypress + movw $0x1234, BDA_BOOT // Do a warm boot + ljmp $0xffff,$0x0 // reboot the machine +// +// Display a null-terminated string using the BIOS output. +// +putstr.0: mov $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 + +// +// Overused return code. ereturn is used to return an error from the +// read function. Since we assume putstr succeeds, we (ab)use the +// same code when we return from putstr. +// +ereturn: movb $0x1,%ah // Invalid + stc // argument +return: retw // To caller +// +// Reads sectors from the disk. If EDD is enabled, then check if it is +// installed and use it if it is. If it is not installed or not enabled, then +// fall back to using CHS. Since we use a LBA, if we are using CHS, we have to +// fetch the drive parameters from the BIOS and divide it out ourselves. +// Call with: +// +// %dl - byte - drive number +// stack - 10 bytes - EDD Packet +// +read: push %dx // Save + movb $0x8,%ah // BIOS: Get drive + int $0x13 // parameters + movb %dh,%ch // Max head number + pop %dx // Restore + jc return // If error + andb $0x3f,%cl // Sectors per track + jz ereturn // If zero + cli // Disable interrupts + mov 0x8(%bp),%eax // Get LBA + push %dx // Save + movzbl %cl,%ebx // Divide by + xor %edx,%edx // sectors + div %ebx // per track + movb %ch,%bl // Max head number + movb %dl,%ch // Sector number + inc %bx // Divide by + xorb %dl,%dl // number + div %ebx // of heads + movb %dl,%bh // Head number + pop %dx // Restore + cmpl $0x3ff,%eax // Cylinder number supportable? + sti // Enable interrupts + ja read.7 // No, try EDD + xchgb %al,%ah // Set up cylinder + rorb $0x2,%al // number + orb %ch,%al // Merge + inc %ax // sector + xchg %ax,%cx // number + movb %bh,%dh // Head number + subb %ah,%al // Sectors this track + mov 0x2(%bp),%ah // Blocks to read + cmpb %ah,%al // To read + jb read.2 // this +#ifdef TRACK_AT_A_TIME + movb %ah,%al // track +#else + movb $1,%al // one sector +#endif +read.2: mov $0x5,%di // Try count +read.3: les 0x4(%bp),%bx // Transfer buffer + push %ax // Save + movb $0x2,%ah // BIOS: Read + int $0x13 // from disk + pop %bx // Restore + jnc read.4 // If success + dec %di // Retry? + jz read.6 // No + xorb %ah,%ah // BIOS: Reset + int $0x13 // disk system + xchg %bx,%ax // Block count + jmp read.3 // Continue +read.4: movzbw %bl,%ax // Sectors read + add %ax,0x8(%bp) // Adjust + jnc read.5 // LBA, + incw 0xa(%bp) // transfer +read.5: shlb %bl // buffer + add %bl,0x5(%bp) // pointer, + sub %al,0x2(%bp) // block count + ja read // If not done +read.6: retw // To caller +read.7: testb $FL_PACKET,%cs:MEM_REL+flags-start // LBA support enabled? + jz ereturn // No, so return an error + mov $0x55aa,%bx // Magic + push %dx // Save + movb $0x41,%ah // BIOS: Check + int $0x13 // extensions present + pop %dx // Restore + jc return // If error, return an error + cmp $0xaa55,%bx // Magic? + jne ereturn // No, so return an error + testb $0x1,%cl // Packet interface? + jz ereturn // No, so return an error + mov %bp,%si // Disk packet + movb $0x42,%ah // BIOS: Extended + int $0x13 // read + retw // To caller + +// Messages + +msg_read: .asciz "Read" +msg_part: .asciz "Boot" + +prompt: .asciz " error\r\n" + +flags: .byte FLAGS // Flags + + .org PRT_OFF,0x90 + +// Partition table + + .fill 0x30,0x1,0x0 +part4: .byte 0x80, 0x00, 0x01, 0x00 + .byte 0xa5, 0xfe, 0xff, 0xff + .byte 0x00, 0x00, 0x00, 0x00 + .byte 0x50, 0xc3, 0x00, 0x00 // 50000 sectors long, bleh + + .word 0xaa55 // Magic number diff --git a/sys/boot/i386/boot2/boot1.s b/sys/boot/i386/boot2/boot1.s new file mode 100644 index 0000000..f7d9df6 --- /dev/null +++ b/sys/boot/i386/boot2/boot1.s @@ -0,0 +1,365 @@ +// +// 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. +// + +// $FreeBSD$ + +// Memory Locations + .set MEM_REL,0x700 // Relocation address + .set MEM_ARG,0x900 // Arguments + .set MEM_ORG,0x7c00 // Origin + .set MEM_BUF,0x8cec // Load area + .set MEM_BTX,0x9000 // BTX start + .set MEM_JMP,0x9010 // BTX entry point + .set MEM_USR,0xa000 // Client start + .set BDA_BOOT,0x472 // Boot howto flag + +// Partition Constants + .set PRT_OFF,0x1be // Partition offset + .set PRT_NUM,0x4 // Partitions + .set PRT_BSD,0xa5 // Partition type + +// Flag Bits + .set FL_PACKET,0x80 // Packet mode + +// Misc. Constants + .set SIZ_PAG,0x1000 // Page size + .set SIZ_SEC,0x200 // Sector size + + .set NSECT,0x10 + .globl start + .globl xread + .code16 + +start: jmp main // Start recognizably + +// This is the start of a standard BIOS Parameter Block (BPB). Most bootable +// FAT disks have this at the start of their MBR. While normal BIOS's will +// work fine without this section, IBM's El Torito emulation "fixes" up the +// BPB by writing into the memory copy of the MBR. Rather than have data +// written into our xread routine, we'll define a BPB to work around it. +// The data marked with (T) indicates a field required for a ThinkPad to +// recognize the disk and (W) indicates fields written from IBM BIOS code. +// The use of the BPB is based on what OpenBSD and NetBSD implemented in +// their boot code but the required fields were determined by trial and error. +// +// Note: If additional space is needed in boot1, one solution would be to +// move the "prompt" message data (below) to replace the OEM ID. + + .org 0x03, 0x00 +oemid: .space 0x08, 0x00 // OEM ID + + .org 0x0b, 0x00 +bpb: .word 512 // sector size (T) + .byte 0 // sectors/clustor + .word 0 // reserved sectors + .byte 0 // number of FATs + .word 0 // root entries + .word 0 // small sectors + .byte 0 // media type (W) + .word 0 // sectors/fat + .word 18 // sectors per track (T) + .word 2 // number of heads (T) + .long 0 // hidden sectors (W) + .long 0 // large sectors + + .org 0x24, 0x00 +ebpb: .byte 0 // BIOS physical drive number (W) + + .org 0x25,0x90 +// +// Trampoline used by boot2 to call read to read data from the disk via +// the BIOS. Call with: +// +// %cx:%ax - long - LBA to read in +// %es:(%bx) - caddr_t - buffer to read data into +// %dl - byte - drive to read from +// %dh - byte - num sectors to read +// + +xread: push %ss // Address + pop %ds // data +// +// Setup an EDD disk packet and pass it to read +// +xread.1: // Starting + pushl $0x0 // absolute + push %cx // block + push %ax // number + push %es // Address of + push %bx // transfer buffer + xor %ax,%ax // Number of + movb %dh,%al // blocks to + push %ax // transfer + push $0x10 // Size of packet + mov %sp,%bp // Packet pointer + callw read // Read from disk + lea 0x10(%bp),%sp // Clear stack + lret // To far caller +// +// Load the rest of boot2 and BTX up, copy the parts to the right locations, +// and start it all up. +// + +// +// Setup the segment registers to flat addressing (segment 0) and setup the +// stack to end just below the start of our code. +// +main: cld // String ops inc + xor %cx,%cx // Zero + mov %cx,%es // Address + mov %cx,%ds // data + mov %cx,%ss // Set up + mov $start,%sp // stack +// +// Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets +// %cx == 0x100. +// + mov %sp,%si // Source + mov $MEM_REL,%di // Destination + incb %ch // Word count + rep // Copy + movsw // code +// +// If we are on a hard drive, then load the MBR and look for the first +// FreeBSD slice. We use the fake partition entry below that points to +// the MBR when we call nread. The first pass looks for the first active +// FreeBSD slice. The second pass looks for the first non-active FreeBSD +// slice if the first one fails. +// + mov $part4,%si // Partition + cmpb $0x80,%dl // Hard drive? + jb main.4 // No + movb $0x1,%dh // Block count + callw nread // Read MBR + mov $0x1,%cx // Two passes +main.1: mov $MEM_BUF+PRT_OFF,%si // Partition table + movb $0x1,%dh // Partition +main.2: cmpb $PRT_BSD,0x4(%si) // Our partition type? + jne main.3 // No + jcxz main.5 // If second pass + testb $0x80,(%si) // Active? + jnz main.5 // Yes +main.3: add $0x10,%si // Next entry + incb %dh // Partition + cmpb $0x1+PRT_NUM,%dh // In table? + jb main.2 // Yes + dec %cx // Do two + jcxz main.1 // passes +// +// If we get here, we didn't find any FreeBSD slices at all, so print an +// error message and die. +// + mov $msg_part,%si // Message + jmp error // Error +// +// Floppies use partition 0 of drive 0. +// +main.4: xor %dx,%dx // Partition:drive +// +// Ok, we have a slice and drive in %dx now, so use that to locate and load +// boot2. %si references the start of the slice we are looking for, so go +// ahead and load up the first 16 sectors (boot1 + boot2) from that. When +// we read it in, we conveniently use 0x8cec as our transfer buffer. Thus, +// boot1 ends up at 0x8cec, and boot2 starts at 0x8cec + 0x200 = 0x8eec. +// The first part of boot2 is the disklabel, which is 0x114 bytes long. +// The second part is BTX, which is thus loaded into 0x9000, which is where +// it also runs from. The boot2.bin binary starts right after the end of +// BTX, so we have to figure out where the start of it is and then move the +// binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, but +// when we use btxld to create boot2, we use an entry point of 0x2000. That +// entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000. +// +main.5: mov %dx,MEM_ARG // Save args + movb $NSECT,%dh // Sector count + callw nread // Read disk + mov $MEM_BTX,%bx // BTX + mov 0xa(%bx),%si // Get BTX length and set + add %bx,%si // %si to start of boot2.bin + mov $MEM_USR+SIZ_PAG*2,%di // Client page 2 + mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx // Byte + sub %si,%cx // count + rep // Relocate + movsb // client + sub %di,%cx // Byte count + xorb %al,%al // Zero assumed bss from + rep // the end of boot2.bin + stosb // up to 0x10000 + callw seta20 // Enable A20 + jmp start+MEM_JMP-MEM_ORG // Start BTX +// +// Enable A20 so we can access memory above 1 meg. +// +seta20: cli // Disable interrupts +seta20.1: inb $0x64,%al // Get status + testb $0x2,%al // Busy? + jnz seta20.1 // Yes + movb $0xd1,%al // Command: Write + outb %al,$0x64 // output port +seta20.2: inb $0x64,%al // Get status + testb $0x2,%al // Busy? + jnz seta20.2 // Yes + movb $0xdf,%al // Enable + outb %al,$0x60 // A20 + sti // Enable interrupts + retw // To caller +// +// Trampoline used to call read from within boot1. +// +nread: mov $MEM_BUF,%bx // Transfer buffer + mov 0x8(%si),%ax // Get + mov 0xa(%si),%cx // LBA + push %cs // Read from + callw xread.1 // disk + jnc return // If success, return + mov $msg_read,%si // Otherwise, set the error + // message and fall through to + // the error routine +// +// Print out the error message pointed to by %ds:(%si) followed +// by a prompt, wait for a keypress, and then reboot the machine. +// +error: callw putstr // Display message + mov $prompt,%si // Display + callw putstr // prompt + xorb %ah,%ah // BIOS: Get + int $0x16 // keypress + movw $0x1234, BDA_BOOT // Do a warm boot + ljmp $0xffff,$0x0 // reboot the machine +// +// Display a null-terminated string using the BIOS output. +// +putstr.0: mov $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 + +// +// Overused return code. ereturn is used to return an error from the +// read function. Since we assume putstr succeeds, we (ab)use the +// same code when we return from putstr. +// +ereturn: movb $0x1,%ah // Invalid + stc // argument +return: retw // To caller +// +// Reads sectors from the disk. If EDD is enabled, then check if it is +// installed and use it if it is. If it is not installed or not enabled, then +// fall back to using CHS. Since we use a LBA, if we are using CHS, we have to +// fetch the drive parameters from the BIOS and divide it out ourselves. +// Call with: +// +// %dl - byte - drive number +// stack - 10 bytes - EDD Packet +// +read: push %dx // Save + movb $0x8,%ah // BIOS: Get drive + int $0x13 // parameters + movb %dh,%ch // Max head number + pop %dx // Restore + jc return // If error + andb $0x3f,%cl // Sectors per track + jz ereturn // If zero + cli // Disable interrupts + mov 0x8(%bp),%eax // Get LBA + push %dx // Save + movzbl %cl,%ebx // Divide by + xor %edx,%edx // sectors + div %ebx // per track + movb %ch,%bl // Max head number + movb %dl,%ch // Sector number + inc %bx // Divide by + xorb %dl,%dl // number + div %ebx // of heads + movb %dl,%bh // Head number + pop %dx // Restore + cmpl $0x3ff,%eax // Cylinder number supportable? + sti // Enable interrupts + ja read.7 // No, try EDD + xchgb %al,%ah // Set up cylinder + rorb $0x2,%al // number + orb %ch,%al // Merge + inc %ax // sector + xchg %ax,%cx // number + movb %bh,%dh // Head number + subb %ah,%al // Sectors this track + mov 0x2(%bp),%ah // Blocks to read + cmpb %ah,%al // To read + jb read.2 // this +#ifdef TRACK_AT_A_TIME + movb %ah,%al // track +#else + movb $1,%al // one sector +#endif +read.2: mov $0x5,%di // Try count +read.3: les 0x4(%bp),%bx // Transfer buffer + push %ax // Save + movb $0x2,%ah // BIOS: Read + int $0x13 // from disk + pop %bx // Restore + jnc read.4 // If success + dec %di // Retry? + jz read.6 // No + xorb %ah,%ah // BIOS: Reset + int $0x13 // disk system + xchg %bx,%ax // Block count + jmp read.3 // Continue +read.4: movzbw %bl,%ax // Sectors read + add %ax,0x8(%bp) // Adjust + jnc read.5 // LBA, + incw 0xa(%bp) // transfer +read.5: shlb %bl // buffer + add %bl,0x5(%bp) // pointer, + sub %al,0x2(%bp) // block count + ja read // If not done +read.6: retw // To caller +read.7: testb $FL_PACKET,%cs:MEM_REL+flags-start // LBA support enabled? + jz ereturn // No, so return an error + mov $0x55aa,%bx // Magic + push %dx // Save + movb $0x41,%ah // BIOS: Check + int $0x13 // extensions present + pop %dx // Restore + jc return // If error, return an error + cmp $0xaa55,%bx // Magic? + jne ereturn // No, so return an error + testb $0x1,%cl // Packet interface? + jz ereturn // No, so return an error + mov %bp,%si // Disk packet + movb $0x42,%ah // BIOS: Extended + int $0x13 // read + retw // To caller + +// Messages + +msg_read: .asciz "Read" +msg_part: .asciz "Boot" + +prompt: .asciz " error\r\n" + +flags: .byte FLAGS // Flags + + .org PRT_OFF,0x90 + +// Partition table + + .fill 0x30,0x1,0x0 +part4: .byte 0x80, 0x00, 0x01, 0x00 + .byte 0xa5, 0xfe, 0xff, 0xff + .byte 0x00, 0x00, 0x00, 0x00 + .byte 0x50, 0xc3, 0x00, 0x00 // 50000 sectors long, bleh + + .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 0000000..bb58353 --- /dev/null +++ b/sys/boot/i386/boot2/boot2.c @@ -0,0 +1,645 @@ +/*- + * 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/diskmbr.h> +#include <sys/dirent.h> +#include <sys/reboot.h> + +#include <machine/bootinfo.h> +#include <machine/elf.h> + +#include <stdarg.h> + +#include <a.out.h> + +#include <btxv86.h> + +#include "boot2.h" +#include "lib.h" + +#define IO_KEYBOARD 1 +#define IO_SERIAL 2 + +#define SECOND 18 /* Circa that many ticks in a second. */ + +#define RBX_ASKNAME 0x0 /* -a */ +#define RBX_SINGLE 0x1 /* -s */ +/* 0x2 is reserved for log2(RB_NOSYNC). */ +/* 0x3 is reserved for log2(RB_HALT). */ +/* 0x4 is reserved for log2(RB_INITNAME). */ +#define RBX_DFLTROOT 0x5 /* -r */ +#define RBX_KDB 0x6 /* -d */ +/* 0x7 is reserved for log2(RB_RDONLY). */ +/* 0x8 is reserved for log2(RB_DUMP). */ +/* 0x9 is reserved for log2(RB_MINIROOT). */ +#define RBX_CONFIG 0xa /* -c */ +#define RBX_VERBOSE 0xb /* -v */ +#define RBX_SERIAL 0xc /* -h */ +#define RBX_CDROM 0xd /* -C */ +/* 0xe is reserved for log2(RB_POWEROFF). */ +#define RBX_GDB 0xf /* -g */ +#define RBX_MUTE 0x10 /* -m */ +/* 0x11 is reserved for log2(RB_SELFTEST). */ +/* 0x12 is reserved for boot programs. */ +/* 0x13 is reserved for boot programs. */ +#define RBX_PAUSE 0x14 /* -p */ +#define RBX_NOINTR 0x1c /* -n */ +/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */ +#define RBX_DUAL 0x1d /* -D */ +#define RBX_PROBEKBD 0x1e /* -P */ +/* 0x1f is reserved for log2(RB_BOOTINFO). */ + +/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */ +#define RBX_MASK 0x2005ffff + +#define PATH_CONFIG "/boot.config" +#define PATH_BOOT3 "/boot/loader" +#define PATH_KERNEL "/kernel" + +#define ARGS 0x900 +#define NOPT 12 +#define NDEV 3 +#define MEM_BASE 0x12 +#define MEM_EXT 0x15 +#define V86_CY(x) ((x) & 1) +#define V86_ZR(x) ((x) & 0x40) + +#define DRV_HARD 0x80 +#define DRV_MASK 0x7f + +#define TYPE_AD 0 +#define TYPE_DA 1 +#define TYPE_MAXHARD TYPE_DA +#define TYPE_FD 2 + +extern uint32_t _end; + +static const char optstr[NOPT] = "DhaCgmnPprsv"; +static const unsigned char flags[NOPT] = { + RBX_DUAL, + RBX_SERIAL, + RBX_ASKNAME, + RBX_CDROM, + RBX_GDB, + RBX_MUTE, + RBX_NOINTR, + RBX_PROBEKBD, + RBX_PAUSE, + 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 struct dsk { + unsigned drive; + unsigned type; + unsigned unit; + unsigned slice; + unsigned part; + unsigned start; + int init; +} dsk; +static char cmd[512]; +static char kname[1024]; +static uint32_t opts; +static struct bootinfo bootinfo; +static uint8_t ioctrl = IO_KEYBOARD; + +void exit(int); +static void load(void); +static int parse(void); +static int xfsread(ino_t, void *, size_t); +static int dskread(void *, unsigned, unsigned); +static void printf(const char *,...); +static void putchar(int); +static uint32_t memsize(void); +static int drvread(void *, unsigned, unsigned); +static int keyhit(unsigned); +static int xputc(int); +static int xgetc(int); +static int getc(int); + +#define memcpy __builtin_memcpy + +static inline int +strcmp(const char *s1, const char *s2) +{ + for (; *s1 == *s2 && *s1; s1++, s2++); + return (unsigned char)*s1 - (unsigned char)*s2; +} + +#include "ufsread.c" + +static int +xfsread(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 uint32_t +memsize(void) +{ + v86.addr = MEM_EXT; + v86.eax = 0x8800; + v86int(); + return v86.eax; +} + +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.addr = 0x10; + v86.eax = 0xe00 | (c & 0xff); + v86.ebx = 0x7; + v86int(); +} + +int +main(void) +{ + int autoboot; + ino_t ino; + + dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); + v86.ctl = V86_FLAGS; + dsk.drive = *(uint8_t *)PTOV(ARGS); + dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; + dsk.unit = dsk.drive & DRV_MASK; + dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; + bootinfo.bi_version = BOOTINFO_VERSION; + bootinfo.bi_size = sizeof(bootinfo); + bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ + bootinfo.bi_extmem = memsize(); + bootinfo.bi_memsizes_valid++; + + /* Process configuration file */ + + autoboot = 1; + + if ((ino = lookup(PATH_CONFIG))) + fsread(ino, cmd, sizeof(cmd)); + + if (*cmd) { + printf("%s: %s", PATH_CONFIG, cmd); + if (parse()) + autoboot = 0; + /* 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 (autoboot && !*kname) { + memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); + if (!keyhit(3*SECOND)) { + load(); + memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); + } + } + + /* Present the user with the boot2 prompt. */ + + for (;;) { + printf("\nFreeBSD/i386 boot\n" + "Default: %u:%s(%u,%c)%s\n" + "boot: ", + dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, + 'a' + dsk.part, kname); + if (ioctrl & IO_SERIAL) + sio_flush(); + if (!autoboot || keyhit(5*SECOND)) + getstr(); + else + 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; + 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(kname))) { + if (!ls) + printf("No %s\n", kname); + return; + } + if (xfsread(ino, &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); + 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; + p += hdr.ex.a_data + 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); + if (hdr.ex.a_syms) { + if (xfsread(ino, p, hdr.ex.a_syms)) + return; + p += hdr.ex.a_syms; + if (xfsread(ino, p, sizeof(int))) + return; + x = *(uint32_t *)p; + p += sizeof(int); + x -= sizeof(int); + if (xfsread(ino, p, x)) + return; + p += x; + } + } else { + fs_off = hdr.eh.e_phoff; + for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { + 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++) { + memcpy(p, &es[i].sh_size, sizeof(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); + bootinfo.bi_kernelname = VTOP(kname); + bootinfo.bi_bios_dev = dsk.drive; + __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), + MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part), + 0, 0, 0, VTOP(&bootinfo)); +} + +static int +parse() +{ + char *arg = cmd; + char *p, *q; + unsigned int drv; + int c, i; + + while ((c = *arg++)) { + if (c == ' ' || c == '\t' || c == '\n') + continue; + for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; 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]; + } + if (opts & 1 << RBX_PROBEKBD) { + i = *(uint8_t *)PTOV(0x496) & 0x10; + printf("Keyboard: %s\n", i ? "yes" : "no"); + if (!i) + opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL; + opts &= ~(1 << RBX_PROBEKBD); + } + ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) : + opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD; + if (ioctrl & IO_SERIAL) + sio_init(); + } 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 > NDOSPART) + 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.drive = (dsk.type <= TYPE_MAXHARD + ? DRV_HARD : 0) + drv; + dsk_meta = 0; + } + if ((i = p - arg - !*(p - 1))) { + if ((size_t)i >= sizeof(kname)) + return -1; + memcpy(kname, arg, i + 1); + } + } + arg = p; + } + return 0; +} + +static int +dskread(void *buf, unsigned lba, unsigned nblk) +{ + struct dos_partition *dp; + struct disklabel *d; + char *sec; + unsigned sl, i; + + if (!dsk_meta) { + sec = dmadat->secbuf; + dsk.start = 0; + if (drvread(sec, DOSBBSECTOR, 1)) + return -1; + dp = (void *)(sec + DOSPARTOFF); + sl = dsk.slice; + if (sl < BASE_SLICE) { + for (i = 0; i < NDOSPART; i++) + if (dp[i].dp_typ == DOSPTYP_386BSD && + (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { + sl = BASE_SLICE + i; + if (dp[i].dp_flag & 0x80 || + dsk.slice == COMPATIBILITY_SLICE) + break; + } + if (dsk.slice == WHOLE_DISK_SLICE) + dsk.slice = sl; + } + if (sl != WHOLE_DISK_SLICE) { + if (sl != COMPATIBILITY_SLICE) + dp += sl - BASE_SLICE; + if (dp->dp_typ != DOSPTYP_386BSD) { + printf("Invalid %s\n", "slice"); + return -1; + } + dsk.start = dp->dp_start; + } + if (drvread(sec, dsk.start + LABELSECTOR, 1)) + return -1; + d = (void *)(sec + LABELOFFSET); + if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { + if (dsk.part != RAW_PART) { + printf("Invalid %s\n", "label"); + return -1; + } + } else { + if (!dsk.init) { + if (d->d_type == DTYPE_SCSI) + dsk.type = TYPE_DA; + dsk.init++; + } + if (dsk.part >= d->d_npartitions || + !d->d_partitions[dsk.part].p_size) { + printf("Invalid %s\n", "partition"); + return -1; + } + dsk.start += d->d_partitions[dsk.part].p_offset; + dsk.start -= d->d_partitions[RAW_PART].p_offset; + } + } + return drvread(buf, dsk.start + lba, nblk); +} + +static void +printf(const char *fmt,...) +{ + va_list ap; + 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, unsigned nblk) +{ + static unsigned c = 0x2d5c7c2f; + + printf("%c\b", c = c << 8 | c >> 24); + v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; + v86.addr = XREADORG; /* call to xread in boot1 */ + v86.es = VTOPSEG(buf); + v86.eax = lba; + v86.ebx = VTOPOFF(buf); + v86.ecx = lba >> 16; + v86.edx = nblk << 8 | dsk.drive; + v86int(); + v86.ctl = V86_FLAGS; + if (V86_CY(v86.efl)) { + printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); + return -1; + } + return 0; +} + +static int +keyhit(unsigned ticks) +{ + uint32_t t0, t1; + + if (opts & 1 << RBX_NOINTR) + return 0; + t0 = 0; + for (;;) { + if (xgetc(1)) + return 1; + t1 = *(uint32_t *)PTOV(0x46c); + if (!t0) + t0 = t1; + if (t1 < t0 || t1 >= t0 + ticks) + return 0; + } +} + +static int +xputc(int c) +{ + if (ioctrl & IO_KEYBOARD) + putc(c); + if (ioctrl & IO_SERIAL) + sio_putc(c); + return c; +} + +static int +xgetc(int fn) +{ + if (opts & 1 << RBX_NOINTR) + return 0; + for (;;) { + if (ioctrl & IO_KEYBOARD && getc(1)) + return fn ? 1 : getc(0); + if (ioctrl & IO_SERIAL && sio_ischar()) + return fn ? 1 : sio_getc(); + if (fn) + return 0; + } +} + +static int +getc(int fn) +{ + v86.addr = 0x16; + v86.eax = fn << 8; + v86int(); + return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); +} diff --git a/sys/boot/i386/boot2/lib.h b/sys/boot/i386/boot2/lib.h new file mode 100644 index 0000000..4bb93cf --- /dev/null +++ b/sys/boot/i386/boot2/lib.h @@ -0,0 +1,24 @@ +/* + * 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. + */ + +/* + * $FreeBSD$ + */ + +void sio_init(void); +void sio_flush(void); +void sio_putc(int); +int sio_getc(void); +int sio_ischar(void); diff --git a/sys/boot/i386/boot2/sio.S b/sys/boot/i386/boot2/sio.S new file mode 100644 index 0000000..e298205 --- /dev/null +++ b/sys/boot/i386/boot2/sio.S @@ -0,0 +1,80 @@ +# +# 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. +# + +# $FreeBSD$ + + .set SIO_PRT,SIOPRT # Base port + .set SIO_FMT,SIOFMT # 8N1 + .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD + + .globl sio_init + .globl sio_flush + .globl sio_putc + .globl sio_getc + .globl sio_ischar + +# void sio_init(void) + +sio_init: movw $SIO_PRT+0x3,%dx # Data format reg + movb $SIO_FMT|0x80,%al # Set format + outb %al,(%dx) # and DLAB + pushl %edx # Save + subb $0x3,%dl # Divisor latch reg + movw $SIO_DIV,%ax # Set + outw %ax,(%dx) # BPS + popl %edx # Restore + movb $SIO_FMT,%al # Clear + outb %al,(%dx) # DLAB + incl %edx # Modem control reg + movb $0x3,%al # Set RTS, + outb %al,(%dx) # DTR + incl %edx # Line status reg + +# void sio_flush(void) + +sio_flush.0: call sio_getc.1 # Get character +sio_flush: call sio_ischar # Check for character + jnz sio_flush.0 # Till none + ret # To caller + +# void sio_putc(int c) + +sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg + xor %ecx,%ecx # Timeout + movb $0x40,%ch # counter +sio_putc.1: inb (%dx),%al # Transmitter + testb $0x20,%al # buffer empty? + loopz sio_putc.1 # No + jz sio_putc.2 # If timeout + movb 0x4(%esp,1),%al # Get character + subb $0x5,%dl # Transmitter hold reg + outb %al,(%dx) # Write character +sio_putc.2: ret $0x4 # To caller + +# int sio_getc(void) + +sio_getc: call sio_ischar # Character available? + jz sio_getc # No +sio_getc.1: subb $0x5,%dl # Receiver buffer reg + inb (%dx),%al # Read character + ret # To caller + +# int sio_ischar(void) + +sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register + xorl %eax,%eax # Zero + inb (%dx),%al # Received data + andb $0x1,%al # ready? + ret # To caller diff --git a/sys/boot/i386/boot2/sio.s b/sys/boot/i386/boot2/sio.s new file mode 100644 index 0000000..e298205 --- /dev/null +++ b/sys/boot/i386/boot2/sio.s @@ -0,0 +1,80 @@ +# +# 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. +# + +# $FreeBSD$ + + .set SIO_PRT,SIOPRT # Base port + .set SIO_FMT,SIOFMT # 8N1 + .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD + + .globl sio_init + .globl sio_flush + .globl sio_putc + .globl sio_getc + .globl sio_ischar + +# void sio_init(void) + +sio_init: movw $SIO_PRT+0x3,%dx # Data format reg + movb $SIO_FMT|0x80,%al # Set format + outb %al,(%dx) # and DLAB + pushl %edx # Save + subb $0x3,%dl # Divisor latch reg + movw $SIO_DIV,%ax # Set + outw %ax,(%dx) # BPS + popl %edx # Restore + movb $SIO_FMT,%al # Clear + outb %al,(%dx) # DLAB + incl %edx # Modem control reg + movb $0x3,%al # Set RTS, + outb %al,(%dx) # DTR + incl %edx # Line status reg + +# void sio_flush(void) + +sio_flush.0: call sio_getc.1 # Get character +sio_flush: call sio_ischar # Check for character + jnz sio_flush.0 # Till none + ret # To caller + +# void sio_putc(int c) + +sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg + xor %ecx,%ecx # Timeout + movb $0x40,%ch # counter +sio_putc.1: inb (%dx),%al # Transmitter + testb $0x20,%al # buffer empty? + loopz sio_putc.1 # No + jz sio_putc.2 # If timeout + movb 0x4(%esp,1),%al # Get character + subb $0x5,%dl # Transmitter hold reg + outb %al,(%dx) # Write character +sio_putc.2: ret $0x4 # To caller + +# int sio_getc(void) + +sio_getc: call sio_ischar # Character available? + jz sio_getc # No +sio_getc.1: subb $0x5,%dl # Receiver buffer reg + inb (%dx),%al # Read character + ret # To caller + +# int sio_ischar(void) + +sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register + xorl %eax,%eax # Zero + inb (%dx),%al # Received data + andb $0x1,%al # ready? + ret # To caller |