Subject: RFC: start.S
To: None <port-i386@NetBSD.ORG>
From: VaX#n8 <vax@linkdead.paranoia.com>
List: port-i386
Date: 06/18/1996 23:27:25
I accidentally posted my last rough draft to current-users
(or so I am told -- I have this black period just after I
started coding in assembly, next thing I knew it was hours later.
Some might call it "hack mode").

To summarize, I'm looking for comments, criticisms, whatever.
The geometry-compensation stuff isn't quite done yet.  The other
things are marked TODO.

It's just a stage1 booter right now.  stage2 will have to come
in another block (which leaves plenty of room for improvement, no?)
Occam's razor applied heavily to original source.
Many improvements borrowed from GRUB.

Among other things, it should be able to boot from extended partitions
via a boot manager (once I test&debug it; my second machine split into two,
and each is missing a part).  You should now be able to debug to any serial
port supported by the BIOS, not just COM1/serial0.

Debugging code appears to work.  The floppy portion appears to work.

Be nice, it's my first x86 asm project :)

/*	$NetBSD: start.S,v 1.12 1995/01/18 17:34:18 mycroft Exp $	*/

/* heavily edited by VaX#n8 (vax@linkdead.paranoia.com) 16 Jun 1996 */

/*
 * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
 *
 * Mach Operating System
 * Copyright (c) 1992, 1991 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

/*
  Copyright 1988, 1989, 1990, 1991, 1992 
   by Intel Corporation, Santa Clara, California.

                All Rights Reserved

Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appears in all
copies and that both the copyright notice and this permission notice
appear in supporting documentation, and that the name of Intel
not be used in advertising or publicity pertaining to distribution
of the software without specific, written prior permission.

INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <machine/asm.h>

BOOTSEG		=	0x0100	/* boot2 will be loaded here (below 640K) */
BOOTSTACK	=	0xfffc	/* boot stack */
SIGNATURE	=	0xaa55	/* magic num at end of partition table */
LOADSZ		=	15	/* size of unix boot */
PARTSTART	=	0x1be	/* starting address of partition table */
NUMPART		=	4	/* number of partitions in partition table */
PARTSZ		=	16	/* each partition table entry is 16 bytes */
BSDPART		=	0xA5	/* value of boot_ind, means bsd partition */
BOOTABLE	=	0x80	/* value of boot_ind, means bootable part'n */
BOOT2		=	0x200	/* offset to boot2 from $BOOTSEG */
CHAR_S		=	'S'
CHAR_F		=	'F'
CHAR_H		=	'H'
CHAR_D		=	'D'
CHAR_L		=	'L'

	.file "start.S"
	.code16

	/* boot1 is automatically located at 0x0:7c00 by your BIOS int 0x19 */
ENTRY(boot1)
	cli	/* disable interrupts until we set up a stack */

	/* set up %ss:%sp */
	/* TODO: determine if I can just load %ss directly */
	movw	$BOOTSEG, %ax
	movw	%ax, %ss
	movw	$BOOTSTACK, %sp

	sti	/* enable interrupts */

	/* set up %es, (where we will load boot2 and possibly MBR to) */
	movw	%ax, %es

#ifdef	SERIAL
	/* BIOS int 0x14 service 0x00 : initialize serial port */
	/* %al = port parameters */
	/* %dx = port number */
	/* results in %ax */
	pushw	%dx
	movw	$0x00e3, %ax	/* port to 9600N81 */
	movw	$SERIAL, %dx	/* port (%dx=0) */
	int	$0x14
	popw	%dx
	/* TODO: maybe test for errors here */

#endif	/* SERIAL */

#ifdef	DEBUG
	movb	$CHAR_S, %al
	call	print_char
#endif

#ifdef	GEOMETRY_COMPENSATION
	/* bootstrap passes us drive number in %dl */
	pushw	%dx

	/* BIOS int 0x13 service 0x08 : get drive parameters */
	/* results in %ah, %bl, %cx, %dx, %es:%di */
	movb	$8, %ah
	int	$0x13
	jnc	worked

	/* call failed due to funky BIOS, assume CHS of 80:2:15 */
	movw	$0x4f0f, %cx	/* max_cyl_num <- 79, max_sec_num <- 15 */
	movb	$1, %dh		/* max_head_num <- 1 */

worked:
	/* get number of heads */
	xorb	%ah, %ah	/* %ah <- 0 */
	movb	%dh, %al	/* %al <- (max_head_num) */
	incw	%ax		/* %ax <- (number_of_heads) */
	movw	%ax, heads

	/* get number of cylinders */
	xorb	%ah, %ah	/* %ah <- 0 */
	movb	%cl, %al
	shlw	$2, %ax		/* %ah <- (max_cyl_num:8-9) */
	movb	%ch, %al	/* %al <- (max_cyl_num:0-7) */
	incw	%ax		/* %ax <- (number_of_cyls) */
	movw	%ax, cylinders

	/* get number of sectors */
	movb	%cl, %al
	andb	$0x3F, %al
	movb	%al, sectors

	popw	%dx	/* restore drive number */
#endif	/* GEOMETRY_COMPENSATION */

	/* test drive type (fd or hd) */
	cmpb	$0x80, %dl
	jae	hd

fd:     /* start reading at 0:0:1 */
#ifdef	DEBUG
	movb	$CHAR_F, %al
	call	print_char
#endif

	movw	$0x0001, %cx	/* cyl 0, sector 1 */
	xorb	%dh, %dh	/* head (%dh=0) */
	jmp	load

hd:	/* look at the partition table */
#ifdef	DEBUG
	movb	$CHAR_H, %al
	call	print_char
#endif
	/* see if the partition table entry is pointed to by %ds:%si */
	/* for compatibility with OS-BS and many other bootloaders */
	/* this enables us to boot from extended partitions or 2nd disk */

	/* a partition table entry: */
	/* byte	meaning */
	/* 0	active */
	/* 1-3	starting CHS */
	/* 4	system type id */

	/* prepare for a successful comparison: */

	/* TODO: fix gas for these addressing modes */
	/* movb	1(%si), %dh */	/* starting head */
	.byte	0x8a, 0x74, 0x01
	/* movw	2(%si), %cx */	/* starting cyl/sector */
	.byte	0x8b, 0x4c, 0x02
	/* movb	4(%si), %al */	/* system id / partition type */
	.byte	0x8a, 0x44, 0x04
	cmpb	$BSDPART, %al

	/* jump if we know where our partition is */
	je	load

	/* no; time to search the disk */

#ifdef	DEBUG
	movb	$CHAR_D, %al
	call	print_char
#endif

	/* BIOS int 0x13 service 0x02 : read from disk */
	/* results in %ax */
	movw	$0x0201, %ax	/* will read (%al=1) sector */
	xorw	%bx, %bx	/* to %es:%bx (where %bx=0) */
	movw	$0x0001, %cx	/* cyl=0, sector=1 */
	movb	%dh, %dh	/* head (%dh=0) */
	int	$0x13
	jc	read_error

	/* find the first BSD partition */
	movw	$PARTSTART, %bx
	movw	$NUMPART, %cx
again:
	/* TODO: again, fix gas for this addressing mode */
	/* movb    %es:4(%bx), %al */
	.byte	0x26, 0x8a, 0x47, 0x04
	cmpb	$BSDPART, %al
	je	found
	addw	$PARTSZ, %bx
	loop	again
	/* could not find partition */
	/* TODO: search in extended partitions */
	movw	$enoboot, %si
	jmp	err_stop

found:
	/* TODO: again, stop gas from hallucinating wildly here */
	/* movb	%es:1(%bx), %dh */	/* head */
	.byte	0x26, 0x8a, 0x77, 0x01
	movw	%es:2(%ebx), %cx	/* sect, cyl */

load:
#ifdef	DEBUG
	movb	$CHAR_L, %al
	call	print_char
#endif
	/* TODO: add one to the sector number (with carry) to */
	/* avoid loading "boot sector" (usually this program) */
	/* over again -- probably requires GEOMETRY_COMPENSATION */

	/* BIOS int 0x13 service 0x02 : read sectors from disk */
	/* %al = number of sectors */
	/* %ch = cylinder */
	/* %cl = sector (bits 6-7 are high bits of cyl on hd) */
	/* %dh = head */
	/* %dl = drive (0x80 for hard disk, 0x0 for floppy disk) */
	/* %es:%bx = segment:offset of buffer */
	/* results in %ax, carry flag = clear if ok/set if error */

	movw	$0x200 | LOADSZ, %ax	/* read $LOADSZ blocks */
	xorw	%bx, %bx		/* put it at BOOTSEG:0x00 */
	int	$0x13
	jc	read_error

	jmp	stop	/* TODO: remove this when done testing */

	/* ljmp to the second stage boot loader (boot2). */
	/* After ljmp, %cs is BOOTSEG */
	ljmp	$BOOTSEG, $BOOT2

print_char:	/* write char in %al to console */
	/* %al = char to print */
	/* %ah = scratch (serial printchar retval) */

#ifndef SERIAL
	/* BIOS int 0x10 service 0x0e : video - teletype output */
	movb	$0x0e, %ah
	pushw	%bx
	movw	$0x001, %bx	/* page=%bh=0x00, forecolor=%bl=0x01 */
	int	$0x10
	popw	%bx
#else	/* SERIAL */
	/* BIOS int 0x14 service 0x01 : serial - write char to port */
	movb	$0x01, %ah
	pushw	%dx
	movw	$SERIAL, %dx	/* port=%dx */
	int	$0x14
	popw	%dx
#endif	/* SERIAL */
	ret

read_error:	/* create "read error" message and stop */
	movw	$eread, %si
	/* fall through: */
err_stop:	/* print a message in %ds:%si and stop */
	call	message
	jmp	stop

message:	/* write the error message in %ds:%si to console */
	/* %ds:%si = string to print */
	/* %al = scratch */
	/* %ah = scratch (serial char retval) */

	cld
#ifndef SERIAL
	pushw	%bx		/* save %bx */
#else	/* SERIAL */
	pushw	%dx		/* save %dx */
#endif 	/* SERIAL */

nextb:
	lodsb			/* load a byte into %al */
	testb	%al, %al	/* test for null terminator byte */
	jz	done		/* if null, we're done */
#ifndef SERIAL
	/* BIOS int 0x10 service 0x0e : video - teletype output */
	movb	$0x0e, %ah
	movw	$0x001, %bx	/* page=%bh=0x00, forecolor=%bl=0x01 */
	int	$0x10
#else	/* SERIAL */
	/* BIOS int 0x14 service 0x01 : serial - write char to port */
	movb	$0x01, %ah
	movw	$0x00, %dx	/* port=%dx=0x00 */
	int	$0x14
#endif	/* SERIAL */
	jmp	nextb

done:
#ifndef SERIAL
	popw	%bx		/* restore %bx */
#else	/* SERIAL */
	popw	%dx		/* restore %dx */
#endif 	/* SERIAL */
	ret

stop:	/* halt the machine */
	cli
	hlt

eread:		ASMSTR	"Read error\r\n"
enoboot:	ASMSTR	"No bootable partition\r\n"

/* scratch space */
#ifdef	GEOMETRY_COMPENSATION
sectors:	.byte	0
heads:		.word	0
cylinders:	.word	0
#endif	/* GEOMETRY_COMPENSATION */

endofcode:
	/* throw in a partition in case we are block0 as well */
	/* flag, head, sec, cyl, typ, ehead, esect, ecyl, start, len */
	. = _C_LABEL(boot1) + PARTSTART
	.byte	0x0,0,0,0,0,0,0,0
	.long	0,0
	.byte	0x0,0,0,0,0,0,0,0
	.long	0,0
	.byte	0x0,0,0,0,0,0,0,0
	.long	0,0
	.byte	BOOTABLE,0,1,0,BSDPART,255,255,255
	.long	0,50000
	/* the last 2 bytes in the sector 0 contain the signature */
	. = _C_LABEL(boot1) + 0x1fe
	.short	SIGNATURE
	. = _C_LABEL(boot1) + BOOT2