Rigid Disk Block (RDB)

The Rigid Disk Block is the Amiga's on-disk description of a hard disk: drive geometry, partitions, the file systems needed to mount them, bad block maps and optional drive initialisation code. It is not part of any DOS volume — the blocks live in a reserved area at the start of the drive and are read by the controller's boot ROM, by Mounter, by HDToolBox and by any tool that needs to know how a disk is partitioned. All structures are defined in devices/hardblocks.h (47.1 in NDK 3.2); the partition environment uses struct DosEnvec from dos/filehandler.h.

This article documents the five block types, the checksum, the partition environment vector and how the OS 3.2 tools use them.

Layout overview

The header comment of hardblocks.h sets the rules:

  • The RigidDiskBlock must exist "within the first RDB_LOCATION_LIMIT blocks" (RDB_LOCATION_LIMIT = 16).
  • Storing RDB data in the reserved blocks of a partition "is discouraged since the reserved blocks of a partition are overwritten by Format, Install, DiskCopy, etc." The recommended layout uses "the first cylinder(s) to store all the drive data specified by these blocks: i.e. partition descriptions, file system load images, drive bad block maps, spare blocks, etc."
  • All blocks are "tagged with a unique identifier, checksummed, and linked together".
  • Block pointers are block numbers, not byte offsets. Optional block addresses contain $ffffffff to mean NULL, "as zero is a valid address".
  • The description works "with any block size"; array sizes in the structures ([61], [123]) assume 512-byte blocks. 256-byte sectors are deprecated.
 block 0..15        RDSK  ──┬─ rdb_PartitionList ──► PART ─► PART ─► ... (pb_Next)
 (first match)              ├─ rdb_FileSysHeaderList ► FSHD ─► FSHD ─► ... (fhb_Next)
                            │                           └─ fhb_SegListBlocks ► LSEG ─► LSEG ...
                            ├─ rdb_BadBlockList ─────► BADB ─► BADB ─► ... (bbb_Next)
                            └─ rdb_DriveInit ────────► LSEG chain (drive init code)

Block identifiers

Constant Value ASCII Structure
IDNAME_RIGIDDISK 1.3+ 0x5244534B RDSK struct RigidDiskBlock
IDNAME_PARTITION 1.3+ 0x50415254 PART struct PartitionBlock
IDNAME_FILESYSHEADER 1.3+ 0x46534844 FSHD struct FileSysHeaderBlock
IDNAME_LOADSEG 1.3+ 0x4C534547 LSEG struct LoadSegBlock
IDNAME_BADBLOCK 1.3+ 0x42414442 BADB struct BadBlockBlock

Every block starts with the same four longwords:

Offset Field Meaning
0x00 xx_ID 4-character identifier above
0x04 xx_SummedLongs size of the checksummed structure, in longwords
0x08 xx_ChkSum "block checksum (longword sum to zero)"
0x0C xx_HostID SCSI Target ID of host

Checksum

The checksum covers the first SummedLongs longwords of the block. ChkSum is chosen so that the 32-bit sum of all of them, including ChkSum itself, is zero:

#include <exec/types.h>

/* Returns TRUE if the RDB-style block is intact. */
BOOL CheckRDBSum(const ULONG *block, ULONG blockbytes)
{
    ULONG n = block[1];          /* xx_SummedLongs */
    ULONG sum = 0;

    if (n == 0 || n > blockbytes / 4)
        return FALSE;            /* claims more data than the block holds */
    while (n--)
        sum += *block++;
    return sum == 0;
}

/* Recompute the checksum before writing a modified block. */
void FixRDBSum(ULONG *block)
{
    ULONG n = block[1], i, sum = 0;

    block[2] = 0;
    for (i = 0; i < n; i++)
        sum += block[i];
    block[2] = (ULONG)(-(LONG)sum);
}

The n > blockbytes / 4 guard mirrors HDToolBox 40.14: "RDB blocks which report themselves as containing more data than was read from the medium are now ignored and their block checksums are no longer verified."

RigidDiskBlock (RDSK)

struct RigidDiskBlock {
    ULONG   rdb_ID;               /* 'RDSK' */
    ULONG   rdb_SummedLongs;
    LONG    rdb_ChkSum;
    ULONG   rdb_HostID;
    ULONG   rdb_BlockBytes;       /* size of disk blocks */
    ULONG   rdb_Flags;            /* RDBF_* */
    /* block list heads */
    ULONG   rdb_BadBlockList;     /* optional bad block list */
    ULONG   rdb_PartitionList;    /* optional first partition block */
    ULONG   rdb_FileSysHeaderList;/* optional file system header block */
    ULONG   rdb_DriveInit;        /* optional drive-specific init code */
                                  /* DriveInit(lun,rdb,ior): "C" stk & d0/a0/a1 */
    ULONG   rdb_Reserved1[6];     /* set to $ffffffff */
    /* physical drive characteristics */
    ULONG   rdb_Cylinders, rdb_Sectors, rdb_Heads, rdb_Interleave, rdb_Park;
    ULONG   rdb_Reserved2[3];
    ULONG   rdb_WritePreComp, rdb_ReducedWrite, rdb_StepRate;
    ULONG   rdb_Reserved3[5];
    /* logical drive characteristics */
    ULONG   rdb_RDBBlocksLo;      /* low block of range reserved for hardblocks */
    ULONG   rdb_RDBBlocksHi;      /* high block of range for these hardblocks */
    ULONG   rdb_LoCylinder;       /* low cylinder of partitionable disk area */
    ULONG   rdb_HiCylinder;       /* high cylinder of partitionable data area */
    ULONG   rdb_CylBlocks;        /* number of blocks available per cylinder */
    ULONG   rdb_AutoParkSeconds;  /* zero for no auto park */
    ULONG   rdb_HighRDSKBlock;    /* highest block used by RDSK */
    ULONG   rdb_Reserved4;
    /* drive identification */
    char    rdb_DiskVendor[8], rdb_DiskProduct[16], rdb_DiskRevision[4];
    char    rdb_ControllerVendor[8], rdb_ControllerProduct[16], rdb_ControllerRevision[4];
    char    rdb_DriveInitName[40];/* filename of DriveInit source, terminated */
};

(The header declares each field on its own line; they are grouped here for brevity, order unchanged.)

rdb_DriveInitName and fhb_FileSysName are the 47.1 additions: storage "for inserting the actual source filename for files installed on the RDBs as either DriveInit code or Filesystem code. This makes creating a mountfile suitable for use with the C:Mount command" possible.

rdb_Flags

Bit Flag Meaning
0 RDBFF_LAST (0x01) no disks exist to be configured after this one on this controller
1 RDBFF_LASTLUN (0x02) no LUNs exist greater than this one at this SCSI Target ID
2 RDBFF_LASTTID (0x04) no Target IDs exist greater than this one on this SCSI bus
3 RDBFF_NORESELECT (0x08) don't try reselection when talking to this drive
4 RDBFF_DISKID (0x10) rdb_Disk... identification valid
5 RDBFF_CTRLRID (0x20) rdb_Controller... identification valid
6 RDBFF_SYNCH (0x40) drive supports SCSI synchronous mode — "CAN BE DANGEROUS TO USE IF IT DOESN'T!"

PartitionBlock (PART)

struct PartitionBlock {
    ULONG   pb_ID;                /* 'PART' */
    ULONG   pb_SummedLongs;
    LONG    pb_ChkSum;
    ULONG   pb_HostID;
    ULONG   pb_Next;              /* block number of the next PartitionBlock */
    ULONG   pb_Flags;             /* PBFF_* */
    ULONG   pb_Reserved1[2];
    ULONG   pb_DevFlags;          /* preferred flags for OpenDevice */
    UBYTE   pb_DriveName[32];     /* preferred DOS device name: BSTR form */
    ULONG   pb_Reserved2[15];     /* filler to 32 longwords */
    ULONG   pb_Environment[20];   /* environment vector for this partition */
    ULONG   pb_EReserved[12];     /* reserved for future environment vector */
};
Bit Flag Meaning
0 PBFF_BOOTABLE (1) partition intended to be bootable (expected directories and files exist)
1 PBFF_NOMOUNT (2) do not mount this partition (e.g. manually mounted, but space reserved here)

pb_DriveName is a BSTR (length byte first) and is "not used if this name is in use".

The environment vector

pb_Environment[20] has exactly the layout of struct DosEnvec from dos/filehandler.h — the same vector a mountlist produces and that file systems find through FileSysStartupMsg.fssm_Environ. de_TableSize holds the number of longwords that follow (the header: "set to the number of longwords in the table minus 1"; minimum 11).

Index DE_* constant DosEnvec field Mountlist keyword (where listed in the Mountlist reference) Notes from filehandler.h
0 DE_TABLESIZE de_TableSize — size of vector
1 DE_SIZEBLOCK de_SizeBlock SectorSize (÷4) in longwords; standard 128
2 DE_SECORG de_SecOrg — not used; must be 0
3 DE_NUMHEADS de_Surfaces Surfaces heads
4 DE_SECSPERBLK de_SectorPerBlock — physical sectors per logical block
5 DE_BLKSPERTRACK de_BlocksPerTrack BlocksPerTrack
6 DE_RESERVEDBLKS de_Reserved Reserved reserved blocks at start, usually 2
7 DE_PREFAC de_PreAlloc — reserved blocks at end
8 DE_INTERLEAVE de_Interleave Interleave low 16 bits usually 0; upper bits = ENVF_* flags
9 DE_LOWCYL de_LowCyl LowCyl
10 DE_UPPERCYL de_HighCyl HighCyl
11 DE_NUMBUFFERS de_NumBuffers Buffers
12 DE_BUFMEMTYPE de_BufMemType BufMemType "1 is public, 3 is chip, 5 is fast"
13 DE_MAXTRANSFER de_MaxTransfer MaxTransfer max bytes per transfer
14 DE_MASK de_Mask Mask DMA address mask
15 DE_BOOTPRI de_BootPri BootPri
16 DE_DOSTYPE de_DosType DosType must match an FSHD fhb_DosType
17 DE_BAUD de_Baud — serial handlers
18 DE_CONTROL de_Control —
19 DE_BOOTBLOCKS de_BootBlocks — blocks containing boot code

Partition size in bytes is (HighCyl − LowCyl + 1) × Surfaces × BlocksPerTrack × SizeBlock × 4. HDToolBox and Mounter compute this in 64 bits (Mounter 44.7 "Partition size calculation now also takes the number of sectors per block into account").

Interleave flags (3.1.4+)

Flag Value Effect
ENVF_SCSIDIRECT 3.2+ 1L << 16 "Use HD_SCSICMD instead of TD_READ/TD_READ64"
ENVF_SUPERFLOPPY 3.2+ 1L << 17 "Request device geometry by TD_GETGEOMETRY or HD_SCSICMD, DE_LOWCYL must be 0"
ENVF_DISABLENSD 3.2+ 1L << 18 "Do not query for NSD"

See OS 3.2 File System and Handler Changes for how FFS interprets them.

FileSysHeaderBlock (FSHD)

An FSHD block describes a file system stored on the disk. Its layout after the common header mirrors a DOS DeviceNode (and struct FileSysEntry in resources/filesysres.h):

struct FileSysHeaderBlock {
    ULONG   fhb_ID;               /* 'FSHD' */
    ULONG   fhb_SummedLongs;
    LONG    fhb_ChkSum;
    ULONG   fhb_HostID;
    ULONG   fhb_Next;             /* block number of next FileSysHeaderBlock */
    ULONG   fhb_Flags;
    ULONG   fhb_Reserved1[2];
    ULONG   fhb_DosType;          /* match with partition's DE_DOSTYPE */
    ULONG   fhb_Version;          /* release version of this code */
    ULONG   fhb_PatchFlags;       /* which of the following to substitute */
    ULONG   fhb_Type;             /* device node type: zero */
    ULONG   fhb_Task;             /* standard dos "task" field: zero */
    ULONG   fhb_Lock;             /* not used for devices: zero */
    ULONG   fhb_Handler;          /* filename to loadseg: zero placeholder */
    ULONG   fhb_StackSize;        /* stacksize to use when starting task */
    LONG    fhb_Priority;         /* task priority when starting task */
    LONG    fhb_Startup;          /* startup msg: zero placeholder */
    LONG    fhb_SegListBlocks;    /* first of linked list of LoadSegBlocks */
    LONG    fhb_GlobalVec;        /* BCPL global vector when starting task */
    ULONG   fhb_Reserved2[23];    /* (those reserved by PatchFlags) */
    char    fhb_FileSysName[84];  /* File system file name as loaded. */
};

fhb_PatchFlags

Bit n of fhb_PatchFlags means "substitute the n-th longword starting at fhb_Type into the device node". The header's example is 0x180 "to substitute SegList & GlobalVec":

Bit Mask FSHD field DeviceNode field
0 0x001 fhb_Type dn_Type
1 0x002 fhb_Task dn_Task
2 0x004 fhb_Lock dn_Lock
3 0x008 fhb_Handler dn_Handler
4 0x010 fhb_StackSize dn_StackSize
5 0x020 fhb_Priority dn_Priority
6 0x040 fhb_Startup dn_Startup
7 0x080 fhb_SegListBlocks dn_SegList (after loading the LSEG chain)
8 0x100 fhb_GlobalVec dn_GlobalVec

Remember the stack unit rule when filling fhb_StackSize: LONGs for BCPL handlers (GlobVec 0), bytes for C handlers (GlobVec −1). HDToolBox 46.23 now writes a larger default than 600 bytes. FFS 46.20 changed its own patch flags "to also include a larger stack size, and to signal a C type mount."

HDToolBox 46.24 stopped generating patch flags that override the block size when the user selected a block size other than 512: such an entry made the file system use that size "regardless of what the RDB has to say about it, potentially causing invalid mounts or even data loss."

LoadSegBlock (LSEG)

struct LoadSegBlock {
    ULONG   lsb_ID;               /* 'LSEG' */
    ULONG   lsb_SummedLongs;
    LONG    lsb_ChkSum;
    ULONG   lsb_HostID;
    ULONG   lsb_Next;             /* block number of the next LoadSegBlock */
    ULONG   lsb_LoadData[123];    /* data for "loadseg" (512-byte blocks) */
};

The lsb_LoadData of consecutive blocks, concatenated, form an ordinary hunk executable. A loader feeds this stream to InternalLoadSeg() with a custom read function. Mounter 44.4 records the trap: the read function in the function table "was following the dos.library documentation and would consequently use the wrong registers" — LSEG loading in Mounter was broken until that fix. The header asks that LSEG blocks "make most efficient use of the disk block size possible".

BadBlockBlock (BADB)

struct BadBlockEntry {
    ULONG   bbe_BadBlock;         /* block number of bad block */
    ULONG   bbe_GoodBlock;        /* block number of replacement block */
};

struct BadBlockBlock {
    ULONG   bbb_ID;               /* 'BADB' */
    ULONG   bbb_SummedLongs;
    LONG    bbb_ChkSum;
    ULONG   bbb_HostID;
    ULONG   bbb_Next;             /* block number of the next BadBlockBlock */
    ULONG   bbb_Reserved;
    struct BadBlockEntry bbb_BlockPairs[61];  /* assumes 512-byte blocks */
};

rdb_HighRDSKBlock is the highest block used by the RDB structures "not including replacement bad blocks".

DOS types

fhb_DosType and de_DosType use the identifiers from dos/dos.h:

Constant Value ASCII File system
ID_DOS_DISK 1.3+ 0x444F5300 DOS\0 OFS
ID_FFS_DISK 2.0+ 0x444F5301 DOS\1 FFS
ID_INTER_DOS_DISK 2.0+ 0x444F5302 DOS\2 OFS, international
ID_INTER_FFS_DISK 2.0+ 0x444F5303 DOS\3 FFS, international
ID_FASTDIR_DOS_DISK 3.1+ 0x444F5304 DOS\4 OFS, directory cache
ID_FASTDIR_FFS_DISK 3.1+ 0x444F5305 DOS\5 FFS, directory cache
ID_LONG_DOS_DISK 3.2+ 0x444F5306 DOS\6 OFS, long file names (FFS V46+)
ID_LONG_FFS_DISK 3.2+ 0x444F5307 DOS\7 FFS, long file names (FFS V46+)

dos/dos.h also defines ID_COMPLONG_FFS_DISK (DOS\8), the identifier of the earlier long-name FFS variant that HDToolBox 46.1 replaced with DOS\6/DOS\7. HDToolBox 46.8 made DOS\3 the default for newly created drive types. The 3.9-era method of selecting SCSI direct access by setting bit 7 of the DOS type (0x444F5383) is phased out in favour of ENVF_SCSIDIRECT.

The partition's own boot block (first reserved block) carries the DOS type as well; devices/bootblock.h defines BBID_DOS / BBNAME_DOS (0x444F5300), BBID_KICK / BBNAME_KICK (0x4B49434B) and BOOTSECTS = 2 ("1K bootstrap"):

struct BootBlock {
    UBYTE   bb_id[4];     /* 4 character identifier */
    LONG    bb_chksum;    /* boot block checksum (balance) */
    LONG    bb_dosblock;  /* reserved for DOS patch */
};

Reading the RDB: example

The program reads the first RDB_LOCATION_LIMIT blocks of a unit, locates RDSK, and lists the partitions. It assumes 512-byte blocks; production code must handle rdb_BlockBytes (HDToolBox 40.13: the RDB is read "with each block containing as many bytes as there are in a sector", so the RDB "is by no means independent of the medium which it is stored on").

#include <exec/memory.h>
#include <exec/io.h>
#include <devices/hardblocks.h>
#include <dos/filehandler.h>
#include <proto/exec.h>
#include <proto/dos.h>

#define BSIZE 512

static BOOL ReadBlock(struct IOStdReq *io, ULONG blk, APTR buf)
{
    io->io_Command = CMD_READ;
    io->io_Data    = buf;
    io->io_Length  = BSIZE;
    io->io_Offset  = blk * BSIZE;           /* < 4 GB only; use TD64/NSD beyond */
    return (BOOL)(DoIO((struct IORequest *)io) == 0);
}

static BOOL SumOK(const ULONG *b)
{
    ULONG n = b[1], s = 0;
    if (n == 0 || n > BSIZE / 4) return FALSE;
    while (n--) s += *b++;
    return (BOOL)(s == 0);
}

int main(void)
{
    struct MsgPort  *port;
    struct IOStdReq *io;
    ULONG *buf;
    ULONG blk, part;
    char name[32];

    if (!(port = CreateMsgPort())) return 20;
    if ((io = (struct IOStdReq *)CreateIORequest(port, sizeof(*io))) != NULL)
    {
        /* MEMF_PUBLIC is not enough for every controller: honour the
           partition Mask/BufMemType in real code */
        if ((buf = AllocVec(BSIZE, MEMF_PUBLIC)) != NULL)
        {
            if (OpenDevice("scsi.device", 0, (struct IORequest *)io, 0) == 0)
            {
                for (blk = 0; blk < RDB_LOCATION_LIMIT; blk++)
                    if (ReadBlock(io, blk, buf) && buf[0] == IDNAME_RIGIDDISK && SumOK(buf))
                        break;

                if (blk < RDB_LOCATION_LIMIT)
                {
                    struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)buf;
                    Printf("RDSK at block %lu: %lu cyl, %lu heads, %lu sectors\n",
                           blk, rdb->rdb_Cylinders, rdb->rdb_Heads, rdb->rdb_Sectors);

                    part = rdb->rdb_PartitionList;
                    while (part != 0xFFFFFFFF && ReadBlock(io, part, buf))
                    {
                        struct PartitionBlock *pb = (struct PartitionBlock *)buf;
                        ULONG *env = pb->pb_Environment;
                        ULONG len;

                        if (pb->pb_ID != IDNAME_PARTITION || !SumOK(buf))
                            break;
                        len = pb->pb_DriveName[0] < 31 ? pb->pb_DriveName[0] : 31;
                        CopyMem(&pb->pb_DriveName[1], name, len);
                        name[len] = '\0';
                        Printf("%-8s cyl %lu-%lu DosType 0x%08lx%s%s\n", name,
                               env[DE_LOWCYL], env[DE_UPPERCYL],
                               env[DE_TABLESIZE] >= DE_DOSTYPE ? env[DE_DOSTYPE] : 0,
                               (pb->pb_Flags & PBFF_BOOTABLE) ? " bootable" : "",
                               (pb->pb_Flags & PBFF_NOMOUNT)  ? " nomount"  : "");
                        part = pb->pb_Next;
                    }
                }
                else
                    PutStr("No RDB found\n");
                CloseDevice((struct IORequest *)io);
            }
            FreeVec(buf);
        }
        DeleteIORequest((struct IORequest *)io);
    }
    DeleteMsgPort(port);
    return 0;
}

How the OS 3.2 tools use the RDB

Boot ROM and system-startup

Autobooting controllers add their partitions as boot nodes to ExpansionBase's MountList. In 3.2, system-startup mounts those nodes (dos 47.9 no longer does) and, if a newer FFS than the ROM one is found in the RDB, places it in FileSystem.resource and the resident list. The FFS itself is upgraded only from the RDB, "not from file" (system-startup autodoc). See OS 3.2 Boot Process.

Mounter

Mounter (Workbench tool, 3.2 version 47.x) mounts partitions from an RDB that were not mounted at boot.

  • LSEG loading via InternalLoadSeg() fixed in 44.4 (see above).
  • Media size via the SCSI "Read capacity" command (44.6), 64-bit size math (44.7).
  • On remount, checks whether the previously active partition will let go of its volume (51.5).
  • NOREMOUNT option (44.3); LUN support (47.3); requires dos.library V37+.

HDToolBox

HDToolBox writes the RDB. Behaviour and options documented in the release notes:

Item Detail
LIMIT_4GBYTE Limit partitions to the first 4 GB; values on/off, true/false, yes/no (47.8). Automatically applied for scsi.device < 43.
MIN_SECTOR_SIZE, MAX_SECTOR_SIZE Override the sector size reported by the drive; multiple of 512 and power of two. Cylinders are adjusted to preserve medium size.
MASK, BUFMEMTYPE, MAXTRANSFER Override defaults written into partitions; hexadecimal, 0x or $ prefix accepted (46.13). HDToolBox honours MASK/BUFMEMTYPE for its own I/O.
SKIP_WRITE_PROTECTED Skip updating the drive definitions file if it or its volume is write-protected (47.7).
SCSI direct Checkmark gadget to enable SCSI direct transfers (40.23).
Long names / dir cache File system type gadgets for DOS\6/DOS\7 (46.1) and DOS\4/DOS\5.
BufMemType Editable per partition (47.1).
Large disks With more than 2^23 blocks the default block size is 4096 (8 sectors per block), and blocks per cylinder are rounded to a multiple of 8 so partitions start on 4K boundaries (46.21).
RDB area The first partition's low cylinder is chosen so the RDB area is at least 128K (46.20) instead of always cylinder 2.
FFS No warning if the ROM contains FFS ≥ V45 and the disk has none (46.11).

See Also


Sources: NDK 3.2 R4 Include_H/devices/hardblocks.h, devices/bootblock.h, dos/filehandler.h, dos/dos.h, resources/filesysres.h, libraries/expansionbase.h; ReleaseNotes/HDToolBox-RelNotes, Mounter-RelNotes, FastFileSystem-RelNotes, system-startup-RelNotes, DOS-RelNotes; Autodocs/system-startup.doc Updated: 2026-11-13