the LEAN file system

eMBR partitioning specification 1.05

This document describes version 1.05 of the eMBR partitioning system: a free, simple, fully featured disk partitioning system.

The eMBR partitioning system is in the release development stage: this document supersedes any previous version of the specification with no care for backward compatibility.

Since this is a unique partitioning system, and some aspects are still to be defined, suggestions or corrections are welcome, for either the partitioning system or this document. Please contact the author at: fys at fysnet.net. The author wishes to thank those who have submitted comments and criticism in order to improve the eMBR partitioning system.

This document contains the formal specification of the eMBR partitioning on-disk format. For an overview of emBR and for downloads, please see the eMBR home page.

Table of contents

Differences from the previous versions

Definitions

The following terms and conventions will be used throughout this specification:

Binary multiples

Table 1 below shows the Binary Multiples and their names which may be used within this specification.

Binary multiples
nameSymbolValue (Binary)Value (Decimal, General disk size labels)
Kilok/K210 = 1,024103 = 1,000
MegaM220 = 1,048,576106 = 1,000,000
GigaG230 = 1,073,741,824109 = 1,000,000,000
TeraT240 = 1,099,511,627,7761012 = 1,000,000,000,000
PetaP250 = 1,125,899,906,842,6241015 = 1,000,000,000,000,000
ExaE260 = 1,152,921,504,606,846,9761018 = 1,000,000,000,000,000,000
ZettaZ270 = 1,180,591,620,717,411,303,4241021 = 1,000,000,000,000,000,000,000
YottaY280 = 1,208,925,819,614,629,174,706,1761024 = 1,000,000,000,000,000,000,000,000
Next would be Ronna then Quetta, but who needs more than 64k of RAM anyway!

Structure identification and checksum

Some structures of the eMBR specification may include a field to make the partitioning system more robust.

A sensitive structure, such as the partition header, may store a checksum field and is computed on a specified block of data. The definition and technique to calculate this checksum is defined by the official CRC-32 standard. The checksum must be recomputed at least every time a sensitive structure or data area is written back to disk.

The following functions show how to calculate the checksum. The data parameter is a pointer to the data area to be checked. The size parameter is the size in bytes of the structure pointed to by data. A driver must initialize the crc32_table once before calling any of the remaining routines. A call to crc32_initialize() may be used.

Please note that the checksum field in the partition header must not be included in the checksum calculation. Initially setting this field to zero will allow it to be a part of the check.

/* Predefined polynomial */
#define CRC32_POLYNOMIAL 0x04C11DB7

/* Lookup table. Must be pre-initialized. */
uint32_t crc32_table[256];

/* Initialize table.
 *  no parameters
 */
void crc32_initialize(void) {
  // 256 values representing ASCII character codes.
  for (int i=0; i<256; i++) {
    crc32_table[i] = crc32_reflect(i, 8) << 24;
    
    for (int j=0; j<8; j++)
      crc32_table[i] = (crc32_table[i] << 1) ^ ((crc32_table[i] & (1 << 31)) ? CRC32_POLYNOMIAL : 0);
    
    crc32_table[i] = crc32_reflect(crc32_table[i], 32);
  }
}

/* Reflection:
 * reflect = current value to process
 * ch = size in bits of value
 * (Reflection is a requirement for the official CRC-32 standard.
 *  You can create CRCs without it, but they won't conform to the standard.)
 */
uint32_t crc32_reflect(uint32_t reflect, char ch) {
  uint32_t ret = 0;
  
  // Swap bit 0 for bit ch-1, bit 1 For bit ch-2, etc....
  for (int i=1; i<(ch + 1); i++) {
    if (reflect & 1)
      ret |= 1 << (ch - i);
    reflect >>= 1;
  }
  
  return ret;
}

/* Compute the checksum of an area.
 * data -> data area to be checked.
 * len = count in bytes of area to check.
 */
uint32_t crc32(void *data, uint32_t len) {
  uint32_t crc = 0xFFFFFFFF;
  crc32_partial(&crc, data, len);
  return (crc ^ 0xFFFFFFFF);
}

/* Compute the checksum of a partial area.
 * crc -> running checksum value.
 * ptr -> data area to be checked.
 * len = count in bytes of area to check.
 */
void crc32_partial(uint32_t *crc, void *ptr, uint32_t len) {
  uint8_t *data = (uint8_t *) ptr;
  while (len--)
    *crc = (*crc >> 8) ^ crc32_table[(*crc & 0xFF) ^ *data++];
}

Some structures also contain one or more magic fields storing a 32-bit constant signature identifying the structure. This can be used as a first test to validate a sensitive structure.

Layout of an eMBR partitioned device

Figure 1: Layout of an eMBR partitioned disk.

Layout

A typical eMBR partitioned media device would contain two or more partitions, each just after the other, all listed in the eMBR headers. At boot time, the eMBR application would load the specified amount of sectors into memory to be able to enumerate the partitions, and display the partitions in a form that the user could choose which partition to boot. The eMBR app would also watch for a key press from the user. If a key press was not found within the specified amount of time, the eMBR app would boot the partition that was most recently booted.

This specification will show the required format and steps to create and boot the eMBR partition scheme and show the format of the partition entries. It is outside this specification on what type of firmware is used to boot from the eMBR partitioned device. If a legacy firmware is used, it is assumed to be a 16-bit Intel® x86 style firmware.

Figure 1 shown to the right, shows a typical layout for an eMBR partitioned disk.

The format of the first few sectors of the disk must follow the outline within this specification. Once the eMBR format is complete, the format of the remaining sectors of the disk is outside this specification.

This specification does not limit the sector size to 512 bytes per sector. However, please note that with the MBR explained below and the offset of the Signature Block within the first sector of the eMBR partitioned area, the sector size is assumed 512. Therefore, if the sector size is more than 512, these offsets remain at the locations as indicated within this specification.

Legacy MBR

The first sector of the disk must contain a Legacy Master Boot Record. The format of this MBR must follow the format originally designed for PC DOS 2.0 in March of 1983. It may contain more modern features but must maintain the standard partition entry table starting at offset 0x01BE. This MBR must contain these four partition entries, with the first one starting at offset 0x01BE, and must contain the standard boot signature at offset 0x01FE, a byte of 0x55 at offset 0x01FE followed by a byte of 0xAA.

The first partition entry must point to the second sector of the disk, LBA 1. This partition table entry must have the values listed in Table 2 below.

First Legacy Partition Entry
uint8_t boot_indicatorThis value, when set to 0x80, instructs a legacy firmware that this partition is a bootable partition. For legacy systems, this field must be 0x80. For UEFI systems, this field can be ignored.
uint8_t start[3]This is the Legacy CHS value for the start of this partition. This field must be 0x00 (head), 0x02 (sector), 0x00 (cyl) respectively.
uint8_t sys_indicatorThis is the System Indicator. This field must have a value of 0xE0. This is an otherwise unused value that I have chosen for this specification.
uint8_t end[3]This is the Legacy CHS value for the end of this partition. This field might be 0xFF, 0xFF, 0xFF respectively, though read a later note for why it may be different.
uint32_t start_lbaThe starting sector of the partition. this field must have a value of 0x00000001.
uint32_t countThis is the count of sectors used for this eMBR partitioning scheme starting at LBA 1. This field might have a value of 0xFFFFFFFF

The values in the other three partition entries are not defined within this specification. A driver may use the other three entries to boot other partitions. This specification does not require that the eMBR be the only bootable partition. However, see the notes on "other partitions entries" below.

Please note that it is not a requirement for the Ending CHS and Sector Count fields to be the values defined above. If the disk is not larger than the amount specified, you must use the actual size values to denote the size of the disk. However, if the disk has less than 232 sectors, there would be little reason to use this specification.

Definition of any other part of the Legacy MBR is outside this specification. An implementation may choose to place code within this Legacy MBR to boot one of the included partitions, as long as that partition is within the location and size specifications of a Legacy partition specification. A Legacy boot code must not modify any part of the sectors described within this specification.

eMBR Partition Start

The eMBR partition scheme must start at LBA 1 of the disk. When booted with legacy firmware, within these first few sectors is the code to load the remaining sectors that contain the eMBR Partition Entries, along with the code to display and allow the user to select which partition to boot. This specification limits the amount of sectors reserved for this code and any eMBR partition entries. See the end of this section for information on this limit. When a modern UEFI firmware is used, only the first sector is required, allowing the remaining sectors to be used for entries.

The first sector of the eMBR partition scheme, LBA 1, must contain the following information at offset 0x01F2, shown in Table 3 below. Remember that any code before this offset must jump over this memory block when continuing any (optionally) remaining code.

Table 3: eMBR Signature Block
uint8_t signature[8]This is the eMBR signature defining this block contains valid values. It must be checked before proceeding. This field must contain the string 'EmbrrbmE' = 0x456D627272626D45.
uint16_t sect_offsetThis is the sector offset to the eMBR Partition Header Block, relative from LBA 0. This limits the count of sectors for the boot code at LBA 1 to 65,534 sectors. This value must be at least 2, leaving one (optionally) used sector for the code and this required Signature Block. See the note below for why this field may be limited.
uint16_t remainingThis is the count of sectors remaining that must be loaded to include all of the code (not counting this first sector), the eMBR Partition Header, and all of the eMBR Partition Entries that are within the containment of this eMBR partition. A Legacy firmware driver must load these sectors into memory, following the current sector, to be able to read and parse the available entries to display and boot. An UEFI firmware driver must load count = ((remaining + 1) - (sect_offset - 1) + 1) sectors from sect_offset. See the note below for a limit on this field.
uint16_t legacy_sigThis is the Legacy boot sector signature, 0xAA55, of offset 0x01FE of the sector (in little-endian format).

The remaining area from LBA 1 to the LBA specified by 1 + remaining, not counting the Signature Block specified above, is for the use of the eMBR driver. If this is a Legacy bootable device, this area must contain all of the code and data needed to parse, display, and boot the desired partition.

It is recommended, but not required, that you include at least one unused sector at the end of this code block for future improvements. It is also recommended that you included a few unused sectors at the end of the partition entry list for future partition entries. i.e.: The sect_offset value be a few more sectors past what is needed for the code and the remaining value be a few more sectors than is needed to hold the current count of partition entries.

An eMBR partitioning scheme partitioned device does not necessarily have to be bootable by legacy firmware. A device can be partitioned using this scheme and booted via modern UEFI firmware. With this in mind, there only needs to be a minimal of one sector present after the MBR and before the sector containing the Partition Header. If this is the case, this sector must contain a minimal amount of 16-bit realmode code to simply display a message and halt if ever it is booted from legacy firmware.

If this partitioned device is legacy firmware bootable, there must be a limit to the count of sectors used for code and sectors used for Partition Entries, so that a 16-bit legacy firmware can access all code and Partition Entries with a single 64k offset. Therefore, for legacy firmware devices, this specification limits the count of remaining sectors used (defined in the remaining member above) times the size of a sector to 64k. In other words, all code and data used must fit within 65,536 bytes starting with the first byte at LBA 1.

Therefore, for Legacy firmware devices, the following statement must be true: ((1 + remaining) * bytes_per_sector) <= 65536, giving a limit of just more than 500 partition entries, depending on the size of the code present to parse them. If legacy firmware code is present, it must check that this limit is adhered to before continuing on.

When a device is booted via modern UEFI firmware, the count of partition entries is only limited by the 16-bit entry_count field below. Therefore, when booting using UEFI firmware, this limit is now at 65,535 entries.

The eMBR Partition Header will be at the LBA specified in the 16-bit value of sect_offset in the Signature Block detailed in the previous section. This block and all remaining partition entries should have been read into memory with a capable driver outlined in the previous section. The size in bytes of this block containing this eMBR Partition Header and all Partition Entries will be the value in remaining in the Signature Block above, plus 1 for the sector containing the Signature Block, minus sect_offset, then multiple by the size of a sector.

The eMBR Partition Header, detailed below, contains the information needed to know how many partition entries are included, the boot delay time to wait for user input, and a 32-bit checksum to verify validity of the entries. This header is shown in Table 4 below.

Table 4: eMBR Partition Header Block
uint32_t magic0This is the first signature value used to validate this block. It must contain 'EMBR' = 0x52424D45
uint32_t checksumThis is the 32-bit checksum of this block plus a count of entry_count entries that follow. When calling a checksum calculation that includes this field, be sure to initially zero this field to get a correct calculation, optionally restoring the value or setting to the new calculated value. See the end of this section for a calculation on the count of bytes to check.
uint16_t entry_countThis is the count of entries that follow this block. See the next section for the format of an entry.
uint8_t boot_delayThis is the count in seconds that the driver should delay before automatically choosing the last booted partition. Zero may be used for no delay. However, please note that if there is no delay, the user must use a different form of access to modify the saved delay value. It is recommended that a driver check for a key press before checking the delay value of zero.
uint8_t versionVersion of the specification this partitioning scheme supports. The high 3 bits are the major version, with the lower 5 bits as the minor version. If this document is version 1.05, it would be stored as a byte as 00100101b in binary, 0x25 in hex, or 37 in decimal. Since backward compatibility makes no sense, this value must be no less than 0x25, allowing the minor version to increase since it designates no structural changes.
uint64_t total_sectorsThis is the total count of sectors used by this partitioning scheme. This will be the count of sectors from LBA 0 to the last used sector this eMBR describes. Optionally, if they exist, this can also be used as the LBA of the first sector after all the sectors this eMBR encompasses.
uint8_t reserved[8]Reserved and preserved.
uint32_t magic1This is the ending signature value used to validate this block. It must contain 'RBME' = 0x454D4252

A driver should verify the two 32-bit signature values along with verifying the 32-bit checksum of this header and the following partition entries. The checksum follows that of the official CRC-32 standard and uses the 0x04C11DB7 Polynomial.

The checksum is done consecutively on this 32-byte block and the amount of memory following, sized by the entry_count field, times the size of a partition entry defined later. If a partition entry is marked invalid, the checksum is still done on that entry. For example, the checksum is done on the memory starting at this Partition Header Block and for the bytes that follow with the length calculated as:

byte count to check = ('size of this header' + (entry_count * 'size of an entry'))

eMBR Partition Entry

All eMBR Partition Entries follow the Partition Header described in the previous section. Each partition entry is 128 bytes in length and contains the format shown in Table 5 below.

Table 5: eMBR Partition Entry Format
uint32_t flagsFlags for this partition: bit 0, when set, is used to indicate that this partition entry is valid and used. bit 1, when set, instructs the driver to hide this partition from a default listing. If bit 1 is set, it should hide any unused (bit 0 = 0) entries as well. All other bits are reserved and must be preserved.
uint32_t magicThis is a signature to help ensure a valid/used entry. When this entry is used, bit 0 above is set, this must contain 'eMBR' = 0x52424D65. When this entry is not used, bit 0 above is clear, this field must zero.
uint64_t base_lbaThis is the starting sector of this partition relative to the start of the disk.
uint64_t sizeThis is the sector count of this partition.
uint8_t name[64]This is a null terminated UTF-8 string describing this partition.
uint64_t createdThis is the seconds since 01 Jan 1980 this partition was created.
uint64_t bootedThis is the seconds since 01 Jan 1980 this partition was last booted. If this value is zero, this partition has not yet been booted.
uint64_t os_signatureThis is an 8-byte signature used by the operating system that exists on this partition. The value of this field is not defined by this specification. This field must be preserved by a driver when not in use.
uint8_t reserved[16]Reserved and preserved.

Requirements

It is not required that all partition entries be valid. However, any entry (valid or not) must be at an offset of 128 bytes from the last entry with the first entry following directly after the Partition Header.

A partition must not overlap another partition. Any sector contained within one partition must not be contained within another partition.

The size of a partition does not have to occupy any sectors after that partition and before the next partition. Any sector count between partitions must be considered lost and unusable. This space must not be written to or if read from byte any eMBR driver.

The first partition, the partition with the lowest starting LBA, is not required to start directly after the Partition Entry List.

The booted entry must have its elapsed time updated to the current elapsed seconds since the Epoch date specified within this specification just before the driver transfers control to the loaded partition. Other than this item, it is outside this specification on whether the remaining values within the partition entry and Partition Header are modified. However, please note that when you modify an entry, you must update the checksum in the Partition Header.

It is outside this specification on how a user adds, deletes, or modifies a Partition Entry, as long as the resulting modification follows the format listed within this specification.

All LBA's used within this specification, unless otherwise noted, are relative to the start of the disk, LBA 0.

A combined value of a partition's base_lba and size must not exceed 264-1 sectors.

Any of the other three partition entries in the Legacy MBR at LBA 0 may point to any partitions within the eMBR scheme with the restriction that any of these partitions must reside within the 232-1 count of sectors from the start of the disk. Any non-eMBR driver must not modify any of the sectors from LBA 1 to LBA 1 + remaining.

The volume boot record residing at the first sector of any partition must not assume that it was initiated by an eMBR loader. It must parse the disk or use an alternative procedure to determine where it is located, and any procedure to do so is outside this specification.

User interface

It is outside of this specification on how the user interfaces with the driver or how the driver displays the list of partitions. It is suggested that a list be shown similar to Figure 2 shown here. This is an example of a Legacy firmware booted disk.

Suggested User Interface

Figure 2: Suggested User Interface.

It is suggested that at the first point of user intervention (a key press) that the countdown to boot be stopped and the interface wait for the user to continue. It is also suggested that the driver allow the user to adjust the countdown time for future boots.

It is suggested that the user be able to indicate to the driver to display and possibly edit the OS Signature Block within the Partition Entry.

Please see the code listed in the downloads section for a good example of a legacy firmware booted app that follows this specification when listing and booting the included partitions.

Things to consider

Since this technique of disk partitioning allows for much larger disk sizes than what was previously available, you will need to take the following notes into consideration.

End of the eMBR partitioning system specification