storage — Storage Device Models
The storage sub-module provides block storage device models for gem5 full-system simulation: an IDE (ATA) disk controller, IDE disk device with full state machine, and flexible disk image backends supporting raw and copy-on-write formats.
IDE Controller IDE Disk Disk Images PIO + DMA
📚 What is this?
The storage module simulates IDE (Integrated Drive Electronics) hard disks so a guest OS can boot from and read/write to virtual disk images. It models the full ATA command protocol, PIO and DMA transfer modes, and the controller-disk interface — allowing standard IDE drivers in Linux or other operating systems to work without modification inside gem5.
💡 Analogy: A Filing Cabinet
Think of the storage sub-module as a filing cabinet system. The IdeDisk is an individual filing cabinet drawer — it holds data organized in numbered sectors (like numbered folders). The IdeController is the desk clerk who accepts requests from the CPU ("Please get me folder #1234") and manages the physical retrieval. The clerk can work in two modes: PIO (manual mode — the CPU carries each folder personally) or DMA (courier mode — the data is delivered directly to your desk without the CPU waiting). The DiskImage is the actual paper stored in the filing cabinet — it can be a real image file or a "copy-on-write" overlay that records changes without modifying the original.
📜 Table of Contents
Storage Architecture
The storage subsystem connects the PCI bus (via the IDE controller) to disk images through a layered architecture:
flowchart TD
subgraph CPU_Side["CPU / Memory Bus"]
CPU["CPU Core"]
BUS["Memory Bus"]
end
subgraph Controller["IDE Controller (PCI Device)"]
CTRL["IdeController\n(BasicPioDevice)"]
CH0["Channel 0\n(Primary)"]
CH1["Channel 1\n(Secondary)"]
end
subgraph Disks["IDE Disks"]
DISK0["IdeDisk 0\n(Master)"]
DISK1["IdeDisk 1\n(Slave)"]
DISK2["IdeDisk 2\n(Master)"]
DISK3["IdeDisk 3\n(Slave)"]
end
subgraph Images["Disk Image Backends"]
RAW["RawDiskImage\n(.img file)"]
COW["CowDiskImage\n(copy-on-write overlay)"]
PARENT["Parent Image\n(read-only base)"]
end
CPU -->|"PIO/DMA"| BUS
BUS -->|"BAR0/BAR1 regs"| CTRL
CTRL --> CH0
CTRL --> CH1
CH0 --> DISK0
CH0 --> DISK1
CH1 --> DISK2
CH1 --> DISK3
DISK0 --> RAW
DISK1 --> COW
COW -->|"reads fall through"| PARENT
IDE Controller
IdeController is a PCI device (inheriting from BasicPioDevice) that implements the standard IDE/ATA controller interface. It manages two channels (primary and secondary), each supporting up to two drives (master and slave).
Controller Registers
The controller exposes its registers through PCI BARs:
| BAR | Space | Contents |
|---|---|---|
| BAR0 | Primary Command Block | Data, Error/Features, Sector Count, LBA registers, Status/Command |
| BAR1 | Primary Control Block | Alternate Status, Device Control |
| BAR2 | Secondary Command Block | Same as BAR0 for secondary channel |
| BAR3 | Secondary Control Block | Same as BAR1 for secondary channel |
| BAR4 | Bus Master IDE | DMA command, status, and PRD table address registers |
Controller Operation
When the guest OS writes to a command register, the controller dispatches the operation to the selected disk on the appropriate channel. For DMA operations, it manages the Physical Region Descriptor (PRD) table that maps scatter-gather DMA transfers.
// IdeController channel structure
class IdeController::Channel {
IdeController *controller();
IdeDisk *device(int index); // 0=master, 1=slave
// Register access from PCI BAR
void accessCommand(Addr offset, int size,
uint8_t *data, bool read);
void accessControl(Addr offset, int size,
uint8_t *data, bool read);
void accessBMI(Addr offset, int size,
uint8_t *data, bool read);
};
IDE Disk
IdeDisk models a single IDE hard drive with a complete ATA state machine. It handles ATA commands (read, write, identify), manages PIO and DMA transfers, and interfaces with a DiskImage backend for persistent storage.
Disk State Machine
The IDE disk operates through a detailed state machine handling the ATA protocol:
stateDiagram-v2
[*] --> Device_Idle_S : Power on / Reset
Device_Idle_S --> Command_Execution : Non-data command
Device_Idle_S --> Prepare_Data_In : PIO read command
Device_Idle_S --> Prepare_Data_Out : PIO write command
Device_Idle_S --> Prepare_Data_Dma : DMA command
Device_Idle_S --> Device_Srst : Software reset
Command_Execution --> Device_Idle_S : Command complete
Prepare_Data_In --> Data_Ready_INTRQ_In : Data loaded
Data_Ready_INTRQ_In --> Transfer_Data_In : Host reads data
Transfer_Data_In --> Data_Ready_INTRQ_In : More sectors
Transfer_Data_In --> Device_Idle_S : All sectors done
Prepare_Data_Out --> Data_Ready_INTRQ_Out : Ready for data
Data_Ready_INTRQ_Out --> Transfer_Data_Out : Host writes data
Transfer_Data_Out --> Data_Ready_INTRQ_Out : More sectors
Transfer_Data_Out --> Device_Idle_S : All sectors done
Prepare_Data_Dma --> Transfer_Data_Dma : DMA engine started
Transfer_Data_Dma --> Device_Idle_S : DMA complete
Transfer_Data_Dma --> Device_Dma_Abort : DMA error
Device_Srst --> Device_Idle_S : Reset complete
Device_Dma_Abort --> Device_Idle_S : Abort handled
Supported ATA Commands
| Command | Transfer Mode | Description |
|---|---|---|
WIN_READ | PIO | Read sectors using PIO transfer |
WIN_WRITE | PIO | Write sectors using PIO transfer |
WIN_READDMA | DMA | Read sectors using DMA transfer |
WIN_WRITEDMA | DMA | Write sectors using DMA transfer |
WIN_IDENTIFY | PIO | Return drive identification data (model, capacity, features) |
WIN_SETMULT | Non-data | Set multiple sector count for PIO multi-sector transfers |
WIN_SRST | Non-data | Software reset of the drive |
WIN_SEEK | Non-data | Seek to a specific LBA sector |
DMA Transfer Flow
DMA transfers use a Physical Region Descriptor (PRD) table in host memory. The controller reads PRD entries to build a scatter-gather list for the DMA engine:
sequenceDiagram
participant OS as Guest OS Driver
participant CTRL as IdeController
participant DISK as IdeDisk
participant DMA as DMA Engine
participant MEM as System Memory
OS->>CTRL: Write LBA, sector count to command regs
OS->>CTRL: Write PRD table base to BMI BAR
OS->>CTRL: Write DMA Read command to BMI
CTRL->>DISK: startDma(prdTableBase)
DISK->>DMA: Read PRD entry from memory
DMA->>MEM: dmaRead(prdAddr)
MEM-->>DMA: PRD entry (baseAddr, byteCount, EOT)
DISK->>DISK: readDisk(sector, dataBuffer)
DISK->>DMA: dmaWrite(prdBaseAddr, data)
DMA->>MEM: Write sector data to host buffer
MEM-->>DMA: Write complete
Note over DISK: Check EOT flag in PRD entry
DISK->>DISK: If more PRDs, repeat
DISK->>CTRL: Transfer complete
CTRL->>OS: Post interrupt (DMA done)
PRD Table Structure
// Physical Region Descriptor entry
struct PrdEntry_t {
uint32_t baseAddr; // Physical address of buffer (bit 0 must be 0)
uint16_t byteCount; // Byte count (0 = 64KB)
uint16_t endOfTable; // Bit 15 = End of Table flag
};
// Constants
#define PRD_BASE_MASK 0xfffffffe // Mask for base address
#define PRD_COUNT_MASK 0xfffe // Mask for byte count
#define PRD_EOT_MASK 0x8000 // End-of-table bit
#define MAX_DMA_SIZE 0x20000 // 128KB max DMA per transfer
#define MAX_SINGLE_DMA_SIZE 0x10000 // 64KB max per PRD entry
Disk Image Backends
The DiskImage hierarchy provides the actual storage backing for IDE disks:
RawDiskImage
A Atomic and Timing Simple CPUs — gem5 Wiki" href="../../cpu_doc/simple/index.html">simple image backed by a raw binary file. Every sector maps directly to a file offset. Supports both read-only and read-write modes.
// Python configuration
raw_image = RawDiskImage(
image_file='disk.img',
read_only=False
)
CowDiskImage (Copy-on-Write)
CowDiskImage layers a write overlay on top of a parent image. Reads fall through to the parent; writes are captured in the overlay. This enables multiple simulations to share one base image without modification:
flowchart TD
READ["Read sector N"] --> CHECK{Sector N in COW table?}
CHECK -->|"Yes"| COW_DATA["Return data from COW overlay"]
CHECK -->|"No"| PARENT["Read from parent image"]
PARENT --> RETURN["Return data"]
COW_DATA --> RETURN
WRITE["Write sector N"] --> COW_WRITE["Store in COW overlay table"]
COW_WRITE --> DONE["Write complete\n(parent unchanged)"]
// Python configuration for copy-on-write
base_image = RawDiskImage(image_file='base.img', read_only=True)
cow_image = CowDiskImage(
child=base_image,
table_size='16MiB', // Hash table size for COW sector tracking
image_file='' // Empty = no persistence; or path to save overlay
)
SimpleDisk
SimpleDisk is a lightweight disk interface that bypasses the full IDE controller. It provides direct disk access for simple full-system configurations where full ATA emulation is not needed.
Key Classes Reference
| Class | File | Inherits | Role |
|---|---|---|---|
IdeController | ide_ctrl.hh | BasicPioDevice | PCI IDE controller with two channels, DMA bus master |
IdeController::Channel | ide_ctrl.hh | — | Single IDE channel managing master/slave disks |
IdeDisk | ide_disk.hh | SimObject | IDE disk device with ATA state machine, PIO/DMA transfers |
DiskImage | disk_image.hh | SimObject | Abstract base class for disk image backends |
RawDiskImage | disk_image.hh | DiskImage | Raw binary file-backed disk image |
CowDiskImage | disk_image.hh | DiskImage | Copy-on-write overlay disk image |
SimpleDisk | simple_disk.hh | SimObject | Simplified direct-access disk (no controller emulation) |
PrdTableEntry | ide_disk.hh | — | Physical Region Descriptor for DMA scatter-gather |
Disk Statistics
IdeDisk tracks detailed I/O statistics for performance analysis:
| Statistic | Description |
|---|---|
dmaReadFullPages | Number of full-page DMA reads |
dmaReadBytes | Total bytes read via DMA |
dmaReadTxs | Number of DMA read transactions |
dmaWriteFullPages | Number of full-page DMA writes |
dmaWriteBytes | Total bytes written via DMA |
dmaWriteTxs | Number of DMA write transactions |
Configuration Reference
| Parameter | Python Class | Default | Description |
|---|---|---|---|
image | IdeDisk | — | DiskImage object providing the storage backend |
driveID | IdeDisk | 'device0' | Drive identifier: device0 (master) or device1 (slave) |
delay | IdeDisk | '1us' | Disk access latency (simulated seek/rotation time) |
image_file | RawDiskImage | — | Path to the raw disk image file |
read_only | RawDiskImage | False | Whether the image is read-only |
child | CowDiskImage | — | Parent DiskImage for read fall-through |
table_size | CowDiskImage | 65536 | Number of entries in the COW hash table |
image_file | CowDiskImage | '' | Optional file path to persist the COW overlay |
pci_bus | IdeController | 0 | PCI bus number |
pci_dev | IdeController | 0 | PCI device number |
disks | IdeController | [] | List of IdeDisk objects (up to 4: 2 per channel) |
Example Configuration
A typical IDE disk setup for full-system simulation:
// Python config — IDE disk with copy-on-write image
base = RawDiskImage(image_file='ubuntu-22.04.img', read_only=True)
disk_image = CowDiskImage(child=base)
disk0 = IdeDisk(driveID='device0', image=disk_image, delay='1us')
disk1 = IdeDisk(driveID='device1',
image=RawDiskImage(image_file='data.img'))
ide = IdeController(
disks=[disk0, disk1], // Channel 0: master + slave
pci_bus=0, pci_dev=1, pci_func=0,
InterruptLine=14
)
system.pci_host.attach(ide)
Walkthrough: Reading a Disk Sector
0xC8 (WIN_READDMA) to the Command register triggers the IdeDisk state machine to transition from Device_Idle_S to Prepare_Data_Dma.readDisk(sector, dataBuffer) calls DiskImage::read() — either hitting the raw file or the COW overlay.Device_Idle_S and posts an interrupt to the CPU through the IDE controller.File Map
storage/ directory structure
src/dev/storage/
├── ide_ctrl.hh / ide_ctrl.cc ← IdeController (PCI IDE controller with channels)
├── ide_disk.hh / ide_disk.cc ← IdeDisk (ATA disk state machine + DMA)
├── ide_atareg.h ← ATA register definitions and ataparams struct
├── ide_wdcreg.h ← WDC (Western Digital) register constants
├── disk_image.hh / disk_image.cc ← DiskImage, RawDiskImage, CowDiskImage
├── simple_disk.hh / simple_disk.cc ← SimpleDisk (lightweight direct access)
├── Ide.py ← Python config for IdeController, IdeDisk
├── DiskImage.py ← Python config for RawDiskImage, CowDiskImage
├── SimpleDisk.py ← Python config for SimpleDisk
└── SConscript ← Build definitions
Glossary
| Term | Definition |
|---|---|
| ATA | AT Attachment — standard interface for connecting storage devices to PCs |
| IDE | Integrated Drive Electronics — the disk has its own controller on-board |
| PIO | Programmed I/O — CPU directly reads/writes data through I/O registers |
| DMA | Direct Memory Access — disk transfers data to/from memory without CPU |
| PRD | Physical Region Descriptor — scatter-gather entry for DMA transfers |
| LBA | Logical Block Addressing — sector addressing scheme (vs. CHS) |
| COW | Copy-on-Write — overlay layer capturing writes, reads fall through to base |
| BMI | Bus Master IDE — DMA controller registers in the IDE controller |