Okay I send the IDENTIFY command and what command I should send or that enough?Octocontrabass wrote:You have to send a command that involves a data transfer before the drive will set DRQ.
ATA driver problem
-
- Member
- Posts: 97
- Joined: Thu Jul 14, 2022 9:45 am
- Contact:
Re: ATA driver problem
-
- Member
- Posts: 5549
- Joined: Mon Mar 25, 2013 7:01 pm
Re: ATA driver problem
That should be enough.
Check the other registers for the ATAPI signature. You might be dealing with an ATAPI drive that doesn't follow ATA correctly.
Check the other registers for the ATAPI signature. You might be dealing with an ATAPI drive that doesn't follow ATA correctly.
-
- Member
- Posts: 97
- Joined: Thu Jul 14, 2022 9:45 am
- Contact:
Re: ATA driver problem
So my problem is that my driver trying to send command to drive that still reseting after soft reset command, so my question is how I can correctly wait for the reset using only the disk IO ports or I must use the system sleep functions?
-
- Member
- Posts: 5549
- Joined: Mon Mar 25, 2013 7:01 pm
Re: ATA driver problem
The status register will tell you when the drive is done resetting and ready to accept commands.
-
- Member
- Posts: 97
- Joined: Thu Jul 14, 2022 9:45 am
- Contact:
Re: ATA driver problem
How I can identify hard drive in SATA IDE mode? My drive with my driver returns me device fault and error code is 0xff
-
- Member
- Posts: 5549
- Joined: Mon Mar 25, 2013 7:01 pm
Re: ATA driver problem
Identifying a SATA drive on an IDE controller works the same as any other ATA drive on an IDE controller.
What is the value of the status register? The device fault bit only applies when the busy bit is clear.
What is the value of the status register? The device fault bit only applies when the busy bit is clear.
-
- Member
- Posts: 97
- Joined: Thu Jul 14, 2022 9:45 am
- Contact:
Re: ATA driver problem
Octocontrabass wrote:What is the value of the status register?
0x7f
-
- Member
- Posts: 5549
- Joined: Mon Mar 25, 2013 7:01 pm
Re: ATA driver problem
That combination of status and error register values means there's no drive present.
-
- Member
- Posts: 97
- Joined: Thu Jul 14, 2022 9:45 am
- Contact:
Re: ATA driver problem
So, hello again! I have some problems with reading the sectors, my driver is getting the base register address and ctrl register address from PCI as descripted in the wiki, when i run this code on bochs, virtualbox or qemu the code successfully read the sectors, but when i runed it on the real hardware i got strange behaivor, the register reports successfully sector reading, but the drive actually doesn't do that(the disk active led doesn't power on and only 0xff transfered from the port). On bochs also i got continiosly interrupt 0x27. Here is my read code:
Code: Select all
static void ata_vdev_readBlock(vfs_node_t *node,int blockNo,int how, void *buf) {
ata_device_t *dev = node->device;
if (dev->type == IDE_ATAPI) {
kprintf("Currently doesn't supported!\n");
return;
}
//outb(dev->base+ATA_REG_CONTROL,2);
kprintf("ata: read: dev ctrl: 0x%x\n",dev->base);
void *dd = buf;
int doCopy = false;
uint64_t lba = blockNo;
uint16_t sectors = how/512;
if (sectors == 0) {
sectors = 1;
buf = kmalloc(512);
doCopy = 1;
}
kprintf("Read %d sectors at %d\r\n",sectors,lba);
if (sectors == 0) sectors = 1;
outb(dev->base+ATA_REG_HDDEVSEL,0x40);
ata_io_wait(dev);
outportb(dev->base + ATA_REG_SECCOUNT0, 0);
outportb(dev->base + ATA_REG_LBA0, (lba & 0xff000000) >> 24);
outportb(dev->base + ATA_REG_LBA1, (lba & 0xff00000000) >> 32);
outportb(dev->base + ATA_REG_LBA2, (lba & 0xff0000000000) >> 40);
outportb(dev->base + ATA_REG_SECCOUNT0, sectors);
outportb(dev->base + ATA_REG_LBA0, (lba & 0x000000ff) >> 0);
outportb(dev->base + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8);
outportb(dev->base + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16);
outb(dev->base+ATA_REG_COMMAND,ATA_CMD_READ_PIO_EXT); // ATA_CMD_READ_PIO_EXT
ata_io_wait(dev);
//kprintf("ata: command setted\n");
//kprintf("ata: interrupts disabled\n");
uint8_t i;
// convert buffer
uint8_t *bu = (uint8_t *)buf;
//arch_sti();
//kprintf("going to read loop\n");
for (i = 0; i < sectors; i++) {
while(1) {
uint8_t status = inb(dev->base+ATA_REG_STATUS);
//kprintf("Waiting untill drive remove the ATA_SR_DRQ\n");
if (status & ATA_SR_DRQ) {
// Disk is ready to transfer data
break;
} else if (status & ATA_SR_ERR) {
kprintf("ATA: Disk error. Cancel\n");
return;
}
}
//kprintf("ata: sector readed\n");
insw(dev->base,bu,256);
bu+=256;
}
//kprintf("ata: sectors readed\n");
if (doCopy) {
memcpy(dd,buf,how);
kfree(buf);
}
//kprintf("ata: goodbay\n");
}
-
- Member
- Posts: 5549
- Joined: Mon Mar 25, 2013 7:01 pm
Re: ATA driver problem
Which devices are connected to that interrupt?WinExperements wrote:On bochs also i got continiosly interrupt 0x27.
Drives smaller than about 128GB often don't support 48-bit LBA.WinExperements wrote:Code: Select all
outb(dev->base+ATA_REG_COMMAND,ATA_CMD_READ_PIO_EXT); // ATA_CMD_READ_PIO_EXT
If BSY is set, none of the other bits in the status register are valid. You must wait until BSY is clear before trying to interpret the other bits. You should use interrupts instead of polling the status register.WinExperements wrote:Code: Select all
uint8_t status = inb(dev->base+ATA_REG_STATUS); //kprintf("Waiting untill drive remove the ATA_SR_DRQ\n"); if (status & ATA_SR_DRQ) {
-
- Member
- Posts: 97
- Joined: Thu Jul 14, 2022 9:45 am
- Contact:
Re: ATA driver problem
Hello again! So i have problem sending ATA Identify command to drive.
When i send the command the drive, the drive is raise the IRQ, i read the status register to indicate for the drive that we handle the interrupt, but drive won't stop send there IRQs. I receive infinite count of interrupts from ATA when send the Identify command even if i readed the data from data port.
Also i noticed that the bug occurs if i boot system in Legacy Mode or when i boot it on Legacy BIOS machine.
On virtual machines such as VirtualBox,QEMU all works fine. On Bochs i also got infinite amount of interrupts from ATA, but debug messages are empty.
What i did wrong?
Here is my drive detect code:
And the IRQ handler code:
When i send the command the drive, the drive is raise the IRQ, i read the status register to indicate for the drive that we handle the interrupt, but drive won't stop send there IRQs. I receive infinite count of interrupts from ATA when send the Identify command even if i readed the data from data port.
Also i noticed that the bug occurs if i boot system in Legacy Mode or when i boot it on Legacy BIOS machine.
On virtual machines such as VirtualBox,QEMU all works fine. On Bochs i also got infinite amount of interrupts from ATA, but debug messages are empty.
What i did wrong?
Here is my drive detect code:
Code: Select all
bool ata_device_init(ata_device_t *dev,bool second) {
arch_sti();
ata_soft_reset(dev);
ata_io_wait(dev);
int bit = (second ? 0xB0 : 0xA0);
outb(dev->base+ATA_REG_HDDEVSEL, bit | dev->slave << 4);
ata_io_wait(dev);
outb(dev->base+ATA_REG_SECCOUNT0,0);
outb(dev->base+ATA_REG_LBA0,0);
outb(dev->base+ATA_REG_LBA1,0);
outb(dev->base+ATA_REG_LBA2,0);
// Send drive identify command
inter_ata = dev;
dev->ident = 1;
kprintf("ATA: sending command\r\n");
outb(dev->base+ATA_REG_COMMAND,ATA_CMD_IDENTIFY);
if (!inb(dev->base + ATA_REG_STATUS)) {
kprintf("ATA: no drive\r\n");
return false;
}
uint8_t lba1 = inb(dev->base+ATA_REG_LBA1);
uint8_t lba2 = inb(dev->base+ATA_REG_LBA2);
if (lba1 != 0 || lba2 != 0) {
kprintf("Not ATA device\r\n");
return false;
}
kprintf("Waiting until DRQ or ERR to be setted\r\n");
uint8_t drq,err = 0;
while(!drq && !err) {
if (dev->ident == 0) break;
//kprintf("DRQ: %d ERR: %d. STATUS: %d\r\n",drq,err,inb(dev->base+ATA_REG_STATUS));
drq = inb(dev->base + ATA_REG_STATUS) & ATA_SR_DRQ;
err = inb(dev->base + ATA_REG_STATUS) & ATA_SR_ERR;
}
if (err) {
kprintf("ATA: Drive error!\r\n");
return false;
}
kprintf("ATA: Reading using PIO mode\r\n");
if (dev->ident) {
kprintf("ATA: No IRQ rised to read\r\n");
// kprintf("The device is ready and doesn't reported any errors, reading the identify structure...");
uint16_t *buf = (uint16_t *)&dev->identify;
for (int i = 0; i < 256; i++) {
buf[i] = inw(dev->base);
}
}
kprintf("readed\n");
uint8_t *ptr = (uint8_t *)&dev->identify.model;
for (int i = 0; i < 39; i+=2) {
uint8_t tmp = ptr[i+1];
ptr[i+1] = ptr[i];
ptr[i] = tmp;
}
return true;
}
Code: Select all
void *ata_irq_handler(void *stack) {
// ATA specifications
kprintf("ATA IRQ!\r\n");
inb(inter_ata->base+ATA_REG_STATUS);
if (inter_ata->ident) {
uint16_t *data = (uint16_t *)&inter_ata->identify;
for (int i = 0; i < 256; i++) {
data[i] = inw(inter_ata->base);
}
kprintf("Device identify readed!\r\n");
inter_ata->ident = 0;
}
//inb(inter_ata->base+ATA_REG_STATUS);
if (inter_ata->busy) {
inter_ata->busy = 0;
}
outb(inter_ata->bar4+4,0x00);
/*kprintf("ATA IRQ handler!\n");
kprintf("Status of drive(after sending there commands): 0x%x\n",inb(inter_ata->base+ATA_REG_STATUS));
kprintf("Error register: 0x%x\n",inb(inter_ata->base+ATA_REG_ERROR));*/
return stack;
}
-
- Member
- Posts: 5549
- Joined: Mon Mar 25, 2013 7:01 pm
Re: ATA driver problem
Why do you have two different methods to set bit 4?WinExperements wrote:Code: Select all
int bit = (second ? 0xB0 : 0xA0); outb(dev->base+ATA_REG_HDDEVSEL, bit | dev->slave << 4);
This is a race condition. If you need to check the drive's status while you're waiting for an interrupt, use the alternate status register.WinExperements wrote:Code: Select all
outb(dev->base+ATA_REG_COMMAND,ATA_CMD_IDENTIFY); if (!inb(dev->base + ATA_REG_STATUS)) {
You can't read these registers while the drive is busy, and the drive is probably going to be busy if you just sent a command.WinExperements wrote:Code: Select all
uint8_t lba1 = inb(dev->base+ATA_REG_LBA1); uint8_t lba2 = inb(dev->base+ATA_REG_LBA2);
Again, you need to use the alternate status register to prevent a race condition. You must check the status of the BSY bit before trying to interpret other bits in the status register. You don't need to read the status register twice to read two bits.WinExperements wrote:Code: Select all
drq = inb(dev->base + ATA_REG_STATUS) & ATA_SR_DRQ; err = inb(dev->base + ATA_REG_STATUS) & ATA_SR_ERR;
You have code to read the buffer twice.WinExperements wrote:Code: Select all
uint16_t *buf = (uint16_t *)&dev->identify; for (int i = 0; i < 256; i++) { buf[i] = inw(dev->base);
You shouldn't discard the value of the status register. It's probably fine if you're just testing, though.WinExperements wrote:Code: Select all
inb(inter_ata->base+ATA_REG_STATUS);
You have code to read the buffer twice.WinExperements wrote:Code: Select all
uint16_t *data = (uint16_t *)&inter_ata->identify; for (int i = 0; i < 256; i++) { data[i] = inw(inter_ata->base);
Which register is this supposed to be? You don't need to touch any DMA registers when you're doing a PIO transfer.WinExperements wrote:Code: Select all
outb(inter_ata->bar4+4,0x00);
-
- Member
- Posts: 97
- Joined: Thu Jul 14, 2022 9:45 am
- Contact:
Re: ATA driver problem
So, i rewrited the device init code using the Wiki code, but as soon as i enable the device interrupts using the ATA_REG_CONTROL and issue the ATA DMA read command i start to receiving endless count of interrupts from IDE controller.
Here is my new version of device init code:
The DMA reading code itself:
Current version of device block reading code is for testing, so for example we don't read the real count of cache sectors.
Also i notice that the bug/problem shows only if system boots in Legacy Mode using the EFI legacy boot or by real legacy BIOS.
EDIT: Rewrite some parts of code like in the wiki, nothing changed.
Here is my new version of device init code:
Code: Select all
bool ata_device_init(ata_device_t *dev,int id) {
unsigned char err = 0,status;
ide_write(dev,ATA_REG_CONTROL,2);
ide_write(dev,ATA_REG_HDDEVSEL,0xA0 | (id << 4));
ata_io_wait(dev);
ide_write(dev,ATA_REG_COMMAND,ATA_CMD_IDENTIFY);
ata_io_wait(dev);
if (ide_read(dev,ATA_REG_STATUS) == 0) return false;
kprintf("Waiting device to clear BSY state...");
while(1) {
status = ide_read(dev,ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) {
// Looks like, we send wrong command?
uint8_t t1 = ide_read(dev,ATA_REG_LBA1);
uint8_t t2 = ide_read(dev,ATA_REG_LBA2);
if (t1 == 0x14 && t2 == 0xEB) {
// fault caused by WRONG command.
kprintf("ATAPI device fault(normal,ignore)\r\n");
dev->type = IDE_ATAPI;
break;
} else {
kprintf("First device fault received 0x%x 0x%x\r\n",t1,t2);
return false;
}
}
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break;
}
kprintf("cleared\r\n");
if (dev->type == IDE_ATAPI) {
// Selected by first fault handler.
goto atapiDetect;
}
uint8_t ch = ide_read(dev,ATA_REG_LBA1);
uint8_t cl = ide_read(dev,ATA_REG_LBA2);
if (cl == 0x14 && ch == 0xEB) {
atapiDetect:
kprintf("ATAPI device detected!\r\n");
dev->type = IDE_ATAPI;
ide_write(dev,ATA_REG_COMMAND,ATA_CMD_IDENTIFY_PACKET);
ata_io_wait(dev);
// Repeat process
while(1) {
status = ide_read(dev,ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) {
kprintf("Device fault fallback!\r\n");
return false;
}
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break;
}
} else if (cl == 0x69 && ch == 0x96) {
kprintf("ATA device!\r\n");
} else if (cl == 0x0 && ch == 0) {
kprintf("We skiped something?!\r\n");
} else {
kprintf("Unknown type\r\n");
return false;
}
uint16_t *identf = (uint16_t *)&dev->identify;
for (int i = 0; i < 256; i++) {
identf[i] = inw(dev->base);
}
kprintf("readed\n");
uint8_t *ptr = (uint8_t *)&dev->identify.model;
for (int i = 0; i < 39; i+=2) {
uint8_t tmp = ptr[i+1];
ptr[i+1] = ptr[i];
ptr[i] = tmp;
}
kprintf("ATA: Detected device name: %s, type: %s\r\n",dev->identify.model,dev->type == IDE_ATAPI ? "ATAPI" : "IDE");
ata_create_device(dev->type == IDE_ATA,dev);
dev->type = IDE_ATA;
return true;
}
Code: Select all
static void ata_vdev_readBlock(vfs_node_t *node,int blockNo,int how, void *buf) {
ata_device_t *dev = node->device;
if (dev->type == IDE_ATAPI) {
kprintf("Currently doesn't supported!\n");
return;
}
void *dd = buf;
int doCopy = false;
uint64_t lba = blockNo;
uint16_t sectors = how/512;
//kprintf("Read %d sectors at %d\r\n",sectors,lba);
if (sectors == 0) sectors = 1;
if (sectors > 255) {
kprintf("ATA: Maximum count of sectors that able to read per one command is 255!\n");
sectors = 254;
}
if (blockNo == 0) {
goto readActual;
}
kprintf("%s: called\r\n",__func__);
// Before issusing real READ DMA command, check if we don't readed the sectors previously
readActual:
ide_write(dev,ATA_REG_CONTROL,0);
kprintf("%s: actual reading begin\r\n",__func__);
// DMA support!
outb(dev->bar4,0x00);
outl(dev->bar4 + 0x04, dev->dma_prdt_phys);
outb(dev->bar4 + 0x2, inb(dev->bar4 + 0x02) | 0x04 | 0x02);
outb(dev->bar4, 0x08);
while (1) {
uint8_t status = inb(dev->base + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY)) break;
}
int cache_sectors = sectors;
outb(dev->base+ATA_REG_HDDEVSEL,0x40);
ata_io_wait(dev);
outb(dev->base + ATA_REG_FEATURES, 0x01);
outb(dev->base+ATA_REG_SECCOUNT0,(cache_sectors >> 8) & 0xFF);
outb(dev->base+ATA_REG_LBA0, (lba & 0xff000000) >> 24);
outb(dev->base+ATA_REG_LBA1, (lba & 0xff000000) >> 32);
outb(dev->base+ATA_REG_LBA2, (lba & 0xff000000) >> 48);
outb(dev->base+ATA_REG_SECCOUNT0,cache_sectors & 0xFF);
outb(dev->base+ATA_REG_LBA0,lba & 0xFF);
outb(dev->base+ATA_REG_LBA1,(lba >> 8) & 0xFF);
outb(dev->base+ATA_REG_LBA2,(lba >> 16) & 0xFF);
uint8_t i;
// convert buffer
uint8_t *bu = (uint8_t *)buf;
while (1) {
uint8_t status = inb(dev->base + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) break;
}
outb(dev->base + ATA_REG_COMMAND, ATA_CMD_READ_DMA_EXT);
ata_io_wait(dev);
outb(dev->bar4, 0x08 | 0x01);
inter_ata->busy = 1;
arch_sti();
/*while (1) {
int status = inb(dev->bar4 + 0x02);
int dstatus = inb(dev->base + ATA_REG_STATUS);
if (!(status & 0x04)) {
continue;
}
if (!(dstatus & ATA_SR_BSY)) {
break;
}
}
*/
while(inter_ata->busy);
/* Inform device we are done. */
outb(dev->bar4 + 0x2, inb(dev->bar4 + 0x02) | 0x04 | 0x02);
if (doCopy) {
memcpy(dd,buf,how);
kfree(buf);
}
if (sectors > 255) {
kprintf("ATA: Sectors count are bigger that 0xFF, repeat process\n");
sectors-=255;
ata_vdev_readBlock(node,blockNo+255,sectors*512,bu);
}
// Dump the PDRT
if (how > 8192) {
kprintf("WARRNING: DMA support only 8 sectors :). So we copy only 8 sectors\r\n");
}
if (buf == NULL) {
//kprintf("Zero buffer!!!\r\n");
dev->latestSector = blockNo;
return;
}
memcpy(bu,dev->dma_start,(how > 8192) ? 8192 : how);
kprintf("%s: ended\r\n",__func__);
}
Also i notice that the bug/problem shows only if system boots in Legacy Mode using the EFI legacy boot or by real legacy BIOS.
EDIT: Rewrite some parts of code like in the wiki, nothing changed.
-
- Member
- Posts: 5549
- Joined: Mon Mar 25, 2013 7:01 pm
Re: ATA driver problem
You must wait for the BSY bit to clear before you try to interpret any other status bits.WinExperements wrote:Code: Select all
if ((status & ATA_SR_ERR)) {
SATA devices only return SATA-specific signatures after reset, not after an IDENTIFY DEVICE command. This signature indicates a SATA port multiplier.WinExperements wrote:Code: Select all
} else if (cl == 0x69 && ch == 0x96) {
This signature indicates an IDE disk.WinExperements wrote:Code: Select all
} else if (cl == 0x0 && ch == 0) {
No, the maximum is 65536 with READ DMA EXT (and 256 with READ DMA).WinExperements wrote:Code: Select all
kprintf("ATA: Maximum count of sectors that able to read per one command is 255!\n");
Why are you polling the status register? You should already know whether you're waiting for the drive to finish the last command you sent.WinExperements wrote:Code: Select all
while (1) { uint8_t status = inb(dev->base + ATA_REG_STATUS); if (!(status & ATA_SR_BSY)) break; }
I'm sure it makes no difference for READ DMA EXT, but it's a good idea to use 0xE0 instead of 0x40.WinExperements wrote:Code: Select all
outb(dev->base+ATA_REG_HDDEVSEL,0x40);
READ DMA EXT doesn't use the features register.WinExperements wrote:Code: Select all
outb(dev->base + ATA_REG_FEATURES, 0x01);
These values will always be zero instead of the correct LBA bits.WinExperements wrote:Code: Select all
outb(dev->base+ATA_REG_LBA1, (lba & 0xff000000) >> 32); outb(dev->base+ATA_REG_LBA2, (lba & 0xff000000) >> 48);
Why are you polling the status register again?WinExperements wrote:Code: Select all
while (1) { uint8_t status = inb(dev->base + ATA_REG_STATUS); if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) break; }
DMA is not limited to 8 sectors, and 8192 bytes is either 16 sectors or 2 sectors depending on the sector size.WinExperements wrote:Code: Select all
kprintf("WARRNING: DMA support only 8 sectors :). So we copy only 8 sectors\r\n");
How does your interrupt handler acknowledge the interrupt? I could see you receiving an interrupt storm if that isn't done correctly.
-
- Member
- Posts: 97
- Joined: Thu Jul 14, 2022 9:45 am
- Contact:
Re: ATA driver problem
This is my IDE IRQ handler code:Octocontrabass wrote: How does your interrupt handler acknowledge the interrupt? I could see you receiving an interrupt storm if that isn't done correctly.
Code: Select all
void *ata_irq_handler(void *stack) {
// ATA specifications
kprintf("ATA IRQ!\r\n");
inb(inter_ata->base+ATA_REG_STATUS);
//inb(inter_ata->base+ATA_REG_STATUS);
if (inter_ata->busy) {
inter_ata->busy = 0;
}
outb(inter_ata->bar4+4,0x00);
/*kprintf("ATA IRQ handler!\n");
kprintf("Status of drive(after sending there commands): 0x%x\n",inb(inter_ata->base+ATA_REG_STATUS));
kprintf("Error register: 0x%x\n",inb(inter_ata->base+ATA_REG_ERROR));*/
return stack;
}