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: