|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
masterboot.s |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Skip to line: 1050 - 1100 - 1150 - 1200 - 1250 - 1281 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| If you have a comment for masterboot.s, please click here. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
1000
!
masterboot 1.9 - Master bootblock code
Author: Kees J. Bot
|
|
|
|
An exclamation mark (!) indicates a comment. The assembler ignores
everything to the right of an exclamation point.
Before going any further, read the first 4 paragraphs in section 2.5.2 and read the entire 2.6.5 section of Operating Systems by Andrew Tanenbaum and Albert Woodhull. Haven't bought the book? You won't be able to get through this web site without buying the book - so buy the book. I won't be going over things that the book already discusses. | ||
|
|
|
|
|
|
| The code below checks to see if this code was loaded from a floppy; however, I don't understand why a master boot record would ever be put on a floppy. You typically do not partition a floppy. In the third paragraph of section 2.6.5, Tanenbaum and Woodhull discuss a circumstance in which a floppy might be partitioned but indicate that even under this circumstance, a master boot record would not exist on the floppy. Also, the comment on line 1151 suggests the same. If anyone can see a point to having a master boot record on a floppy, please submit a comment to the site which will be displayed below. | |||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
hd0 is the entire first hard drive, hd1 is the first partition on that
hard drive, hd2 is the second, hd3 is the third and hd4 is the fourth.
hd5 is the entire second hard drive and hd6-hd9 are the partitions on that
drive. hd10 is the third hard drive and hd11-hd14 are the partitions
on that drive and hd15 is the fourth hard drive and hd16-hd19 are the partitions
on that drive. Within each partition there can be four further subpartitions.
hd1a is the first subpartition within hd1, hd1b is the second subpartition,
hd1c is the third and hd1d is the fourth.
If a drive is partitioned, there must be 4 partitions. If a partition is subpartitioned, there must be 4 subpartitions. Any partition or subpartition may have a size of 0 but there must be 4 partitions or subpartitions. Note that a partition can be specified after hitting the ALT key but a subpartition cannot be specified. For example, if subpartitions hd3b and hd3c are both bootable, there's no way of specifying either one after hitting the ALT key. (You could use the installboot utility program to mark one of the subpartitions as active but that takes a little more effort.) | ||
| 1011 ! |
|
|
| See line 1053 for an explanation of this comment. | ||
|
1013 !
1014 ! - If the booted device is a hard disk and one of the partitions is active 1015 ! then the active partition is booted. 1016 ! 1017 ! - Otherwise the next floppy or hard disk device is booted, trying them one 1018 ! by one. 1019 ! 1020 ! To make things a little clearer, the boot path might be: |
|
|
| Older machines are likely to halt the process until you remove the floppy. | ||
|
1022 ! [/dev/fd1]
- Drive empty
1023 ! /dev/hd0 - Master boot block, selects active partition 3 1024 ! /dev/hd3 - Submaster, selects active subpartition 1 |
|
|
| The code found in the bootblock.s file is called the bootstrap. | ||
|
|
| If a hard drive partition (as the book describes in section 2.6.5) is booted, the sequence is master boot (this code), bootstrap, boot monitor and finally the minix operating system. If a hard drive subpartition is booted, the sequence is master boot, master boot (again), bootstrap, boot monitor and finally the minix operating system. | ||
|
The "0x" indicates that the value is in hexadecimal notation.
If you are unfamiliar with the term "hexadecimal," look at this site.
The first instruction of this code (the jmp instruction on line 1052) is loaded at an offset of 0x7C00 into memory. The figure below shows the memory layout. This code is initially loaded at memory address LOADOFF (0x7C00) but then copied to memory address BUFFER (0x0600). The code jmp 's there to make room for the next block we'll load, which will either be another master boot block or a bootstrap (as found in bootblock.s).
Although you won't find it explicitly in this code or in bootblock.s, both the master boot blocks and boot blocks on disk have the magic number at an offset of 510. How'd they get there? The installboot utility is responsible for patching in the values. Go to this link for the master boot and this link for the boot block. These links show where SIGNATURE (0xAA55, the magic number) is patched into the master boot blocks and boot blocks at MAGIC (which is the same value as installboot's SIGPOS). Don't spend too much time studying the installboot.c code. It is only important that you begin to understand the role that the installboot utility plays. | ||
|
|
|
|
|
|
|
|
| ||
|
1038
1039 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
There are 3 main assembler files in the boot sequence (masterboot.s,
bootblock.s, and boothead.s
) and only 2 of the files .define begtext, begdata,
and begbss and only masterboot.s (this file).defines
endtext, enddata,
endbss, and _main.
The convention used in bootblock.s
is the simplest - bootblock.s begins text (i.e. code) sections with .text,
data sections with .data, and (if it had a bss section) bss sections with .bss
but does not .define begtext,begdata, begbss,
endtext, enddata, endbss, and _main.
If anyone sees a point to the extra .defines (begtext, begdata, etc.), please submit a comment to the site which will be displayed below. (The bss section holds uninitialized global variables; the bss section is discussed later.) |
|||||
|
1048
1049 ! Find active (sub)partition, load its first sector, run it. 1050 1051 master: 1052 jmp over |
|
|
|
.data1 reserves 1 byte of memory at the given address (in this
case, fix). This memory address is initialized to a value
of zero (0). If this value is anything other than 0, we are "locked"
into booting the specific partition. (See line 1012 above).
How do we change the value of fix? Again, this is a responsibility
of the installboot utility.
Why does data immediately follow the first instruction? Directly after the first instruction is a convenient place to stick the data. You'll see this trick elsewhere also. Look at line 06051 in the book. This is the first instruction of the minix kernel. | ||
|
|
|
|
|
|
|
The first instruction zeroes the ax register (any number xor'ed with
itself is zero). This is a pretty common practice. The instruction
mov ax, #0 is slower and takes up 3 bytes compared with xor's 2 bytes. One thing that initially confuses people with assembler is the order of the operands. The syntax of the mov instruction is: mov destination, source This seems a little strange to me (probably because this is not consistent with the syntax of the Unix cp command) but most assemblers use this syntax. | ||
|
|
|
|
|
|
|
If you mov a value into the stack register (ss)
or the stack pointer (sp), disable the interrupts first.
The ss and sp registers hold the address to which the
interrupt will return after its completion. If the ss and
sp
register are in flux, you have no idea where the code will return.
The interrupts are disabled with the cli (clear interrupts) instruction and reenabled with the sti (set interrupts) instruction. What's the pound sign (#) all about? The pound sign indicates that the value of LOADOFF rather than the contents of the memory location LOADOFF is mov'ed into the register. | ||
|
|
| bp is used here as a general purpose register. The value of bp doesn't change in this code. | ||
| 1063 |
|
|
| The figure in the comments of line 1027 describes copying this code from LOADOFF to BUFFER and then jmp'ing there. This is accomplished in lines 1065-1073. | ||
| 1065 mov si, sp ! si = start of this code |
|
|
| This line is important later in the code. The ret instruction on lines 1161, 1183, and 1243 jumps to LOADOFF, which is where either another master boot block or a boot block will have been loaded. | ||
| 1067 mov di, #BUFFER ! Buffer area |
|
|
| Since the rep movs instruction moves cx words (not bytes) from ds:si (which is 0:LOADOFF) to es:di (which is 0:BUFFER), the number of bytes is divided by 2. (The size of a word is architecture dependent. On a 16-bit architecture - like real-mode on the 80x86's - 2 bytes=1 word and on a 32-bit architecture - like protected mode on the 80x86's - 4 bytes=1 word. Since the processor is in real mode during the boot sequence and switches to protected mode (if the minix kernel has been compiled for protected mode) immediately before jumping to the kernel, 2 bytes=1 word. Note that word is sometimes used to mean 2 bytes, regardless of the architecture. ) | ||
|
|
| cld (clear direction flag) specifies that the rep movs instruction copies the bytes from 0:LOADOFF to 0:LOADOFF+512, not the bytes from 0:LOADOFF to 0:LOADOFF-512. If the latter is desired, use std (set direction flag) instead of cld. | ||
|
1070
rep
1071 movs |
| jmpf (far jump) obtains a new segment (in this case 0) and a new offset (BUFFER+migrate). Even though movb ah, #0x02 is the next instruction executed, this instruction is located LOADOFF-BUFFER bytes lower in memory. | ||
|
1073 migrate:
1074 |
|
|
|
|
|
|
|
|
|
|
|
|
|
int 0x16 is the keyboard BIOS function call. If ah=2,
int
0x16 tests the status of the control, shift and ALT keys. If
the third bit from the right (0x08 = 00000100) in the return value (al)
is high, the ALT key has been pressed.
The "b" in movb and testb indicates that the instructions operate on bytes rather than words. The difference between the test and the and instruction is that the test instruction doesn't change the destination operand (in this case al). It only affects the flag register. In this case we're concerned with the Z (zero) flag. If al's third bit from the right isn't 1 (the alt key wasn't pressed), the testb instruction will set the Z flag and the jump is made in the next instruction. noalt is on line 1098. | ||
|
.data2 reserves 2 bytes for data. Study print
(line 1256) to see how this data is used. Also look at memory location
choice
on line 1272
BUFFER+devhd is the string's beginning offset address in memory. | ||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
At this point, the user has hit the ALT key and the code is waiting
for the user to specify a partition (0-9, a-j). When the user types
in a value, the ascii value of the pressed key is placed in choice
(line 1272), overwriting the initial '0'. This ascii value is then
converted to its corresponding integer value. The ascii values for
'0' through '9' are converted to the integers 0 through 9 and the ascii
values for 'a' or 'A' through 'j' or 'J' are converted to the integers
10 through 19. For a little help, here's an ascii
chart.
cmp is a subtraction that only affects the flag register. In this way, cmp is similar to test. The following jae jumps if the first operand in cmp is above or equal to the second operand. | ||
|
|
| Since print uses ax, ax is pushed onto the stack; if ax isn't pushed (and then later popped (line 1096)), its value will be lost. This is commonly done. For example, it's also done on line 1109. | ||
|
|
|
The ALT key wasn't pressed. If fix is 0, a jump to findactive
is made and the partition tables on the hard drives are searched for a
non-zero bootind field.
A partition table entry with the non-zero bootind field indicates a bootable partition. | ||
|
1099
movb al, BUFFER+fix
! Always boot a certain partition?
1100 testb al, al 1101 jz findactive ! No, boot the active partition |
|
|
| A value between 0 and 19 is in al; this is the partition that will be booted. Another master boot block from another device must be loaded if the partition isn't on the current device (remember that the current device is in dl). | ||
|
1103
cbw
! ax = partition choice
1104 movb dl, #5 |
|
|
| For divb, ax is divided by dl (in this case) and the quotient is placed in al and the remainder in ah. al will have the value of the hard drive (0-3) and ah will have the value of the partition (0-4). | ||
| 1106 movb dl, #0x80 |
|
|
| The BIOS uses the values 0x80, 0x81, 0x82, and 0x83 to indicate the 1st, 2nd, 3rd, and 4th hard drives. | ||
| 1108 movb al, ah ! al = partition within disk |
|
|
| The ax register is used by load0; since ax is needed on line 1113, its value is pushed and then popped (see line 1112). | ||
|
load0 loads the first sector of the hard drive specified
by dl. Keep in mind that this master boot record may be
the same as the master boot record that is currently executing. We
could have checked for this and saved the time it takes to load the master
boot record but it would have added complexity to the code.
If the partition is 0, 5, 10, or 15, then the master boot record of the appropriate hard drive is loaded and executed (line 1114). If the partition is not 0, 5, 10, or 15, the partition table from the master boot record that has just been loaded is sorted (line 1121) and the first sector from the desired partition is loaded (line 1146). This sector either has another master boot record (in which case there are subpartitions on the partition that was chosen) or a bootstrap that will load the boot monitor from that partition. | ||