Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 79 additions & 1 deletion src/drivers/disk/ata.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#define ATA_STATUS_DRQ 0x08u // data request (ready to transfer data)
#define ATA_STATUS_BSY 0x80u // busy (device is processing a command and cannot accept new commands)

#define ATA_COMMAND_READ_SECTORS 0x20u // command to read sectors from the disk
#define ATA_COMMAND_READ_SECTORS 0x20u // command to read sectors from the disk
#define ATA_COMMAND_WRITE_SECTORS 0x30u // command to write sectors to the disk
#define ATA_COMMAND_CACHE_FLUSH 0xE7u // command to flush the drive cache

/**
* write a byte to an I/O port.
Expand Down Expand Up @@ -44,6 +46,14 @@ static inline void io_insw(uint16_t port, void *buffer, uint32_t word_count)
__asm__ __volatile__("cld; rep insw" : "+D"(buffer), "+c"(word_count) : "d"(port) : "memory");
}

/**
* write words (2 bytes each) to an I/O port from a buffer.
*/
static inline void io_outsw(uint16_t port, const void *buffer, uint32_t word_count)
{
__asm__ __volatile__("cld; rep outsw" : "+S"(buffer), "+c"(word_count) : "d"(port) : "memory");
}

/**
* perform a 400ns delay by reading the alternate status register four times.
*/
Expand Down Expand Up @@ -152,6 +162,64 @@ static bool ata_read_lba28(uint32_t lba, uint8_t sector_count, void *dest)
return true;
}

/**
* write sectors to an ATA device using 28-bit LBA addressing.
*/
static bool ata_write_lba28(uint32_t lba, uint8_t sector_count, const void *src)
{
const uint8_t *in;
uint32_t sector_index;

if (!src || sector_count == 0)
{
return false;
}

if (lba > 0x0FFFFFFFu)
{
return false;
}

if (!ata_wait_not_busy())
{
return false;
}

io_outb(ATA_IO_DRIVE_HEAD, (uint8_t)(0xE0u | ((lba >> 24u) & 0x0Fu)));
ata_400ns_delay();

io_outb(ATA_IO_SECTOR_COUNT, sector_count);
io_outb(ATA_IO_LBA_LOW, (uint8_t)(lba & 0xFFu));
io_outb(ATA_IO_LBA_MID, (uint8_t)((lba >> 8u) & 0xFFu));
io_outb(ATA_IO_LBA_HIGH, (uint8_t)((lba >> 16u) & 0xFFu));
io_outb(ATA_IO_STATUS_COMMAND, ATA_COMMAND_WRITE_SECTORS);

in = (const uint8_t *)src;
for (sector_index = 0; sector_index < sector_count; sector_index++)
{
if (!ata_wait_data_request())
{
return false;
}

io_outsw(ATA_IO_DATA, in, 256u);
in += 512u;
}

if (!ata_wait_not_busy())
{
return false;
}

io_outb(ATA_IO_STATUS_COMMAND, ATA_COMMAND_CACHE_FLUSH);
if (!ata_wait_not_busy())
{
return false;
}

return true;
}

/**
* read blocks from an ATA device.
*/
Expand All @@ -161,6 +229,15 @@ static bool ata_block_read(BLOCK_DEVICE *device, uint32_t startBlock, uint8_t bl
return ata_read_lba28(startBlock, blockCount, dest);
}

/**
* write blocks to an ATA device.
*/
static bool ata_block_write(BLOCK_DEVICE *device, uint32_t startBlock, uint8_t blockCount, const void *src)
{
(void)device;
return ata_write_lba28(startBlock, blockCount, src);
}

bool ATA_Initialize(ATA_DEVICE *device)
{
if (!device)
Expand All @@ -171,5 +248,6 @@ bool ATA_Initialize(ATA_DEVICE *device)
device->block.bytes_per_block = 512;
device->block.context = device;
device->block.read_blocks = ata_block_read;
device->block.write_blocks = ata_block_write;
return true;
}
2 changes: 1 addition & 1 deletion src/drivers/disk/ata.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ typedef struct
} ATA_DEVICE;

/**
* initialize an ATA device for reading blocks.
* initialize an ATA device for reading and writing blocks.
* @param device ATA device object to initialize.
* @return true on success, false on failure.
*/
Expand Down
10 changes: 10 additions & 0 deletions src/drivers/disk/block_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ bool block_device_read(BLOCK_DEVICE *device, uint32_t startBlock, uint8_t blockC

return device->read_blocks(device, startBlock, blockCount, dest);
}

bool block_device_write(BLOCK_DEVICE *device, uint32_t startBlock, uint8_t blockCount, const void *src)
{
if (!device || !device->write_blocks || !src || blockCount == 0)
{
return false;
}

return device->write_blocks(device, startBlock, blockCount, src);
}
18 changes: 15 additions & 3 deletions src/drivers/disk/block_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
typedef struct BLOCK_DEVICE BLOCK_DEVICE;

typedef bool (*BlockReadFn)(BLOCK_DEVICE *device, uint32_t startBlock, uint8_t blockCount, void *dest);
typedef bool (*BlockWriteFn)(BLOCK_DEVICE *device, uint32_t startBlock, uint8_t blockCount, const void *src);

/**
* block device abstraction for reading blocks from a storage device.
*/
struct BLOCK_DEVICE
{
uint16_t bytes_per_block; // number of bytes in each block (sector) of the device.
void *context; // pointer to device-specific context data (e.g., ATA device structure).
BlockReadFn read_blocks; // function pointer to the block read function for the device.
uint16_t bytes_per_block; // number of bytes in each block (sector) of the device.
void *context; // pointer to device-specific context data (e.g., ATA device structure).
BlockReadFn read_blocks; // function pointer to the block read function for the device.
BlockWriteFn write_blocks; // function pointer to the block write function for the device.
};

/**
Expand All @@ -25,3 +27,13 @@ struct BLOCK_DEVICE
* @return true on success. false on failure.
*/
bool block_device_read(BLOCK_DEVICE *device, uint32_t startBlock, uint8_t blockCount, void *dest);

/**
* write blocks to a block device.
* @param device initialized block device backend.
* @param startBlock starting block number to write to.
* @param blockCount number of blocks to write.
* @param src source buffer containing the data to write.
* @return true on success. false on failure.
*/
bool block_device_write(BLOCK_DEVICE *device, uint32_t startBlock, uint8_t blockCount, const void *src);
21 changes: 21 additions & 0 deletions src/drivers/disk/partition.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@ static bool partition_block_read(BLOCK_DEVICE *device, uint32_t startBlock, uint
return block_device_read(part->parent, part->start_lba + startBlock, blockCount, dest);
}

/**
* write blocks to a partition.
*/
static bool partition_block_write(BLOCK_DEVICE *device, uint32_t startBlock, uint8_t blockCount, const void *src)
{
PARTITION_DEVICE *part;
if (!device || !device->context)
{
return false;
}

part = (PARTITION_DEVICE *)device->context;
if (!part->parent)
{
return false;
}

return block_device_write(part->parent, part->start_lba + startBlock, blockCount, src);
}

bool PARTITION_Initialize(PARTITION_DEVICE *part, BLOCK_DEVICE *parent, uint32_t start_lba)
{
if (!part || !parent)
Expand All @@ -34,6 +54,7 @@ bool PARTITION_Initialize(PARTITION_DEVICE *part, BLOCK_DEVICE *parent, uint32_t
part->block.bytes_per_block = parent->bytes_per_block;
part->block.context = part;
part->block.read_blocks = partition_block_read;
part->block.write_blocks = partition_block_write;
return true;
}

Expand Down
Loading