This year I've made it all the way to accessing harddrives(!) and I've run into a problem that I've spent the last month trying to debug, but I'm in it to win it at this point. I've debugged the hell out of it, tried to fix it every way I know how, and even just re-written it from scratch a few times. The symptom at this point is that when I try to read from a drive I get a drive fault. The trick of it is that it's virtual hardware... IDENTIFY (and all the fiddling with that) works fine, so I know that things work in general. I'm stuck. Like I said, I've been pushing this rock for a month, and I've come to it: asking for help.
Please, oh people who have done this before me. Help.
Here's the code where the problem is (as best I can find):
Code: Select all
static u32int ata_read_lba28(drive_info_t drive, u32int start_sector, u8int sector_count, void *buffer) {
// Do some bit-twiddling, and set up the address.
u8int magic = 0xE0 | drive.drive;
outb(drive.controller + ATA_DRIVE_PORT, magic | ((start_sector >> 24) & 0x0F));
outb(drive.controller + ATA_ERROR_PORT, 0);
// Send the sector count
outb(drive.controller + ATA_SECTOR_COUNT_PORT, sector_count);
// And the address, (lo, mid, high)
outb(drive.controller + ATA_SECTOR_NUM_PORT, (u8int)((start_sector >> 0) & 0xFF));
outb(drive.controller + ATA_LOW_PORT, (u8int)((start_sector >> 8) & 0xFF));
outb(drive.controller + ATA_HIGH_PORT, (u8int)((start_sector >> 16) & 0xFF));
// Send the read command
outb(drive.controller + ATA_COMMAND_PORT, ATA_CMD_READ_PIO);
u8int sector = 0;
u32int total = 0;
for(sector = 0; sector < sector_count; sector++) {
// Wait till we are ready
u8int result = ata_poll(drive);
if(result == 2) {
ata_print_error(drive);
return total;
} else if(result == 1) {
monitor_write("Drive failure.\n");
return total;
} else if(result == 3) {
monitor_write("DRQ not set...\n");
return total;
}
// Get 256 bytes (one sector's worth) of data.
u32int i = 0;
for(i = 0; i < 128; i++) {
u16int temp = inw(drive.controller + ATA_DATA_PORT);
monitor_write_hex(temp); monitor_put(' ');
((u16int*)buffer)[total] = temp;
total++;
}
}
return total;
}
Code: Select all
static u8int ata_poll(drive_info_t drive) {
u16int port = drive.controller + ATA_COMMAND_PORT;
inb(port);
inb(port);
inb(port);
inb(port);
while((inb(port) & ATA_STATUS_BSY));
// Get the status register
u8int status = inb(drive.controller + ATA_COMMAND_PORT);
if(status & ATA_STATUS_ERR) return 2; // Describable error
if(status & ATA_STATUS_DF) return 1; // Device fault
if(!(status & ATA_STATUS_DRQ)) return 3; // DRQ should be set
return 0;
}
Insights, flames, and haikus welcome.
Thanks!