Detecting ATA drives while using QEMU always reports the primary slave as present
Posted: Sun Feb 23, 2025 11:16 am
I am try to get drive detection working. I am using QEMU to run my OS.
Even though I have only set up 1 drive in QEMU my drive detection code always reports that the primary slave exists. I can't figure out what I am doing wrong. Here is my drive detection routine. I call this routine 4 times. Once for each combination of I/O port and master/slave. As I mentioned the primary slave is being reported as present and when I dump the fields from the IDENTIFY buffer it seems like it is reporting the same values as the master. i.e. the max LBA is the same for both drives. It reports 25 for both drives which is the size of my raw disk image in sectors.
Any help is appreciated.
Even though I have only set up 1 drive in QEMU my drive detection code always reports that the primary slave exists. I can't figure out what I am doing wrong. Here is my drive detection routine. I call this routine 4 times. Once for each combination of I/O port and master/slave. As I mentioned the primary slave is being reported as present and when I dump the fields from the IDENTIFY buffer it seems like it is reporting the same values as the master. i.e. the max LBA is the same for both drives. It reports 25 for both drives which is the size of my raw disk image in sectors.
Any help is appreciated.
Code: Select all
static int detect_disk(int channel, int master) {
static struct SAtaDisk *last_disk = NULL;
int i;
byte select, status, b1, b2;
uint16 bfr[256], w;
struct SAtaDisk *disk;
select = (master ? 0xa0 : 0xb0); // Set master / slave
kprintf("%x\r\n", select);
// Try to select the drive
outb(select, channel + ATA_SELECT);
// 400ns delay to allow channel time to select the drive
for (i=0; i < 15; i++) {
status = inb(channel + ATA_STATUS);
}
// The ATA specification says that we need to clear these ports to zero
outw(0, channel + ATA_SECTOR_COUNT);
outw(0, channel + ATA_LBA_LOW);
outw(0, channel + ATA_LBA_MID);
outw(0, channel + ATA_LBA_HIGH);
// Send the IDENTIFY command
outb(ATA_IDENTIFY, channel + ATA_COMMAND);
// Check status. If zero, drive doesn't exist. Otherwise,
// wait until BUSY clears;
status = inb(channel + ATA_STATUS);
if (status == 0) {
return FALSE;
}
while (status & STATUS_BSY) {
status = inb(channel + ATA_STATUS);
}
if (status & STATUS_ERR) {
return FALSE;
}
// Check these ports for zero. If they are not zero the
// drive is not an ATA drive.
b1 = inb(channel + ATA_LBA_MID);
b2 = inb(channel + ATA_LBA_HIGH);
if ((b1 != 0) || (b2 != 0)) {
return FALSE;
}
// Wait until DRQ or ERR is set
status = inb(channel + ATA_STATUS);
while (! (status & (STATUS_DRQ | STATUS_ERR))) {
status = inb(channel + ATA_STATUS);
}
if (status & STATUS_ERR) {
return FALSE;
}
// Read the IDENTIFY data
for (i = 0; i < 256; i++) {
bfr[i] = inw(channel + ATA_DATA);
}
// snap(&bfr[0], 256);
// Create a disk control block from the IDENTIFY Data
disk = kalloc(sizeof(struct SAtaDisk));
if (! disk) {
kputs("Out of memory in ata_init\r\n");
panic();
}
if (! low_memory->first_ata) {
low_memory->first_ata = disk;
}
if (last_disk) {
last_disk->next = disk;
}
last_disk = disk;
disk->channel = channel;
disk->device = select;
disk->dma = ATA_IDENT_DMA;
disk->max_lba = bfr[ATA_IDENT_LBA28SECTORS] + (bfr[ATA_IDENT_LBA28SECTORS + 1] << 16);
if (bfr[ATA_IDENT_SUPPORTLBA48] & 0x400) {
disk->flags |= HD_LBA48;
}
if (disk->flags & HD_LBA48) {
disk->max_lba = (uint64)bfr[ATA_IDENT_LBA28SECTORS] +
((uint64)bfr[ATA_IDENT_LBA28SECTORS + 1] << 16) +
((uint64)bfr[ATA_IDENT_LBA28SECTORS + 2] << 32) +
((uint64)bfr[ATA_IDENT_LBA28SECTORS + 3] << 48);
}
for (i = 0; i < 40; i +=2) {
w = bfr[ATA_IDENT_MODEL + (i / 2)];
disk->model[i] = (w >> 8) & 0xff;
disk->model[i+1] = w & 0xff;
}
bfr[40] = 0;
return TRUE;
}