diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp index 76df856f6d..57c3e4f83c 100644 --- a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp +++ b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp @@ -10,6 +10,7 @@ #include "scsi_cmds.h" #include +#include #include #include @@ -21,12 +22,20 @@ AHCIPort::AHCIPort(AHCIController *controller, int index) : fIndex(index) , fRegs(&controller->fRegs->port[index]) , fArea(-1) + , fRequestSem(-1) + , fResponseSem(-1) + , fCommandActive(false) + , fHarddiskSize(0) { + fRequestSem = create_sem(1, "ahci request"); + fResponseSem = create_sem(1, "ahci response"); } AHCIPort::~AHCIPort() { + delete_sem(fRequestSem); + delete_sem(fResponseSem); } @@ -256,6 +265,9 @@ AHCIPort::Interrupt() uint32 is = fRegs->is; TRACE("AHCIPort::Interrupt port %d, status %#08lx\n", fIndex, is); + if (fCommandActive) + release_sem_etc(fResponseSem, 1, B_RELEASE_IF_WAITING_ONLY | B_DO_NOT_RESCHEDULE); + // clear interrupts fRegs->is = is; } @@ -284,6 +296,7 @@ AHCIPort::FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax, const while (sgCount > 0) { size_t size = sgTable->size; void *address = sgTable->address; + TRACE("FillPrdTable: sg-entry addr %p, size %lu\n", address, size); if ((uint32)address & 1) { TRACE("AHCIPort::FillPrdTable: data alignment error\n"); return B_ERROR; @@ -294,6 +307,7 @@ AHCIPort::FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax, const TRACE("AHCIPort::FillPrdTable: prd table exhausted\n"); return B_ERROR; } + TRACE("FillPrdTable: prd-entry %u, addr %p, size %lu\n", *prdCount, address, bytes); prdTable->dba = LO32(address); prdTable->dbau = HI32(address); prdTable->res = 0; @@ -312,6 +326,32 @@ AHCIPort::FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax, const } +void +AHCIPort::StartTransfer() +{ + acquire_sem(fRequestSem); + fCommandActive = true; +} + + +status_t +AHCIPort::WaitForTransfer(int *status, bigtime_t timeout) +{ + status_t result = B_OK; + if (acquire_sem_etc(fResponseSem, 1, B_RELATIVE_TIMEOUT, timeout) < B_OK) { + result = B_TIMED_OUT; + } + fCommandActive = false; + return result; +} + + +void +AHCIPort::FinishTransfer() +{ + release_sem(fRequestSem); +} + /* void AHCIPort::IdentifyDevice(scsi_ccb *request) @@ -422,6 +462,8 @@ AHCIPort::ScsiInquiry(scsi_ccb *request) memset(&ataData, 0, sizeof(ataData)); + StartTransfer(); + int prdEntrys; FillPrdTable(fPRDTable, &prdEntrys, PRD_TABLE_ENTRY_COUNT, &ataData, sizeof(ataData), true); @@ -443,10 +485,12 @@ AHCIPort::ScsiInquiry(scsi_ccb *request) fRegs->ci |= 1; FlushPostedWrites(); - snooze(500000); + int status; + WaitForTransfer(&status, 100000); TRACE("prdbc %ld\n", fCommandList->prdbc); +/* TRACE("ci 0x%08lx\n", fRegs->ci); TRACE("ie 0x%08lx\n", fRegs->ie); TRACE("is 0x%08lx\n", fRegs->is); @@ -461,7 +505,7 @@ AHCIPort::ScsiInquiry(scsi_ccb *request) for (int i = 0; i < 512; i += 8) { TRACE(" %02x %02x %02x %02x %02x %02x %02x %02x\n", data[i], data[i+1], data[i+2], data[i+3], data[i+4], data[i+5], data[i+6], data[i+7]); } - +*/ scsiData.device_type = scsi_dev_direct_access; scsiData.device_qualifier = scsi_periph_qual_connected; scsiData.device_type_modifier = 0; @@ -479,22 +523,28 @@ AHCIPort::ScsiInquiry(scsi_ccb *request) scsiData.write_bus16 = true; scsiData.write_bus32 = false; scsiData.relative_address = false; + memcpy(scsiData.vendor_ident, ataData.model_number, sizeof(scsiData.vendor_ident)); + memcpy(scsiData.product_ident, ataData.model_number + 8, sizeof(scsiData.product_ident)); + memcpy(scsiData.product_rev, ataData.serial_number, sizeof(scsiData.product_rev)); bool lba = (ataData.words[49] & (1 << 9)) != 0; bool lba48 = (ataData.words[83] & (1 << 10)) != 0; uint32 sectors = *(uint32*)&ataData.words[60]; uint64 sectors48 = *(uint64*)&ataData.words[100]; - uint64 size = !(lba || sectors) ? 0 : lba48 ? (sectors48 * 512) : (sectors * 512ull); + fHarddiskSize = !(lba || sectors) ? 0 : lba48 ? (sectors48 * 512) : (sectors * 512ull); - TRACE("model_number: %40.40s\n", ataData.model_number); - TRACE("serial_number: %20.20s\n", ataData.serial_number); - TRACE("firmware_revision: %8.8s\n", ataData.firmware_revision); + swap_words(ataData.model_number, sizeof(ataData.model_number)); + swap_words(ataData.serial_number, sizeof(ataData.serial_number)); + swap_words(ataData.firmware_revision, sizeof(ataData.firmware_revision)); + + TRACE("model_number: %.40s\n", ataData.model_number); + TRACE("serial_number: %.20s\n", ataData.serial_number); + TRACE("firmware_revision: %.8s\n", ataData.firmware_revision); TRACE("lba %d, lba48 %d, sectors %lu, sectors48 %llu, size %llu\n", - lba, lba48, sectors, sectors48, size); + lba, lba48, sectors, sectors48, fHarddiskSize); - memcpy(scsiData.vendor_ident, ataData.model_number, sizeof(scsiData.vendor_ident)); - memcpy(scsiData.product_ident, ataData.model_number + 8, sizeof(scsiData.product_ident)); - memcpy(scsiData.product_rev, ataData.serial_number, sizeof(scsiData.product_rev)); + if (fHarddiskSize >= (512ull * 0xffffffff)) + panic("ahci: SCSI emulation doesn't support harddisks larger than 2TB"); if (sg_memcpy(request->sg_list, request->sg_count, &scsiData, sizeof(scsiData)) < B_OK) { request->subsys_status = SCSI_DATA_RUN_ERR; @@ -504,6 +554,96 @@ AHCIPort::ScsiInquiry(scsi_ccb *request) request->data_length = sizeof(scsiData); // ??? } + FinishTransfer(); + + gSCSI->finished(request, 1); +} + + +void +AHCIPort::ScsiReadCapacity(scsi_ccb *request) +{ + TRACE("AHCIPort::ScsiReadCapacity port %d\n", fIndex); + + scsi_cmd_read_capacity *cmd = (scsi_cmd_read_capacity *)request->cdb; + scsi_res_read_capacity scsiData; + + if (cmd->pmi || cmd->lba) { + TRACE("invalid request\n"); + return; + } + + uint64 sectorCount = fHarddiskSize / 512; + uint32 sectorSize = 512; + TRACE("sectorCount 0x%llx, sectorSize %lu\n", sectorCount, sectorSize); +/* + while (sectorCount > 0xffffffff) { + sectorCount /= 2; + sectorSize *= 2; + } + TRACE("sectorCount 0x%llx, sectorSize %lu\n", sectorCount, sectorSize); +*/ + + scsiData.block_size = B_HOST_TO_BENDIAN_INT32(sectorSize); + scsiData.lba = B_HOST_TO_BENDIAN_INT32(sectorCount - 1); + + if (sg_memcpy(request->sg_list, request->sg_count, &scsiData, sizeof(scsiData)) < B_OK) { + request->subsys_status = SCSI_DATA_RUN_ERR; + } else { + request->subsys_status = SCSI_REQ_CMP; + request->data_resid = request->data_length - sizeof(scsiData);// ??? + request->data_length = sizeof(scsiData); // ??? + } + gSCSI->finished(request, 1); +} + + +void +AHCIPort::ScsiReadWrite(scsi_ccb *request, uint64 position, size_t length, bool isWrite) +{ + TRACE("ScsiReadWrite: pos %llu, size %lu, isWrite %d\n", position, length, isWrite); + + StartTransfer(); + + int prdEntrys; + FillPrdTable(fPRDTable, &prdEntrys, PRD_TABLE_ENTRY_COUNT, request->sg_list, request->sg_count, true); + + TRACE("prdEntrys %d\n", prdEntrys); + + memset((void *)fCommandTable->cfis, 0, 5 * 4); + fCommandTable->cfis[0] = 0x27; + fCommandTable->cfis[1] = 0x80; + fCommandTable->cfis[2] = 0x25; + fCommandTable->cfis[4] = position & 0xff; + fCommandTable->cfis[5] = (position >> 8) & 0xff; + fCommandTable->cfis[6] = (position >> 16) & 0xff; + fCommandTable->cfis[7] = 0x40; + fCommandTable->cfis[8] = (position >> 24) & 0xff; + fCommandTable->cfis[9] = (position >> 32) & 0xff; + fCommandTable->cfis[10] = (position >> 40) & 0xff; + fCommandTable->cfis[12] = length & 0xff; + fCommandTable->cfis[13] = (length >> 8) & 0xff; + + fCommandList->prdtl_flags_cfl = 0; + fCommandList->prdtl = prdEntrys; +// fCommandList->c = 1; + fCommandList->cfl = 5; + fCommandList->prdbc = 0; + + fRegs->ci |= 1; + FlushPostedWrites(); + + int status; + WaitForTransfer(&status, 100000); + + TRACE("prdbc %ld\n", fCommandList->prdbc); + + request->subsys_status = SCSI_REQ_CMP; + request->data_resid = request->data_length - fCommandList->prdbc;// ??? + request->data_length = fCommandList->prdbc; // ??? + + FinishTransfer(); + gSCSI->finished(request, 1); } @@ -530,6 +670,52 @@ AHCIPort::ScsiExecuteRequest(scsi_ccb *request) case SCSI_OP_INQUIRY: ScsiInquiry(request); break; + case SCSI_OP_READ_CAPACITY: + ScsiReadCapacity(request); + break; + case SCSI_OP_READ_6: + case SCSI_OP_WRITE_6: + { + scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb; + uint32 position = ((uint32)cmd->high_lba << 16) | ((uint32)cmd->mid_lba << 8) | (uint32)cmd->low_lba; + size_t length = cmd->length != 0 ? cmd->length : 256; + bool isWrite = request->cdb[0] == SCSI_OP_WRITE_6; + ScsiReadWrite(request, position, length, isWrite); + break; + } + case SCSI_OP_READ_10: + case SCSI_OP_WRITE_10: + { + scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb; + uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba); + size_t length = B_BENDIAN_TO_HOST_INT16(cmd->length); + bool isWrite = request->cdb[0] == SCSI_OP_WRITE_10; + if (length) { + ScsiReadWrite(request, position, length, isWrite); + } else { + TRACE("AHCIPort::ScsiExecuteRequest error: transfer without data!\n"); + request->subsys_status = SCSI_DEV_NOT_THERE; + gSCSI->finished(request, 1); + } + break; + } + case SCSI_OP_READ_12: + case SCSI_OP_WRITE_12: + { + scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb; + uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba); + size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length); + bool isWrite = request->cdb[0] == SCSI_OP_WRITE_12; + if (length) { + ScsiReadWrite(request, position, length, isWrite); + } else { + TRACE("AHCIPort::ScsiExecuteRequest error: transfer without data!\n"); + request->subsys_status = SCSI_DEV_NOT_THERE; + gSCSI->finished(request, 1); + } + break; + } + default: request->subsys_status = SCSI_DEV_NOT_THERE; gSCSI->finished(request, 1); diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h index 550fa12251..63d7b2b3ae 100644 --- a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h +++ b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h @@ -29,13 +29,18 @@ public: private: void ScsiTestUnitReady(scsi_ccb *request); void ScsiInquiry(scsi_ccb *request); - + void ScsiReadCapacity(scsi_ccb *request); + void ScsiReadWrite(scsi_ccb *request, uint64 position, size_t length, bool isWrite); status_t ResetDevice(); status_t PostResetDevice(); void FlushPostedWrites(); void DumpD2HFis(); + void StartTransfer(); + status_t WaitForTransfer(int *status, bigtime_t timeout); + void FinishTransfer(); + // uint8 * SetCommandFis(volatile command_list_entry *cmd, volatile fis *fis, const void *data, size_t dataSize); status_t FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax, const void *data, size_t dataSize, bool ioc = false); @@ -45,11 +50,15 @@ private: int fIndex; volatile ahci_port * fRegs; area_id fArea; + sem_id fRequestSem; + sem_id fResponseSem; + volatile bool fCommandActive; volatile fis * fFIS; volatile command_list_entry * fCommandList; volatile command_table * fCommandTable; volatile prd * fPRDTable; + uint64 fHarddiskSize; }; inline void diff --git a/src/add-ons/kernel/busses/scsi/ahci/util.c b/src/add-ons/kernel/busses/scsi/ahci/util.c index 66e78d64ef..be5160dc41 100644 --- a/src/add-ons/kernel/busses/scsi/ahci/util.c +++ b/src/add-ons/kernel/busses/scsi/ahci/util.c @@ -100,3 +100,15 @@ sg_memcpy(const physical_entry *sgTable, int sgCount, const void *data, size_t d return B_ERROR; return B_OK; } + + +void +swap_words(void *data, size_t size) +{ + uint16 *word = data; + size_t count = size / 2; + while (count--) { + *word = (*word << 8) | (*word >> 8); + word++; + } +} diff --git a/src/add-ons/kernel/busses/scsi/ahci/util.h b/src/add-ons/kernel/busses/scsi/ahci/util.h index 094836f8a1..afb2a14f3b 100644 --- a/src/add-ons/kernel/busses/scsi/ahci/util.h +++ b/src/add-ons/kernel/busses/scsi/ahci/util.h @@ -16,6 +16,8 @@ area_id map_mem(void **virt, void *phy, size_t size, uint32 protection, const ch status_t sg_memcpy(const physical_entry *sgTable, int sgCount, const void *data, size_t dataSize); +void swap_words(void *data, size_t size); + #ifdef __cplusplus } #endif