Boot Record and Partitioning Innards

What are partitions, and why have them?

Partitions are a way of dividing up a disk into pieces in order to store multiple filesystems. This is often done in order to store different types of filesystems on the same physical disk, or to create independent filesystems so that damage to one won't affect another. The boundaries set by partition tables are logical boundaries. The BIOS sees the disk as a single entity; the separation into partitions is a function of the operating system. It is also up to the operating system whether it wants to respect partition boundaries.

When booting from a partition, the first sector of the partition is loaded and executed. This sector is often referred to as the boot sector or as a logical boot record (LBR). Generally, it will contain information about the size of the filesystem and the location of key filesystem structures. It will also begin the process of booting the operating system, usually by searching a limited area of the filesystem for the operating system's boot files (e.g. IO.SYS, NTLDR).

The Master Boot Record (MBR)

The MBR is the first sector of the disk, and contains a table of partitions and the code necessary to load the boot sector of the active partition. If the disk is being booted from, the MBR code will typically look for the active partition, load the LBR of that partition, and execute it. If the disk is not being booted from, the MBR code is not necessary, and only the partition table matters.

Both LBRs and MBRs have a 2-byte boot marker at the end of the sector at offset 0x1FE, the hex sequence 0x55 0xAA. This indicates that the sector contains executable code. The MBR will usually check for this signature, and not execute the MBR/LBR if the signature is not there. The reason is that if the signature is not there, then the LBR code could be filled with random junk and shouldn't be executed. MBRs are pretty consistent about this, but BIOSes are not; some BIOSes will ignore whether the flag is there. Others will require that flag, plus the initial JMP at offset 0 (0xEB 0x3C).

Disk Geometry

Originally, a location on a disk was located by specifying three values: the cylinder (or track), the head, and the sector. I'll skip the physical details of this since good explanations of this are available elsewhere. Addressing a sector on the disk using these coordinates is referred to as CHS, with the cylinder number being the coarsest coordinate and the sector number being the finest. Sector numbering beings with 1, while cylinder and head numbering begins with 0. Due to limitations in the original BIOS API, CHS addressing through this method is limited to about 8GB (roughly 16M sectors). Sectors are limited to 1-63, heads are limited to 0-255 (although due to a DOS bug, 254 is the highest head number commonly used), and cylinders are limited to 0-1023. The CHS info about the drive returned by the BIOS is actually translated from what the drive considers to be its own CHS counts (which is actually translated again, since modern drives use a number of techniques such as varying the sectors/track, and no standard PC drive actually has 255 heads). However, using CHS addressing extensions that bypass the original BIOS API removes these limitations and can address the entire disk, with one possible exception which will be addressed in the next section.

Logical Block Addressing (LBA) simplifies addressing greatly. Since most programs that perform disk access used some sort of LBA internally anyway (even boot sector code), and just converted to CHS when the API was called, this came very naturally. LBA starts numbering sectors from 0, and can potentially address more than the older CHS style addressing. This is because the cylinders/heads/sectors actually returned by the drive is limited to full cylinders. If the last cylinder ends before reaching the last maximum head and sector numbers, then the cylinder is not reported. Although they may be addressable via the drive's CHS scheme, they're not reported. This is interesting because when Windows partitions a drive, it will use the CHS figures reported by the drive to size the partition, instead of using the LBA sector count. Consequently, there is often a small amount of empty space after the last partition. Using a figure of 255 heads and 63 sectors, there could be up to 8MB of unpartitioned space there.

Disk Overlays

On older machines, disk overlays were sometimes used to allow the computer to see and address larger hard disks. They do this by placing a program in the MBR (which in turn would usually load more code from other unused sectors). There were side effects to this though. They usually replaced the partition table, or made the partition type undefined so that if you booted the machine with a floppy, the drive would be unreadable to prevent accidental damage. The real MBR was kept elsewhere or generated at boot time. If the machine was booted without an overlay, the OS could potentially see a partition with the wrong geometry or smaller than its actual size, and if the filesystem was checked/modified, it would likely be seen as corrupt or modified in a destructive way (a more modern version of this behavior is what happens when you allow Windows to see a partition over 128GB when it doesn't have 48-bit LBA enabled -- it will think the partition is corrupt).

Another common overlay actually reserved 63 sectors for itself, causing all addresses to be shifted by 63 sectors when comparing the disk with and without the overlay running. Partition tables and boot sectors would have to be adjusted accordingly if the overlay is removed manually.

The Partition Table

The partition table is actually a very simple data structure. Most of its mysteriousness simply comes from its fragility. One wrong byte and you can lose access to all your data. However, with a little understanding and care, mistakes are easy to undo. It's only a 64 byte structure, usually in one place, making it easy to back up. Since it is at a lower level than the filesystem, it is not normally accessed as a file (although *nix can access the entire disk as a file). The partition table consists of four 16 byte entries at offset 0x1BE. The format of an entry is as follows:

Offset Size (bytes) Usage
0 1 Bootable flag (partition "active")
1 1 Starting CHS head
2 2 Starting CHS sector/cylinder (more on this below)
4 1 Partition type
5 1 Ending CHS head
6 2 Ending CHS sector/cylinder (more on this below)
8 4 LBA offset of partition
12 4 Length of partition in sectors

Bootable flag: This field is used to mark a partition is active. When the MBR code runs, it scans the partition table for non-zero entries here. The value 0x80 is expected, although some MBRs will react to other values. For example, the Windows 2000 MBR will look for values in the range 0x80-0xFF, and pass this to the BIOS int 13h functions (the upshot of this is that it can load an LBR from another hard drive if the value is greater than 0x80, although I'm not sure to what end). It will also look for values in the range 0x01-0x7F, and report an invalid partition table. Furthermore, if more than one entry is non-zero, this will also trigger the invalid partition table message.

Starting/Ending CHS head: The head part of the CHS address of the first or last sector of the partition. If the word at offset 0 containing the boot indicator and the head number is read as a little-endian word into the DX register, it can be loaded without modification to call the BIOS int 13h disk I/O functions. The boot indicator 0x80 is also the drive number of the first drive, and will go into DL, and the head number will go into DH.

Starting/Ending CHS sector and cylinder: This is a little trickier because it's in a packed format. The sector number occupies 6 bits, while the cylinder number occupies 10 bits. The pair should be read as a little-endian word. The low byte of this little-endian word contains the sector number in the lower 6 bits, and bits 8-9 of the cylinder in the upper 2 bits. The high byte of this word (offset 3 for the starting pair, offset 7 for the ending pair) contains bits 0-7 of the cylinder number. The packing comes from the BIOS int 13h call -- the word can be read directly into the CX register and used.

Byte: Byte 0 Byte 1
Bit: 76543210 76543210
Use: Cyl high bits 9-8 Sector bits 5-0 Cyl low bits 7-0

Partition type: A magic byte indicating what filesystem is in the partition. Sometimes the OS will ignore the value here if the partition type is different than indicated by the LBR of the partition. Some common values are:

Value Partition type
0x00 Empty entry in the table
0x05 Extended partition, CHS addressing
0x06 FAT16, CHS
0x07 NTFS
0x0B FAT32, CHS
0x0C FAT32, LBA
0x0E FAT16, LBA
0x0F Extended partition, LBA
0x42 Dynamic disk

Side note: Although dynamic disk partitions are a placeholder for Windows to allocate filesystems however it wants, the most common scenario is just a single NTFS partition. The upshot is that converting a dynamic disk back to basic is usually just a matter of changing the partition type from 0x42 to 0x07.

Windows NT 4.0 would add 0x80 to partition types that were part of a fault-tolerant set. This is a Windows-centric list; there are more complete lists of partition types available on the internet.

LBA offset of partition: This is the starting LBA of the partition. In the most common case, a single partition the size of the disk, this value is 0x3F (63). Normally this address is absolute (or relative to the MBR at sector 0), but this is different for extended and logical partitions which will be explained in the next section. It's stored as an unsigned 32-bit value in little-endian byte order.

Length of partition in sectors: Number of sectors the partition occupies. The last sector number of the partition can be calculated as (LBAOffset + Length - 1). It's stored as an unsigned 32-bit value in little-endian byte order.

Extended and Chained Partitions

Extended partitions are different than the other partition types, in that they don't represent a filesystem partition. Instead, they reference an entirely separate partition table stored in another sector, known as an extended boot record (EBR). As with LBRs, the 0x55 0xAA signature is required, despite the lack of bootable code in these boot records. Only one extended partition is allowed in the MBR, at least by Windows. Because of this, extended partitions are actually chained together (rather than the root MBR sprouting several EBR "branches").

The outermost EBR, referenced by the MBR, is used to allocate all the space required for all the logical drives contained within it. We'll call it the primary extended partition. Like the MBR, an extended partition is only allowed to contain one extended partition (a chained extended partition). This limits the partitioning structure to a chain instead of a tree. The chain is made up of the MBR, EBR, and chained partitions (CBRs), and can be followed straight from start to finish. No branches are allowed on this chain, only leaves ("primary partitions" in the MBR, "logical drives" in the EBR/CBR) which contain no further partitions.

Regular partitions within EBRs/CBRs are typically called logical drives. Commonly, each EBR/CBR will allocate one logical drive, and if another one is needed, it will create a CBR slightly larger than the logical drive and then create the logical drive within the newly allocated CBR. However, EBRs and CBRs can contain multiple logical drives within themselves (multiple leaves) instead of extending the chain.

The LBA of partitions within the primary extended partition is not relative to the start of the drive. All chained extended partitions (CBRs) contained within the primary EBR are addressed relative to the start of the primary EBR. In contrast, all logical drives are addressed relative to the start of the EBR/CBR that contains them. As an example, we'll start with a 150GB drive. The first 10GB will be allocated to a primary partition, and the remaining 140GB will be allocated to the primary extended partition:

MBR at LBA 0 on disk
LBA Length Allocated for Physical LBA on disk
0GB 10GB Primary partition 1 0GB (0GB offset of MBR + 0GB offset of partition)
10GB 140GB Extended partition (primary) 10GB (0GB offset of MBR + 10GB offset of partition)

Note:The 0GB LBA for primary partitions and logical drives is actually usually 63 sectors in, which is about 32KB.

If we wanted to partition the extended partition into three partitions of sizes 20GB, 30GB, 40GB, and 50GB it will typically be done this way: The first entry in the EBR will be the first logical drive, with an offset of 0GB and a size of 20GB. The second entry would then be another extended partition (CBR) with an offset of 20GB and a size of 30GB.

Primary EBR at LBA 10GB on disk
LBA Length Allocated for Physical LBA on disk
0GB 20GB Logical drive 1 10GB (10GB offset of EBR + 0GB offset of partition)
20GB 30GB Chained extended partition (CBR 1) 30GB (10GB offset of EBR + 20GB offset of partition)

Note: Primary partitions and logical drives contain the same thing. The reason for the different names is that primary partitions are addressed relative to the MBR at the start of the disk, while logical drives are addressed relative to the EBR/CBR that contains them.

In the new chained partition, the first entry will be for a logical drive at offset 0GB, and a size of 30GB (minus the overhead of the CBR, usually 63 sectors). The second entry will be another CBR with an offset of 50GB, and a length of 40GB.

CBR 1 at LBA 30GB on disk
LBA Length Allocated for Physical LBA on disk
0GB 30GB Logical drive 2 30GB (30GB offset of CBR 1 + 0GB offset of partition)
50GB 40GB Chained extended partition (CBR 2) 60GB (10GB offset of EBR + 50GB offset of partition)

CBR 2 is addressed relative to the start of the primary EBR, as opposed to logical drive 2, which is addressed relative to the start of CBR 1. In CBR 2 there will be an entry for a logical drive at offset 0GB, size 40GB, and one entry for CBR 3 at offset 100GB, size 50GB.

CBR 2 at LBA 60GB on disk
LBA Length Allocated for Physical LBA on disk
0GB 40GB Logical drive 3 60GB (60GB offset of CBR 2 + 0GB offset of partition)
90GB 50GB Chained extended partition (CBR 2) 100GB (10GB offset of EBR + 90GB offset of partition)

Finally, CBR 3 would contain a single entry for logical drive 4 at offset 0GB, size 50GB.

CBR 3 at LBA 100GB on disk
LBA Length Allocated for Physical LBA on disk
0GB 50GB Logical drive 4 100GB (100GB offset of CBR 3 + 0GB offset of partition)

Other Notes

The MBR by itself is enough to determine how much of the disk is partitioned.
Assuming the partitions are well-formed (no logical drives or extended partitions that extend beyond the size of the primary extended partition), the partitions declared in the MBR should encompass all partitions on the disk. Primary partitions are directly represented in this table, and the primary extended partition should encompass all chained extended partitions and logical drives.

Walking the partition tables does not need to be recursive.
Because only one extended partition is allowed in any partition table, the partition tables form a chain, not a tree. Each node in this chain can have either up to four primary partitions, or one extended partition and up to three primary/logical partitions. This makes enumerating partitions easier since you don't have to use a stack or other such structure.

The starting LBA of a partition is relative.
What it's relative to depends on the partition type. For primary/logical partitions, it's relative to the sector the partition is declared in. For primary partitions, this means the MBR at sector 0, and for logical partitions, this means the extended or chained partition they're declared in. The primary extended partition is always declared in the MBR, so it's always relative to sector 0. Chained extended partitions are always relative to the start of the primary extended partition.

That's pretty much it for partition tables. Some of this information is reflected in LBRs of various filesystems. Some LBRs will contain the offset of the partition from the start of the drive ("hidden sectors") which can be a factor when moving a partition, but I don't know if this is actually used or not. LBRs will contain their own length, which can be matched against the partition table for consistency (in the case of NTFS, it can also be used to find the backup boot sector). There's a lot more data and structures in LBRs, but they're specific to individual filesystem types (FAT, FAT32, NTFS, Linux, etc), and beyond the scope of this document.

Copyright (c) 2007 Paul Miner <$firstname.$>, Last Updated 2007/12/03