|
|
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:
1.1.1.6 root 155: #if defined(__clang__)
156: // デストラクタがインラインで書けないのは面倒なのでとりあえず
157: #pragma clang diagnostic push
158: #pragma clang diagnostic ignored "-Wweak-vtables"
159: #endif
160:
1.1 root 161: //
162: // SCSI コマンドの共通部分
163: //
164: class SCSICmd
165: {
166: public:
167: SCSICmd(SCSITarget *p) {
168: parent = p;
169: }
170: virtual ~SCSICmd() { }
171:
172: // 転送バッファと転送バイト数。
173: // IN (ターゲットが送出側)なら、各フェーズ開始までに送出バイト数分の
174: // バッファを buf に用意すること。recvbytes は操作不要。
175: //
176: // OUT(ターゲットが受信側)なら、各フェーズ開始前までに受信バイト数を
177: // recvbytes にセットすること。buf は操作不要。
178: // なお parent は OUT(受信)フェーズ中にこの recvbytes を変更しない。
179: std::vector<uint8> buf;
180: uint32 recvbytes;
181:
1.1.1.5 root 182: // このコマンドの動作時間 [nsec]
183: // シークタイムとかを簡易的に再現するため、次フェーズ遷移時の最初の
184: // REQ アサートを引き延ばすために使う。
185: // 主にコマンドフェーズで使うことになると思う。
186: uint64 optime {};
187:
1.1 root 188: // 以下いずれも戻り値は遷移先のフェーズ。
189:
190: // コマンドフェーズを処理する。
1.1.1.4 root 191: // コマンドが確定したところで呼ばれる。戻り値に遷移先フェーズを返すこと。
192: // また、コマンド開始時に各自で ClearSense() を呼ぶこと。センスというか
193: // CA 状態は次のコマンド実行でクリアされるが (ネクサスはとりあえず無視)、
194: // RequestSense の場合は現在保持しているセンス情報をホストに返すので、
195: // つまり RequestSense 以外のコマンドが全員各自でクリアする必要がある。
1.1 root 196: virtual SCSI::XferPhase Command(std::vector<uint8>& cmdseq) {
1.1.1.4 root 197: parent->ClearSense();
1.1 root 198: return SCSI::XferPhase::Status;
199: }
200:
201: // データインフェーズを完了する。
202: virtual SCSI::XferPhase DoneDataIn() { return SCSI::XferPhase::Status; }
203:
204: // データアウトフェーズを完了する。
205: virtual SCSI::XferPhase DoneDataOut() { return SCSI::XferPhase::Status; }
206:
207: // ステータスフェーズを開始する。
1.1.1.4 root 208: void BeginStatus() {
1.1 root 209: buf.clear();
1.1.1.4 root 210: buf.push_back(status_byte);
1.1 root 211: }
212:
213: // ステータスフェーズを完了する。
214: virtual SCSI::XferPhase DoneStatus() { return SCSI::XferPhase::MsgIn; }
215:
216: // メッセージインフェーズを開始する。
217: // デフォルトでは Command Complete を返す実装を用意しておく。
218: virtual void BeginMsgIn() {
219: buf.clear();
220: buf.push_back((uint8)SCSI::MsgByte::CommandComplete);
221: }
222:
223: // メッセージインフェーズを完了する。
224: virtual SCSI::XferPhase DoneMsgIn() { return SCSI::XferPhase::End; }
225:
226: // メッセージアウトフェーズを開始する。
227: virtual void BeginMsgOut() { }
228:
229: // メッセージアウトフェーズを完了する。
230: virtual SCSI::XferPhase DoneMsgOut() { return SCSI::XferPhase::End; }
231:
232: protected:
233: // このコマンドを実行しているターゲットデバイス。
234: SCSITarget *parent;
1.1.1.4 root 235:
236: // ステータスバイト (デフォルトは 0 = Good)
237: uint8 status_byte {};
1.1 root 238: };
239:
240: //
241: // SCSI コマンド 0x00: TestUnitReady (共通)
242: //
243: class SCSICmdTestUnitReady : public SCSICmd
244: {
1.1.1.3 root 245: using inherited = SCSICmd;
1.1 root 246: public:
247: SCSICmdTestUnitReady(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 248: virtual ~SCSICmdTestUnitReady() override { }
1.1 root 249: };
250:
251: //
1.1.1.2 root 252: // SCSI コマンド 0x01: RezeroUnit (共通)
253: //
254: class SCSICmdRezeroUnit : public SCSICmd
255: {
1.1.1.3 root 256: using inherited = SCSICmd;
1.1.1.2 root 257: public:
258: SCSICmdRezeroUnit(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 259: virtual ~SCSICmdRezeroUnit() override { }
1.1.1.2 root 260:
261: // とりあえず黙って成功するだけでいいか
262: };
263:
264: //
1.1 root 265: // SCSI コマンド 0x03: RequestSense (共通)
266: //
267: class SCSICmdRequestSense : public SCSICmd
268: {
1.1.1.3 root 269: using inherited = SCSICmd;
1.1 root 270: public:
271: SCSICmdRequestSense(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 272: virtual ~SCSICmdRequestSense() override { }
1.1 root 273:
1.1.1.6 root 274: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.4 root 275: // 応答のセンスデータは SCSI-2 形式(最小18バイト) を実装。
276: // 今の所、センスキー、ASC、ASCQ だけで返せる内容にだけ対応している。
277: // これ以外のフィールドを返す必要が出来たらまた考えること。
278:
1.1 root 279: buf.clear();
1.1.1.6 root 280: buf.resize(18);
1.1.1.4 root 281: buf[0] = 0xf0; // Valid、エラーコード 0x70
282: buf[1] = 0; // セグメント番号(?)
283: buf[2] = parent->sensekey; // センスキー (上位4ビットは今の所 0)
284: buf[3] = 0; // Information
285: buf[4] = 0; // Information
286: buf[5] = 0; // Information
287: buf[6] = 0; // Information
288: buf[7] = 0; // 追加センスデータ長 0
289: buf[8] = 0; // Command specific Information
290: buf[9] = 0; // Command specific Information
291: buf[10] = 0; // Command specific Information
292: buf[11] = 0; // Command specific Information
293: buf[12] = parent->senseasc >> 8;
294: buf[13] = parent->senseasc & 0xff;
295: buf[14] = 0; // FRU
296: buf[15] = 0; // センスキー固有情報
297: buf[16] = 0; // センスキー固有情報
298: buf[17] = 0; // センスキー固有情報
299: // 追加センスデータなし
1.1 root 300:
301: // それからバッファを指定の長さに変更
302: buf.resize((uint)cmdseq[4]);
303:
1.1.1.4 root 304: // 転送した後にセンスデータをクリア
305: parent->ClearSense();
306:
1.1 root 307: return SCSI::XferPhase::DataIn;
308: }
309: };
310:
311: //
312: // SCSI コマンド 0x08: Read(6) (共通)
313: //
314: class SCSICmdRead6 : public SCSICmd
315: {
1.1.1.3 root 316: using inherited = SCSICmd;
1.1 root 317: public:
318: SCSICmdRead6(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 319: virtual ~SCSICmdRead6() override { }
1.1 root 320:
1.1.1.6 root 321: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1 root 322: SCSIHD *hdparent = dynamic_cast<SCSIHD*>(parent);
323: uint32 lba;
324: uint32 blks;
325:
1.1.1.4 root 326: // センスデータをクリア
327: parent->ClearSense();
328:
1.1 root 329: lba = (cmdseq[1] & 0x1f) << 16;
330: lba |= cmdseq[2] << 8;
331: lba |= cmdseq[3];
332: blks = cmdseq[4];
333: // blks == 0 は 256 ブロック転送を意味する。
334: if (blks == 0) {
335: blks = 256;
336: }
1.1.1.4 root 337: parent->putlog(1, "READ6 LBA=0x%x blks=0x%x", lba, blks);
338:
339: if (((uint64)lba + (uint64)blks) * parent->GetBlocksize() >
340: hdparent->GetSize())
341: {
342: parent->sensekey = SCSI::SenseKey::IllegalRequest;
343: parent->senseasc = SCSI::ASC::InvalidFieldInCDB;
344: status_byte = SCSI::StatusByte::CheckCondition;
345: return SCSI::XferPhase::Status;
346: }
347:
1.1.1.2 root 348: uint8 *start = hdparent->mem + lba * parent->GetBlocksize();
349: uint8 *end = start + blks * parent->GetBlocksize();
1.1 root 350: buf.assign(start, end);
351:
1.1.1.5 root 352: // 平均シークタイム
353: optime = hdparent->seektime;
354:
1.1 root 355: return SCSI::XferPhase::DataIn;
356: }
357: };
358:
359: //
360: // SCSI コマンド 0x0a: Write(6) (共通)
361: //
362: class SCSICmdWrite6 : public SCSICmd
363: {
1.1.1.3 root 364: using inherited = SCSICmd;
1.1 root 365: public:
366: SCSICmdWrite6(SCSITarget *p) : inherited(p) {
367: hdparent = dynamic_cast<SCSIHD*>(parent);
368: }
1.1.1.6 root 369: virtual ~SCSICmdWrite6() override { }
1.1 root 370:
1.1.1.6 root 371: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.4 root 372: // センスデータをクリア
373: parent->ClearSense();
374:
1.1 root 375: lba = (cmdseq[1] & 0x1f) << 16;
376: lba |= cmdseq[2] << 8;
377: lba |= cmdseq[3];
378: blks = cmdseq[4];
379: // blks == 0 は 256 ブロック転送を意味する。
380: if (blks == 0) {
381: blks = 256;
382: }
383:
1.1.1.4 root 384: if (((uint64)lba + (uint64)blks) * parent->GetBlocksize() >
385: hdparent->GetSize())
386: {
387: parent->sensekey = SCSI::SenseKey::IllegalRequest;
388: parent->senseasc = SCSI::ASC::InvalidFieldInCDB;
389: status_byte = SCSI::StatusByte::CheckCondition;
390: return SCSI::XferPhase::Status;
391: }
392:
1.1.1.5 root 393: // 平均シークタイム
394: optime = hdparent->seektime;
395:
1.1.1.4 root 396: recvbytes = blks * parent->GetBlocksize();
1.1 root 397: return SCSI::XferPhase::DataOut;
398: }
399:
1.1.1.6 root 400: SCSI::XferPhase DoneDataOut() override {
1.1 root 401: parent->putlog(1, "WRITE6 LBA=0x%x blks=0x%x", lba, blks);
402: if (hdparent->writeprotect == false) {
1.1.1.2 root 403: memcpy(hdparent->mem + (lba * parent->GetBlocksize()),
1.1 root 404: buf.data(), buf.size());
405: }
406: return SCSI::XferPhase::Status;
407: }
408:
409: private:
410: SCSIHD *hdparent;
411: uint32 lba;
412: uint32 blks;
413: };
414:
415: //
416: // SCSI コマンド 0x12: Inquiry (共通)
417: //
418: class SCSICmdInquiry : public SCSICmd
419: {
1.1.1.3 root 420: using inherited = SCSICmd;
1.1 root 421: public:
422: SCSICmdInquiry(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 423: virtual ~SCSICmdInquiry() override { }
1.1 root 424:
1.1.1.6 root 425: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.4 root 426: // センスデータをクリア
427: parent->ClearSense();
428:
1.1 root 429: // 応答データはターゲットデバイスが持っている
430: buf = parent->inquiry_data;
431:
432: // 本来の応答データ長か reqlen の短いほうまで。
433: uint reqlen = (uint)cmdseq[4];
434: if (reqlen < buf.size()) {
435: buf.resize(reqlen);
436: }
437: return SCSI::XferPhase::DataIn;
438: }
439: };
440:
441: //
442: // SCSI コマンド 0x15 ModeSelect(6) (共通)
443: //
444: class SCSICmdModeSelect6 : public SCSICmd
445: {
1.1.1.3 root 446: using inherited = SCSICmd;
1.1 root 447: public:
448: SCSICmdModeSelect6(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 449: virtual ~SCSICmdModeSelect6() override { }
1.1 root 450:
1.1.1.6 root 451: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.4 root 452: // センスデータをクリア
453: parent->ClearSense();
454:
1.1 root 455: recvbytes = cmdseq[4];
456: return SCSI::XferPhase::DataOut;
457: }
458:
1.1.1.6 root 459: SCSI::XferPhase DoneDataOut() override {
1.1 root 460: // Mode Select(6) のデータは以下の構造で並んでいる。
461: // ヘッダ
462: // ブロックディスクリプタ#1
463: // :
464: // ブロックディスクリプタ#n
465: // パラメータページ #1
466: // :
467: // パラメータページ #m
468:
469: const SCSI::ModeParamHdr *hdr = (const SCSI::ModeParamHdr *)buf.data();
470:
471: if (hdr->block_desc_len > 0) {
472: const SCSI::BlockDesc *desc =
473: (const SCSI::BlockDesc *)(buf.data() + sizeof(*hdr));
474: uint blocksize;
475: blocksize = (desc->block_size[0]) << 16;
476: blocksize |= (desc->block_size[1]) << 8;
477: blocksize |= (desc->block_size[2]);
478:
479: // ブロック長を書き戻す
1.1.1.2 root 480: parent->SetBlocksize(blocksize);
1.1 root 481: }
482: return SCSI::XferPhase::Status;
483: }
484: };
485:
486: //
1.1.1.2 root 487: // SCSI コマンド 0x1a ModeSense(6) (共通)
488: //
489: class SCSICmdModeSense6 : public SCSICmd
490: {
1.1.1.3 root 491: using inherited = SCSICmd;
1.1.1.2 root 492: public:
493: SCSICmdModeSense6(SCSITarget *p) : inherited(p) { }
1.1.1.6 root 494: virtual ~SCSICmdModeSense6() override { }
1.1.1.2 root 495:
1.1.1.6 root 496: SCSI::XferPhase Command(std::vector<uint8>& cmdseq) override {
1.1.1.2 root 497: uint8 pagecode = (cmdseq[2] & 0x3f);
1.1.1.4 root 498:
499: // センスデータをクリア
500: parent->ClearSense();
501:
502: // 未実装 (XXX センスデータは適当)
1.1.1.2 root 503: parent->putlog(1, "ModeSense(6) PageCode $%02x 未実装", 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.4 root 527: parent->putlog(1, "StartStopUnit");
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) {
550: parent->putmsg("SCSI ReadCapacity PMI=1 not supported");
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)) {
591: parent->putmsg("SCSI Read(10) RelAdr not supported");
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.4 root 600: parent->putlog(1, "READ10 LBA=0x%x blks=0x%x", lba, blks);
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 {
! 662: parent->putlog(1, "WRITE10 LBA=0x%x blks=0x%x", lba, blks);
! 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: }
842: putlog("%s", cmdbuf.c_str());
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.