SASI FORMAT opcode fix, SASI segfault fix, added SASI INQUIRY/READ CAPACITY, 512 bytes per sector SASI drives (#724)

* Fixed opcode

* Fixed segfault

* Re-added 0x06 as additional SASI FORMAT opcode

* Added support of SASI drives with 512 bytes

* SASI LUN must always be taken from CDB and must be 0 or 1

* Fixed typo

* Fixed one more SASI segfault

* Removed duplicate code

* Updated error handling

* Updated error handling

* Logging update

* Added enum value for SPC-6

* Comment update

* Added support for SASI Inquiry

* Updated SASI LUN check

* Updated SASI LUN handling

* Comment update

* Revert "Comment update"

This reverts commit c6adbde25c.

* Updated logging

* Implemented SASI READ CAPACITY

* Validate SASI block count

* Do not support ICD semantics for SASI drives

* SASI READ CAPACITY is a group 1 command with 10 bytes

* Comment update
This commit is contained in:
Uwe Seimet 2022-03-06 16:17:23 +01:00 committed by GitHub
parent 536e77cd9d
commit 9099d7249c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 111 additions and 265 deletions

View File

@ -309,7 +309,7 @@ void SASIDEV::Command()
ctrl.blocks = 1;
// If no byte can be received move to the status phase
int count = ctrl.bus->CommandHandShake(ctrl.buffer);
int count = ctrl.bus->CommandHandShake(ctrl.buffer, IsSASI());
if (!count) {
Error();
return;
@ -355,74 +355,95 @@ void SASIDEV::Execute()
ctrl.blocks = 1;
ctrl.execstart = SysTimer::GetTimerLow();
int lun = GetEffectiveLun();
if (!ctrl.unit[lun]) {
ctrl.device->SetStatusCode(STATUS_INVALIDLUN);
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
return;
}
ctrl.device = ctrl.unit[lun];
ctrl.device->SetCtrl(&ctrl);
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
if ((SASIDEV::sasi_command)ctrl.cmd[0] != SASIDEV::eCmdRequestSense) {
ctrl.status = 0;
ctrl.device->SetStatusCode(0);
}
// Process by command
// TODO This code does not belong here. Each device type needs such a dispatcher, which the controller has to call.
switch ((SASIDEV::sasi_command)ctrl.cmd[0]) {
// TEST UNIT READY
case SASIDEV::eCmdTestUnitReady:
CmdTestUnitReady();
LOGTRACE( "%s TEST UNIT READY Command", __PRETTY_FUNCTION__);
ctrl.device->TestUnitReady(this);
return;
// REZERO UNIT
case SASIDEV::eCmdRezero:
CmdRezero();
LOGTRACE( "%s REZERO Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->Rezero(this);
return;
// REQUEST SENSE
case SASIDEV::eCmdRequestSense:
CmdRequestSense();
LOGTRACE( "%s REQUEST SENSE Command", __PRETTY_FUNCTION__);
ctrl.device->RequestSense(this);
return;
// FORMAT
// FORMAT (the old RaSCSI code used 0x06 as opcode, which is not compliant with the SASI specification)
// The FORMAT command of RaSCSI does not do anything but just returns a GOOD status
case SASIDEV::eCmdFormat:
CmdFormat();
case SASIDEV::eCmdFormatLegacy:
LOGTRACE( "%s FORMAT UNIT Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->FormatUnit(this);
return;
case SASIDEV::eCmdReadCapacity:
LOGTRACE( "%s READ CAPACITY Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->ReadCapacity10(this);
return;
// REASSIGN BLOCKS
case SASIDEV::eCmdReassign:
CmdReassignBlocks();
LOGTRACE( "%s REASSIGN BLOCKS Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->ReassignBlocks(this);
return;
// READ(6)
case SASIDEV::eCmdRead6:
CmdRead6();
LOGTRACE( "%s READ Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->Read6(this);
return;
// WRITE(6)
case SASIDEV::eCmdWrite6:
CmdWrite6();
LOGTRACE( "%s WRITE Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->Write6(this);
return;
// SEEK(6)
case SASIDEV::eCmdSeek6:
CmdSeek6();
LOGTRACE( "%s SEEK Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->Seek(this);
return;
case SASIDEV::eCmdInquiry:
LOGTRACE( "%s INQUIRY Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->Inquiry(this);
return;
// ASSIGN (SASI only)
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
// This doesn't exist in the SASI Spec, but was in the original RaSCSI code.
// leaving it here for now....
case SASIDEV::eCmdSasiCmdAssign:
CmdAssign();
return;
// RESERVE UNIT(16)
case SASIDEV::eCmdReserve6:
CmdReserveUnit();
LOGTRACE( "%s RESERVE Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->Reserve(this);
return;
// RELEASE UNIT(17)
case eCmdRelease6:
CmdReleaseUnit();
LOGTRACE( "%s RELEASE Command", __PRETTY_FUNCTION__);
((Disk *)ctrl.device)->Release(this);
return;
// SPECIFY (SASI only)
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
// This doesn't exist in the SASI Spec, but was in the original RaSCSI code.
// leaving it here for now....
case SASIDEV::eCmdInvalid:
CmdSpecify();
@ -432,18 +453,11 @@ void SASIDEV::Execute()
break;
}
// Unsupported command
LOGTRACE("%s ID %d received unsupported command: $%02X", __PRETTY_FUNCTION__, GetSCSIID(), (BYTE)ctrl.cmd[0]);
// Logical Unit
DWORD lun = GetEffectiveLun();
if (ctrl.unit[lun]) {
// Command processing on drive
ctrl.unit[lun]->SetStatusCode(STATUS_INVALIDCMD);
}
ctrl.device->SetStatusCode(STATUS_INVALIDCMD);
// Failure (Error)
Error();
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
}
//---------------------------------------------------------------------------
@ -618,11 +632,6 @@ void SASIDEV::DataOut()
Receive();
}
//---------------------------------------------------------------------------
//
// Error
//
//---------------------------------------------------------------------------
void SASIDEV::Error(sense_key sense_key, asc asc, status status)
{
// Get bus information
@ -644,205 +653,13 @@ void SASIDEV::Error(sense_key sense_key, asc asc, status status)
return;
}
// Logical Unit
DWORD lun = GetEffectiveLun();
// Set status and message
ctrl.status = (lun << 5) | status;
ctrl.status = (GetEffectiveLun() << 5) | status;
// status phase
Status();
}
//---------------------------------------------------------------------------
//
// TEST UNIT READY
//
//---------------------------------------------------------------------------
void SASIDEV::CmdTestUnitReady()
{
LOGTRACE("%s TEST UNIT READY Command ", __PRETTY_FUNCTION__);
// Command processing on drive
ctrl.device->TestUnitReady(this);
}
//---------------------------------------------------------------------------
//
// REZERO UNIT
//
//---------------------------------------------------------------------------
void SASIDEV::CmdRezero()
{
LOGTRACE( "%s REZERO UNIT Command ", __PRETTY_FUNCTION__);
// Command processing on drive
((Disk *)ctrl.device)->Rezero(this);
}
//---------------------------------------------------------------------------
//
// REQUEST SENSE
//
//---------------------------------------------------------------------------
void SASIDEV::CmdRequestSense()
{
LOGTRACE( "%s REQUEST SENSE Command ", __PRETTY_FUNCTION__);
// Command processing on drive
ctrl.device->RequestSense(this);
}
//---------------------------------------------------------------------------
//
// FORMAT UNIT
//
//---------------------------------------------------------------------------
void SASIDEV::CmdFormat()
{
LOGTRACE( "%s FORMAT UNIT Command ", __PRETTY_FUNCTION__);
// Command processing on drive
((Disk *)ctrl.device)->FormatUnit(this);
}
//---------------------------------------------------------------------------
//
// REASSIGN BLOCKS
//
//---------------------------------------------------------------------------
void SASIDEV::CmdReassignBlocks()
{
LOGTRACE("%s REASSIGN BLOCKS Command ", __PRETTY_FUNCTION__);
// Command processing on drive
((Disk *)ctrl.device)->ReassignBlocks(this);
}
//---------------------------------------------------------------------------
//
// RESERVE UNIT(16)
//
// The reserve/release commands are only used in multi-initiator
// environments. RaSCSI doesn't support this use case. However, some old
// versions of Solaris will issue the reserve/release commands. We will
// just respond with an OK status.
//
//---------------------------------------------------------------------------
void SASIDEV::CmdReserveUnit()
{
LOGTRACE( "%s Reserve(6) Command", __PRETTY_FUNCTION__);
// status phase
Status();
}
//---------------------------------------------------------------------------
//
// RELEASE UNIT(17)
//
// The reserve/release commands are only used in multi-initiator
// environments. RaSCSI doesn't support this use case. However, some old
// versions of Solaris will issue the reserve/release commands. We will
// just respond with an OK status.
//
//---------------------------------------------------------------------------
void SASIDEV::CmdReleaseUnit()
{
LOGTRACE( "%s Release(6) Command", __PRETTY_FUNCTION__);
// status phase
Status();
}
//---------------------------------------------------------------------------
//
// READ(6)
//
//---------------------------------------------------------------------------
void SASIDEV::CmdRead6()
{
// Get record number and block number
DWORD record = ctrl.cmd[1] & 0x1f;
record <<= 8;
record |= ctrl.cmd[2];
record <<= 8;
record |= ctrl.cmd[3];
ctrl.blocks = ctrl.cmd[4];
if (ctrl.blocks == 0) {
ctrl.blocks = 0x100;
}
LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl.blocks);
// Command processing on drive
ctrl.length = ((Disk *)ctrl.device)->Read(ctrl.cmd, ctrl.buffer, record);
if (ctrl.length <= 0) {
// Failure (Error)
Error();
return;
}
// Set next block
ctrl.next = record + 1;
// Read phase
DataIn();
}
//---------------------------------------------------------------------------
//
// WRITE(6)
//
//---------------------------------------------------------------------------
void SASIDEV::CmdWrite6()
{
// Get record number and block number
DWORD record = ctrl.cmd[1] & 0x1f;
record <<= 8;
record |= ctrl.cmd[2];
record <<= 8;
record |= ctrl.cmd[3];
ctrl.blocks = ctrl.cmd[4];
if (ctrl.blocks == 0) {
ctrl.blocks = 0x100;
}
LOGTRACE("%s WRITE(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (WORD)record, (WORD)ctrl.blocks);
// Command processing on drive
ctrl.length = ((Disk *)ctrl.device)->WriteCheck(record);
if (ctrl.length <= 0) {
// Failure (Error)
Error();
return;
}
// Set next block
ctrl.next = record + 1;
// Write phase
DataOut();
}
//---------------------------------------------------------------------------
//
// SEEK(6)
//
//---------------------------------------------------------------------------
void SASIDEV::CmdSeek6()
{
LOGTRACE("%s SEEK(6) Command ", __PRETTY_FUNCTION__);
// Command processing on drive
((Disk *)ctrl.device)->Seek6(this);
}
//---------------------------------------------------------------------------
//
// ASSIGN
//
//---------------------------------------------------------------------------
void SASIDEV::CmdAssign()
{
LOGTRACE("%s ASSIGN Command ", __PRETTY_FUNCTION__);
@ -862,11 +679,6 @@ void SASIDEV::CmdAssign()
DataOut();
}
//---------------------------------------------------------------------------
//
// SPECIFY
//
//---------------------------------------------------------------------------
void SASIDEV::CmdSpecify()
{
LOGTRACE("%s SPECIFY Command ", __PRETTY_FUNCTION__);
@ -1276,6 +1088,6 @@ void SASIDEV::FlushUnit()
int SASIDEV::GetEffectiveLun() const
{
return ctrl.lun != -1 ? ctrl.lun : (ctrl.cmd[1] >> 5) & 0x07;
return (ctrl.cmd[1] >> 5) & 0x07;
}

View File

@ -36,12 +36,15 @@ private:
eCmdTestUnitReady = 0x00,
eCmdRezero = 0x01,
eCmdRequestSense = 0x03,
eCmdFormat = 0x06,
eCmdFormat = 0x04,
eCmdReadCapacity = 0x05,
eCmdFormatLegacy = 0x06,
eCmdReassign = 0x07,
eCmdRead6 = 0x08,
eCmdWrite6 = 0x0A,
eCmdSeek6 = 0x0B,
eCmdSetMcastAddr = 0x0D, // DaynaPort specific command
eCmdInquiry = 0x12,
eCmdModeSelect6 = 0x15,
eCmdReserve6 = 0x16,
eCmdRelease6 = 0x17,
@ -137,8 +140,7 @@ public:
void MsgIn(); // Message in phase
void DataOut(); // Data out phase
// Get LUN based on IDENTIFY message, with LUN from the CDB as fallback
int GetEffectiveLun() const;
virtual int GetEffectiveLun() const;
virtual void Error(scsi_defs::sense_key sense_key = scsi_defs::sense_key::NO_SENSE,
scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
@ -152,16 +154,6 @@ protected:
virtual void Execute(); // Execution phase
// Commands
void CmdTestUnitReady(); // TEST UNIT READY command
void CmdRezero(); // REZERO UNIT command
void CmdRequestSense(); // REQUEST SENSE command
void CmdFormat(); // FORMAT command
void CmdReassignBlocks(); // REASSIGN BLOCKS command
void CmdReserveUnit(); // RESERVE UNIT command
void CmdReleaseUnit(); // RELEASE UNIT command
void CmdRead6(); // READ(6) command
void CmdWrite6(); // WRITE(6) command
void CmdSeek6(); // SEEK(6) command
void CmdAssign(); // ASSIGN command
void CmdSpecify(); // SPECIFY command

View File

@ -274,7 +274,7 @@ void SCSIDEV::Execute()
int lun = GetEffectiveLun();
if (!ctrl.unit[lun]) {
if ((scsi_command)ctrl.cmd[0] != eCmdInquiry &&
if ((scsi_command)ctrl.cmd[0] != scsi_command::eCmdInquiry &&
(scsi_command)ctrl.cmd[0] != scsi_command::eCmdRequestSense) {
LOGDEBUG("Invalid LUN %d for ID %d", lun, GetSCSIID());
@ -911,3 +911,8 @@ bool SCSIDEV::XferOut(bool cont)
return false;
}
int SCSIDEV::GetEffectiveLun() const
{
return ctrl.lun != -1 ? ctrl.lun : (ctrl.cmd[1] >> 5) & 0x07;
}

View File

@ -62,6 +62,9 @@ public:
void Receive() override;
// Get LUN based on IDENTIFY message, with LUN from the CDB as fallback
int GetEffectiveLun() const;
bool IsSASI() const override { return false; }
bool IsSCSI() const override { return true; }

View File

@ -25,7 +25,7 @@ using namespace rascsi_interface;
DeviceFactory::DeviceFactory()
{
sector_sizes[SAHD] = { 256, 1024 };
sector_sizes[SAHD] = { 256, 512, 1024 };
sector_sizes[SCHD] = { 512, 1024, 2048, 4096 };
sector_sizes[SCRM] = { 512, 1024, 2048, 4096 };
sector_sizes[SCMO] = { 512, 1024, 2048, 4096 };

View File

@ -71,6 +71,8 @@ public:
bool Eject(bool) override;
private:
friend class SASIDEV;
typedef ModePageDevice super;
// Commands covered by the SCSI specification (see https://www.t10.org/drafts.htm)
@ -94,7 +96,7 @@ private:
void Verify16(SASIDEV *);
void Seek(SASIDEV *);
void Seek10(SASIDEV *);
void ReadCapacity10(SASIDEV *) override;
virtual void ReadCapacity10(SASIDEV *) override;
void ReadCapacity16(SASIDEV *) override;
void Reserve(SASIDEV *);
void Release(SASIDEV *);

View File

@ -128,8 +128,6 @@ void PrimaryDevice::RequestSense(SASIDEV *controller)
memcpy(ctrl->buffer, buf.data(), allocation_length);
ctrl->length = allocation_length;
LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, ctrl->status, ctrl->buffer[2], ctrl->buffer[12]);
controller->DataIn();
}
@ -205,6 +203,8 @@ vector<BYTE> PrimaryDevice::RequestSense(int)
buf[12] = GetStatusCode() >> 8;
buf[13] = GetStatusCode();
LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, ctrl->status, ctrl->buffer[2], ctrl->buffer[12]);
return buf;
}

View File

@ -30,6 +30,7 @@ public:
void TestUnitReady(SASIDEV *);
void RequestSense(SASIDEV *);
virtual void Inquiry(SASIDEV *);
void SetCtrl(SASIDEV::ctrl_t *ctrl) { this->ctrl = ctrl; }
@ -49,6 +50,5 @@ private:
Dispatcher<PrimaryDevice, SASIDEV> dispatcher;
void Inquiry(SASIDEV *);
void ReportLuns(SASIDEV *);
};

View File

@ -53,6 +53,11 @@ void SASIHD::Open(const Filepath& path)
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 256, true);
SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount()));
// SASI only supports READ/WRITE(6), limiting the block count to 2^21
if (GetBlockCount() > 2097152) {
throw io_exception("SASI drives are limited to 2097152 blocks");
}
#if defined(REMOVE_FIXED_SASIHD_SIZE)
// Effective size must be a multiple of the sector size
size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes();
@ -83,8 +88,9 @@ void SASIHD::Open(const Filepath& path)
vector<BYTE> SASIHD::Inquiry() const
{
assert(false);
return vector<BYTE>(0);
// Byte 0 = 0: Direct access device
return vector<BYTE>(2);
}
vector<BYTE> SASIHD::RequestSense(int allocation_length)
@ -96,5 +102,29 @@ vector<BYTE> SASIHD::RequestSense(int allocation_length)
buf[0] = (BYTE)(GetStatusCode() >> 16);
buf[1] = (BYTE)(GetLun() << 5);
LOGTRACE("%s Status $%02X",__PRETTY_FUNCTION__, buf[0]);
return buf;
}
void SASIHD::ReadCapacity10(SASIDEV *controller)
{
BYTE *buf = ctrl->buffer;
// Create end of logical block address (disk.blocks-1)
uint32_t blocks = disk.blocks - 1;
buf[0] = (BYTE)(blocks >> 24);
buf[1] = (BYTE)(blocks >> 16);
buf[2] = (BYTE)(blocks >> 8);
buf[3] = (BYTE)blocks;
// Create block length (1 << disk.size)
uint32_t length = 1 << disk.size;
buf[4] = (BYTE)(length >> 8);
buf[5] = (BYTE)length;
// the size
ctrl->length = 6;
controller->DataIn();
}

View File

@ -35,4 +35,5 @@ public:
vector<BYTE> RequestSense(int) override;
vector<BYTE> Inquiry() const override;
virtual void ReadCapacity10(SASIDEV *) override;
};

View File

@ -817,7 +817,7 @@ BOOL GPIOBUS::GetDP()
// Receive command handshake
//
//---------------------------------------------------------------------------
int GPIOBUS::CommandHandShake(BYTE *buf)
int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi)
{
int count;
@ -869,7 +869,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf)
// semantics. I fact, these semantics have become a standard in the Atari world.
// RaSCSI becomes ICD compatible by ignoring the prepended $1F byte before processing the CDB.
if (*buf == 0x1F) {
if (!is_sasi && *buf == 0x1F) {
SetSignal(PIN_REQ, ON);
ret = WaitSignal(PIN_ACK, TRUE);
@ -1592,7 +1592,7 @@ int GPIOBUS::GetCommandByteCount(BYTE opcode) {
else if (opcode == 0xA0) {
return 12;
}
else if (opcode >= 0x20 && opcode <= 0x7D) {
else if (opcode == 0x05 || (opcode >= 0x20 && opcode <= 0x7D)) {
return 10;
} else {
return 6;

View File

@ -565,7 +565,7 @@ public:
// Set DAT signal
BOOL GetDP() override;
// Get Data parity signal
int CommandHandShake(BYTE *buf) override;
int CommandHandShake(BYTE *buf, bool) override;
// Command receive handshake
int ReceiveHandShake(BYTE *buf, int count) override;
// Data receive handshake

View File

@ -98,7 +98,7 @@ public:
virtual BOOL GetDP() = 0; // Get parity signal
virtual DWORD Aquire() = 0;
virtual int CommandHandShake(BYTE *buf) = 0;
virtual int CommandHandShake(BYTE *buf, bool) = 0;
virtual int ReceiveHandShake(BYTE *buf, int count) = 0;
virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0;
@ -131,7 +131,8 @@ namespace scsi_defs {
SPC_2 = 4,
SPC_3 = 5,
SPC_4 = 6,
SPC_5 = 7
SPC_5 = 7,
SPC_6 = 8
};
enum device_type : int {