Hello OSDev!
I'm trying to write a simple ATA driver, to read and write to an HDD (I'm running on the QEMU emulator) in PIO mode, with 28 bit LBA addressing.
However, when I try to write information to the disk, and then read it, the result is all garbage (0xFF bytes).
I'm using C++ as my language of choice.
My ata initialization routine is basically just doing the identify command. The information returned by the command accurately describes the HDD i'm trying to emulate (the sector count matches up). In the identify command, I use the select command to select the Master device.
Here are the macro functions and enum classes I use in my code:
Code:
constexpr uint32_t PRIMARY_IO_BASE = 0x1F0
constexpr uint32_t PRIMARY_COMMAND_BASE = 0x3F7
enum class IoRegister : uint32_t {
// name // offset from
Data = 0,
Error = 1,
Features = 1,
SectorCount = 2,
// different names for same ports
SectorNumber = 3, LBAlow = 3,
CylinderLow = 4, LBAmid = 4,
CylinderHigh = 5, LBAhigh = 5,
Head = 6,
Status = 7,
Command = 7
};
enum class Command : uint8_t {
Identify = 0xEC,
ReadBuffer = 0xE4,
WriteBuffer = 0xE8
};
inline uint32_t IO_REG_OFFSET(IoRegister reg, Bus bus)
{
return static_cast<int>(reg) + IO_BASE(bus);
}
inline void WAIT_NO_BUSY()
{
StatusRegister status;
do {
status.value = READ_BYTE(IoRegister::Status);
} while (status.flags.Bsy);
}
inline void WAIT_UNTIL_READY()
{
StatusRegister status;
do {
status.value = READ_BYTE(IoRegister::Status);
} while (status.flags.Rdy == 0);
}
inline void COMMAND(Command cmd)
{
WAIT_NO_BUSY();
asm volatile("cli;");
WAIT_UNTIL_READY();
uint32_t port = (selected_controller == Bus::Primary ? PRIMARY_IO_BASE : SECONDARY_IO_BASE) + ((int)IoRegister::Command);
uint8_t val = static_cast<uint8_t>(cmd);
outb(port, val);
asm volatile("sti;");
}
inline uint8_t READ_BYTE(IoRegister reg)
{
uint16_t port = IO_REG_OFFSET(reg, selected_controller);
return inb(port);
}
inline uint32_t IO_BASE(Bus bus)
{
return (bus == Bus::Primary ? PRIMARY_IO_BASE : SECONDARY_IO_BASE);
}
inline uint32_t IO_REG_OFFSET(IoRegister reg, Bus bus)
{
return static_cast<int>(reg) + IO_BASE(bus);
}
in my kernel_main, i call write, and the read.
Here is the actual read\write code:
(There are hard coded values, such as the LBA and the 'A' bytes im trying to write - this is just for testing)
Code:
void ata::read()
{
SET_SECTOR_COUNT(1);
SET_LBA(100);
COMMAND(Command::ReadBuffer);
WAIT_NO_BUSY();
WAIT_UNTIL_READY();
char buf[512] = { 0 };
for (int i = 0; i < 512; i++)
{
buf[i] = READ_BYTE(IoRegister::Data);
}
printf("data read: %s\n", buf);
}
void ata::write(char* buf, size_t len)
{
SET_SECTOR_COUNT(1);
SET_LBA(100);
COMMAND(Command::WriteBuffer);
WAIT_NO_BUSY();
WAIT_UNTIL_READY();
for (int i = 0; i < 512; i++)
{
outb(IO_REG_OFFSET(IoRegister::Data, selected_controller), 'A');
}
DELAY_400_NS();
}
Also, the outb and inb functions (if they are relevant):
Code:
inline uint8_t inb(short port) {
uint8_t ret;
asm volatile("inb %1, %0" : "=a" (ret) : "dN" (port));
return ret;
}
inline uint16_t inw(short port) {
uint16_t ret;
asm volatile("inw %1, %0" : "=a" (ret) : "dN" (port));
return ret;
}
I know this is alot of code, hopefuly it is not a problem and is enough to spot any problem I have with the code.
Please tell me if there is more information you need to identify the problem.
Thank you in advance to all