Hi there. In the previous two articles of this series, I discussed the physical structure of the disks and how they are partitioned. In this article, I will handle a more specific subject instead of a generic one. There is no boot sector in linux as we know it from DOS. NTFS on Windows is complex enough to be a topic on its own. Therefore, in this (and next) article, I will concentrate on DOS. The topic, which I will be handling in this article, will be the structure discussed in the Volume Boot Record (VBR) article on Wikipedia. At the end of this article, I explained why I chose to call this structure as "boot sector". Now, what is this boot sector really?
Yes, What is This Boot Sector?
Boot sector is the first sector of any partition with FAT and NTFS (and its predecessor HPFS) file systems (FS). This structure is not available on FSs such as ext and XFS. Its structure contains a block of code and some disk related information. Its function is similar to MBR, such that, the MBR loads the boot sector and executes it, likewise boot sector needs to continue the boot process and load the DOS kernel (IO.SYS) or the WinNT pre-kernel (NTLDR) and then NTOSKRNL.EXE. By the way, as of Windows Vista, there is /boot/bcd file instead of NTLDR [1].
Floppy disks and some USB disks cannot be partitioned because they don't have an MBR. In contrary, hard disks can be partitioned thanks to their MBR. From my previous article, we already know what BIOS and MBR code do. I also wrote repeatedly that there is no difference between MBR and boot sector from BIOS point of view (Citation: Code in the MBR and VBR is in essence loaded the same way. [2]). While the system is booting from the hard drive:
- BIOS reads and runs the MBR from the first sector.
- MBR code reads and executes the first sector of the bootable partition.
- Boot sector code finds and loads IO.SYS and the boot process continues.
Second step is skipped when DOS is booting from the floppy disk (or any non-partitioned media) because the first sector of a floppy is the boot sector. And the boot sector code is loaded to 0:7C00h like MBR code.
Note: UEFI provides another method for loading OS [3]. The steps above are correct in environments without UEFI.
Note: UEFI provides another method for loading OS [3]. The steps above are correct in environments without UEFI.
[1]: Windows NT 6 startup process
[2]: https://en.wikipedia.org/wiki/Volume_boot_record
[3]: https://en.wikipedia.org/wiki/Boot_sector#UEFI
[2]: https://en.wikipedia.org/wiki/Volume_boot_record
[3]: https://en.wikipedia.org/wiki/Boot_sector#UEFI
What is in the Boot Sector?
In the boot sector, there are data about the disk structure and the file system in that partition. Using these data, location of the (kernel) files is calculated which is required for the boot process. These data are primarily information about clusters, which are the logical unit of the file system, CHS/LBA info, disk label shown in the dir output in DOS and file allocation table (FAT) version. I will first copy the boot sectors of the systems, I installed, to files. Then, I will examine these according to the boot sector format.
As I explained at the end of this article (under the "Difference between Boot Sector and VBR"), the terms I use for the concepts are slightly different from those on Wikipedia. The data, I mentioned above, are explained in the BIOS Parameter Block (BPB) and Design of the FAT file system articles in Wikipedia, apart from Volume Boot Record article. My personal opinion is that the term "BIOS parameter block" can be get mixed up with the term "BIOS Data Area (BDA)".
Reading the Boot Sector
I used HxD again to read the boot sector in Windows. Like I did it previously, I run HxD as administrator and opened MBR*. There were three partitions on my computers disk. I am interested in the last one with Windows installed (can be seen in the screenshot from the previous article). I can find out at which sector this partition is starting, using the information from the previous article. I wrote the UInt32 value at 1E6h offset, which is the starting address of the third partition, into the sector box above and brought up the boot sector. I am only doing this to show how much NTFS boot sector looks like FAT.
* To find partitions on GPT disks, it is necessary to read GUID entries. I will explain GPT format and how to read it later in separate article.
Red marked region is code, blue is data and green is the boot sector signature.
I had created and formatted two primary partitions on the DOS622 VM. I boot this machine with Damn Small Linux (DSL) and open the terminal. There is no need to install any program to read the boot sector in linux. I see two disks: /dev/hda1 and /dev/hda2, when I check them with sudo fdisk -l command. I can either read the disk with dd and redirect the output to hexdump, or I can read the disk with hexdump and look at the first 32 lines (512 bytes) using head command:
I had created and formatted two primary partitions on the DOS622 VM. I boot this machine with Damn Small Linux (DSL) and open the terminal. There is no need to install any program to read the boot sector in linux. I see two disks: /dev/hda1 and /dev/hda2, when I check them with sudo fdisk -l command. I can either read the disk with dd and redirect the output to hexdump, or I can read the disk with hexdump and look at the first 32 lines (512 bytes) using head command:
sudo hexdump -C -v /dev/hda1 | head -n 32
or
sudo dd if=/dev/hda1 count=1 bs=512 | hexdump -C -v
Same commands can be also applied to /dev/hda2. I actually do not need to copy these files to my machine but they can be easily copied with scp just in case, as explained in the previous article.
In FreeDOS VM, I had created one primary and one extended partition and two virtual partitions within the extended partition which I did not format. When I booted this VM with DSL and check the partitions with sudo fdisk -l, I see primary partition /dev/hda1 and extended partition /dev/hda2.
Disk /dev/hda: 1073 MB, 1073741824 bytes
64 heads, 63 sectors/track, 520 cylinders
Units = cylinders of 4032 * 512 = 2064384 bytes
Device Boot Start End Blocks Id System
/dev/hda1 * 1 173 348736+ 6 FAT16
/dev/hda2 174 520 699552 5 Extended
/dev/hda5 174 346 348736+ 6 FAT16
/dev/hda6 347 520 350752+ 6 FAT16
Considering their start and end sectors, /dev/hda5 and /dev/hda6 are virtual partitions in the extended partition. When I run the above commands on /dev/hda2, I see the sector that I already saw like in the previous article using the disk editor, which does have a partition table but no code. Sectors filled with 0F6h byte can be seen in /dev/hda5 and /dev/hda6 because I hadn't formatted these disks. This means that the boot sector is written only when the partition is formatted, according to the FS, it is formatted in.
But why 0F6h? This value is designed to be written on empty space when formatting Atari disks. Then IBM added it to the PC standard as it is and it has not been changed since then. As far as I know, there is no particular reason.
Finally, I will check the boot sector of a floppy disk. Therefore, I will create a 1.44MB floppy image file using dd on my linux host:
dd if=/dev/zero of=floppy.ima bs=512 count=2880
The number 2880 is the total number of sectors on a floppy disk as a product of 80 cylinders, 18 sectors and 2 heads. On Windows, a text file of this size can be created and then renamed to floppy.ima. The important thing is that the file size has to be 1 474 560 bytes.
After creating the file, I start DOS622 VM and insert the virtual floppy, I created, to the virtual drive from Settings -> Storage -> Floppy Controller. If I change to drive A: right now, I will get a General failure reading drive A error. (Please note: I came across a bug of format command in FreeDOS. Floppy disk needs to be formatted twice in FreeDOS. Changing to A: after first format, causes an error.)
After creating the file, I start DOS622 VM and insert the virtual floppy, I created, to the virtual drive from Settings -> Storage -> Floppy Controller. If I change to drive A: right now, I will get a General failure reading drive A error. (Please note: I came across a bug of format command in FreeDOS. Floppy disk needs to be formatted twice in FreeDOS. Changing to A: after first format, causes an error.)
format A: /S |
I formatted the floppy disk with /S parameter as a system disk. So, system files (i.e. IO.SYS, MSDOS.SYS) were copied to the floppy. To look inside the floppy disk, simply open floppy.ima with hexdump. In Windows, HxD must be used, though.
Data Structures of Boot Sector
Of the structures, which are explained in BIOS parameter block article, almost only the NTFS structure is in use nowadays. There is DOS 4.0 EBPB on the VM disks and on the floppy, we just created. Let's take a look at DOS 4.0 EBPB first:
Data Structures of Boot Sector
Of the structures, which are explained in BIOS parameter block article, almost only the NTFS structure is in use nowadays. There is DOS 4.0 EBPB on the VM disks and on the floppy, we just created. Let's take a look at DOS 4.0 EBPB first:
Sector Offset | Size | Description |
---|---|---|
0x00 | 3 byte | JMP to the code |
0x03 | 8 byte | OEM NameNote1 |
0x0B | word | Bytes per sector |
0x0D | byte | Sectors per cluster |
0x0E | word | Reserved sectors |
0x10 | byte | Number of FATs |
0x11 | word | Max. num. of entries in root dirNote2 |
0x13 | word | Total number of sectorsNote3 |
0x15 | byte | Media descriptorNote4 |
0x16 | word | Sectors per FAT |
0x18 | word | Sectors per track |
0x1A | word | Number of heads |
0x1C | dword | Number of hidden sectors |
0x20 | dword | Total number of sectorsNote5 |
0x24 | byte | Physical drive numberNote6 |
0x25 | byte | ReservedNote7 |
0x26 | byte | Extended signature (0x28 or 0x29) |
0x27 | dword | Volume serial number |
0x2B | 11 byte | Volume label |
0x36 | 8 byte | FS Type |
Note1: OEM name field is a signature of the system formatting the disk. Normally it has no special value. However, arbitrary values might cause some version of DOS not to read the disk.
Note2: (pre FAT32) Due to the FAT FS limitations, number of entries (files + directories) under the directory "\" cannot exceed a specific limit. Root directory is special by design, it has to be in the second cluster and be fixed in size. Of course, this limitation isn't applied to sub-directories.
Note3: Since this field is 16-bit, it is deprecated for the partitions with more than 65 535 sectors. The new field at offset 0x20 takes its place. Its value is usually zero.
Note4: This byte is used to determine the type of the media. Its value is 0xF0 for hard disks and 0xF8 for 1.44M floppies. I do not think that other values will be encountered in practice.
Note5: When the field at offset 0x13 was deprecated (see Note3), this field has taken over its place. If this value is zero, too, besides 0x13, OS finds the total number of sectors from MBR.
Note6: 0x00, 0x01, ... for floppies and 0x80, 0x81, ... for hard disks.
Note7: Even though this space is reserved in documents, chkdsk command of WinNT uses this, as a flag for disk errors.
Let's examine the boot sector of a floppy disk using this information:
Note2: (pre FAT32) Due to the FAT FS limitations, number of entries (files + directories) under the directory "\" cannot exceed a specific limit. Root directory is special by design, it has to be in the second cluster and be fixed in size. Of course, this limitation isn't applied to sub-directories.
Note3: Since this field is 16-bit, it is deprecated for the partitions with more than 65 535 sectors. The new field at offset 0x20 takes its place. Its value is usually zero.
Note4: This byte is used to determine the type of the media. Its value is 0xF0 for hard disks and 0xF8 for 1.44M floppies. I do not think that other values will be encountered in practice.
Note5: When the field at offset 0x13 was deprecated (see Note3), this field has taken over its place. If this value is zero, too, besides 0x13, OS finds the total number of sectors from MBR.
Note6: 0x00, 0x01, ... for floppies and 0x80, 0x81, ... for hard disks.
Note7: Even though this space is reserved in documents, chkdsk command of WinNT uses this, as a flag for disk errors.
Let's examine the boot sector of a floppy disk using this information:
00000000 eb 3c 90 4d 53 44 4f 53 35 2e 30 00 02 01 01 00 |.<.MSDOS5.0.....|
00000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00 |...@............|
00000020 00 00 00 00 00 00 29 05 14 76 1b 4e 4f 20 4e 41 |......)..v.NO NA|
00000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 fa 33 |ME FAT12 .3|
First three bytes contains "JMP +3Ch; NOP" commands. Since JMP itself is two bytes, the beginning of the code is at the offset 3Eh (=2+3Ch). Bytes at 3Eh (0FAh, 33h, 0C0h) is CLI; XOR AX, AX (0C0h is not shown above). Other values are:
OEM Name: MSDOS5.0Bytes per sector: 0200h = 512 byte
Sectors per cluster: 1
Reserved sectors: 1 (the boot sector itself)
Number of FATs: 2
Max. num. of entries in root dir: 0E0h = 224
Total num. of sectors1: 0B40h = 2880
Media Descriptor: 0F0h (1.44M Floppy)
Sectors per FAT: 9
Sectors per track: 12h = 18
Number of heads: 2
Hidden sectors: 0
Total num. of sectors2: 0
Physical drive number: 0
Reserved: 0
Extended boot signature: 29h
Volume serial number: 05h, 14h, 76h, 1Bh = 1B76-1405 (same as the value in the image above)
Volume Label: NO NAME
FS Type: FAT12
Likewise, let's examine the hard disk of DOS622:
00000000 eb 3c 90 4d 53 44 4f 53 35 2e 30 00 02 04 01 00 |.<.MSDOS5.0.....|
00000010 02 00 02 00 00 f8 fa 00 3f 00 10 00 3f 00 00 00 |........?...?...|
00000020 e1 e7 03 00 80 00 29 12 b6 0b 4f 4d 53 2d 44 4f |......)...OMS-DO|
00000030 53 5f 36 20 20 20 46 41 54 31 36 20 20 20 fa 33 |S_6 FAT16 .3|
OEM Name: MSDOS5.0
Bytes per sector: 0200h = 512 byte
Sectors per cluster: 4 (so each cluster is 2048 bytes)
Reserved sectors: 1 (the boot sector itself)
Number of FATs: 2
Max. num. of entries in root dir: 0200h =512
Total num. of sectors1: 0
Media Descriptor: 0F8h (hard disk)
Sectors per FAT: 250
Sectors per track: 3Fh = 63
Number of heads: 10h = 16
Hidden sectors: 3Fh = 63
Total num. of sectors2: 03 E7E1h = 255 969
Physical drive number: 80h
Reserved: 0
Extended boot signature: 29h
Volume serial number: 12h, 0B6h, 0Bh, 4Fh = 4F0B-B612
Volume Label: MS-DOS_6
FS Type: FAT16
And the structure of FAT32, which is referred as DOS 7.1 EBPB on Wikipedia. First 36 bytes of the structures are same:
Sector Offset | Size | Description |
---|---|---|
0x00 | 36 byte | (see FAT16 structure) |
0x24 | dword | Sectors per FAT |
0x28 | word | Mirroring flagsNote8 |
0x2A | word | Version |
0x2C | dword | Root directory clusterNote9 |
0x30 | word | FSINFO sectorNote10 |
0x32 | word | Backup boot sectorNote11 |
0x34 | 12 byte | ReservedNote12 |
0x40 | byte | Physical drive numberNote6 |
0x41 | byte | ReservedNote7 |
0x42 | byte | Extended signature (0x28 or 0x29) |
0x43 | dword | Volume serial number |
0x47 | 11 byte | Volume label |
0x52 | 8 byte | FS type |
Note8: Normally, file allocation table is kept as at least two copies. Each file read or write operation is done using both copies. With this flag, single FAT can be set as active.
Note9: This field is added in the new design, in order to remove the design limitations mentioned in Note2.
Note10: With FAT32, theoretical maximum number of clusters increased from 65526 to 228=268 435 456. Correct, 228, not 232 because 4 bits are reserved. Even in this case, there are too many clusters to examine, in order to calculate free space in FS or find an empty cluster. This causes performance issues. To overcome this, a separate data structure, called FSINFO, is defined to keep information about free space and clusters. This structure is usually kept in first sector, right after the boot sector and it has RRaA signature.
Note11: With FAT32, boot sector is backed up against any damage. This backup is always written to the sixth sector. If MBR code cannot read the zeroth sector, it tries to read the sixth sector. Although this behavior is hard-coded, a field is also defined in the primary boot sector, to point backup boot sector however it is not recommended to have a value other than six.
Not12: This field originally appears "boot file name" in the documentation. Normally, the name of the kernel file, that boot sector will load, is hard-coded in the boot code. Looks like this field is intended to keep the file name here.
I compiled some of the above notes from Microsoft's FAT32 File System Specification document. Unfortunately, this document is no longer available on MS website, but it can be downloaded here.
Finally, NTFS partitions have a very similar structure to FAT32. Like FAT32, first 36 bytes of this structure are almost the same as FAT16.
Note9: This field is added in the new design, in order to remove the design limitations mentioned in Note2.
Note10: With FAT32, theoretical maximum number of clusters increased from 65526 to 228=268 435 456. Correct, 228, not 232 because 4 bits are reserved. Even in this case, there are too many clusters to examine, in order to calculate free space in FS or find an empty cluster. This causes performance issues. To overcome this, a separate data structure, called FSINFO, is defined to keep information about free space and clusters. This structure is usually kept in first sector, right after the boot sector and it has RRaA signature.
Note11: With FAT32, boot sector is backed up against any damage. This backup is always written to the sixth sector. If MBR code cannot read the zeroth sector, it tries to read the sixth sector. Although this behavior is hard-coded, a field is also defined in the primary boot sector, to point backup boot sector however it is not recommended to have a value other than six.
Not12: This field originally appears "boot file name" in the documentation. Normally, the name of the kernel file, that boot sector will load, is hard-coded in the boot code. Looks like this field is intended to keep the file name here.
I compiled some of the above notes from Microsoft's FAT32 File System Specification document. Unfortunately, this document is no longer available on MS website, but it can be downloaded here.
Finally, NTFS partitions have a very similar structure to FAT32. Like FAT32, first 36 bytes of this structure are almost the same as FAT16.
Sector Offset | Size | Description |
---|---|---|
0x00 | 36 byte | (see FAT16 structure) |
0x24 | byte | Physical drive numberNote6 |
0x25 | byte | Reserved / Unused |
0x26 | byte | Extended signature (0x80) |
0x27 | byte | Reserved |
0x28 | qword | Sectors in volume |
0x30 | qword | Cluster number of Master File Table (MFT) |
0x38 | qword | Cluster number of MFT mirror |
0x40 | dword | Clusters per File Record Segment (FRS)Note13 |
0x44 | dword | Clusters per Index BlockNote13 |
0x48 | qword | Volume serial number |
0x50 | dword | Checksum |
Note13: Two fields at the offsets 0x40 and 0x44 are actually defined as 1 byte + 3 bytes, not dwords. Three bytes reserved fields may have been intentionally defined for compatibility because these offsets has a different usage in FAT32. More precisely, to prevent a FAT32 disk utility from accidental corruption of an NTFS partition.
As I mentioned before, NTFS boot sector is similar to its predecessor, FAT but more complex. Therefore, I will explain NTFS in a separate article in order to keep this one short.
In MBR article, I mentioned two different terms about disk size. One of them was raw disk space and the other was formattable (usable) disk space. There are two fields in the tables above, i.e. reserved sectors and hidden sectors. Although they are used in some calculations, they can also be used to allocate some hidden space between root directory and boot sector, in theory. Other than that, there are two FATs that keep the location of the files on the partition. Without these structures, there is no FS at all. In other words, files cannot be written or even if they are written directly to the sectors, their beginning and end cannot be known. While raw space is the space that comes out when the number of sectors in MBR is multiplied by 512 bytes (bytes per sector), formattable space is remaining space on the disk when the above mentioned structures are taken out from the raw space.
For example, I had created a floppy image file of 1 474 560 bytes (2880 sectors). This is its raw space: I can write so many bytes on a floppy without any file structure. What does DOS say about the formatted floppy in the screenshot below?
As I mentioned before, NTFS boot sector is similar to its predecessor, FAT but more complex. Therefore, I will explain NTFS in a separate article in order to keep this one short.
In MBR article, I mentioned two different terms about disk size. One of them was raw disk space and the other was formattable (usable) disk space. There are two fields in the tables above, i.e. reserved sectors and hidden sectors. Although they are used in some calculations, they can also be used to allocate some hidden space between root directory and boot sector, in theory. Other than that, there are two FATs that keep the location of the files on the partition. Without these structures, there is no FS at all. In other words, files cannot be written or even if they are written directly to the sectors, their beginning and end cannot be known. While raw space is the space that comes out when the number of sectors in MBR is multiplied by 512 bytes (bytes per sector), formattable space is remaining space on the disk when the above mentioned structures are taken out from the raw space.
For example, I had created a floppy image file of 1 474 560 bytes (2880 sectors). This is its raw space: I can write so many bytes on a floppy without any file structure. What does DOS say about the formatted floppy in the screenshot below?
It says, 2847 out of 2880 sectors can be used. Two FATs each of them 16 sectors plus one boot sector makes 33 sectors in total. This means, I cannot create a file larger than 1 457 664 bytes on this floppy disk.
This claim can be tested by booting with DSL and running following commands:
This claim can be tested by booting with DSL and running following commands:
sudo mount /dev/fd0 /mnt
sudo rm /mnt/*
sudo dd if=/dev/zero of=/mnt/test
sudo ls -la /mnt
sudo rm /mnt/*
sudo dd if=/dev/zero of=/mnt/test
sudo ls -la /mnt
Remember, FreeDOS VM has three partitions. Two of them are extended and one is primary. DOS622 VM has two primary partitions and the half of the disk has no partition. Now I will demonstrate an easy way for all that is mentioned above but unfortunately only for those who have Norton Disk Editor.
I ran diskedit.exe (v2000) on the DOS622 VM and opened the partition table of the disk with Alt+A (see the next figure upper part). Afterwards, I pressed Enter again (cursor was on the first partition) and the boot sector appeared with all information read (bottom part). Moreover, the values reported by DOS, are shown in the right column. What could this be used for? Let's assume, I connect a bad hard disk or insert a bad floppy disk (which is not physically bad, of course), to my computer which is running DOS. I check the disk with the disk editor and find out, "Sectors per cluster" value in the boot sector is different from what the OS reports. By replacing this value with its correct value, I can possibly make the disk readable again - even though with a small possibility. On this screen, I can note the partition label and partition serial number down and verify them with the output of dir command after exiting the disk editor.
It is possible to open the MBR again, using Alt + A key combination in the disk editor (please note the "Absolute Sector 0" on the bottom right) and check the other partition (or partitions of FreeDOS) in the same way. Since there is no big difference between other partitions I will not examine them separately.
How Does the Boot Code Work?
a. FreeDOS Boot Code
In this section, I will examine boot code of FreeDOS and give an overview of DOS boot code as well. First of all, I should emphasize that FreeDOS boot code differs slightly on floppies (FAT12) and hard disks (mostly FAT16). In the text, I will mention the difference in the related paragraph.
When a floppy disk is formatted in FreeDOS without /S parameter, the boot code terminates the boot process abruptly with a simple error message. In other words, no boot code is written. This might be most likely because FreeDOS boot code is more complex than DOS, there may be no space for error messages like "Missing IO.SYS etc.". This code is so simple that it's not worth to dwell on it.
It is possible to open the MBR again, using Alt + A key combination in the disk editor (please note the "Absolute Sector 0" on the bottom right) and check the other partition (or partitions of FreeDOS) in the same way. Since there is no big difference between other partitions I will not examine them separately.
How Does the Boot Code Work?
a. FreeDOS Boot Code
In this section, I will examine boot code of FreeDOS and give an overview of DOS boot code as well. First of all, I should emphasize that FreeDOS boot code differs slightly on floppies (FAT12) and hard disks (mostly FAT16). In the text, I will mention the difference in the related paragraph.
When a floppy disk is formatted in FreeDOS without /S parameter, the boot code terminates the boot process abruptly with a simple error message. In other words, no boot code is written. This might be most likely because FreeDOS boot code is more complex than DOS, there may be no space for error messages like "Missing IO.SYS etc.". This code is so simple that it's not worth to dwell on it.
The best thing of FreeDOS is that it is open source. I downloaded FreeDOS boot code from github and included my own comments on some lines. These start with "; --" to distinguish easily. The original file can be downloaded from this link and the file containing my comments can be downloaded here. What this code does is not very complicated. First, a jmp command (line** 69) branches into the real code block. Right after that, there are definitions of the boot sector data structures (lines 73-90). I mentioned that this code is loaded at the address 0:7C00h. The code copies itself to address 1FE0h:0 (lines 162-168) to prevent the kernel to be loaded to address 0060h:0 from overwriting itself and continues to run from where the execution left by executing a far jmp command. Once the kernel is loaded, kernel.sys may need some data from the boot sector even if the boot code has finished running.
**: The given line numbers are with respect to the file, in which I added my comments.
There are three important values in the boot code:
1. FAT start (fat_start): The sector, from which the FAT starts. It's the sum of reserved sector and total number of sectors.
I mentioned above that reserved sectors can be used to allocate space between boot sector and FAT. This field gives how many sectors FAT is after boot sector. For FAT12 and FAT16, this field is almost always 1, meaning FAT starts right after the boot sector. The term "hidden sectors" is kinda misleading. This field actually keeps on which sector that boot sector is. In other words, it is the LBA address of that sector. This field is only taken into account when booting. For a non-bootable disk, this field can be zero without problem.
**: The given line numbers are with respect to the file, in which I added my comments.
There are three important values in the boot code:
1. FAT start (fat_start): The sector, from which the FAT starts. It's the sum of reserved sector and total number of sectors.
fat_start = hidden_sectors + reserved_sectors
I mentioned above that reserved sectors can be used to allocate space between boot sector and FAT. This field gives how many sectors FAT is after boot sector. For FAT12 and FAT16, this field is almost always 1, meaning FAT starts right after the boot sector. The term "hidden sectors" is kinda misleading. This field actually keeps on which sector that boot sector is. In other words, it is the LBA address of that sector. This field is only taken into account when booting. For a non-bootable disk, this field can be zero without problem.
2. Start of root directory (root_dir_start): Where the list of files in the root directory start. It is obtained by adding the product of number of FATs and sectors per FAT to the FAT start value from the first step.
3.First data sector (data_start): This is found by dividing the max. number of root directory entries to the 1/32 of the bytes per sector value and adding start of root directory value (from above step) to this. Since a file entry is 32 bytes in size, bytes per sector divided by 32 gives the number of file entries in a sector. And maximum number of root directory entries divided by this number gives the exact length of root directory table in sectors.
Note: File allocation table and structure of directory entries, in detail, are the topics of the next article.
The values above, are calculated and assigned to their variables between the lines 192 and 228. The memory addresses of these variables are 7BD2h, 7BD6h and 7BDAh, respectively. Next, entire root directory is read into the memory (lines 238 - 243). The value of DI after pop instruction in the line 240, comes originally from the value of AX pushed in the line 221. This value is the length of root directory, in sectors, when calculating the data_start value. The code between the lines 249 and 263 is searching for KERNEL.SYS file in the root directory table. If the file is found, the cluster number containing the file is pushed to the stack (line 263) and the entire FAT is read from the disk (lines 277 - 290) in size of "sectors per FAT" sectors.
Here comes the exact point, where the hard disk (FAT16) boot code differs from floppy disk (FAT12) boot code in FreeDOS. These codes probably exist in format.com as precompiled for both FAT12 and FAT16. I will explain the structure of this table and how it's read and interpreted in the next article. Basically, this table holds the consecutive cluster number or an "end of file (EOF)" mark, similar to linked list. With the help of first cluster number of KERNEL.SYS, following cluster numbers are extracted from file allocation table. The list of these clusters is written to a list at 0060h:2000h word by word (lines 301 - 327). This list is then read in a loop (lines 349 - 355) and the sectors corresponding to the clusters in the list are loaded to the address 0060h:0 and the kernel is run with the far jmp in line 353.
Note that because this code is relatively complex (and long), the only error message is simply an "Error!." (line 379). In the readDisk function, a dot char is printed on the screen for each sector read. This is probably necessary to pinpoint an unreadable sector. LBA support is checked in the readDisk function, but LBS isn't supported on floppies and this test is skipped anyway when boot media is a floppy disk (line 416). If LBA is supported, reading the disk is done using the DAP package created in an earlier phase and related LBA read function of int 13h. Otherwise, read_normal_BIOS (line 442) function is called. In this function (lines 445 - 486), the dword value, hold in LBA_SECTOR_0 and LBA_SECTOR_16 adresses is converted to CHS address. This function reads each sector to ES:63A0h and then copies it to the destination address in ES:BX (lines 493 - 500).
FreeDOS also supports FAT32 drives. In the previous article, I intentionally disabled FAT32 while installing the VM. Therefore, current disk boot sector is same as floppy boot sector, except for the FAT16 code block, mentioned above. There are two other boot sector codes named boot32.asm and boot32lb.asm for FAT32 disks. If I ever analyze them, I will do it in a separate article.
b. DOS Boot Code
DOS 6.x boot code is still under copyright and therefore there is no source code of it available but it is simpler than FreeDOS code. Even though it is under copyright, it can be disassembled in linux with following commands:
root_dir_start = fat_start + number_of_FATs * sectors_per_FAT
3.First data sector (data_start): This is found by dividing the max. number of root directory entries to the 1/32 of the bytes per sector value and adding start of root directory value (from above step) to this. Since a file entry is 32 bytes in size, bytes per sector divided by 32 gives the number of file entries in a sector. And maximum number of root directory entries divided by this number gives the exact length of root directory table in sectors.
data_start = root_dir_start + root_dir_entries / (bytes_per_sector >> 5)
Note: File allocation table and structure of directory entries, in detail, are the topics of the next article.
The values above, are calculated and assigned to their variables between the lines 192 and 228. The memory addresses of these variables are 7BD2h, 7BD6h and 7BDAh, respectively. Next, entire root directory is read into the memory (lines 238 - 243). The value of DI after pop instruction in the line 240, comes originally from the value of AX pushed in the line 221. This value is the length of root directory, in sectors, when calculating the data_start value. The code between the lines 249 and 263 is searching for KERNEL.SYS file in the root directory table. If the file is found, the cluster number containing the file is pushed to the stack (line 263) and the entire FAT is read from the disk (lines 277 - 290) in size of "sectors per FAT" sectors.
Here comes the exact point, where the hard disk (FAT16) boot code differs from floppy disk (FAT12) boot code in FreeDOS. These codes probably exist in format.com as precompiled for both FAT12 and FAT16. I will explain the structure of this table and how it's read and interpreted in the next article. Basically, this table holds the consecutive cluster number or an "end of file (EOF)" mark, similar to linked list. With the help of first cluster number of KERNEL.SYS, following cluster numbers are extracted from file allocation table. The list of these clusters is written to a list at 0060h:2000h word by word (lines 301 - 327). This list is then read in a loop (lines 349 - 355) and the sectors corresponding to the clusters in the list are loaded to the address 0060h:0 and the kernel is run with the far jmp in line 353.
Note that because this code is relatively complex (and long), the only error message is simply an "Error!." (line 379). In the readDisk function, a dot char is printed on the screen for each sector read. This is probably necessary to pinpoint an unreadable sector. LBA support is checked in the readDisk function, but LBS isn't supported on floppies and this test is skipped anyway when boot media is a floppy disk (line 416). If LBA is supported, reading the disk is done using the DAP package created in an earlier phase and related LBA read function of int 13h. Otherwise, read_normal_BIOS (line 442) function is called. In this function (lines 445 - 486), the dword value, hold in LBA_SECTOR_0 and LBA_SECTOR_16 adresses is converted to CHS address. This function reads each sector to ES:63A0h and then copies it to the destination address in ES:BX (lines 493 - 500).
FreeDOS also supports FAT32 drives. In the previous article, I intentionally disabled FAT32 while installing the VM. Therefore, current disk boot sector is same as floppy boot sector, except for the FAT16 code block, mentioned above. There are two other boot sector codes named boot32.asm and boot32lb.asm for FAT32 disks. If I ever analyze them, I will do it in a separate article.
b. DOS Boot Code
DOS 6.x boot code is still under copyright and therefore there is no source code of it available but it is simpler than FreeDOS code. Even though it is under copyright, it can be disassembled in linux with following commands:
dd if=floppy.ima of=floppy.bin count=1 bs=512
objdump -M intel -D -b binary -m i8086 --adjust-vma=0x7C00 floppy.bin > floppy.asm
objdump -M intel -D -b binary -m i8086 --adjust-vma=0x7C00 floppy.bin > floppy.asm
The variables in the boot block needs to be separated from the code. This code has to calculate the same variables to find the starting address of IO.SYS file. I don't think that this code is capable of using LBA because its simplicity. The problem with MSDOS is, it is not flexible as FreeDOS in some terms because it is running under some constraints to keep the code short but on the other hand it is very susceptible to something wrong:
How Are These Structures in Linux?
None of these exist, in short. There is neither number of sectors, heads, reserved sectors etc, nor any executable code. There are boot loaders to keep the boot chain continuous between BIOS and the kernel. If I consider GRUB, (1) GRUB Stage1 code can be written in MBR. In this case, GRUB MBR code runs core.img, finds grub.conf file and loads the selected OS. Or (2), Stage1 code is preferably in the first sector of the /boot partition. In this case, that partition has to be marked as bootable in the partition table and standard MBR code loads Stage1 code from the first sector of the partition, then core.img is loaded by Stage1 code and first step continues.
The process is similar in LILO and syslinux. By the way, since UEFI itself is a boot loader, linux kernel can be called from BIOS via UEFI without the need of any boot loader on UEFI supported machines.
Appendix: The difference between VBR and Boot Sector
The "Boot sector" article in Wikipedia covers (more generally) the structure that I previously mentioned as MBR. The hat-note in the MBR article says: "This article is about a PC-specific type of boot sector on partitioned media. For the first sector on non-partitioned media, see volume boot record.". The hat-note in the "Boot sector" article says: "This article is about the generic concept of boot sectors. For the VBR in PCs, see Volume Boot Record. For the MBR in PCs, see Master Boot Record.".
Why has there been such a naming confusion? While I was learning these topics, I never encountered the term VBR. I have always used the terms "MBR" and "boot sector" to distinguish between these two data structures, as they are in the sources which I learned these from. I could not find any VBR term in the references of VBR article itself. In short, VBR should be a relatively new term. I also checked from Michael Tischer's "PC Intern 3.0: System Programming (1st edition, 1992)". On page 557, MBR is called "partition sector". The subject, I explained in this article, is also mentioned as "Boot Sector" in this book (page 919).
- DOS requires the entries of kernel files (IO.SYS and MSDOS.SYS) to exist at the first and second place in the root directory listing. Even if these files are in disk, DOS boot will fail if their entries are not in first and second place. So, if I copy IO.SYS and MSDOS.SYS to a floppy or a disk manually, the boot process could fail. FreeDOS, on the other hand, allows booting even if KERNEL.SYS is copied manually as long as boot sector code searches for it.
- DOS also requires the IO.SYS file to be not fragmented. Most probably some sequential sectors are read instead of calculating the next cluster in which the continuation of the IO.SYS exists. If IO.SYS is fragmented, there could be no guarantee that the file read into memory is really IO.SYS. This also makes the system vulnerable to viruses.
- If only some parts of IO.SYS is loaded to memory by the boot sector, this leads that IO.SYS is responsible for reading the rest of itself into the memory.
How Are These Structures in Linux?
None of these exist, in short. There is neither number of sectors, heads, reserved sectors etc, nor any executable code. There are boot loaders to keep the boot chain continuous between BIOS and the kernel. If I consider GRUB, (1) GRUB Stage1 code can be written in MBR. In this case, GRUB MBR code runs core.img, finds grub.conf file and loads the selected OS. Or (2), Stage1 code is preferably in the first sector of the /boot partition. In this case, that partition has to be marked as bootable in the partition table and standard MBR code loads Stage1 code from the first sector of the partition, then core.img is loaded by Stage1 code and first step continues.
The process is similar in LILO and syslinux. By the way, since UEFI itself is a boot loader, linux kernel can be called from BIOS via UEFI without the need of any boot loader on UEFI supported machines.
Appendix: The difference between VBR and Boot Sector
The "Boot sector" article in Wikipedia covers (more generally) the structure that I previously mentioned as MBR. The hat-note in the MBR article says: "This article is about a PC-specific type of boot sector on partitioned media. For the first sector on non-partitioned media, see volume boot record.". The hat-note in the "Boot sector" article says: "This article is about the generic concept of boot sectors. For the VBR in PCs, see Volume Boot Record. For the MBR in PCs, see Master Boot Record.".
Why has there been such a naming confusion? While I was learning these topics, I never encountered the term VBR. I have always used the terms "MBR" and "boot sector" to distinguish between these two data structures, as they are in the sources which I learned these from. I could not find any VBR term in the references of VBR article itself. In short, VBR should be a relatively new term. I also checked from Michael Tischer's "PC Intern 3.0: System Programming (1st edition, 1992)". On page 557, MBR is called "partition sector". The subject, I explained in this article, is also mentioned as "Boot Sector" in this book (page 919).