--- nono/vm/scsidev.cpp 2026/04/29 17:04:39 1.1.1.4 +++ nono/vm/scsidev.cpp 2026/04/29 17:04:47 1.1.1.7 @@ -32,6 +32,8 @@ SCSIDevice::~SCSIDevice() // コンストラクタ SCSIHostDevice::SCSIHostDevice() { + devtype = SCSI::DevType::Initiator; + // バスデバイスを作成する。 // ただし自身がバスに参加するのは継承側で行う // (例えば SPC なら BDID 書き込みで ID が決まった後、など)。 @@ -63,6 +65,14 @@ SCSIHostDevice::Create() // 空ならデバイスなし if (image.length() == 0) { + // 未接続部分のパラメータは減らしておくか。 + // --show-config するとさすがに無駄に多くて邪魔なので。 + const std::string wpkey = string_format("%s-id%d-writeprotect", + GetConfigName(), id); + const std::string stkey = string_format("%s-id%d-seektime", + GetConfigName(), id); + gConfig->Delete(wpkey); + gConfig->Delete(stkey); continue; } @@ -96,7 +106,7 @@ SCSIHostDevice::Init() std::string typestr; if (target[id]) { switch (target[id]->GetDevType()) { - case SCSI::DevType::None: + default: break; case SCSI::DevType::HD: typestr = "HD"; @@ -142,6 +152,12 @@ SCSIHostDevice::BusFreeCB(int id) SetData(0); } +#if defined(__clang__) +// デストラクタがインラインで書けないのは面倒なのでとりあえず +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + // // SCSI コマンドの共通部分 // @@ -163,6 +179,12 @@ class SCSICmd std::vector buf; uint32 recvbytes; + // このコマンドの動作時間 [nsec] + // シークタイムとかを簡易的に再現するため、次フェーズ遷移時の最初の + // REQ アサートを引き延ばすために使う。 + // 主にコマンドフェーズで使うことになると思う。 + uint64 optime {}; + // 以下いずれも戻り値は遷移先のフェーズ。 // コマンドフェーズを処理する。 @@ -223,7 +245,7 @@ class SCSICmdTestUnitReady : public SCSI using inherited = SCSICmd; public: SCSICmdTestUnitReady(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdTestUnitReady() { } + virtual ~SCSICmdTestUnitReady() override { } }; // @@ -234,7 +256,7 @@ class SCSICmdRezeroUnit : public SCSICmd using inherited = SCSICmd; public: SCSICmdRezeroUnit(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdRezeroUnit() { } + virtual ~SCSICmdRezeroUnit() override { } // とりあえず黙って成功するだけでいいか }; @@ -247,15 +269,15 @@ class SCSICmdRequestSense : public SCSIC using inherited = SCSICmd; public: SCSICmdRequestSense(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdRequestSense() { } + virtual ~SCSICmdRequestSense() override { } - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { // 応答のセンスデータは SCSI-2 形式(最小18バイト) を実装。 // 今の所、センスキー、ASC、ASCQ だけで返せる内容にだけ対応している。 // これ以外のフィールドを返す必要が出来たらまた考えること。 buf.clear(); - buf.resize(17); + buf.resize(18); buf[0] = 0xf0; // Valid、エラーコード 0x70 buf[1] = 0; // セグメント番号(?) buf[2] = parent->sensekey; // センスキー (上位4ビットは今の所 0) @@ -294,9 +316,9 @@ class SCSICmdRead6 : public SCSICmd using inherited = SCSICmd; public: SCSICmdRead6(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdRead6() { } + virtual ~SCSICmdRead6() override { } - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { SCSIHD *hdparent = dynamic_cast(parent); uint32 lba; uint32 blks; @@ -327,6 +349,9 @@ class SCSICmdRead6 : public SCSICmd uint8 *end = start + blks * parent->GetBlocksize(); buf.assign(start, end); + // 平均シークタイム + optime = hdparent->seektime; + return SCSI::XferPhase::DataIn; } }; @@ -341,9 +366,9 @@ class SCSICmdWrite6 : public SCSICmd SCSICmdWrite6(SCSITarget *p) : inherited(p) { hdparent = dynamic_cast(parent); } - virtual ~SCSICmdWrite6() { } + virtual ~SCSICmdWrite6() override { } - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { // センスデータをクリア parent->ClearSense(); @@ -365,11 +390,14 @@ class SCSICmdWrite6 : public SCSICmd return SCSI::XferPhase::Status; } + // 平均シークタイム + optime = hdparent->seektime; + recvbytes = blks * parent->GetBlocksize(); return SCSI::XferPhase::DataOut; } - virtual SCSI::XferPhase DoneDataOut() { + SCSI::XferPhase DoneDataOut() override { parent->putlog(1, "WRITE6 LBA=0x%x blks=0x%x", lba, blks); if (hdparent->writeprotect == false) { memcpy(hdparent->mem + (lba * parent->GetBlocksize()), @@ -392,9 +420,9 @@ class SCSICmdInquiry : public SCSICmd using inherited = SCSICmd; public: SCSICmdInquiry(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdInquiry() { } + virtual ~SCSICmdInquiry() override { } - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { // センスデータをクリア parent->ClearSense(); @@ -418,9 +446,9 @@ class SCSICmdModeSelect6 : public SCSICm using inherited = SCSICmd; public: SCSICmdModeSelect6(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdModeSelect6() { } + virtual ~SCSICmdModeSelect6() override { } - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { // センスデータをクリア parent->ClearSense(); @@ -428,7 +456,7 @@ class SCSICmdModeSelect6 : public SCSICm return SCSI::XferPhase::DataOut; } - virtual SCSI::XferPhase DoneDataOut() { + SCSI::XferPhase DoneDataOut() override { // Mode Select(6) のデータは以下の構造で並んでいる。 // ヘッダ // ブロックディスクリプタ#1 @@ -463,9 +491,9 @@ class SCSICmdModeSense6 : public SCSICmd using inherited = SCSICmd; public: SCSICmdModeSense6(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdModeSense6() { } + virtual ~SCSICmdModeSense6() override { } - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { uint8 pagecode = (cmdseq[2] & 0x3f); // センスデータをクリア @@ -489,13 +517,13 @@ class SCSICmdStartStopUnit : public SCSI using inherited = SCSICmd; public: SCSICmdStartStopUnit(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdStartStopUnit() { } + virtual ~SCSICmdStartStopUnit() override { } // スピンアップ/ダウンやメディアのロード/アンロードを指示する。 // ので今の所エミュレーション上は特にすることはない。 // XXX それっぽい時間待つくらいはしてもいいかも知れない - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { parent->putlog(1, "StartStopUnit"); return inherited::Command(cmdseq); } @@ -509,9 +537,9 @@ class SCSICmdReadCapacity : public SCSIC using inherited = SCSICmd; public: SCSICmdReadCapacity(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdReadCapacity() { } + virtual ~SCSICmdReadCapacity() override { } - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { SCSIHD *hdparent = dynamic_cast(parent); // センスデータをクリア @@ -549,9 +577,9 @@ class SCSICmdRead10 : public SCSICmd using inherited = SCSICmd; public: SCSICmdRead10(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdRead10() { } + virtual ~SCSICmdRead10() override { } - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { SCSIHD *hdparent = dynamic_cast(parent); uint32 lba; uint32 blks; @@ -584,11 +612,68 @@ class SCSICmdRead10 : public SCSICmd uint8 *end = start + blks * parent->GetBlocksize(); buf.assign(start, end); + // 平均シークタイム + optime = hdparent->seektime; + return SCSI::XferPhase::DataIn; } }; // +// SCSI コマンド 0x2a Write(10) (ダイレクトアクセスデバイス) +// +class SCSICmdWrite10 : public SCSICmd +{ + using inherited = SCSICmd; + public: + SCSICmdWrite10(SCSITarget *p) : inherited(p) { + hdparent = dynamic_cast(parent); + } + virtual ~SCSICmdWrite10() override { } + + SCSI::XferPhase Command(std::vector& cmdseq) override { + // センスデータをクリア + parent->ClearSense(); + + lba = cmdseq[2] << 24; + lba |= cmdseq[3] << 16; + lba |= cmdseq[4] << 8; + lba |= cmdseq[5]; + blks = cmdseq[7] << 8; + blks |= cmdseq[8]; + + 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; + } + + // 平均シークタイム + optime = hdparent->seektime; + + recvbytes = blks * parent->GetBlocksize(); + return SCSI::XferPhase::DataOut; + } + + SCSI::XferPhase DoneDataOut() override { + parent->putlog(1, "WRITE10 LBA=0x%x blks=0x%x", lba, blks); + if (hdparent->writeprotect == false) { + memcpy(hdparent->mem + (lba * parent->GetBlocksize()), + buf.data(), buf.size()); + } + return SCSI::XferPhase::Status; + } + + private: + SCSIHD *hdparent; + uint32 lba; + uint32 blks; +}; + +// // SCSI コマンド サポートしていないコマンド // class SCSICmdNotSupportedCommand : public SCSICmd @@ -596,9 +681,9 @@ class SCSICmdNotSupportedCommand : publi using inherited = SCSICmd; public: SCSICmdNotSupportedCommand(SCSITarget *p) : inherited(p) { } - virtual ~SCSICmdNotSupportedCommand() { } + virtual ~SCSICmdNotSupportedCommand() override { } - virtual SCSI::XferPhase Command(std::vector& cmdseq) { + SCSI::XferPhase Command(std::vector& cmdseq) override { // XXX センスキーよく分からんけどとりあえず parent->sensekey = SCSI::SenseKey::IllegalRequest; parent->senseasc = SCSI::ASC::InvalidCommandOperationCode; @@ -607,6 +692,9 @@ class SCSICmdNotSupportedCommand : publi } }; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // // SCSI ターゲットデバイスの共通部分 @@ -633,6 +721,22 @@ SCSITarget::~SCSITarget() void SCSITarget::BusResetCB() { + // リセットされたので持ってる信号を全部解放。 + // 一応自分が変えていい信号線だけを落とす。 + switch (GetPhase()) { + case SCSI::Phase::BusFree: + break; + case SCSI::Phase::Arbitration: + case SCSI::Phase::Selection: + case SCSI::Phase::Reselection: + NegateSEL(); + NegateBSY(); + break; + case SCSI::Phase::Transfer: + NegateBSY(); + break; + } + // センスキーをクリア ClearSense(); } @@ -797,6 +901,12 @@ SCSITarget::TransferAckCB() } putlog(2, "%s done", SCSI::GetXferPhaseName(xfer)); + // 簡略化のため、フェーズ完了時だけ REQ を引き伸ばすことができる + if (cmd->optime != 0) { + bus->SetOneshotReqWait(cmd->optime); + cmd->optime = 0; + } + if (next != xfer) { // End ならバスフリーにして終了。 if (next == SCSI::XferPhase::End) { @@ -929,6 +1039,7 @@ SCSIHD::Init() host->GetConfigName(), myid); const std::string imgkey = keybody + "image"; const std::string wpkey = keybody + "writeprotect"; + const std::string stkey = keybody + "seektime"; const ConfigItem& itemimg = gConfig->Find(imgkey); const std::string& image = itemimg.AsString(); @@ -954,6 +1065,10 @@ SCSIHD::Init() putmsg(0, "%s: write protected", myname.c_str()); } + // 設定は [msec]、変数は [nsec]。 + seektime = gConfig->Find(stkey).AsInt(); + seektime *= 1_msec; + return true; } @@ -982,6 +1097,10 @@ SCSIHD::DispatchCmd() cmd.reset(new SCSICmdRead10(this)); break; + case SCSI::Command::Write10: // 0x2a + cmd.reset(new SCSICmdWrite10(this)); + break; + default: // 見付からなければ基本クラス側に任せる inherited::DispatchCmd();