|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: // SCSI デバイス共通部分
8:
9: #include "scsidev.h"
1.1.1.2 root 10: #include "config.h"
11: #include "mainapp.h"
1.1 root 12:
13: //
14: // SCSI デバイス
15: //
16:
17: // コンストラクタ
18: SCSIDevice::SCSIDevice()
19: {
20: }
21:
22: // デストラクタ
23: SCSIDevice::~SCSIDevice()
24: {
25: }
26:
27: //
28: // SCSI ホストデバイス
29: //
30:
31: // コンストラクタ
32: SCSIHostDevice::SCSIHostDevice()
33: {
1.1.1.5 root 34: devtype = SCSI::DevType::Initiator;
35:
1.1 root 36: // バスデバイスを作成する。
37: // ただし自身がバスに参加するのは継承側で行う
38: // (例えば SPC なら BDID 書き込みで ID が決まった後、など)。
39: scsibus.reset(new SCSIBus());
40: }
41:
42: // デストラクタ
43: SCSIHostDevice::~SCSIHostDevice()
44: {
45: }
46:
1.1.1.2 root 47: // 設定ファイルに使われるこのホストデバイスを示す接頭語を設定する
48: // (継承クラス側のコンストラクタで config_name を設定後に呼ぶこと)
49: void
50: SCSIHostDevice::SetConfigName(const char *name_)
51: {
52: config_name = name_;
53: }
54:
1.1 root 55: bool
56: SCSIHostDevice::Create()
57: {
58: // 設定を読み込む。
59: for (int id = 0; id < 8; id++) {
1.1.1.2 root 60: const std::string key = string_format("%s-id%d-image",
1.1 root 61: GetConfigName(), id);
1.1.1.3 root 62: const ConfigItem& item = gConfig->Find(key);
1.1.1.2 root 63: const std::string& image = item.AsString();
64:
65: // 空ならデバイスなし
66: if (image.length() == 0) {
1.1.1.5 root 67: // 未接続部分のパラメータは減らしておくか。
68: // --show-config するとさすがに無駄に多くて邪魔なので。
69: const std::string wpkey = string_format("%s-id%d-writeprotect",
70: GetConfigName(), id);
71: const std::string stkey = string_format("%s-id%d-seektime",
72: GetConfigName(), id);
73: gConfig->Delete(wpkey);
74: gConfig->Delete(stkey);
1.1.1.2 root 75: continue;
76: }
77:
78: // ',' より前がデバイス種別 (ここではパスは不要)
79: auto pos = image.find(',');
80: if (pos == std::string::npos) {
81: item.Err("no device type specified");
82: return false;
83: }
84: std::string type = string_tolower(image.substr(0, pos));
85:
86: if (type == "hd") {
1.1 root 87: target[id].reset(new SCSIHD(this, id));
88: scsibus->Attach(target[id].get(), id);
1.1.1.2 root 89: } else {
90: item.Err("invaild device type '%s'", type.c_str());
1.1 root 91: }
92: }
93:
94: return true;
95: }
96:
97: bool
98: SCSIHostDevice::Init()
99: {
100: // 初期化ではないけどログレベルがセットできたここでデバッグ表示。
101: // -L オプションの処理は Create() の後なので。
102:
103: if (loglevel >= 1) {
104: for (int id = 0; id < 8; id++) {
105: std::string typestr;
106: if (target[id]) {
107: switch (target[id]->GetDevType()) {
1.1.1.5 root 108: default:
1.1 root 109: break;
1.1.1.4 root 110: case SCSI::DevType::HD:
1.1 root 111: typestr = "HD";
112: break;
113: }
114: }
115: if (!typestr.empty()) {
1.1.1.8 ! root 116: putmsgn("%s target #%d %s", GetConfigName(), id,
1.1 root 117: typestr.c_str());
118: }
119: }
120: }
121:
122: return true;
123: }
124:
125: // バスフリーコールバック (SCSIBus から呼ばれる)
126: void
127: SCSIHostDevice::BusFreeCB(int id)
128: {
129: // 実際にはバスフリーを検出したデバイスは各自自身が送出している信号を
130: // 下げるのだが、色々面倒なので、ここではホストのバスフリーコールバックが
131: // 一括して全部の線を下げる。
132: // ただしさすがにログがうるさいので、上がってる線だけ下げる。
133:
134: uint32 data;
135: while ((data = GetBSY()) != 0) {
136: bus->NegateBSY(DecodeID(data));
137: }
138: while ((data = GetSEL()) != 0) {
139: bus->NegateSEL(DecodeID(data));
140: }
141: if (GetREQ()) {
142: NegateREQ();
143: }
144: if (GetACK()) {
145: NegateACK();
146: }
147: if (GetATN()) {
148: NegateATN();
149: }
150: SetXfer(0);
151: SetData(0);
152: }
153:
1.1.1.6 root 154: #if defined(__clang__)
155: // デストラクタがインラインで書けないのは面倒なのでとりあえず
156: #pragma clang diagnostic push
157: #pragma clang diagnostic ignored "-Wweak-vtables"
158: #endif
159:
1.1 root 160: //
161: // SCSI コマンドの共通部分
162: //
163: class SCSICmd
164: {
165: public:
166: SCSICmd(SCSITarget *p) {
167: parent = p;
168: }
169: virtual ~SCSICmd() { }
170:
171: // 転送バッファと転送バイト数。
172: // IN (ターゲットが送出側)なら、各フェーズ開始までに送出バイト数分の
173: // バッファを buf に用意すること。recvbytes は操作不要。
174: //
175: // OUT(ターゲットが受信側)なら、各フェーズ開始前までに受信バイト数を
176: // recvbytes にセットすること。buf は操作不要。
177: // なお parent は OUT(受信)フェーズ中にこの recvbytes を変更しない。
178: std::vector<uint8> buf;
179: uint32 recvbytes;
180:
1.1.1.5 root 181: // このコマンドの動作時間 [nsec]
182: // シークタイムとかを簡易的に再現するため、次フェーズ遷移時の最初の
183: // REQ アサートを引き延ばすために使う。
184: // 主にコマンドフェーズで使うことになると思う。
185: uint64 optime {};
186:
1.1 root 187: // 以下いずれも戻り値は遷移先のフェーズ。
188:
189: // コマンドフェーズを処理する。
1.1.1.4 root 190: // コマンドが確定したところで呼ばれる。戻り値に遷移先フェーズを返すこと。
191: // また、コマンド開始時に各自で ClearSense() を呼ぶこと。センスというか
192: // CA 状態は次のコマンド実行でクリアされるが (ネクサスはとりあえず無視)、
193: // RequestSense の場合は現在保持しているセンス情報をホストに返すので、
194: // つまり RequestSense 以外のコマンドが全員各自でクリアする必要がある。
1.1 root 195: virtual SCSI::XferPhase Command(std::vector<uint8>& cmdseq) {
1.1.1.4 root 196: parent->ClearSense();
1.1 root 197: return SCSI::XferPhase::Status;
198: }
199:
200: // データインフェーズを完了する。
201: virtual SCSI::XferPhase DoneDataIn() { return SCSI::XferPhase::Status; }
202:
203: // データアウトフェーズを完了する。
204: virtual SCSI::XferPhase DoneDataOut() { return SCSI::XferPhase::Status; }
205:
206: // ステータスフェーズを開始する。
1.1.1.4 root 207: void BeginStatus() {
1.1 root 208: buf.clear();
1.1.1.4 root 209: buf.push_back(status_byte);
1.1 root 210: }
211:
212: // ステータスフェーズを完了する。
213: virtual SCSI::XferPhase DoneStatus() { return SCSI::XferPhase::MsgIn; }
214:
215: // メッセージインフェーズを開始する。
216: // デフォルトでは Command Complete を返す実装を用意しておく。
217: virtual void BeginMsgIn() {
218: buf.clear();
219: buf.push_back((uint8)SCSI::MsgByte::CommandComplete);
220: }
221:
222: // メッセージインフェーズを完了する。
223: virtual SCSI::XferPhase DoneMsgIn() { return SCSI::XferPhase::End; }
224:
225: // メッセージアウトフェーズを開始する。
226: virtual void BeginMsgOut() { }
227:
228: // メッセージアウトフェーズを完了する。
229: virtual SCSI::XferPhase DoneMsgOut() { return SCSI::XferPhase::End; }
230:
231: protected:
232: // このコマンドを実行しているターゲットデバイス。
233: SCSITarget *parent;
1.1.1.4 root 234:
235: // ステータスバイト (デフォルトは 0 = Good)
236: uint8 status_byte {};
1.1 root 237: };
238:
239: //
240: // SCSI コマンド 0x00: TestUnitReady (共通)
241: //
242: class SCSICmdTestUnitReady : public SCSICmd
243: {
1.1.1.3 root 244: using inherited = SCSICmd;
1.1 root 245: public:
246: SCSICmdTestUnitReady(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 247: virtual ~SCSICmdTestUnitReady() override { }
1.1 root 248: };
249:
250: //
1.1.1.2 root 251: // SCSI コマンド 0x01: RezeroUnit (共通)
252: //
253: class SCSICmdRezeroUnit : public SCSICmd
254: {
1.1.1.3 root 255: using inherited = SCSICmd;
1.1.1.2 root 256: public:
257: SCSICmdRezeroUnit(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 258: virtual ~SCSICmdRezeroUnit() override { }
1.1.1.2 root 259:
260: // とりあえず黙って成功するだけでいいか
261: };
262:
263: //
1.1 root 264: // SCSI コマンド 0x03: RequestSense (共通)
265: //
266: class SCSICmdRequestSense : public SCSICmd
267: {
1.1.1.3 root 268: using inherited = SCSICmd;
1.1 root 269: public:
270: SCSICmdRequestSense(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 271: virtual ~SCSICmdRequestSense() override { }
1.1 root 272:
1.1.1.6 root 273: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.4 root 274: // 応答のセンスデータは SCSI-2 形式(最小18バイト) を実装。
275: // 今の所、センスキー、ASC、ASCQ だけで返せる内容にだけ対応している。
276: // これ以外のフィールドを返す必要が出来たらまた考えること。
277:
1.1 root 278: buf.clear();
1.1.1.6 root 279: buf.resize(18);
1.1.1.4 root 280: buf[0] = 0xf0; // Valid、エラーコード 0x70
281: buf[1] = 0; // セグメント番号(?)
282: buf[2] = parent->sensekey; // センスキー (上位4ビットは今の所 0)
283: buf[3] = 0; // Information
284: buf[4] = 0; // Information
285: buf[5] = 0; // Information
286: buf[6] = 0; // Information
287: buf[7] = 0; // 追加センスデータ長 0
288: buf[8] = 0; // Command specific Information
289: buf[9] = 0; // Command specific Information
290: buf[10] = 0; // Command specific Information
291: buf[11] = 0; // Command specific Information
292: buf[12] = parent->senseasc >> 8;
293: buf[13] = parent->senseasc & 0xff;
294: buf[14] = 0; // FRU
295: buf[15] = 0; // センスキー固有情報
296: buf[16] = 0; // センスキー固有情報
297: buf[17] = 0; // センスキー固有情報
298: // 追加センスデータなし
1.1 root 299:
300: // それからバッファを指定の長さに変更
301: buf.resize((uint)cmdseq[4]);
302:
1.1.1.4 root 303: // 転送した後にセンスデータをクリア
304: parent->ClearSense();
305:
1.1 root 306: return SCSI::XferPhase::DataIn;
307: }
308: };
309:
310: //
311: // SCSI コマンド 0x08: Read(6) (共通)
312: //
313: class SCSICmdRead6 : public SCSICmd
314: {
1.1.1.3 root 315: using inherited = SCSICmd;
1.1 root 316: public:
317: SCSICmdRead6(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 318: virtual ~SCSICmdRead6() override { }
1.1 root 319:
1.1.1.6 root 320: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1 root 321: SCSIHD *hdparent = dynamic_cast<SCSIHD*>(parent);
322: uint32 lba;
323: uint32 blks;
324:
1.1.1.4 root 325: // センスデータをクリア
326: parent->ClearSense();
327:
1.1 root 328: lba = (cmdseq[1] & 0x1f) << 16;
329: lba |= cmdseq[2] << 8;
330: lba |= cmdseq[3];
331: blks = cmdseq[4];
332: // blks == 0 は 256 ブロック転送を意味する。
333: if (blks == 0) {
334: blks = 256;
335: }
1.1.1.8 ! root 336: parent->putlogf(1, lstr("READ6 LBA=0x%x blks=0x%x", lba, blks));
1.1.1.4 root 337:
338: if (((uint64)lba + (uint64)blks) * parent->GetBlocksize() >
339: hdparent->GetSize())
340: {
341: parent->sensekey = SCSI::SenseKey::IllegalRequest;
342: parent->senseasc = SCSI::ASC::InvalidFieldInCDB;
343: status_byte = SCSI::StatusByte::CheckCondition;
344: return SCSI::XferPhase::Status;
345: }
346:
1.1.1.2 root 347: uint8 *start = hdparent->mem + lba * parent->GetBlocksize();
348: uint8 *end = start + blks * parent->GetBlocksize();
1.1 root 349: buf.assign(start, end);
350:
1.1.1.5 root 351: // 平均シークタイム
352: optime = hdparent->seektime;
353:
1.1 root 354: return SCSI::XferPhase::DataIn;
355: }
356: };
357:
358: //
359: // SCSI コマンド 0x0a: Write(6) (共通)
360: //
361: class SCSICmdWrite6 : public SCSICmd
362: {
1.1.1.3 root 363: using inherited = SCSICmd;
1.1 root 364: public:
365: SCSICmdWrite6(SCSITarget *p) : inherited(p) {
366: hdparent = dynamic_cast<SCSIHD*>(parent);
367: }
1.1.1.6 root 368: virtual ~SCSICmdWrite6() override { }
1.1 root 369:
1.1.1.6 root 370: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.4 root 371: // センスデータをクリア
372: parent->ClearSense();
373:
1.1 root 374: lba = (cmdseq[1] & 0x1f) << 16;
375: lba |= cmdseq[2] << 8;
376: lba |= cmdseq[3];
377: blks = cmdseq[4];
378: // blks == 0 は 256 ブロック転送を意味する。
379: if (blks == 0) {
380: blks = 256;
381: }
382:
1.1.1.4 root 383: if (((uint64)lba + (uint64)blks) * parent->GetBlocksize() >
384: hdparent->GetSize())
385: {
386: parent->sensekey = SCSI::SenseKey::IllegalRequest;
387: parent->senseasc = SCSI::ASC::InvalidFieldInCDB;
388: status_byte = SCSI::StatusByte::CheckCondition;
389: return SCSI::XferPhase::Status;
390: }
391:
1.1.1.5 root 392: // 平均シークタイム
393: optime = hdparent->seektime;
394:
1.1.1.4 root 395: recvbytes = blks * parent->GetBlocksize();
1.1 root 396: return SCSI::XferPhase::DataOut;
397: }
398:
1.1.1.6 root 399: SCSI::XferPhase DoneDataOut() override {
1.1.1.8 ! root 400: parent->putlogf(1, lstr("WRITE6 LBA=0x%x blks=0x%x", lba, blks));
1.1 root 401: if (hdparent->writeprotect == false) {
1.1.1.2 root 402: memcpy(hdparent->mem + (lba * parent->GetBlocksize()),
1.1 root 403: buf.data(), buf.size());
404: }
405: return SCSI::XferPhase::Status;
406: }
407:
408: private:
409: SCSIHD *hdparent;
410: uint32 lba;
411: uint32 blks;
412: };
413:
414: //
415: // SCSI コマンド 0x12: Inquiry (共通)
416: //
417: class SCSICmdInquiry : public SCSICmd
418: {
1.1.1.3 root 419: using inherited = SCSICmd;
1.1 root 420: public:
421: SCSICmdInquiry(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 422: virtual ~SCSICmdInquiry() override { }
1.1 root 423:
1.1.1.6 root 424: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.4 root 425: // センスデータをクリア
426: parent->ClearSense();
427:
1.1 root 428: // 応答データはターゲットデバイスが持っている
429: buf = parent->inquiry_data;
430:
431: // 本来の応答データ長か reqlen の短いほうまで。
432: uint reqlen = (uint)cmdseq[4];
433: if (reqlen < buf.size()) {
434: buf.resize(reqlen);
435: }
436: return SCSI::XferPhase::DataIn;
437: }
438: };
439:
440: //
441: // SCSI コマンド 0x15 ModeSelect(6) (共通)
442: //
443: class SCSICmdModeSelect6 : public SCSICmd
444: {
1.1.1.3 root 445: using inherited = SCSICmd;
1.1 root 446: public:
447: SCSICmdModeSelect6(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 448: virtual ~SCSICmdModeSelect6() override { }
1.1 root 449:
1.1.1.6 root 450: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.4 root 451: // センスデータをクリア
452: parent->ClearSense();
453:
1.1 root 454: recvbytes = cmdseq[4];
455: return SCSI::XferPhase::DataOut;
456: }
457:
1.1.1.6 root 458: SCSI::XferPhase DoneDataOut() override {
1.1 root 459: // Mode Select(6) のデータは以下の構造で並んでいる。
460: // ヘッダ
461: // ブロックディスクリプタ#1
462: // :
463: // ブロックディスクリプタ#n
464: // パラメータページ #1
465: // :
466: // パラメータページ #m
467:
468: const SCSI::ModeParamHdr *hdr = (const SCSI::ModeParamHdr *)buf.data();
469:
470: if (hdr->block_desc_len > 0) {
471: const SCSI::BlockDesc *desc =
472: (const SCSI::BlockDesc *)(buf.data() + sizeof(*hdr));
473: uint blocksize;
474: blocksize = (desc->block_size[0]) << 16;
475: blocksize |= (desc->block_size[1]) << 8;
476: blocksize |= (desc->block_size[2]);
477:
478: // ブロック長を書き戻す
1.1.1.2 root 479: parent->SetBlocksize(blocksize);
1.1 root 480: }
481: return SCSI::XferPhase::Status;
482: }
483: };
484:
485: //
1.1.1.2 root 486: // SCSI コマンド 0x1a ModeSense(6) (共通)
487: //
488: class SCSICmdModeSense6 : public SCSICmd
489: {
1.1.1.3 root 490: using inherited = SCSICmd;
1.1.1.2 root 491: public:
492: SCSICmdModeSense6(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 493: virtual ~SCSICmdModeSense6() override { }
1.1.1.2 root 494:
1.1.1.6 root 495: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.2 root 496: uint8 pagecode = (cmdseq[2] & 0x3f);
1.1.1.4 root 497:
498: // センスデータをクリア
499: parent->ClearSense();
500:
501: // 未実装 (XXX センスデータは適当)
1.1.1.8 ! root 502: parent->putlogf(1, lstr("ModeSense(6) PageCode $%02x 未実装",
! 503: pagecode));
1.1.1.4 root 504: parent->sensekey = SCSI::SenseKey::IllegalRequest;
505: parent->senseasc = SCSI::ASC::InvalidFieldInCDB;
506: status_byte = SCSI::StatusByte::CheckCondition;
507:
1.1.1.2 root 508: return SCSI::XferPhase::Status;
509: }
1.1.1.4 root 510: };
1.1.1.2 root 511:
1.1.1.4 root 512: //
513: // SCSI コマンド 0x1b StartStopUnit (ダイレクトアクセスデバイス)
514: //
515: class SCSICmdStartStopUnit : public SCSICmd
516: {
517: using inherited = SCSICmd;
518: public:
519: SCSICmdStartStopUnit(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 520: virtual ~SCSICmdStartStopUnit() override { }
1.1.1.4 root 521:
522: // スピンアップ/ダウンやメディアのロード/アンロードを指示する。
523: // ので今の所エミュレーション上は特にすることはない。
524: // XXX それっぽい時間待つくらいはしてもいいかも知れない
525:
1.1.1.6 root 526: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.8 ! root 527: parent->putlogf(1, lstr("StartStopUnit"));
1.1.1.4 root 528: return inherited::Command(cmdseq);
1.1.1.2 root 529: }
530: };
531:
532: //
1.1 root 533: // SCSI コマンド 0x25 ReadCapacity (ダイレクトアクセスデバイス)
534: //
535: class SCSICmdReadCapacity : public SCSICmd
536: {
1.1.1.3 root 537: using inherited = SCSICmd;
1.1 root 538: public:
539: SCSICmdReadCapacity(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 540: virtual ~SCSICmdReadCapacity() override { }
1.1 root 541:
1.1.1.6 root 542: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1 root 543: SCSIHD *hdparent = dynamic_cast<SCSIHD*>(parent);
544:
1.1.1.4 root 545: // センスデータをクリア
546: parent->ClearSense();
547:
1.1 root 548: // PMI=1 は未サポート
549: if ((cmdseq[8] & 0x01) != 0) {
1.1.1.8 ! root 550: parent->putlogf(0, lstr("SCSI ReadCapacity PMI=1 not supported"));
1.1 root 551: }
552:
553: // 応答データは
554: // +00.L 最終論理ブロックアドレス
555: // +04.L ブロック長
1.1.1.2 root 556: uint32 blksize = parent->GetBlocksize();
1.1.1.3 root 557: uint32 lastblk = (hdparent->GetSize() / blksize) - 1;
1.1 root 558: buf.resize(8);
559: buf[0] = lastblk >> 24;
560: buf[1] = lastblk >> 16;
561: buf[2] = lastblk >> 8;
562: buf[3] = lastblk;
563: buf[4] = blksize >> 24;
564: buf[5] = blksize >> 16;
565: buf[6] = blksize >> 8;
566: buf[7] = blksize;
567:
568: return SCSI::XferPhase::DataIn;
569: }
570: };
571:
572: //
573: // SCSI コマンド 0x28 Read(10) (ダイレクトアクセスデバイス)
574: //
575: class SCSICmdRead10 : public SCSICmd
576: {
1.1.1.3 root 577: using inherited = SCSICmd;
1.1 root 578: public:
579: SCSICmdRead10(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 580: virtual ~SCSICmdRead10() override { }
1.1 root 581:
1.1.1.6 root 582: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1 root 583: SCSIHD *hdparent = dynamic_cast<SCSIHD*>(parent);
584: uint32 lba;
585: uint32 blks;
586:
1.1.1.4 root 587: // センスデータをクリア
588: parent->ClearSense();
589:
1.1 root 590: if ((cmdseq[1] & 0x01)) {
1.1.1.8 ! root 591: parent->putlogf(0, lstr("SCSI Read(10) RelAdr not supported"));
1.1 root 592: }
593:
594: lba = cmdseq[2] << 24;
595: lba |= cmdseq[3] << 16;
596: lba |= cmdseq[4] << 8;
597: lba |= cmdseq[5];
598: blks = cmdseq[7] << 8;
599: blks |= cmdseq[8];
1.1.1.8 ! root 600: parent->putlogf(1, lstr("READ10 LBA=0x%x blks=0x%x", lba, blks));
1.1.1.4 root 601:
602: if (((uint64)lba + (uint64)blks) * parent->GetBlocksize() >
603: hdparent->GetSize())
604: {
605: parent->sensekey = SCSI::SenseKey::IllegalRequest;
606: parent->senseasc = SCSI::ASC::InvalidFieldInCDB;
607: status_byte = SCSI::StatusByte::CheckCondition;
608: return SCSI::XferPhase::Status;
609: }
1.1 root 610:
1.1.1.2 root 611: uint8 *start = hdparent->mem + lba * parent->GetBlocksize();
612: uint8 *end = start + blks * parent->GetBlocksize();
1.1 root 613: buf.assign(start, end);
614:
1.1.1.5 root 615: // 平均シークタイム
616: optime = hdparent->seektime;
617:
1.1 root 618: return SCSI::XferPhase::DataIn;
619: }
620: };
621:
622: //
1.1.1.7 root 623: // SCSI コマンド 0x2a Write(10) (ダイレクトアクセスデバイス)
624: //
625: class SCSICmdWrite10 : public SCSICmd
626: {
627: using inherited = SCSICmd;
628: public:
629: SCSICmdWrite10(SCSITarget *p) : inherited(p) {
630: hdparent = dynamic_cast<SCSIHD*>(parent);
631: }
632: virtual ~SCSICmdWrite10() override { }
633:
634: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
635: // センスデータをクリア
636: parent->ClearSense();
637:
638: lba = cmdseq[2] << 24;
639: lba |= cmdseq[3] << 16;
640: lba |= cmdseq[4] << 8;
641: lba |= cmdseq[5];
642: blks = cmdseq[7] << 8;
643: blks |= cmdseq[8];
644:
645: if (((uint64)lba + (uint64)blks) * parent->GetBlocksize() >
646: hdparent->GetSize())
647: {
648: parent->sensekey = SCSI::SenseKey::IllegalRequest;
649: parent->senseasc = SCSI::ASC::InvalidFieldInCDB;
650: status_byte = SCSI::StatusByte::CheckCondition;
651: return SCSI::XferPhase::Status;
652: }
653:
654: // 平均シークタイム
655: optime = hdparent->seektime;
656:
657: recvbytes = blks * parent->GetBlocksize();
658: return SCSI::XferPhase::DataOut;
659: }
660:
661: SCSI::XferPhase DoneDataOut() override {
1.1.1.8 ! root 662: parent->putlogf(1, lstr("WRITE10 LBA=0x%x blks=0x%x", lba, blks));
1.1.1.7 root 663: if (hdparent->writeprotect == false) {
664: memcpy(hdparent->mem + (lba * parent->GetBlocksize()),
665: buf.data(), buf.size());
666: }
667: return SCSI::XferPhase::Status;
668: }
669:
670: private:
671: SCSIHD *hdparent;
672: uint32 lba;
673: uint32 blks;
674: };
675:
676: //
1.1 root 677: // SCSI コマンド サポートしていないコマンド
678: //
679: class SCSICmdNotSupportedCommand : public SCSICmd
680: {
1.1.1.3 root 681: using inherited = SCSICmd;
1.1 root 682: public:
683: SCSICmdNotSupportedCommand(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 684: virtual ~SCSICmdNotSupportedCommand() override { }
1.1 root 685:
1.1.1.6 root 686: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.4 root 687: // XXX センスキーよく分からんけどとりあえず
688: parent->sensekey = SCSI::SenseKey::IllegalRequest;
689: parent->senseasc = SCSI::ASC::InvalidCommandOperationCode;
690: status_byte = SCSI::StatusByte::CheckCondition;
691: return SCSI::XferPhase::Status;
1.1 root 692: }
693: };
694:
1.1.1.6 root 695: #if defined(__clang__)
696: #pragma clang diagnostic pop
697: #endif
1.1 root 698:
699: //
700: // SCSI ターゲットデバイスの共通部分
701: //
702:
703: // コンストラクタ
704: SCSITarget::SCSITarget(SCSIHostDevice *h, int id, std::vector<uint8>& inq)
705: {
706: logname = "scsitarget";
707: devname = "SCSITarget";
708:
709: inquiry_data = inq;
710: // ホストへのリンク (所有関係のないポインタ)
711: host = h;
712: myid = id;
713: }
714:
715: // デストラクタ
716: SCSITarget::~SCSITarget()
717: {
718: }
719:
1.1.1.4 root 720: // バスリセットされた (SCSIBus から呼ばれる)
721: void
722: SCSITarget::BusResetCB()
723: {
1.1.1.5 root 724: // リセットされたので持ってる信号を全部解放。
725: // 一応自分が変えていい信号線だけを落とす。
726: switch (GetPhase()) {
727: case SCSI::Phase::BusFree:
728: break;
729: case SCSI::Phase::Arbitration:
730: case SCSI::Phase::Selection:
731: case SCSI::Phase::Reselection:
732: NegateSEL();
733: NegateBSY();
734: break;
735: case SCSI::Phase::Transfer:
736: NegateBSY();
737: break;
738: }
739:
1.1.1.4 root 740: // センスキーをクリア
741: ClearSense();
742: }
743:
1.1 root 744: // セレクションで選択された (SCSIBus から呼ばれる)
745: void
746: SCSITarget::SelectionCB()
747: {
748: // ターゲットが、自身が選択されていることを認識してから BSY を
749: // 立てるまでの時間はもうバス側でカウントしてあるので、ここでは
750: // ただちに BSY を上げてよい。
751: putlog(4, "Selection Callback");
752: AssertBSY();
753: }
754:
755: // 情報転送フェーズを開始する (SCSIBus から呼ばれる)
756: void
757: SCSITarget::StartTransferCB()
758: {
759: // 最初はコマンドフェーズ。(ATN はとりあえず放置)
760: putlog(4, "Start Command Phase");
761: cmdseq.clear();
762: cmdlen = 0;
763: SetXfer(SCSI::XferPhase::Command);
764: AssertREQ();
765: }
766:
767: // 情報転送フェーズ間の遷移について
768: //
769: // 情報転送フェーズは TransferCB() で1バイトずつ転送を行い、所定のバイト数
770: // の転送が完了すればフェーズを遷移する。遷移方法は入出力方向別で6通り。
771: // o IN -> IN
772: // o IN -> OUT
773: // o IN -> END
774: // o OUT -> OUT
775: // o OUT -> IN
776: // o OUT -> END
777: //
778: // IN -> IN の遷移。TransferCB() で前フェーズの最終バイト受信後、フェーズを
779: // 変更して REQ を立てるところまで。
780: //
781: // IN -> OUT の遷移。TransferCB() で前フェーズの最終バイト受信後、フェーズを
782: // 変更して (おそらく {Phase}Begin() を発行してから)、データをセットし REQ
783: // を立てる?
784: //
785: // IN -> END の遷移。TransferCB() で前フェーズの最終バイト受信後、BSY を
786: // 下げて終了。
787: //
788: // OUT -> OUT の遷移。前フェーズの最終バイトをイニシエータが受信後に
789: // TransferCB() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin()
790: // を発行してから)、データをセットし REQ を立てる?
791: //
792: // OUT -> IN の遷移。前フェーズの最終バイトをイニシエータが受信後に
793: // TransferCB() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin()
794: // を発行してから)、REQ を立てる?
795: //
796: // OUT -> END の遷移。前フェーズの最終バイトをイニシエータが受信後に
797: // TransferCB() が呼ばれるので、BSY を下げる?
798:
799: // 情報転送フェーズでイニシエータが ACK を上げた。(SCSIBus から呼ばれる)
800: // OUT (イニシエータ → ターゲット方向) なら、ターゲットが上げた REQ に応答
801: // してイニシエータがデータをデータバスに乗せてからこれをコールしてくる。
802: // IN (ターゲット → イニシエータ方向) なら、ターゲットがデータをデータバスに
803: // 乗せて REQ を上げたのをイニシエータが受信した後でこれをコールしてくる。
804: void
805: SCSITarget::TransferCB()
806: {
807: uint8 data;
808: SCSI::XferPhase xfer;
809:
810: // このフェーズの最終バイトか
811: lastbyte = false;
812:
813: // このフェーズの1バイト処理
814: xfer = GetXfer();
815: if (xfer == SCSI::XferPhase::Command) {
816: // コマンドフェーズだけいろいろ特殊なので別処理。
817: // 受信バッファが異なる、1バイト目で受信長が決まるなど。
818: data = GetData();
819: putlog(4, "TransferCB: cmdseq[%02d] = $%02x",
820: (int)cmdseq.size(), data);
821: cmdseq.push_back(data);
822: if (cmdseq.size() == 1) {
823: // 1バイト目ならこのコマンドが何バイトか調べる
824: switch (cmdseq[0] >> 5) {
825: case 0: cmdlen = 6; break;
826: case 1: cmdlen = 10; break;
827: case 2: cmdlen = 10; break;
828: case 5: cmdlen = 12; break;
829: default:
830: // 3,4 はリサーブ、6,7 はベンダー定義。XXX 来たらどうする?
831: cmdlen = 6;
832: break;
833: }
834: } else if (cmdseq.size() >= cmdlen) {
835: if (loglevel >= 1) {
836: std::string cmdbuf;
837: const char *name = SCSI::GetCommandName(cmdseq[0]);
838: cmdbuf += string_format("Command \"%s\"", name ?: "?");
839: for (const auto& v : cmdseq) {
840: cmdbuf += string_format(" $%02x", v);
841: }
1.1.1.8 ! root 842: putlogn("%s", cmdbuf.c_str());
1.1 root 843: }
844: // 最終バイトを受信したらコマンドを選択
845: DispatchCmd();
846: lastbyte = true;
847: }
848: } else if (GetIO() == false) {
849: // OUT 方向 (ターゲットが受信側)
850: // bytecount は減算方向。
851: cmd->buf.push_back(GetData());
852: bytecount--;
853: if (bytecount == 0) {
854: lastbyte = true;
855: }
856: } else {
857: // IN 方向 (ターゲットが送信側)
858: // bytecount は加算方向。
859: // データは前のループの終わり (TransferAckCB()の下のほう) で
860: // セットしているのでここでは引き取られた分をカウントする。
861: bytecount++;
862: if (bytecount >= cmd->buf.size()) {
863: lastbyte = true;
864: }
865: }
866:
867: NegateREQ();
868: }
869:
870: // 情報転送フェーズでイニシエータが ACK を下げた。(SCSIBus から呼ばれる)
871: void
872: SCSITarget::TransferAckCB()
873: {
874: SCSI::XferPhase xfer = GetXfer();
875:
876: // これが最終バイトだったら、フェーズ完了 → フェーズ切り替え。
877: if (lastbyte) {
878: SCSI::XferPhase next;
879: // フェーズ完了
880: switch (xfer) {
881: case SCSI::XferPhase::Command:
882: next = cmd->Command(cmdseq);
883: break;
884: case SCSI::XferPhase::DataOut:
885: next = cmd->DoneDataOut();
886: break;
887: case SCSI::XferPhase::DataIn:
888: next = cmd->DoneDataIn();
889: break;
890: case SCSI::XferPhase::MsgOut:
891: next = cmd->DoneMsgOut();
892: break;
893: case SCSI::XferPhase::MsgIn:
894: next = cmd->DoneMsgIn();
895: break;
896: case SCSI::XferPhase::Status:
897: next = cmd->DoneStatus();
898: break;
899: default:
900: PANIC("TransferCB unknown xfer done %d", (int)xfer);
901: }
902: putlog(2, "%s done", SCSI::GetXferPhaseName(xfer));
903:
1.1.1.5 root 904: // 簡略化のため、フェーズ完了時だけ REQ を引き伸ばすことができる
905: if (cmd->optime != 0) {
906: bus->SetOneshotReqWait(cmd->optime);
907: cmd->optime = 0;
908: }
909:
1.1 root 910: if (next != xfer) {
911: // End ならバスフリーにして終了。
912: if (next == SCSI::XferPhase::End) {
913: NegateBSY();
914: return;
915: }
916:
917: // (必要なら)フェーズを更新
918: SetXfer(next);
919: xfer = next;
920:
921: // 新しいフェーズの開始処理
922: // XXX ここで bytecount とかをリセットできればいいのだが
923: // Command フェーズで Data フェーズの準備をすることが多いので
924: // 悩ましい。
925: switch (xfer) {
926: case SCSI::XferPhase::DataOut:
927: cmd->buf.clear();
928: bytecount = cmd->recvbytes;
929: putlog(3, "DataOut begin (%d bytes)", bytecount);
930: break;
931: case SCSI::XferPhase::DataIn:
932: bytecount = 0;
933: putlog(3, "DataIn begin (%d bytes)", (int)cmd->buf.size());
934: break;
935: case SCSI::XferPhase::MsgOut:
936: cmd->BeginMsgOut();
937: cmd->buf.clear();
938: bytecount = cmd->recvbytes;
939: putlog(3, "MsgOut begin (%d bytes)", bytecount);
940: break;
941: case SCSI::XferPhase::MsgIn:
942: cmd->BeginMsgIn();
943: bytecount = 0;
944: putlog(3, "MsgIn begin (%d bytes)", (int)cmd->buf.size());
945: break;
946: case SCSI::XferPhase::Status:
947: cmd->BeginStatus();
948: bytecount = 0;
949: putlog(3, "Status begin (%d bytes)", (int)cmd->buf.size());
950: break;
951: default:
952: PANIC("TransferCB unknown xfer begin %d", (int)xfer);
953: }
954: }
955: }
956:
957: // IN 方向 (ターゲットが送信側) ならここでデータをバスにセット。
958: if (GetIO()) {
959: SetData(cmd->buf[bytecount]);
960: putlog(4, "SetData $%02x", GetData());
961: }
962:
963: // 次のバイト転送を行うために REQ を上げる。
964: AssertREQ();
965: }
966:
967: // SCSI コマンドのディスパッチャ
968: void
969: SCSITarget::DispatchCmd()
970: {
971: switch (cmdseq[0]) {
972: case SCSI::Command::TestUnitReady: // 0x00
973: cmd.reset(new SCSICmdTestUnitReady(this));
974: break;
975:
1.1.1.2 root 976: case SCSI::Command::RezeroUnit: // 0x01
977: cmd.reset(new SCSICmdRezeroUnit(this));
978: break;
979:
1.1 root 980: case SCSI::Command::RequestSense: // 0x03
981: cmd.reset(new SCSICmdRequestSense(this));
982: break;
983:
984: case SCSI::Command::Inquiry: // 0x12
985: cmd.reset(new SCSICmdInquiry(this));
986: break;
987:
988: case SCSI::Command::ModeSelect6: // 0x15
989: cmd.reset(new SCSICmdModeSelect6(this));
990: break;
991:
1.1.1.2 root 992: case SCSI::Command::ModeSense6: // 0x1a
993: cmd.reset(new SCSICmdModeSense6(this));
994: break;
995:
1.1 root 996: case SCSI::Command::ReadCapacity: // 0x25
997: cmd.reset(new SCSICmdTestUnitReady(this));
998: break;
999:
1000: default:
1001: // サポートしていないコマンド
1002: const char *cname = SCSI::GetCommandName(cmdseq[0]);
1003: std::string name;
1004: if (cname) {
1005: name = string_format(" %s", cname);
1006: }
1007: putlog(0, "未実装 SCSI コマンド $%02x%s len=%d",
1008: cmdseq[0], name.c_str(), (int)cmdseq.size());
1009:
1010: cmd.reset(new SCSICmdNotSupportedCommand(this));
1011: break;
1012: }
1013: }
1014:
1015: //
1016: // SCSI HD
1017: //
1018:
1019: // コンストラクタ
1020: SCSIHD::SCSIHD(SCSIHostDevice *h, int id)
1021: : inherited(h, id, inquiry_data)
1022: {
1023: devname = "SCSIHD";
1.1.1.4 root 1024: devtype = SCSI::DevType::HD;
1.1 root 1025: }
1026:
1027: // デストラクタ
1028: SCSIHD::~SCSIHD()
1029: {
1030: }
1031:
1032: // 初期化
1033: bool
1034: SCSIHD::Init()
1035: {
1036: const std::string myname = string_format("%s:ID%d",
1037: host->GetConfigName(), myid);
1.1.1.2 root 1038: const std::string keybody = string_format("%s-id%d-",
1.1 root 1039: host->GetConfigName(), myid);
1040: const std::string imgkey = keybody + "image";
1041: const std::string wpkey = keybody + "writeprotect";
1.1.1.5 root 1042: const std::string stkey = keybody + "seektime";
1.1 root 1043:
1.1.1.3 root 1044: const ConfigItem& itemimg = gConfig->Find(imgkey);
1.1.1.2 root 1045: const std::string& image = itemimg.AsString();
1046: // ここに来る時にチェックしているので ',' はあるはず
1047: auto pos = image.find(',') + 1;
1048: std::string filename = string_ltrim(image.substr(pos, image.size() - pos));
1049: if (filename.length() == 0) {
1050: itemimg.Err("imagefile not specified");
1.1 root 1051: return false;
1052: }
1053:
1.1.1.3 root 1054: // ファイルをオープン
1.1.1.2 root 1055: const std::string path = gMainApp.GetVMDir() + filename;
1.1.1.3 root 1056: file.SetFilename(path);
1057: file.SetDispname(string_format("%s \"%s\"", myname.c_str(), path.c_str()));
1058: mem = file.OpenFile();
1059: if (mem == NULL) {
1.1 root 1060: return false;
1061: }
1062:
1.1.1.3 root 1063: writeprotect = gConfig->Find(wpkey).AsInt();
1.1 root 1064: if (writeprotect) {
1065: putmsg(0, "%s: write protected", myname.c_str());
1066: }
1067:
1.1.1.5 root 1068: // 設定は [msec]、変数は [nsec]。
1069: seektime = gConfig->Find(stkey).AsInt();
1070: seektime *= 1_msec;
1071:
1.1 root 1072: return true;
1073: }
1074:
1075: // SCSI コマンドのディスパッチャ
1076: void
1077: SCSIHD::DispatchCmd()
1078: {
1079: switch (cmdseq[0]) {
1080: case SCSI::Command::Read6: // 0x08
1081: cmd.reset(new SCSICmdRead6(this));
1082: break;
1083:
1084: case SCSI::Command::Write6: // 0x0a
1085: cmd.reset(new SCSICmdWrite6(this));
1086: break;
1087:
1.1.1.4 root 1088: case SCSI::Command::StartStopUnit: // 0x1b
1089: cmd.reset(new SCSICmdStartStopUnit(this));
1090: break;
1091:
1.1 root 1092: case SCSI::Command::ReadCapacity: // 0x25
1093: cmd.reset(new SCSICmdReadCapacity(this));
1094: break;
1095:
1096: case SCSI::Command::Read10: // 0x28
1097: cmd.reset(new SCSICmdRead10(this));
1098: break;
1099:
1.1.1.7 root 1100: case SCSI::Command::Write10: // 0x2a
1101: cmd.reset(new SCSICmdWrite10(this));
1102: break;
1103:
1.1 root 1104: default:
1105: // 見付からなければ基本クラス側に任せる
1106: inherited::DispatchCmd();
1107: break;
1108: }
1109: }
1110:
1.1.1.2 root 1111: // 指定の場所を自由に読み出す
1112: // (エミュレータ特権アクセス)
1113: bool
1114: SCSIHD::PeekImage(void *buf, uint32 start, uint32 len) const
1115: {
1116: if (mem == NULL) {
1117: return false;
1118: }
1.1.1.3 root 1119: if ((uint64)start + (uint64)len > GetSize()) {
1.1.1.2 root 1120: return false;
1121: }
1122: memcpy(buf, mem + start, len);
1123: return true;
1124: }
1125:
1.1 root 1126: // HD の inquiry データ
1127: /*static*/ std::vector<uint8>
1128: SCSIHD::inquiry_data = {
1129: 0x00, // 00 Qualifier + DeviceType
1130: 0x00, // 01 RMB
1131: 0x40, // 02 ISO version
1132: 0x00, // 03 Response Data Format?
1133: 36 - 4, // 04 追加データ長
1134: 0, // 05 Reserved
1135: 0, // 06 Reserved
1136: 0x00, // 07
1137: 'N', 'O', 'N', 'O', ' ', ' ', ' ', ' ',
1138: 'S', 'C', 'S', 'I', 'H', 'D', ' ', ' ',
1139: ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
1.1.1.2 root 1140: '0', ' ', ' ', ' ',
1.1 root 1141: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.