[comp.os.minix] Booting MINIX from the hard disk

n0ano@wldrdg.UUCP (Don Dugger) (04/20/87)

This is the first technique I used to boot MINIX from
the hard disk.

First partition the disk into 4 slices:
	Partition 1 - MS/DOS, 6 Mbytes
	Partition 2 - MINIX /usr, 3 Mbytes
	Partition 3 - MINIX boot image, 370 Kbytes
	Partition 4 - MINIX /, 370 Kbytes

Change line 61 of file `h/const.h' from
	#define BOOT_DEV (dev_nr)  512
to
	#define BOOT_DEV (dev_nr)  0x304
and remake the `fs' process.

In routine `patch1' of file `tools/build.c' change the four lines
	ubuf[(SECTOR_SIZE/2) - 4] = sectrs + 1;
	ubuf[(SECTOR_SIZE/2) - 3] = ds;
	ubuf[(SECTOR_SIZE/2) - 2] = ip;
	ubuf[(SECTOR_SIZE/2) - 1] = cs;
to
	ubuf[(SECTOR_SIZE/2) - 5] = sectrs + 1;
	ubuf[(SECTOR_SIZE/2) - 4] = ds;
	ubuf[(SECTOR_SIZE/2) - 3] = ip;
	ubuf[(SECTOR_SIZE/2) - 2] = cs;
	ubuf[(SECTOR_SIZE/2) - 1] = 0xAA55;
and remake the `build' program.

Replace file `tools/bootblok.s' with the `bootblok.s' included at
the end of this article and remake the boot block.

Remake the MINIX boot image except build it to `/dev/hd3' rather than
`/dev/fd0'.

Put the root floppy in drive zero and execute the command
	dd if=/dev/fd0 of=/dev/hd4 bs=18b

After doing all of this, booting from Partition 3 will boot MINIX
entirely from the hard disk.

The symbol `LOCYL' in `bootblok.s' is the first cylinder for the
boot partition, just put in the appropriate value for your system.
The code at `set2:' assumes the winchester has 4 heads.  If your
drive is different you'll have to change the code appropriately.
Likewise, you'll have to change the declaration for `tracksiz'
if your disk has other than 17 sectors/track.

Since `bootblok.s' uses the BIOS to read the disk these changes should
work on most machines except for one possible gotcha.  The boot ROM's on
my Zenith 150 expect a Winchester boot block to contain the magic number
0xAA55 at location 510 and 511, otherwise the system won't boot (this is
why I had to change `build.c' to move it's patches down and add in the
magic number).  Other machines might have different ways of identifying
boot blocks which would require slight changes to `bootblok.s' and
`build.c'.

Technique 2:

The above scheme works fine except that it uses 3 partitions, which leaves
none free since I need the MS/DOS partition.  The current technique I
use is to put the boot image at the end of Partition 2, thereby freeing
up Partition 3.  To do this, do everything described above with the following
changes:

1.  Make the `/usr' file system smaller than its partition by at
    least 200K.  If you already have a `/usr' file system you can
    probably still shorten its size.  Just write a small C program
    that reads the super block, changes word 2 (the filesystem size)
    to the appropriate value and then writes the super block back out.
    (Make sure the `/usr' file system is unmounted when you do this.)
    Re-boot and run `fsck' to clean up the file system.

2.  In `bootblok.s' change `LOCYL' to be the first cylinder after the
    end of the `/usr' file system.  On my system there are 34 1K
    blocks per cylinder.  Since I had allocated 100 cynliners for the
    `/usr' partition, I assigned 94 cylinders (3196 1K blocks) to the
    `/usr' file system, leaving 6 cylinders (204 1K blocks) for the
    MINIX boot image.  Since the partition starts at cylinder 182 the
    new value for LOCYL is 276.

3.  Re-make `bootblok' and build the boot image to the file `image.bt'.

4.  Use `dd' to copy the boot image out to the end of the `/usr'
    partition.  Using my system as an example, the command is
	dd if=image.bt of=/dev/hd2 bs=1k seek=3196

5.  Use `dd' to copy the boot block:
	dd if=image.bt of=/dev/hd2 bs=1b count=1

6.  Boot the system off of partition 2 and everything should be OK.

Don Dugger
Wildridge Consulting
...nbires!onecom!wldrdg!n0ano

---- bootblok.s : cut here -----
| When the PC is powered on, it reads the first block from the floppy
| disk into address 0x7C00 and jumps to it.  This boot block must contain
| the boot program in this file.  The boot program first copies itself to
| address 192K - 512 (to get itself out of the way).  Then it loads the 
| operating system from the boot diskette into memory, and then jumps to fsck.
| Loading is not trivial because the PC is unable to read a track into
| memory across a 64K boundary, so the positioning of everything is critical.
| The number of sectors to load is contained at address 504 of this block.
| The value is put there by the build program after it has discovered how
| big the operating system is.  When the bootblok program is finished loading,
| it jumps indirectly to the program (fsck) which address is given by the
| last two words in the boot block. 
|
| Summary of the words patched into the boot block by build:
| Word at 502: # sectors to load
| Word at 504: # DS value for fsck
| Word at 506: # PC value for fsck
| Word at 508: # CS value for fsck
| Word at 510: Magic boot block #
|
| This version of the boot block must be assembled without separate I & D
| space.  

        LOADSEG = 0x0060         | here the boot block will start loading
        BIOSSEG = 0x07C0         | here the boot block itself is loaded
        BOOTSEG = 0x2FE0         | here it will copy itself (192K-512b)
        DSKBASE = 120            | 120 = 4 * 0x1E = ptr to disk parameters
	LOCYL   = 282		 | Low cylinder for boot partition

final   = 502
fsck_ds = 504
fsck_pc = 506
fsck_cs = 508
magic	= 510


.globl begtext, begdata, begbss, endtext, enddata, endbss  | asld needs these
.text
begtext:
.data
begdata:
.bss
begbss:
.text

| copy bootblock to bootseg
        mov     ax,#BIOSSEG
        mov     ds,ax
        xor     si,si           | ds:si - original block
        mov     ax,#BOOTSEG
        mov     es,ax
        xor     di,di           | es:di - new block
        mov     cx,#256         | #  words to move
        rep     
	movw 		        | copy loop

| start boot procedure
        jmpi    start,BOOTSEG   | set cs to bootseg

start:
        mov     dx,cs
        mov     ds,dx           | set ds to cs
        xor     ax,ax
        mov     es,ax           | set es to 0
        mov     ss,ax           | set ss to 0
        mov     sp,#1024        | initialize sp (top of vector table)

| initialize disk parameters
	mov	ax,#pcpar	| tenatively assume 1.2M diskette
	seg	es
	mov	DSKBASE,ax	
	seg	es
	mov	DSKBASE+2,dx

| print greeting
	mov 	ax,#2		| reset video
	int  	0x10
        mov     ax,#0x0200	| BIOS call in put cursor in ul corner
        xor     bx,bx
        xor     dx,dx
        int     0x10
        mov     bx,#greet
        call    print

| Load the operating system from diskette
load:
	call	setreg		| set up ah, cx, dx
	mov	bx,disksec	| bx = number of next sector to read
	add	bx,#2		| diskette sector 1 goes at 1536 ("sector" 3)
	shl	bx,#1		| multiply sector number by 32
	shl	bx,#1		| ditto
	shl	bx,#1		| ditto
	shl	bx,#1		| ditto
	shl	bx,#1		| ditto
	mov	es,bx		| core address is es:bx (with bx = 0)
	xor	bx,bx		| see above
	add	disksec,ax	| ax tells how many sectors to read
	orb	cl,xtra		| cylinder high bits
	movb	ah,#2		| opcode for read
	int	0x13		| call the BIOS for a read
	jb	error		| jump on diskette error
	mov	ax,disksec	| see if we are done loading
	cmp	ax,final	| ditto
	jb	load		| jump if there is more to load

| Loading done.  Finish up.
        cli
	mov	bx,tracksiz	| fsck expects # sectors/track in bx
        mov     ax,fsck_ds      | set segment registers
        mov     ds,ax           | when sep I&D DS != CS
        mov     es,ax           | otherwise they are the same.
        mov     ss,ax           | words 504 - 510 are patched by build

	seg cs
	jmpi	@fsck_pc	| jmp to fsck

| Given the number of the next disk block to read, disksec, compute the
| cylinder, sector, head, and number of sectors to read as follows:
| ah = # sectors to read;  cl = sector #;  ch = cyl;  dh = head; dl = 0
setreg:	
	mov	si,tracksiz	| 9 (PC) or 15 (AT) sectors per track
	mov 	ax,disksec	| ax = next sector to read
	xor	dx,dx		| dx:ax = 32-bit dividend
	div	si		| divide sector # by track size
	mov	cx,ax		| cx = track #; dx = sector (0-origin)
	mov	bx,dx		| bx = sector number (0-origin)
	mov	ax,disksec	| ax = next sector to read
	add	ax,si		| ax = last sector to read + 1
	dec	ax		| ax = last sector to read
	xor	dx,dx		| dx:ax = 32-bit dividend
	div	tracksiz	| divide last sector by track size
	cmpb	al,cl		| is starting track = ending track
	je	set1		| jump if whole read on 1 cylinder
	sub	si,dx		| compute lower sector count
	dec	si		| si = # sectors to read

| Check to see if this read crosses a 64K boundary (128 sectors).
| Such calls must be avoided.  The BIOS gets them wrong.
set1:	mov	ax,disksec	| ax = next sector to read
	add	ax,#2		| disk sector 1 goes in core sector 3
	mov	dx,ax		| dx = next sector to read
	add	dx,si		| dx = one sector beyond end of read
	dec	dx		| dx = last sector to read
	shl	ax,#1		| ah = which 64K bank does read start at
	shl	dx,#1		| dh = which 64K bank foes read end in
	cmpb	ah,dh		| ah != dh means read crosses 64K boundary
	je	set2		| jump if no boundary crossed
	shrb	dl,#1		| dl = excess beyond 64K boundary
	xorb	dh,dh		| dx = excess beyond 64K boundary
	sub	si,dx		| adjust si
	dec	si		| si = number of sectors to read

set2:	mov	ax,si		| ax = number of sectors to read
	xor	dx,dx		| dh = head, dl = drive
	movb	dh,cl		| dh = track
	andb	dh,#0x03	| dh = head
	shr	cx,#1		| get cylinder #
	shr	cx,#1
	add	cx,#LOCYL	| low cylinder for boot partition
	push	cx
	xorb	cl,cl
	shr	cx,#1
	shr	cx,#1		| excess cylinder goes into al
	movb	xtra,cl
	pop	cx	
	movb	ch,cl		| ch = track to read
	movb	cl,bl		| cl = sector number (0-origin)
	incb	cl		| cl = sector number (1-origin)
	movb	dl,#0x80	| dl = driver number (80=hd)
	ret			| return values in ax, cx, dx


|-------------------------------+
|    error & print routines     |
|-------------------------------+

error:
        push    ax
        mov     bx,#fderr
        call    print           | print msg
	xor	cx,cx
err1:	mul	0		| delay
	loop	err1
	int	0x19


print:                          | print string (bx)
        movb	al,(bx)	        | al contains char to be printed
        testb   al,al           | null char?
        jne     prt1            | no
        ret                     | else return
prt1:   movb    ah,*14          | 14 = print char
        inc     bx              | increment string pointer
        push    bx              | save bx
        movb    bl,*1           | foreground color
	xorb	bh,bh		| page 0
        int     0x10            | call BIOS VIDEO_IO
        pop     bx              | restore bx
        jmp     print           | next character



disksec:.word 1
xtra:	.byte	0
tracksiz:	.word 17	| changed to 9 for 360K diskettes
pcpar:	.byte	0xDF, 0x02, 25, 2, 9, 0x2A, 0xFF, 0x50, 0xF6, 1, 3   | for PC
atpar:	.byte	0xDF, 0x02, 25, 2,15, 0x1B, 0xFF, 0x54, 0xF6, 1, 8   | for AT

fderr:	.asciz	"Read error.  Automatic reboot.\r\n"
greet:	.asciz "\rBooting MINIX 1.1\r\n"



| Don't forget that words 504 - 510 are filled in by build.  The regular
| code had better not get that far.
.text
endtext:
.data
enddata:
.bss
endbss: