ATA DMA Mode Troubles [Solved]
Posted: Wed Nov 09, 2011 3:04 pm
Okay, I have been battling with my ATA driver for a couple of weeks. First I noticed that sometimes PIO doesn't seem to work properly (Occasionally if I read the same sector twice I get different data [one set of data is usually half correct and half zero]). So I decided that that would be a perfect time to start getting DMA mode to actually work. So, in my spare time I've been staring at specs, staring at my code, and staring at the buggy behavior. Currently I'm getting Drive Fault errors returned in the status byte of the ATA drive (inb 0x1F7 returns 0x50). I've been unable to locate why this is, I have looked through the wiki pages on ATA stuffs and I don't see it, etc, etc.
My code:Yeah, I will convert stuff to uint16_t and the likes once I get it working.
In Qemu it just flat out fails every time. It gets into the polling loop and spits out 'poll 50 4' until the end of time. In bochs, it seems to sometimes work, at least the first time. Sometimes it will correctly read the first sector, but when it tries to read another sector it freezes, spitting out 'poll 58 0' occasionally interspersed with 'poll 5A 0'. Sometimes it has the same behavior, except that it reads the sector as all zeros. Most of the time its the first one though.
I'm sure that its something really simple, but I've been staring at the code for a long time with no results, so I figured I'd ask here.
Thanks,
-JL
EDIT: I Feel like its a timing issue, since I never had the problems with PIO mode until I switched to a different (slower) computer, but I could be wildly wrong about that.
My code:
Code: Select all
typedef struct {
unsigned addr;
unsigned short size;
unsigned short last;
}__attribute__((packed)) prdtable_t;
int ata_dma_init(struct ata_controller *cont, struct ata_device *dev, int rw, unsigned char *buffer)
{
prdtable_t *t = (prdtable_t *)cont->prdt_virt;
t->addr = cont->dma_buf_phys;
t->size = 512;
t->last = 0x8000;
outl(cont->port_bmr_base + BMR_PRDT, (unsigned)cont->prdt_phys);
if (rw == WRITE)
memcpy(cont->dma_buf_virt, buffer, 512);
return 1;
}
int ata_start_command(struct ata_controller *cont, struct ata_device *dev, unsigned long long addr, char rw)
{
unsigned char cmd=0;
if(rw == READ)
cmd = 0x25;
else
cmd = 0x35;
outb(cont->port_cmd_base+REG_DEVICE, 0x40 | (dev->id << 4));
ATA_DELAY(cont);
outb(cont->port_cmd_base+REG_SEC_CNT, 0x00);
outb(cont->port_cmd_base+REG_LBA_LOW, (unsigned char)(addr >> 24));
outb(cont->port_cmd_base+REG_LBA_MID, (unsigned char)(addr >> 32));
outb(cont->port_cmd_base+REG_LBA_HIG, (unsigned char)(addr >> 40));
outb(cont->port_cmd_base+REG_SEC_CNT, 0x01);
outb(cont->port_cmd_base+REG_LBA_LOW, (unsigned char)addr);
outb(cont->port_cmd_base+REG_LBA_MID, (unsigned char)(addr >> 8));
outb(cont->port_cmd_base+REG_LBA_HIG, (unsigned char)(addr >> 16));
outb(cont->port_cmd_base+REG_COMMAND, cmd);
return 1;
}
int ata_dma_rw(struct ata_controller *cont, struct ata_device *dev, int rw, unsigned blk, char *buf)
{
ata_start_command(cont, dev, blk, rw);
ATA_DELAY(cont);
inb(cont->port_bmr_base + BMR_STATUS);
ata_dma_init(cont, dev, rw, (unsigned char *)buf);
inb(cont->port_bmr_base + BMR_STATUS);
uint8_t cmd = inb(cont->port_bmr_base + BMR_COMMAND);
cmd = 0x1 | (rw == READ ? 8 : 0);
outb(cont->port_bmr_base, cmd);
unsigned char st;
while(1) {
st = inb(cont->port_bmr_base + BMR_STATUS);
uint8_t sus = ata_reg_inb(cont, REG_STATUS);
printk(1, "poll %x %x\n", sus, st);
if(!(st & 0x4))
continue;
if(st & 0x2)
panic("DMA err");
if(sus & STATUS_ERR)
panic("Error in reg_stat");
if(!(sus & STATUS_BSY) && (sus & STATUS_DRQ))
break;
}
st = inb(cont->port_bmr_base + BMR_COMMAND);
outb(cont->port_bmr_base + BMR_COMMAND, st);
st = inb(cont->port_bmr_base + BMR_STATUS);
outb(cont->port_bmr_base + BMR_STATUS, st);
uint8_t status = ata_reg_inb(cont, REG_STATUS);
if (!(status & STATUS_ERR)) {
if(st & 0x2)
panic("B");
} else
panic("A");
if (rw == READ)
memcpy(buf, cont->dma_buf_virt, 512);
return 512;
}
In Qemu it just flat out fails every time. It gets into the polling loop and spits out 'poll 50 4' until the end of time. In bochs, it seems to sometimes work, at least the first time. Sometimes it will correctly read the first sector, but when it tries to read another sector it freezes, spitting out 'poll 58 0' occasionally interspersed with 'poll 5A 0'. Sometimes it has the same behavior, except that it reads the sector as all zeros. Most of the time its the first one though.
I'm sure that its something really simple, but I've been staring at the code for a long time with no results, so I figured I'd ask here.
Thanks,
-JL
EDIT: I Feel like its a timing issue, since I never had the problems with PIO mode until I switched to a different (slower) computer, but I could be wildly wrong about that.