I'm facing some trouble trying to perform reads from a cdrom device (qemu emulated).
As i have read, it is possible to tell the fis to operate in ATAPI mode as explained in:
https://wiki.osdev.org/AHCI#AHCI_.26_ATAPI
The steps i'm following (starting from a working read ata function) are:
1. Changing the ATA DMA READ command (0x25) to ATA PACKET (0xA0).
2. Setting the ATAPI command (READ (0xA8)) in the command table.
3. Setting the ATAPI mode bit on the command header.
4. Changing the FIS type from REG_H2D (0x27) to PIO_SETUP(0x5f).
My code so far is:
Code: Select all
uint8_t read_atapi_port(uint8_t port_no, uint64_t sector, uint32_t sector_count) {
struct ahci_port* port = &ahci_ports[port_no];
uint32_t sector_low = (uint32_t)sector;
uint32_t sector_high = (uint32_t)(sector >> 32);
port->hba_port->interrupt_status = (uint32_t)-1;
int spin = 0;
int slot = (int)find_cmd_slot(port);
if (slot == -1) {
printf("No free command slots\n");
return 0;
}
struct hba_command_header* command_header = (struct hba_command_header*)(uint64_t)(port->hba_port->command_list_base);
command_header += slot;
command_header->command_fis_length = sizeof(struct hba_command_fis) / sizeof(uint32_t);
command_header->write = 0;
command_header->atapi = 1;
command_header->prdt_length = (uint16_t)((sector_count - 1) >> 4) + 1;
struct hba_command_table* command_table = (struct hba_command_table*)(uint64_t)(command_header->command_table_base_address);
memset(command_table, 0, sizeof(struct hba_command_table) + (command_header->prdt_length - 1) * sizeof(struct hba_prdt_entry));
command_table->atapi_command[0] = ATAPI_READ_CMD;
command_table->atapi_command[1] = 0;
command_table->atapi_command[2] = (uint8_t)(sector >> 0x18);
command_table->atapi_command[3] = (uint8_t)(sector >> 0x10);
command_table->atapi_command[4] = (uint8_t)(sector >> 0x08);
command_table->atapi_command[5] = (uint8_t)(sector >> 0x00);
void* buffer = port->buffer;
int i;
for (i = 0; i < command_header->prdt_length - 1; i++) {
command_table->prdt_entry[i].data_base_address = (uint32_t)(uint64_t)buffer;
command_table->prdt_entry[i].data_base_address_upper = (uint32_t)((uint64_t)buffer >> 32);
command_table->prdt_entry[i].byte_count = 8 * 1024 - 1;
command_table->prdt_entry[i].interrupt_on_completion = 1;
buffer = (void*)((uint64_t*)buffer+0x1000);
sector_count -= 16;
}
command_table->prdt_entry[i].data_base_address = (uint32_t)(uint64_t)buffer;
command_table->prdt_entry[i].data_base_address_upper = (uint32_t)((uint64_t)buffer >> 32);
command_table->prdt_entry[i].byte_count = (sector_count << 9) - 1;
command_table->prdt_entry[i].interrupt_on_completion = 1;
struct hba_command_fis* command_fis = (struct hba_command_fis*)command_table->command_fis;
command_fis->fis_type = FIS_TYPE_PIO_SETUP;
command_fis->command_control = 1;
command_fis->command = ATA_CMD_PACKET;
command_fis->lba0 = (uint8_t)sector_low;
command_fis->lba1 = (uint8_t)(sector_low >> 8);
command_fis->lba2 = (uint8_t)(sector_low >> 16);
command_fis->device_register = 1 << 6;
command_fis->lba3 = (uint8_t)(sector_low >> 24);
command_fis->lba4 = (uint8_t)(sector_high);
command_fis->lba5 = (uint8_t)(sector_high >> 8);
command_fis->count_low = sector_count & 0xFF;
command_fis->count_high = (sector_count >> 8);
while (port->hba_port->task_file_data & (ATA_DEV_BUSY | ATA_DEV_DRQ) && spin < 1000000) {
spin++;
};
if (spin == 1000000) {
printf("Port is hung\n");
return 0;
}
port->hba_port->command_issue = (1 << slot);
while(1) {
if ((port->hba_port->command_issue & (1<<slot)) == 0) break;
if (port->hba_port->interrupt_status & HBA_PxIS_TFES) {
return 0;
}
}
if (port->hba_port->interrupt_status & HBA_PxIS_TFES) {
return 0;
}
return 1;
}