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.