I've hit my wits end on an ATA problem
Posted: Mon Mar 21, 2011 7:26 pm
Every year or so I venture into building an OS, and every year I make it a bit further than the year before. It is both a wonderful learning experience, and an exercise in frustration. But I do enjoy it!
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):
It dies in the "else if(result == 1)" bit. The poll function looks like this:
All of it is run after disabling interrupts on the controller. Any help. Any insight. Any ideas. Just hearing another person have a thought about it might help. I need a fresh perspective because I feel like I've got tunnel vision on the issue at this point. I'm using qemu 0.12.5, on ubuntu 10.10. I'm trying to read a single sector from the device starting at sector 0 (though I've tried it with other values as well).
Insights, flames, and haikus welcome.
Thanks!
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!