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: