...making Linux just a little more fun! |
By Muhammad Torabi Dashti |
I was reading a fascinating article in LinuxGazette#77, Writing Your Own Toy OS-Part I by Krishnakumar [1]; where I saw an strange thing, two last bytes of boot sector should be 0x55AA! And as the paper's talk back proves I wasn't the only one to play with this magic number. Any way, I rewrote Krishnakumar's boot sector with nasm [2] (I don't know as86) and removed 0x55AA insertion line from write.c. Guess what happened? My PC booted up! So why Krishnakumar wrote that piece? Or more generally who will read and decide boot sector? The answer was straight, BIOS does! And another interesting fact was that IBM had published its XT BIOS's source code in XT Technical Reference [3]. So lets have a look at it: (I've made some changes to it, so it's not complete and original)
;---INT 19H BOOT_STRAP: ;SOME INITITIALIZATIONS MOV CX,3 ;RETRY COUNT H1: PUSH CX SUB DX,DX SUB AX,AX INT 13H ;INIT FLOPPY JC H2 MOV AX,0201H SUB DX,DX MOV ES,DX ;PREVIOUSLY BOOT_LOCN WAS DEFINED ;ORG 7C00H ;BOOT_LOCN LABEL FAR MOV BX, OFFSET BOOT_LOCN MOV CX,1 INT 13H ;READ FLOPPY'S SECTOR 0 H2: POP CX JNC H4 CMP AH,80H ;CHECK FOR TIMEOUT JZ H5 LOOP H1 JMP H5 H4: JMP BOOT_LOCN H5: ;TRY FIXED DISK SUB AX,AX SUB DX,DX INT 13H MOV CX,3 H6: PUSH CX MOV DX,0080H SUB AX,AX INT 13H JC H7 MOV AX,0201H SUB BX,BX MOV ES,BX MOV BX,OFFSET BOOT_LOCN MOV DX,80H MOV CX,1 INT 13H H7: POP CX JC H8 MOV AX, WORD PTR BOOT_LOCN+510D CMP AX,0AA55H ;MAGIC NUMBER! JZ H4 H8: LOOP H6 INT 18H ;EVERY THING FAILED!
OK! everything got clear. that 0x55AA is checked only if boot sector is loaded from fixed disk, so Krishnakumar used it to ensure compatibility. Also notice that any random bit string in sector 0 of a floppy is considered as boot sector and system runs it!
But I use a Windows 2000 besides my Linux and my floppies have always msdos (fat 12) file system so that both OSs can read them. Now setup a simple experiment: format a floppy using Windows (or any DOS based OS) and let the system boot up with your floppy. You'll see a message that indicates it's not a boot floppy and asks you to change it and press any key. This is the case when you format the floppy in Linux and put an msdos file system on it too (mkfs -V -t msdos /dev/fd0, could be the command or if you use KDE its floppy formatter utility can do the same). And things get more strange when you put an ext2 filesystem on the floppy (#mkfs -V -t ext2 /dev/fd0) . Your PC simply passes the floppy and control is transferred to fixed disk boot sector (e.g. LiLo). So what's the difference between these two formats and with our own boot floppy? I changed Krishnakumar's write.c and this read.c reads boot sector of a floppy and saves it in boot.sec file; also dumps it in a fairly formatted manner. If you want you can use Linux's own tools to do the same thing: to write your boot sector on the floppy (#dd if=boot.sec of=/dev/fd0 bs=1 count=512) and to read floppy's boot sector (#dd if=boot.sec of=/dev/fd0 bs=1 count=512 skip=0).
Have a look at ext2's boot sector. It's pure zero! but msdos's boot sector contains some commands. Lets disassemble it (I use ndisasm[2]):
1. insert an msdos-formatted floppy.
2. run "./read"
3. run "ndisasm boot.sec | more"
4. the first instruction is a jump to 0x3e but code is not aligned correctly.
5. run "ndisasm -s 0x3e boot.sec | more" to see aligned code.
OK, so in this case we have a tiny boot loader, it simply shows a message (the message is OS dependent, and you may change it with any binary editor such as KDE's. For example change boot.sec to display a funny message and then rewrite it using Krishnakumar's write.c on the floppy!) then waits for a key and invokes int 0x19 (BIOS's boot_starp procedure) again. In fact, MS DOS used to store some information (FAT) in the gap between jmp 0x3e and 0x3e itself and this fashion is followed by its successors; that's why there is a jump there. You may find some information about MS DOS file system on the net or consult [4].
Up to this point everything is logical, BIOS simply runs what ever it sees on the boot sector, it may be our toy "show A and halt" or an elegant boot sector such as msdos's. But when I disassembled ext2's boot sector, nothing was clear. it's full of zeros that means a lot of silly ADD instructions! why does the BIOS passes it simply and doesn't get stuck there? The answer is that the BIOS changed from XT to AT! and I could find an AT BIOS source on the net (unfortunately it doesn't have reference, but probably IBM AT Technical Reference includes this boot_strap's code too.)
;---INT 19H BOOT_STRAP_1 PROC NEAR ;SOME INITIALIZATIONS ;CLEAR @BOOT_LOCN STI MOV CX,4 H1: PUSH CX KTOV AH,0 INT 13H JC H2 MOV AX,201H SUB DX,DX MOV ES,DX MOV SX,OFFSET @BOOT_LOCN MOV CX,1 INT 13H H2: POP CX JNC H4 CMP AH,80H JZ H5 LOOP H1 JMP SHORT H5 H4: CMP BYTE PTR @BOOT_LOCN,06H ;TEST#1 JB H10 MOV DI,OFFSET @BOOT_LOCN MOV CX,8 MOV AX,WORD PTR @BOOT_LOCN H4A: ADD DI,2 CMP AX,[DI] ;TEST#2 LOOPZ H4A JZ H10 H4_A: JMP @BOOT_LOCN H5: ;SOME INITIALIZATIONS AND PRE TESTS SUB AX,AX SUB DX,DX INT 13H MOV CX,3 H6: PUSH CX MOV DX,0080H MOV AX,0201H SUB BX,BX MOV ES,BX MOV BX,OFFSET @BOOT_LOCN MOV CX,1 INT 13H POP CX JC H8 CMP WORD PTR @BOOT_LOCN+510D,0AA55H ; MAGIC NUMBER! JZ H4_A H8: PUSH CX MOV DX,0080H SUB AX,AX INT 13H POP CX JC H10A LOOP H6 H9: ;SOME THING NOT OF OUR INTEREST INT 18H H10A: LOOP H8 JMP H9 H10: ;PRINT A MESSAGE H11: jmp H11 BOOT_STRAP_1 ENDP
Two new tests are added to floppy's boot sector:
1. It's first byte should be more than 0x6! it means that the first instruction can't be ADD (OpCode 0 to 5 are for different modes of ADD). Why? it's logical, because no wise programmer would add almost unknown values of the register at the start up. And if it is so (as the ext2 case) a message is shown and PC waits for a reset. puzzle got solved partially! In my PC, control is simply passed to fixed disk's boot sector. Of course it's better, these days almost every PC has a fixed disk. Perhaps that's a change in BIOS after early AT.
2. It's first 8 words can't be the same. why? I don't know. It seems to be just a decision. you may test it easily, add 16 NOPs (0x90) to Krishnakumar's boot sector, and then it won't boot your PC!
Now you may use these tools (read.c and write.c) or Linux's own tools (e.g. lovely 'dd' command) for more investigation in the structure of boot sectors. As an interesting experiment for anybody who has Windows along with their Linux and uses LiLo as their boot loader (so it resides on MBR): make a recursive boot menu! Needed steps are as:
1. use 'dd' or 'read.c' to read MBR: #dd if=/dev/hda of=mbr.sec bs=1 count=512 skip=0
2. some how copy mbr.sec to your Windows' boot partition (almost always c:\). you may use these elegant programs by John Newbigin when all your solutions fail.
3. edit boot.ini and add such a line at its end: c:\mbr.sec = "LiLo Again!"
And this is how boot process goes on...
[2]https://nasm.sourceforge.net/
[3]IBM, "IBM Personal Computer XT Technical Reference", Vol.2,1981.
[4]M.A.Mazidi, J.G.Mazidi, "The 80X86 IBM PC & Compatible Computers",Vol.2, Prentice Hall, 1995.