--- nono/vm/scsidev.cpp 2026/04/29 17:04:36 1.1.1.3 +++ nono/vm/scsidev.cpp 2026/04/29 17:04:39 1.1.1.4 @@ -96,9 +96,9 @@ SCSIHostDevice::Init() std::string typestr; if (target[id]) { switch (target[id]->GetDevType()) { - case SCSI::None: + case SCSI::DevType::None: break; - case SCSI::HD: + case SCSI::DevType::HD: typestr = "HD"; break; } @@ -166,8 +166,13 @@ class SCSICmd // 以下いずれも戻り値は遷移先のフェーズ。 // コマンドフェーズを処理する。 - // コマンドが確定したところで呼ばれる。遷移先フェーズを返すこと。 + // コマンドが確定したところで呼ばれる。戻り値に遷移先フェーズを返すこと。 + // また、コマンド開始時に各自で ClearSense() を呼ぶこと。センスというか + // CA 状態は次のコマンド実行でクリアされるが (ネクサスはとりあえず無視)、 + // RequestSense の場合は現在保持しているセンス情報をホストに返すので、 + // つまり RequestSense 以外のコマンドが全員各自でクリアする必要がある。 virtual SCSI::XferPhase Command(std::vector& cmdseq) { + parent->ClearSense(); return SCSI::XferPhase::Status; } @@ -178,10 +183,9 @@ class SCSICmd virtual SCSI::XferPhase DoneDataOut() { return SCSI::XferPhase::Status; } // ステータスフェーズを開始する。 - // デフォルトでは Good を返す実装を用意しておく。 - virtual void BeginStatus() { + void BeginStatus() { buf.clear(); - buf.push_back((uint8)SCSI::StatusByte::Good); + buf.push_back(status_byte); } // ステータスフェーズを完了する。 @@ -206,6 +210,9 @@ class SCSICmd protected: // このコマンドを実行しているターゲットデバイス。 SCSITarget *parent; + + // ステータスバイト (デフォルトは 0 = Good) + uint8 status_byte {}; }; // @@ -243,22 +250,38 @@ class SCSICmdRequestSense : public SCSIC virtual ~SCSICmdRequestSense() { } virtual SCSI::XferPhase Command(std::vector& cmdseq) { - // この内容は全く不正。でっちあげである。 - // まず 8バイトのバッファに応答を作成 + // 応答のセンスデータは SCSI-2 形式(最小18バイト) を実装。 + // 今の所、センスキー、ASC、ASCQ だけで返せる内容にだけ対応している。 + // これ以外のフィールドを返す必要が出来たらまた考えること。 + buf.clear(); - buf.resize(8); - buf[0] = 0xf0; // Valid, エラークラス7, エラーコード0 - buf[1] = 0; - buf[2] = 0; // No Sense - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - buf[6] = 0; - buf[7] = 0; // 追加センスデータ長 0 + buf.resize(17); + buf[0] = 0xf0; // Valid、エラーコード 0x70 + buf[1] = 0; // セグメント番号(?) + buf[2] = parent->sensekey; // センスキー (上位4ビットは今の所 0) + buf[3] = 0; // Information + buf[4] = 0; // Information + buf[5] = 0; // Information + buf[6] = 0; // Information + buf[7] = 0; // 追加センスデータ長 0 + buf[8] = 0; // Command specific Information + buf[9] = 0; // Command specific Information + buf[10] = 0; // Command specific Information + buf[11] = 0; // Command specific Information + buf[12] = parent->senseasc >> 8; + buf[13] = parent->senseasc & 0xff; + buf[14] = 0; // FRU + buf[15] = 0; // センスキー固有情報 + buf[16] = 0; // センスキー固有情報 + buf[17] = 0; // センスキー固有情報 + // 追加センスデータなし // それからバッファを指定の長さに変更 buf.resize((uint)cmdseq[4]); + // 転送した後にセンスデータをクリア + parent->ClearSense(); + return SCSI::XferPhase::DataIn; } }; @@ -278,20 +301,32 @@ class SCSICmdRead6 : public SCSICmd uint32 lba; uint32 blks; + // センスデータをクリア + parent->ClearSense(); + lba = (cmdseq[1] & 0x1f) << 16; lba |= cmdseq[2] << 8; lba |= cmdseq[3]; blks = cmdseq[4]; - // blks == 0 は 256 ブロック転送を意味する。 if (blks == 0) { blks = 256; } + parent->putlog(1, "READ6 LBA=0x%x blks=0x%x", lba, blks); + + if (((uint64)lba + (uint64)blks) * parent->GetBlocksize() > + hdparent->GetSize()) + { + parent->sensekey = SCSI::SenseKey::IllegalRequest; + parent->senseasc = SCSI::ASC::InvalidFieldInCDB; + status_byte = SCSI::StatusByte::CheckCondition; + return SCSI::XferPhase::Status; + } + uint8 *start = hdparent->mem + lba * parent->GetBlocksize(); uint8 *end = start + blks * parent->GetBlocksize(); buf.assign(start, end); - parent->putlog(1, "READ6 LBA=0x%x blks=0x%x", lba, blks); return SCSI::XferPhase::DataIn; } }; @@ -309,17 +344,28 @@ class SCSICmdWrite6 : public SCSICmd virtual ~SCSICmdWrite6() { } virtual SCSI::XferPhase Command(std::vector& cmdseq) { + // センスデータをクリア + parent->ClearSense(); + lba = (cmdseq[1] & 0x1f) << 16; lba |= cmdseq[2] << 8; lba |= cmdseq[3]; blks = cmdseq[4]; - // blks == 0 は 256 ブロック転送を意味する。 if (blks == 0) { blks = 256; } - recvbytes = blks * parent->GetBlocksize(); + if (((uint64)lba + (uint64)blks) * parent->GetBlocksize() > + hdparent->GetSize()) + { + parent->sensekey = SCSI::SenseKey::IllegalRequest; + parent->senseasc = SCSI::ASC::InvalidFieldInCDB; + status_byte = SCSI::StatusByte::CheckCondition; + return SCSI::XferPhase::Status; + } + + recvbytes = blks * parent->GetBlocksize(); return SCSI::XferPhase::DataOut; } @@ -349,6 +395,9 @@ class SCSICmdInquiry : public SCSICmd virtual ~SCSICmdInquiry() { } virtual SCSI::XferPhase Command(std::vector& cmdseq) { + // センスデータをクリア + parent->ClearSense(); + // 応答データはターゲットデバイスが持っている buf = parent->inquiry_data; @@ -372,6 +421,9 @@ class SCSICmdModeSelect6 : public SCSICm virtual ~SCSICmdModeSelect6() { } virtual SCSI::XferPhase Command(std::vector& cmdseq) { + // センスデータをクリア + parent->ClearSense(); + recvbytes = cmdseq[4]; return SCSI::XferPhase::DataOut; } @@ -415,14 +467,37 @@ class SCSICmdModeSense6 : public SCSICmd virtual SCSI::XferPhase Command(std::vector& cmdseq) { uint8 pagecode = (cmdseq[2] & 0x3f); - // 未実装 + + // センスデータをクリア + parent->ClearSense(); + + // 未実装 (XXX センスデータは適当) parent->putlog(1, "ModeSense(6) PageCode $%02x 未実装", pagecode); + parent->sensekey = SCSI::SenseKey::IllegalRequest; + parent->senseasc = SCSI::ASC::InvalidFieldInCDB; + status_byte = SCSI::StatusByte::CheckCondition; + return SCSI::XferPhase::Status; } +}; - virtual void BeginStatus() { - buf.clear(); - buf.push_back((uint8)SCSI::StatusByte::CheckCondition); +// +// SCSI コマンド 0x1b StartStopUnit (ダイレクトアクセスデバイス) +// +class SCSICmdStartStopUnit : public SCSICmd +{ + using inherited = SCSICmd; + public: + SCSICmdStartStopUnit(SCSITarget *p) : inherited(p) { } + virtual ~SCSICmdStartStopUnit() { } + + // スピンアップ/ダウンやメディアのロード/アンロードを指示する。 + // ので今の所エミュレーション上は特にすることはない。 + // XXX それっぽい時間待つくらいはしてもいいかも知れない + + virtual SCSI::XferPhase Command(std::vector& cmdseq) { + parent->putlog(1, "StartStopUnit"); + return inherited::Command(cmdseq); } }; @@ -439,6 +514,9 @@ class SCSICmdReadCapacity : public SCSIC virtual SCSI::XferPhase Command(std::vector& cmdseq) { SCSIHD *hdparent = dynamic_cast(parent); + // センスデータをクリア + parent->ClearSense(); + // PMI=1 は未サポート if ((cmdseq[8] & 0x01) != 0) { parent->putmsg("SCSI ReadCapacity PMI=1 not supported"); @@ -478,6 +556,9 @@ class SCSICmdRead10 : public SCSICmd uint32 lba; uint32 blks; + // センスデータをクリア + parent->ClearSense(); + if ((cmdseq[1] & 0x01)) { parent->putmsg("SCSI Read(10) RelAdr not supported"); } @@ -488,13 +569,21 @@ class SCSICmdRead10 : public SCSICmd lba |= cmdseq[5]; blks = cmdseq[7] << 8; blks |= cmdseq[8]; + parent->putlog(1, "READ10 LBA=0x%x blks=0x%x", lba, blks); + + if (((uint64)lba + (uint64)blks) * parent->GetBlocksize() > + hdparent->GetSize()) + { + parent->sensekey = SCSI::SenseKey::IllegalRequest; + parent->senseasc = SCSI::ASC::InvalidFieldInCDB; + status_byte = SCSI::StatusByte::CheckCondition; + return SCSI::XferPhase::Status; + } - // XXX 範囲チェック uint8 *start = hdparent->mem + lba * parent->GetBlocksize(); uint8 *end = start + blks * parent->GetBlocksize(); buf.assign(start, end); - parent->putlog(1, "READ10 LBA=0x%x blks=0x%x", lba, blks); return SCSI::XferPhase::DataIn; } }; @@ -509,11 +598,12 @@ class SCSICmdNotSupportedCommand : publi SCSICmdNotSupportedCommand(SCSITarget *p) : inherited(p) { } virtual ~SCSICmdNotSupportedCommand() { } - // Command は何もせずステータスに移るだけなので基底のやつでいい - - virtual void BeginStatus() { - buf.clear(); - buf.push_back((uint8)SCSI::StatusByte::CheckCondition); + virtual SCSI::XferPhase Command(std::vector& cmdseq) { + // XXX センスキーよく分からんけどとりあえず + parent->sensekey = SCSI::SenseKey::IllegalRequest; + parent->senseasc = SCSI::ASC::InvalidCommandOperationCode; + status_byte = SCSI::StatusByte::CheckCondition; + return SCSI::XferPhase::Status; } }; @@ -539,6 +629,14 @@ SCSITarget::~SCSITarget() { } +// バスリセットされた (SCSIBus から呼ばれる) +void +SCSITarget::BusResetCB() +{ + // センスキーをクリア + ClearSense(); +} + // セレクションで選択された (SCSIBus から呼ばれる) void SCSITarget::SelectionCB() @@ -813,7 +911,7 @@ SCSIHD::SCSIHD(SCSIHostDevice *h, int id : inherited(h, id, inquiry_data) { devname = "SCSIHD"; - devtype = SCSI::HD; + devtype = SCSI::DevType::HD; } // デストラクタ @@ -872,6 +970,10 @@ SCSIHD::DispatchCmd() cmd.reset(new SCSICmdWrite6(this)); break; + case SCSI::Command::StartStopUnit: // 0x1b + cmd.reset(new SCSICmdStartStopUnit(this)); + break; + case SCSI::Command::ReadCapacity: // 0x25 cmd.reset(new SCSICmdReadCapacity(this)); break;