BangOS ATA / IDE Storage Driver Architecture
This document provides a comprehensive, educational overview of the Advanced Technology Attachment (ATA/IDE) block storage device driver implemented in BangOS.
1. Hardware Architecture & Register Specifications
The ATA interface (also historically known as IDE - Integrated Drive Electronics) connects storage drives (hard disk drives, solid-state drives, CD-ROMs) to the system bus through dedicated I/O ports.
In standard PC/AT and x86_64 IBM-compatible architectures, two legacy IDE channels exist:
| Channel | Data / Command I/O Base | Control / Alt Status Base | Legacy IRQ |
|---|---|---|---|
| Primary (ATA 0/1) | 0x1F0 - 0x1F7 |
0x3F6 |
IRQ 14 |
| Secondary (ATA 2/3) | 0x170 - 0x177 |
0x376 |
IRQ 15 |
Each IDE channel supports up to two drives:
- Drive 0 (Master)
- Drive 1 (Slave)
1.1 ATA Controller Register Map (Command Block)
| Offset | Register Name | Direction | Function |
|---|---|---|---|
+0 (0x1F0) |
DATA |
Read / Write | 16-bit Data Port for sector PIO transfers |
+1 (0x1F1) |
ERROR / FEATURES |
Read / Write | Error register (read) / Feature register (write) |
+2 (0x1F2) |
SEC_COUNT |
Read / Write | Number of 512-byte sectors to transfer (0 = 256 sectors in LBA28) |
+3 (0x1F3) |
LBA_LO |
Read / Write | LBA Bits 0 - 7 |
+4 (0x1F4) |
LBA_MID |
Read / Write | LBA Bits 8 - 15 |
+5 (0x1F5) |
LBA_HI |
Read / Write | LBA Bits 16 - 23 |
+6 (0x1F6) |
DRIVE_SELECT |
Read / Write | Drive selector & LBA mode flags (bits 24-27 in LBA28) |
+7 (0x1F7) |
STATUS / COMMAND |
Read / Write | Controller Status (read) / Command issue (write) |
1.2 Status Register Bits (0x1F7 / 0x3F6)
7 6 5 4 3 2 1 0
+-------+-------+-------+-------+-------+-------+-------+-------+
| BSY | DRDY | DF | DSC | DRQ | CORR | IDX | ERR |
+-------+-------+-------+-------+-------+-------+-------+-------+
BSY(Bit 7 - Busy): Controller is executing a command; ports cannot be accessed.DRDY(Bit 6 - Drive Ready): Drive has spun up and is ready to accept commands.DF(Bit 5 - Drive Fault): Device write fault occurred.DRQ(Bit 3 - Data Request): Drive is ready to transmit/receive 16-bit sector words via theDATAport (0x1F0).ERR(Bit 0 - Error): Command resulted in an error; details available in0x1F1.
2. Logical Block Addressing (LBA) vs CHS
Older disks addressed sectors using Cylinder-Head-Sector (CHS). BangOS exclusively utilizes Logical Block Addressing (LBA), where every 512-byte sector is indexed as a continuous linear integer from 0 to N-1.
2.1 LBA28 Addressing Format (Up to 128 GB)
In LBA28:
LBA_LOholdsLBA[7:0]LBA_MIDholdsLBA[15:8]LBA_HIholdsLBA[23:16]DRIVE_SELECTholds0xE0 | (drive_num << 4) | LBA[27:24]
DRIVE_SELECT: [ 1 | 1 | 1 | DEV | LBA27 | LBA26 | LBA25 | LBA24 ]
^ ^ ^ ^
| | | +-- 0 = Master, 1 = Slave
+---+---+------- 0xE0 for LBA mode
2.2 LBA48 Addressing Format (Up to 128 PB)
In ATA-6 LBA48, registers are 16-bit latches written by issuing the High-Order Byte (HOB) first, followed by the Low-Order Byte (LOB). Commands use 0x24 (READ SECTORS EXT) and 0x34 (WRITE SECTORS EXT).
3. Drive Identification & Probing Flow
During kernel initialization, ata_init() probes all 4 potential ATA positions:
flowchart TD
A["Start ata_init"] --> B["Select Drive on Channel"]
B --> C["Send 400ns Delay via Alt Status"]
C --> D["Send IDENTIFY Command 0xEC"]
D --> E{"Status == 0x00?"}
E -- Yes --> F["No Drive Present"]
E -- No --> G["Wait for BSY == 0"]
G --> H{"LBA_MID == 0x14 and LBA_HI == 0xEB?"}
H -- Yes --> I["ATAPI / CD-ROM Device"]
H -- No --> J["Wait for DRQ == 1"]
J --> K["Read 256 Words from 0x1F0 into buffer"]
K --> L["Parse Model, Serial, Sector Count, LBA48 support"]
L --> M["Register Block Device with VFS/Block Layer"]
ATA Identify Structure Parsing
The identify payload contains 512 bytes:
- Words 10-19: Serial Number (20 ASCII characters, byte-swapped).
- Words 27-46: Model String (40 ASCII characters, byte-swapped).
- Words 60-61: Total 28-bit LBA sector count.
- Word 83 (Bit 10): LBA48 support flag.
- Words 100-103: Total 48-bit LBA sector count.
4. Sector Read and Write Operations (PIO Mode)
BangOS implements 16-bit Port I/O (PIO) data transfers:
4.1 PIO Sector Read (ata_read_sectors)
int ata_read_sectors(ata_drive_t *drive, uint64_t lba, uint32_t count, void *buf) {
uint16_t *ptr = (uint16_t *)buf;
for (uint32_t s = 0; s < count; s++) {
uint64_t cur_lba = lba + s;
ata_wait_ready(drive->io_base, drive->ctrl_base);
// Select drive and high 4 bits of LBA
outb(drive->io_base + ATA_REG_DRIVE, 0xE0 | (drive->drive_num << 4) | ((cur_lba >> 24) & 0x0F));
ata_delay(drive->ctrl_base);
outb(drive->io_base + ATA_REG_SEC_COUNT, 1);
outb(drive->io_base + ATA_REG_LBA_LO, (uint8_t)cur_lba);
outb(drive->io_base + ATA_REG_LBA_MID, (uint8_t)(cur_lba >> 8));
outb(drive->io_base + ATA_REG_LBA_HI, (uint8_t)(cur_lba >> 16));
outb(drive->io_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO); // 0x20
ata_wait_drq(drive->io_base, drive->ctrl_base);
// Read 256 16-bit words (512 bytes)
for (int i = 0; i < 256; i++) {
*ptr++ = inw(drive->io_base + ATA_REG_DATA);
}
}
return 0;
}
4.2 PIO Sector Write & Cache Flushing (ata_write_sectors)
Following data word transmission to 0x1F0, the driver issues 0xE7 (ATA_CMD_FLUSH) to ensure data is permanently written to physical non-volatile storage media.
5. Block Device Abstraction Layer (kernel/drivers/block.h)
To decouple filesystems (ext2, FAT32) from storage hardware details, the kernel exposes a generic block_dev_t interface:
typedef struct block_dev {
char name[32]; // e.g., "ata0", "ata1"
uint32_t sector_size; // Standard 512 bytes
uint64_t total_sectors; // Total sector capacity
int (*read_blocks)(struct block_dev *dev, uint64_t lba, uint32_t count, void *buf);
int (*write_blocks)(struct block_dev *dev, uint64_t lba, uint32_t count, const void *buf);
void *priv_data;
} block_dev_t;
Devices register via block_register_device() and can be looked up by name (block_get_device("ata0")) or enumerated by index (block_get_device_by_index(i)).