summaryrefslogtreecommitdiffstats
path: root/sys/boot/i386/boot2
diff options
context:
space:
mode:
Diffstat (limited to 'sys/boot/i386/boot2')
-rw-r--r--sys/boot/i386/boot2/Makefile123
-rw-r--r--sys/boot/i386/boot2/boot1.S365
-rw-r--r--sys/boot/i386/boot2/boot1.s365
-rw-r--r--sys/boot/i386/boot2/boot2.c645
-rw-r--r--sys/boot/i386/boot2/lib.h24
-rw-r--r--sys/boot/i386/boot2/sio.S80
-rw-r--r--sys/boot/i386/boot2/sio.s80
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
OpenPOWER on IntegriCloud