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