|
|
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:
1.1.1.14 root 7: //
8: // SCSI デバイス
9: //
10:
1.1.1.11 root 11: // ログレベルは
12: // 1: SCSI コマンドの概要。
13: // デバイスの動作のうち大きな変更のもの
14: // 2: SCSI コマンドの詳細、ダンプ。
15: // デバイスの動作のうち些細なもの、あるいは頻繁なもの
16: // 3: フェーズ遷移
17: // 4: データも含める
1.1 root 18:
1.1.1.11 root 19: #include "scsicmd.h"
1.1 root 20: #include "scsidev.h"
1.1.1.20 root 21: #include "scsidomain.h"
1.1.1.2 root 22: #include "config.h"
1.1.1.22 root 23: #include "event.h"
1.1.1.2 root 24: #include "mainapp.h"
1.1.1.14 root 25: #include "scheduler.h"
1.1.1.11 root 26: #include "uimessage.h"
1.1 root 27:
28: //
1.1.1.11 root 29: // SCSI デバイス共通部分
1.1 root 30: //
31:
32: // コンストラクタ
1.1.1.18 root 33: SCSIDevice::SCSIDevice(uint objid_)
1.1.1.16 root 34: : inherited(objid_)
1.1 root 35: {
1.1.1.18 root 36: myid = -1;
1.1 root 37: }
38:
39: // デストラクタ
40: SCSIDevice::~SCSIDevice()
41: {
42: }
43:
1.1.1.20 root 44: // バスに接続する。
1.1.1.18 root 45: void
1.1.1.20 root 46: SCSIDevice::Attach(SCSIBus *bus_)
1.1.1.18 root 47: {
48: bus = bus_;
49: }
50:
1.1.1.14 root 51:
1.1 root 52: //
1.1.1.18 root 53: // SCSI ホストデバイス (イニシエータ)
1.1 root 54: //
55:
56: // コンストラクタ
1.1.1.20 root 57: SCSIHostDevice::SCSIHostDevice(SCSIDomain *domain_, IODevice *parent_)
58: : inherited(OBJ_NONE)
1.1 root 59: {
1.1.1.20 root 60: domain = domain_;
1.1.1.18 root 61: parent = parent_;
62: // イニシエータの ID は実行時にコントローラによって決まる。
1.1.1.5 root 63: devtype = SCSI::DevType::Initiator;
64:
1.1.1.23! root 65: SetName(string_format("(SCSIHost.%s)", domain_->GetDomainName()));
1.1.1.20 root 66: SetLogLevel(parent->loglevel);
1.1 root 67: }
68:
69: // デストラクタ
70: SCSIHostDevice::~SCSIHostDevice()
71: {
72: }
73:
1.1.1.20 root 74: // SCSI ID を設定する。
75: // (SCSIDomain からのみ呼ぶこと)
76: void
77: SCSIHostDevice::SetID(uint id_)
1.1 root 78: {
1.1.1.20 root 79: myid = id_;
1.1 root 80: }
81:
1.1.1.20 root 82: // SCSIBus からのコールバック関数を設定する。
1.1 root 83: void
1.1.1.20 root 84: SCSIHostDevice::SetBusCallback(
1.1.1.18 root 85: BusFreeCallback_t busfree_,
86: SCSIHostCallback_t selack_,
87: SCSIHostCallback_t xferreq_)
88: {
89: BusFreeCallback = busfree_;
90: SelectionAckCallback = selack_;
91: TransferReqCallback = xferreq_;
92: }
93:
94: // バスフリーになった (SCSIBus から呼ばれる)
95: void
96: SCSIHostDevice::OnBusFree(uint id)
1.1 root 97: {
98: // 実際にはバスフリーを検出したデバイスは各自自身が送出している信号を
99: // 下げるのだが、色々面倒なので、ここではホストのバスフリーコールバックが
100: // 一括して全部の線を下げる。
101: // ただしさすがにログがうるさいので、上がってる線だけ下げる。
102:
103: uint32 data;
104: while ((data = GetBSY()) != 0) {
1.1.1.18 root 105: bus->NegateBSY(SCSIBus::DecodeID(data));
1.1 root 106: }
107: while ((data = GetSEL()) != 0) {
1.1.1.18 root 108: bus->NegateSEL(SCSIBus::DecodeID(data));
1.1 root 109: }
110: if (GetREQ()) {
111: NegateREQ();
112: }
113: if (GetACK()) {
114: NegateACK();
115: }
116: if (GetATN()) {
117: NegateATN();
118: }
119: SetXfer(0);
120: SetData(0);
1.1.1.18 root 121:
122: // 親のバスフリーコールバックを呼ぶ
123: (parent->*(BusFreeCallback))(id);
1.1.1.20 root 124:
125: // RST が見えてる状態で親に通知はしたので、ここで下ろす。
126: if (GetRST()) {
127: NegateRST();
128: }
1.1 root 129: }
130:
1.1.1.18 root 131: // ターゲットがセレクションに応答した (SCSIBus から呼ばれる)
1.1.1.11 root 132: void
1.1.1.18 root 133: SCSIHostDevice::OnSelectionAck()
1.1 root 134: {
1.1.1.18 root 135: // ターゲットが BSY を立てて応答したので、
136: // こちらはデータバスをクリアして SEL を下げる。
137: // これによりセレクションフェーズは成功で完了する。
138: SetData(0);
139: NegateSEL();
1.1.1.7 root 140:
1.1.1.18 root 141: // 親のバスフリーコールバックを呼ぶ
142: (parent->*(SelectionAckCallback))();
143: }
1.1.1.7 root 144:
1.1.1.18 root 145: // ターゲットが REQ を立てた (SCSIBus から呼ばれる)
146: void
147: SCSIHostDevice::OnTransferReq()
148: {
149: (parent->*(TransferReqCallback))();
1.1.1.11 root 150: }
1.1 root 151:
152:
153: //
154: // SCSI ターゲットデバイスの共通部分
155: //
156:
1.1.1.16 root 157: // コンストラクタ。
1.1.1.20 root 158: SCSITarget::SCSITarget(SCSIDomain *domain_, uint id)
1.1.1.16 root 159: : inherited(OBJ_NONE) // オブジェクト ID で検索することはない
1.1 root 160: {
1.1.1.20 root 161: domain = domain_;
1.1 root 162: myid = id;
1.1.1.22 root 163:
164: // ログは出すので名前はつけるがそれは継承先で SetName() している。
1.1 root 165: }
166:
167: // デストラクタ
168: SCSITarget::~SCSITarget()
169: {
170: }
171:
1.1.1.9 root 172: // 電源オン
1.1.1.14 root 173: void
174: SCSITarget::ResetHard(bool poweron)
1.1.1.9 root 175: {
1.1.1.14 root 176: if (poweron) {
177: ClearSense();
178: cmdseq.clear();
179: cmdlen = 0;
180: cmd.reset();
1.1.1.22 root 181: msgseq.clear();
1.1.1.14 root 182: bytecount = 0;
183: lastbyte = false;
184: }
1.1.1.9 root 185: }
186:
1.1.1.4 root 187: // バスリセットされた (SCSIBus から呼ばれる)
188: void
1.1.1.18 root 189: SCSITarget::OnBusReset()
1.1.1.4 root 190: {
1.1.1.11 root 191: // センスキーをクリア
192: ClearSense();
193:
1.1.1.5 root 194: // リセットされたので持ってる信号を全部解放。
195: // 一応自分が変えていい信号線だけを落とす。
196: switch (GetPhase()) {
197: case SCSI::Phase::BusFree:
198: break;
199: case SCSI::Phase::Arbitration:
200: case SCSI::Phase::Selection:
201: case SCSI::Phase::Reselection:
202: NegateSEL();
203: NegateBSY();
204: break;
205: case SCSI::Phase::Transfer:
206: NegateBSY();
207: break;
208: }
1.1.1.4 root 209: }
210:
1.1 root 211: // セレクションで選択された (SCSIBus から呼ばれる)
212: void
1.1.1.18 root 213: SCSITarget::OnSelected()
1.1 root 214: {
215: // ターゲットが、自身が選択されていることを認識してから BSY を
216: // 立てるまでの時間はもうバス側でカウントしてあるので、ここでは
217: // ただちに BSY を上げてよい。
1.1.1.18 root 218: putlog(4, "OnSelected");
1.1 root 219: AssertBSY();
220: }
221:
222: // 情報転送フェーズを開始する (SCSIBus から呼ばれる)
223: void
1.1.1.18 root 224: SCSITarget::OnStartTransfer()
1.1 root 225: {
1.1.1.22 root 226: // ATN が立っていればメッセージアウトフェーズ。
227: // そうでなければコマンドフェーズ。
1.1 root 228: cmdseq.clear();
229: cmdlen = 0;
1.1.1.22 root 230: msgseq.clear();
231: if (GetATN()) {
232: putlog(4, "Start MsgOut Phase by ATN");
233: SetXfer(SCSI::XferPhase::MsgOut);
234: } else {
235: putlog(4, "Start Command Phase");
236: SetXfer(SCSI::XferPhase::Command);
237: }
1.1 root 238: AssertREQ();
239: }
240:
241: // 情報転送フェーズ間の遷移について
242: //
1.1.1.18 root 243: // 情報転送フェーズは OnTransfer() で1バイトずつ転送を行い、所定のバイト数
1.1 root 244: // の転送が完了すればフェーズを遷移する。遷移方法は入出力方向別で6通り。
245: // o IN -> IN
246: // o IN -> OUT
247: // o IN -> END
248: // o OUT -> OUT
249: // o OUT -> IN
250: // o OUT -> END
251: //
1.1.1.18 root 252: // IN -> IN の遷移。OnTransfer() で前フェーズの最終バイト受信後、フェーズを
1.1 root 253: // 変更して REQ を立てるところまで。
254: //
1.1.1.18 root 255: // IN -> OUT の遷移。OnTransfer() で前フェーズの最終バイト受信後、フェーズを
1.1 root 256: // 変更して (おそらく {Phase}Begin() を発行してから)、データをセットし REQ
257: // を立てる?
258: //
1.1.1.18 root 259: // IN -> END の遷移。OnTransfer() で前フェーズの最終バイト受信後、BSY を
1.1 root 260: // 下げて終了。
261: //
262: // OUT -> OUT の遷移。前フェーズの最終バイトをイニシエータが受信後に
1.1.1.18 root 263: // OnTransfer() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin()
1.1 root 264: // を発行してから)、データをセットし REQ を立てる?
265: //
266: // OUT -> IN の遷移。前フェーズの最終バイトをイニシエータが受信後に
1.1.1.18 root 267: // OnTransfer() が呼ばれるので、フェーズを変更して (おそらく {Phase}Begin()
1.1 root 268: // を発行してから)、REQ を立てる?
269: //
270: // OUT -> END の遷移。前フェーズの最終バイトをイニシエータが受信後に
1.1.1.18 root 271: // OnTransfer() が呼ばれるので、BSY を下げる?
1.1 root 272:
273: // 情報転送フェーズでイニシエータが ACK を上げた。(SCSIBus から呼ばれる)
274: // OUT (イニシエータ → ターゲット方向) なら、ターゲットが上げた REQ に応答
275: // してイニシエータがデータをデータバスに乗せてからこれをコールしてくる。
276: // IN (ターゲット → イニシエータ方向) なら、ターゲットがデータをデータバスに
277: // 乗せて REQ を上げたのをイニシエータが受信した後でこれをコールしてくる。
278: void
1.1.1.18 root 279: SCSITarget::OnTransfer()
1.1 root 280: {
281: uint8 data;
282: SCSI::XferPhase xfer;
283:
284: // このフェーズの最終バイトか
285: lastbyte = false;
286:
287: // このフェーズの1バイト処理
288: xfer = GetXfer();
289: if (xfer == SCSI::XferPhase::Command) {
290: // コマンドフェーズだけいろいろ特殊なので別処理。
291: // 受信バッファが異なる、1バイト目で受信長が決まるなど。
292: data = GetData();
1.1.1.18 root 293: putlog(4, "%s: cmdseq[%02u] = $%02x", __func__,
294: (uint)cmdseq.size(), data);
1.1 root 295: cmdseq.push_back(data);
296: if (cmdseq.size() == 1) {
297: // 1バイト目ならこのコマンドが何バイトか調べる
298: switch (cmdseq[0] >> 5) {
299: case 0: cmdlen = 6; break;
300: case 1: cmdlen = 10; break;
301: case 2: cmdlen = 10; break;
302: case 5: cmdlen = 12; break;
303: default:
304: // 3,4 はリサーブ、6,7 はベンダー定義。XXX 来たらどうする?
305: cmdlen = 6;
306: break;
307: }
308: } else if (cmdseq.size() >= cmdlen) {
1.1.1.11 root 309: if (loglevel >= 2
310: #if defined(NO_READWRITE_LOG)
311: && (cmdseq[0] != SCSI::Command::Read6 &&
312: cmdseq[0] != SCSI::Command::Read10 &&
313: cmdseq[0] != SCSI::Command::Write6 &&
314: cmdseq[0] != SCSI::Command::Write10)
315: #endif
316: ) {
1.1 root 317: std::string cmdbuf;
318: const char *name = SCSI::GetCommandName(cmdseq[0]);
319: cmdbuf += string_format("Command \"%s\"", name ?: "?");
320: for (const auto& v : cmdseq) {
1.1.1.11 root 321: cmdbuf += string_format(" %02x", v);
1.1 root 322: }
1.1.1.8 root 323: putlogn("%s", cmdbuf.c_str());
1.1 root 324: }
1.1.1.20 root 325: // 最終バイトを受信したらコマンドを選択。
1.1 root 326: lastbyte = true;
1.1.1.20 root 327: SelectCommand();
328: if ((bool)cmd == false) {
329: std::string namebuf;
330: const char *name = SCSI::GetCommandName(cmdseq[0]);
331: if (name) {
332: namebuf = std::string(name);
333: } else {
334: namebuf = string_format("$%02x", cmdseq[0]);
335: }
336: putlog(0, "Failed to initialize command %s", namebuf.c_str());
337: AssertRST();
338: }
1.1 root 339: }
1.1.1.22 root 340: } else if (xfer == SCSI::XferPhase::MsgOut) {
341: data = GetData();
342: putlog(4, "%s: msgseq[%02u] = $%02x", __func__,
343: (uint)msgseq.size(), data);
344: msgseq.push_back(data);
345: bytecount--;
346: if (bytecount == 0) {
347: lastbyte = true;
348: }
349:
1.1 root 350: } else if (GetIO() == false) {
351: // OUT 方向 (ターゲットが受信側)
352: // bytecount は減算方向。
353: cmd->buf.push_back(GetData());
354: bytecount--;
355: if (bytecount == 0) {
356: lastbyte = true;
357: }
358: } else {
359: // IN 方向 (ターゲットが送信側)
360: // bytecount は加算方向。
1.1.1.18 root 361: // データは前のループの終わり (OnTransferAck()の下のほう) で
1.1 root 362: // セットしているのでここでは引き取られた分をカウントする。
363: bytecount++;
364: if (bytecount >= cmd->buf.size()) {
365: lastbyte = true;
366: }
367: }
368:
369: NegateREQ();
370: }
371:
372: // 情報転送フェーズでイニシエータが ACK を下げた。(SCSIBus から呼ばれる)
373: void
1.1.1.18 root 374: SCSITarget::OnTransferAck()
1.1 root 375: {
376: SCSI::XferPhase xfer = GetXfer();
377:
378: // これが最終バイトだったら、フェーズ完了 → フェーズ切り替え。
379: if (lastbyte) {
380: SCSI::XferPhase next;
381: // フェーズ完了
382: switch (xfer) {
383: case SCSI::XferPhase::Command:
1.1.1.20 root 384: next = cmd->ExecCommand(cmdseq);
1.1 root 385: break;
386: case SCSI::XferPhase::DataOut:
387: next = cmd->DoneDataOut();
388: break;
389: case SCSI::XferPhase::DataIn:
390: next = cmd->DoneDataIn();
391: break;
392: case SCSI::XferPhase::MsgOut:
1.1.1.22 root 393: if (cmd) {
394: next = cmd->DoneMsgOut();
395: } else {
396: // コマンド前のメッセージアウト。
397: if (msgseq.size() == 1 && (msgseq[0] & 0x80)) {
398: putlog(1, "%s: MsgOut $%02x Identify (NOT SUPPORTED)",
399: __func__, msgseq[0]);
400: } else {
401: std::string buf;
402: for (auto m : msgseq) {
403: buf += string_format(" %02x", m);
404: }
405: putlog(0, "%s: MsgOut%s (NOT SUPPORTED)",
406: __func__, buf.c_str());
407: }
408: next = SCSI::XferPhase::Command;
409: }
1.1 root 410: break;
411: case SCSI::XferPhase::MsgIn:
412: next = cmd->DoneMsgIn();
413: break;
414: case SCSI::XferPhase::Status:
415: next = cmd->DoneStatus();
416: break;
417: default:
1.1.1.18 root 418: PANIC("%s unknown xfer done %d", __func__, (int)xfer);
1.1 root 419: }
1.1.1.11 root 420: putlog(3, "%s done", SCSI::GetXferPhaseName(xfer));
1.1 root 421:
1.1.1.5 root 422: // 簡略化のため、フェーズ完了時だけ REQ を引き伸ばすことができる
1.1.1.22 root 423: if (cmd && cmd->optime != 0) {
1.1.1.5 root 424: bus->SetOneshotReqWait(cmd->optime);
425: cmd->optime = 0;
426: }
427:
1.1 root 428: if (next != xfer) {
429: // End ならバスフリーにして終了。
430: if (next == SCSI::XferPhase::End) {
1.1.1.20 root 431: cmd.reset();
1.1 root 432: NegateBSY();
433: return;
434: }
435:
436: // (必要なら)フェーズを更新
437: SetXfer(next);
438: xfer = next;
439:
440: // 新しいフェーズの開始処理
441: // XXX ここで bytecount とかをリセットできればいいのだが
442: // Command フェーズで Data フェーズの準備をすることが多いので
443: // 悩ましい。
444: switch (xfer) {
1.1.1.22 root 445: case SCSI::XferPhase::Command:
446: break;
1.1 root 447: case SCSI::XferPhase::DataOut:
448: cmd->buf.clear();
449: bytecount = cmd->recvbytes;
1.1.1.18 root 450: putlog(3, "DataOut begin (%u bytes)", bytecount);
1.1 root 451: break;
452: case SCSI::XferPhase::DataIn:
453: bytecount = 0;
1.1.1.18 root 454: putlog(3, "DataIn begin (%u bytes)", (uint)cmd->buf.size());
1.1 root 455: break;
456: case SCSI::XferPhase::MsgOut:
457: cmd->BeginMsgOut();
458: cmd->buf.clear();
459: bytecount = cmd->recvbytes;
1.1.1.18 root 460: putlog(3, "MsgOut begin (%u bytes)", bytecount);
1.1 root 461: break;
462: case SCSI::XferPhase::MsgIn:
463: cmd->BeginMsgIn();
464: bytecount = 0;
1.1.1.18 root 465: putlog(3, "MsgIn begin (%u bytes)", (uint)cmd->buf.size());
1.1 root 466: break;
467: case SCSI::XferPhase::Status:
468: cmd->BeginStatus();
469: bytecount = 0;
1.1.1.18 root 470: putlog(3, "Status begin (%u bytes)", (uint)cmd->buf.size());
1.1 root 471: break;
472: default:
1.1.1.18 root 473: PANIC("%s unknown xfer begin %d", __func__, (int)xfer);
1.1 root 474: }
475: }
476: }
477:
478: // IN 方向 (ターゲットが送信側) ならここでデータをバスにセット。
479: if (GetIO()) {
480: SetData(cmd->buf[bytecount]);
481: putlog(4, "SetData $%02x", GetData());
482: }
483:
484: // 次のバイト転送を行うために REQ を上げる。
485: AssertREQ();
486: }
487:
1.1.1.20 root 488: // SCSI コマンドのディスパッチャ。
489: // C++ の例外は呼び出し側で捕捉している。
1.1 root 490: void
491: SCSITarget::DispatchCmd()
492: {
493: switch (cmdseq[0]) {
494: case SCSI::Command::TestUnitReady: // 0x00
1.1.1.11 root 495: cmd.reset(new SCSICmd(this));
1.1 root 496: break;
497:
1.1.1.2 root 498: case SCSI::Command::RezeroUnit: // 0x01
1.1.1.11 root 499: cmd.reset(new SCSICmd(this));
1.1.1.2 root 500: break;
501:
1.1 root 502: case SCSI::Command::RequestSense: // 0x03
503: cmd.reset(new SCSICmdRequestSense(this));
504: break;
505:
506: case SCSI::Command::Inquiry: // 0x12
507: cmd.reset(new SCSICmdInquiry(this));
508: break;
509:
510: case SCSI::Command::ModeSelect6: // 0x15
1.1.1.11 root 511: case SCSI::Command::ModeSelect10: // 0x55
512: cmd.reset(new SCSICmdModeSelect(this));
1.1 root 513: break;
514:
1.1.1.2 root 515: case SCSI::Command::ModeSense6: // 0x1a
1.1.1.11 root 516: case SCSI::Command::ModeSense10: // 0x5a
517: cmd.reset(new SCSICmdModeSense(this));
1.1 root 518: break;
519:
520: default:
521: // サポートしていないコマンド
522: const char *cname = SCSI::GetCommandName(cmdseq[0]);
523: std::string name;
524: if (cname) {
525: name = string_format(" %s", cname);
526: }
1.1.1.18 root 527: putlog(0, "SCSI Command $%02x%s len=%u not supported",
528: cmdseq[0], name.c_str(), (uint)cmdseq.size());
1.1 root 529:
530: cmd.reset(new SCSICmdNotSupportedCommand(this));
531: break;
532: }
533: }
534:
1.1.1.20 root 535: // コマンド列を指定して対応するコマンドを選択する。
536: // 知らないコマンドなら SCSICmdNotSupportedCommand という仮想のコマンドを返す。
537: // コマンドインスタンスが生成できなければ NULL を返す。
538: // 外部向け。
1.1.1.15 root 539: SCSICmd *
540: SCSITarget::SelectCommand(const std::vector<uint8>& cmdseq_)
541: {
542: cmdseq = cmdseq_;
1.1.1.20 root 543: SelectCommand();
1.1.1.15 root 544: return cmd.get();
545: }
546:
1.1.1.20 root 547: // 内部版。
548: // 入力コマンド列はメンバ変数 cmdseq だし、生成した cmd もメンバ変数。
549: void
550: SCSITarget::SelectCommand()
551: {
552: cmd.reset();
553: // DispatchCmd() の中は new が多いので個別に捕捉せずこっちでやる。
554: try {
555: DispatchCmd();
556: } catch (...) { }
557: }
558:
1.1.1.11 root 559:
1.1 root 560: //
1.1.1.11 root 561: // SCSI Disk
1.1 root 562: //
563:
564: // コンストラクタ
1.1.1.20 root 565: SCSIDisk::SCSIDisk(SCSIDomain *domain_, uint id, SCSI::DevType devtype_)
566: : inherited(domain_, id)
1.1 root 567: {
1.1.1.11 root 568: devtype = devtype_;
569:
570: const char *devtypename = SCSI::GetDevTypeName(devtype);
1.1.1.23! root 571: const char *domainname = domain->GetDomainName();
1.1.1.11 root 572:
1.1.1.23! root 573: // ドライブ自身のログ表示名は "HD(SPC0-6)" のような形式にする。
! 574: // ログ指定はそれに合わせて "spc0-6" のような完全形と
! 575: // "hd6" のような短縮形を用意してみる。
! 576: // SCSI コントローラ2台目ではこの短縮形だけなくす?
! 577: //
1.1.1.11 root 578: // コンストラクタの初期化子では devtypename を組み込みにくいので、
579: // コンストラクタ本文でログ名などを設定する。その時の作法に
580: // 従っているのですこし面倒な手法が必要。
581: // lib/object.h 参照。
1.1.1.23! root 582: std::string name = string_format("%s-%u", domainname, id);
! 583: SetName(string_format("%s(%s)", devtypename, name.c_str()));
1.1.1.11 root 584: ClearAlias();
1.1.1.23! root 585: AddAlias(name);
1.1.1.18 root 586: AddAlias(string_format("%s%u", devtypename, id));
1.1.1.11 root 587:
1.1.1.23! root 588: // image はコンストラクタがないので、このあたりで代入する。
! 589: image.SetName(name);
! 590:
1.1.1.11 root 591: switch (devtype) {
592: case SCSI::DevType::HD:
593: removable_device = false;
594: writeable_device = true;
595: blocksize = 512;
596: break;
597: case SCSI::DevType::CD:
598: removable_device = true;
599: writeable_device = false;
600: blocksize = 2048;
601: break;
602: case SCSI::DevType::MO:
603: removable_device = true;
604: writeable_device = true;
605: blocksize = 512;
606: break;
607: default:
1.1.1.18 root 608: PANIC("corrupted devtype=%u", (uint)devtype);
1.1.1.11 root 609: }
1.1.1.10 root 610:
1.1 root 611: }
612:
613: // デストラクタ
1.1.1.11 root 614: SCSIDisk::~SCSIDisk()
1.1 root 615: {
616: }
617:
618: // 初期化
619: bool
1.1.1.11 root 620: SCSIDisk::Init()
1.1 root 621: {
1.1.1.18 root 622: const std::string keybody = string_format("%s-id%u-",
1.1.1.20 root 623: domain->GetConfigName(), myid);
1.1 root 624: const std::string imgkey = keybody + "image";
1.1.1.11 root 625: const std::string wikey = keybody + "writeignore";
1.1.1.5 root 626: const std::string stkey = keybody + "seektime";
1.1 root 627:
1.1.1.11 root 628: // デバイス種別(v[0])とパス(v[1])に分解。
629: // デバイス種別はすでにチェックしてあるのでここでは不要。
1.1.1.3 root 630: const ConfigItem& itemimg = gConfig->Find(imgkey);
1.1.1.11 root 631: const std::string& imageval = itemimg.AsString();
632: auto v = string_split(imageval, ',', 2);
633: std::string filename;
634: if (v.size() > 1) {
635: filename = string_trim(v[1]);
636: }
637:
638: // 固定デバイス(HD)ならファイル名は省略不可
639: if (!IsRemovableDevice() && filename.empty()) {
1.1.1.2 root 640: itemimg.Err("imagefile not specified");
1.1 root 641: return false;
642: }
643:
1.1.1.14 root 644: // リムーバブルデバイスのみ UI からの通知を受け取るイベントを用意
645: if (IsRemovableDevice()) {
1.1.1.16 root 646: scheduler->ConnectMessage(MessageID::SCSIDEV_LOAD(myid), this,
1.1.1.17 root 647: ToMessageCallback(&SCSIDisk::LoadMessage));
1.1.1.16 root 648: scheduler->ConnectMessage(MessageID::SCSIDEV_UNLOAD(myid), this,
1.1.1.17 root 649: ToMessageCallback(&SCSIDisk::UnloadMessage));
1.1.1.14 root 650: }
651:
1.1.1.11 root 652: // オープン前に writeignore をチェック。
653: // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
654: // 行わないといけない。うーん。
1.1.1.23! root 655: write_ignore = gConfig->Find(wikey).AsBool();
1.1 root 656:
1.1.1.21 root 657: // writeignore が決まったので状態を初期化。
658: Clear();
659:
1.1.1.11 root 660: // 設定時点でファイルが指定されていればオープン
661: if (filename.empty() == false) {
1.1.1.17 root 662: std::string path = gMainApp.NormalizePath(filename);
1.1.1.11 root 663: if (LoadDisk(path) == false) {
1.1.1.23! root 664: itemimg.Err("Could not open %s", path.c_str());
1.1.1.11 root 665: return false;
666: }
1.1 root 667: }
668:
1.1.1.23! root 669: // 平均シークタイム。設定は [msec]、変数は [tsec]。
1.1.1.20 root 670: // virtio-scsi にはない。
671: if (strncmp(stkey.c_str(), "virtio", 6) != 0) {
672: seektime = gConfig->Find(stkey).AsInt();
673: seektime *= 1_msec;
674: }
1.1.1.5 root 675:
1.1 root 676: return true;
677: }
678:
1.1.1.11 root 679: // 電源オン
1.1.1.14 root 680: void
681: SCSIDisk::ResetHard(bool poweron)
1.1.1.11 root 682: {
1.1.1.14 root 683: inherited::ResetHard(poweron);
1.1.1.11 root 684:
1.1.1.14 root 685: if (poweron) {
686: // メディアの取り出し禁止を解除
687: PreventMediumRemoval(false);
688: }
1.1.1.11 root 689: }
690:
1.1.1.18 root 691: // バスリセットされた (SCSIBus から呼ばれる)
1.1.1.11 root 692: void
1.1.1.18 root 693: SCSIDisk::OnBusReset()
1.1.1.11 root 694: {
695: // メディアの取り出し禁止を解除
696: PreventMediumRemoval(false);
697:
1.1.1.18 root 698: inherited::OnBusReset();
1.1.1.11 root 699: }
700:
701: // バックエンドのディスクイメージを開く。
702: // 成功すれば true、失敗すれば false を返す。(ロードされれば true を返す
703: // のではないが、medium_loaded が同じ値になるので使いまわしている)
704: bool
705: SCSIDisk::LoadDisk(const std::string& pathname_)
706: {
1.1.1.15 root 707: bool unloaded;
1.1.1.11 root 708: off_t size;
709: bool read_only;
710:
711: // すでにあればクローズ (ここでは通知は行わない)
1.1.1.15 root 712: unloaded = UnloadDisk_internal();
1.1.1.11 root 713:
714: // 新しいイメージをオープン(する準備)
715: pathname = pathname_;
1.1.1.12 root 716: if (image.CreateHandler(pathname) == false) {
1.1.1.11 root 717: goto done;
718: }
719:
1.1.1.22 root 720: // 書き込みモードをここで確定。
721: //
722: // デバイス特性 イメージ write_ignore write_mode
723: // ------------ ---------- ------------ : ----------
724: // 書込み可 書込み可 no Writeable
725: // 〃 〃 yes WriteIgnore
726: // 〃 書込み不可 no WriteProtect (A)
727: // 〃 〃 yes WriteIgnore (B)
728: // 書込み不可 * * ReadOnly
729: //
730: // (A) は「ライトプロテクトした MO を入れた」状態。
731: // (B) は「書き換えたくないので書き込み権を落としてあるイメージファイルを
732: // あたかも書き込めるように見せたい」。(WI マークが出る)
1.1.1.18 root 733: if (IsWriteableDevice()) {
734: // 書き込み可能デバイスの場合
1.1.1.22 root 735: if (write_ignore) {
736: write_mode = RW::WriteIgnore;
737: } else {
1.1.1.23! root 738: int w_ok = image.IsWriteable();
! 739: if (w_ok < 0) {
! 740: goto done;
! 741: } else if (w_ok) {
1.1.1.18 root 742: write_mode = RW::Writeable;
1.1.1.22 root 743: } else {
744: write_mode = RW::WriteProtect;
1.1.1.18 root 745: }
746: }
747: } else {
748: // 読み込み専用デバイスの場合
749: write_mode = RW::ReadOnly;
750: }
751:
752: // ここでオープン
753: read_only = (GetWriteMode() != RW::Writeable);
754: if (image.Open(read_only, write_ignore) == false) {
755: goto done;
756: }
757:
758: size = image.GetSize();
759:
1.1.1.11 root 760: // セクタ単位になっていること
761: if (size % blocksize != 0) {
1.1.1.18 root 762: warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%u)",
1.1.1.11 root 763: pathname.c_str(), (uintmax_t)size, blocksize);
764: goto done;
765: }
766: // メディアごとの最大サイズを越えていないこと
767: switch (GetDevType()) {
768: case SCSI::DevType::HD:
769: // LBA (32bit) * 512 byte/sector
770: if (size >= 0x200'0000'0000ULL) {
771: warnx("%s: Bad image size(%ju): Too large",
772: pathname.c_str(), (uintmax_t)size);
773: goto done;
774: }
775: break;
776: case SCSI::DevType::CD:
777: // https://cdrfaq.org/faq07.html#S7-6
778: // 63min = 283,500 [sect] = 553.7MB CD-ROM = 635.9MB CD-DA
779: // 74min = 333,000 [sect] = 650.3MB CD-ROM = 746.9MB CD-DA
780: // 80min = 360,000 [sect] = 703.1MB CD-ROM = 807.4MB CD-DA
781: // 90min = 405,000 [sect] = 791.0MB CD-ROM = 908.4MB CD-DA
782: // 99min = 445,500 [sect] = 870.1MB CD-ROM = 999.3MB CD-DA
783: if (size >= 445500 * 2048) {
784: warnx("%s: Bad image size(%ju): Too Large",
785: pathname.c_str(), (uintmax_t)size);
786: goto done;
787: }
788: break;
789: case SCSI::DevType::MO:
790: // 128M は 25 [sect] * 10,000 [sect/track] * 512 [byte/sect]
791: // 230M は 25 [sect] * 17,853 [sect/track] * 512 [byte/sect]
792: // 540M は 25 [sect] * 41,660 [sect/track] * 512 [byte/sect]
793: // 640M は 17 [sect] * 18,256 [sect/track] * 2048 [byte/sect]
794: // 1.3G は 17 [sect] * 35,638 [sect/track] * 2048 [byte/sect]
795: // 2.3G は 17 [sect] * 62,538 [sect/track] * 2048 [byte/sect]
796: //
797: // 今の所 512 byte/sector フォーマットのみ対応しておく。
798: if (size >= 25 * 41660 * 512) {
799: warnx("%s: Bad image size(%ju): 640M or larger MO not supported",
800: pathname.c_str(), (uintmax_t)size);
801: goto done;
802: }
803: break;
804: default:
1.1.1.18 root 805: PANIC("corrupted devtype=%u", (uint)devtype);
1.1.1.11 root 806: }
807:
808: // 書き込み無視なら一応ログ出力
809: if (GetWriteMode() == RW::WriteIgnore) {
810: putmsg(0, "write is ignored");
811: }
812:
813: medium_loaded = true;
1.1.1.15 root 814: // リムーバブルデバイスでだけログを出す
815: if (IsRemovableDevice()) {
816: putlog(1, "Medium loaded");
1.1.1.11 root 817: }
1.1.1.15 root 818:
819: done:
1.1.1.11 root 820: if (medium_loaded == false) {
821: // ここで状態をクリアする
822: Clear();
823: }
1.1.1.15 root 824:
825: // 変化があれば通知
826: if (unloaded || medium_loaded) {
827: MediaChanged();
828: }
1.1.1.11 root 829: return medium_loaded;
830: }
831:
832: // バックエンドのディスクイメージを閉じる。
833: // オープンされてなければ何もしない。
834: void
835: SCSIDisk::UnloadDisk(bool force)
836: {
837: if (IsMediumRemovalAllowed() || force) {
838: if (UnloadDisk_internal()) {
839: // 実際に閉じたら通知
840: MediaChanged();
841: }
842: }
843: }
844:
845: // バックエンドのディスクイメージを閉じる (内部用)。
846: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
847: // ここでは通知は行わない。
848: bool
849: SCSIDisk::UnloadDisk_internal()
850: {
851: if ((bool)image == false) {
852: return false;
853: }
854:
855: Clear();
856:
857: // 取り出し禁止はされてても解除する
858: if (IsMediumRemovalPrevented()) {
859: // XXX PreventMediumRemoval() を呼ぶかどうするか
860: medium_removal_prevented = false;
861: }
862:
863: putlog(1, "Medium unloaded");
864: return true;
865: }
866:
867: // ディスク状態をクリアする
868: // (メディアを取り外した時など)
869: void
870: SCSIDisk::Clear()
871: {
872: medium_loaded = false;
1.1.1.12 root 873: image.Close();
1.1.1.11 root 874: pathname.clear();
1.1.1.21 root 875:
876: if (IsWriteableDevice()) {
877: if (write_ignore) {
878: write_mode = RW::WriteIgnore;
879: } else {
880: write_mode = RW::Writeable;
881: }
882: } else {
883: write_mode = RW::ReadOnly;
884: }
1.1.1.11 root 885: }
886:
887: // メディア状態変更を UI に通知する
888: void
889: SCSIDisk::MediaChanged() const
890: {
1.1.1.22 root 891: gMainApp.GetUIMessage()->Post(UIMessage::SCSI_MEDIA_CHANGE, GetMyID());
1.1.1.11 root 892: }
893:
894: // メディアを挿入する。
895: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
896: void
897: SCSIDisk::LoadDiskUI(const std::string& pathname_)
898: {
899: new_pathname = pathname_;
1.1.1.16 root 900: scheduler->SendMessage(MessageID::SCSIDEV_LOAD(GetMyID()));
1.1.1.11 root 901: }
902:
903: // メディアを排出する。
904: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
905: void
906: SCSIDisk::UnloadDiskUI(bool force)
907: {
1.1.1.16 root 908: scheduler->SendMessage(MessageID::SCSIDEV_UNLOAD(GetMyID()), force);
1.1.1.11 root 909: }
910:
1.1.1.14 root 911: // メディア挿入メッセージコールバック
1.1.1.11 root 912: void
1.1.1.17 root 913: SCSIDisk::LoadMessage(MessageID msgid, uint32 arg)
1.1.1.11 root 914: {
915: if (LoadDisk(new_pathname) == false) {
916: // 失敗したら UI に通知
1.1.1.22 root 917: gMainApp.GetUIMessage()->Post(UIMessage::SCSI_MEDIA_FAILED);
1.1.1.11 root 918: }
919: }
920:
1.1.1.14 root 921: // メディア排出メッセージコールバック
1.1.1.11 root 922: void
1.1.1.17 root 923: SCSIDisk::UnloadMessage(MessageID msgid, uint32 arg)
1.1.1.11 root 924: {
1.1.1.14 root 925: bool force = arg;
1.1.1.11 root 926: UnloadDisk(force);
927: }
928:
1.1.1.20 root 929: // SCSI コマンドのディスパッチャ。
930: // C++ の例外は呼び出し側で捕捉している。
1.1 root 931: void
1.1.1.11 root 932: SCSIDisk::DispatchCmd()
1.1 root 933: {
934: switch (cmdseq[0]) {
935: case SCSI::Command::Read6: // 0x08
1.1.1.11 root 936: case SCSI::Command::Read10: // 0x28
937: cmd.reset(new SCSICmdRead(this));
1.1 root 938: break;
939:
940: case SCSI::Command::Write6: // 0x0a
1.1.1.11 root 941: case SCSI::Command::Write10: // 0x2a
942: if (IsWriteableDevice()) {
943: cmd.reset(new SCSICmdWrite(this));
944: } else {
945: cmd.reset(new SCSICmdNotSupportedCommand(this));
946: }
1.1 root 947: break;
948:
1.1.1.4 root 949: case SCSI::Command::StartStopUnit: // 0x1b
950: cmd.reset(new SCSICmdStartStopUnit(this));
951: break;
952:
1.1.1.11 root 953: case SCSI::Command::PreventAllowMediumRemoval: // 0x1e
954: if (IsRemovableDevice() == false) {
955: // HD は取り出せないので、とりあえず。
956: // XXX 要調査
957: cmd.reset(new SCSICmdNotSupportedCommand(this));
958: return;
959: }
960: cmd.reset(new SCSICmdPreventAllowMediumRemoval(this));
961: break;
962:
1.1 root 963: case SCSI::Command::ReadCapacity: // 0x25
1.1.1.11 root 964: // DA デバイスでは Read Capacity (0x25)、
965: // CD-ROM デバイスでは Read CDROM Capacity (0x25) だが
966: // 今の所同じ動作なので区別せず処理する。
1.1 root 967: cmd.reset(new SCSICmdReadCapacity(this));
968: break;
969:
1.1.1.11 root 970: case SCSI::Command::SynchronizeCache: // 0x35
971: // このクラスは、物理ドライブにあるようなディスクとの間の
972: // キャッシュ機構は持っていないので、そのキャッシュをフラッシュ
973: // しろという指示に対しては、黙って成功するだけでいい気がする。
974: // このコマンドはオプションだが NetBSD カーネルが使う。
975: //
976: // CD-ROM でキャッシュの同期とは読み出し側のことだろうか?
977: // とりあえず黙って成功しておく。
978: cmd.reset(new SCSICmd(this));
1.1 root 979: break;
980:
1.1.1.11 root 981: case SCSI::Command::ReadTOC: // 0x43
982: if (devtype == SCSI::DevType::CD) {
983: cmd.reset(new SCSICmdReadTOC(this));
984: } else {
985: cmd.reset(new SCSICmdNotSupportedCommand(this));
986: }
987: break;
988:
989: case SCSI::Command::ReadFormatCapacities: // 0x23
990: // SCSI-2 規格でないので詳細不明。
991: // ディスク用か共通かも分からないけど、とりあえず
992: case SCSI::Command::ReadDiscInformation: // 0x51
993: // CD-ROM デバイスのオプションコマンドで
994: // CD-R ライター用(?)らしいので無視する。
995: cmd.reset(new SCSICmdNotSupportedCommand(this));
1.1.1.7 root 996: break;
997:
1.1 root 998: default:
999: // 見付からなければ基本クラス側に任せる
1000: inherited::DispatchCmd();
1001: break;
1002: }
1003: }
1004:
1.1.1.11 root 1005: // Inquiry データを返す
1006: bool
1.1.1.18 root 1007: SCSIDisk::Inquiry(std::vector<uint8>& buf, uint lun)
1.1.1.11 root 1008: {
1009: uint8 inqtype;
1010: const char *prodname;
1011:
1012: switch (devtype) {
1013: case SCSI::DevType::HD:
1014: inqtype = SCSI::InquiryDeviceType::DirectAccess;
1015: prodname = "SCSIHD";
1016: break;
1017: case SCSI::DevType::CD:
1018: inqtype = SCSI::InquiryDeviceType::CDROM;
1019: prodname = "SCSICD";
1020: break;
1021: case SCSI::DevType::MO:
1022: inqtype = SCSI::InquiryDeviceType::MO;
1023: prodname = "SCSIMO";
1024: break;
1025: default:
1.1.1.18 root 1026: PANIC("Unexpected devtype=%u", (uint)devtype);
1.1.1.11 root 1027: }
1028:
1029: buf[0] = inqtype;
1030: if (lun != 0) {
1031: buf[0] |= SCSI::InquiryQualifier::LU_NotPreseted;
1032: }
1033: buf[1] = 0;
1034: if (IsRemovableDevice()) {
1035: buf[1] |= 0x80;
1036: }
1037: buf[2] = 2; // SCSI-2
1038: buf[3] = 2; // Response Data Format = SCSI-2
1039: buf[4] = buf.size() - 4; // 追加データ長
1040:
1041: // 文字列はゼロ終端ではなく、余りは空白で埋める
1042: memset(&buf[8], ' ', 36 - 8);
1043: memcpy(&buf[8], "NONO", 4);
1044: memcpy(&buf[16], prodname, strlen(prodname));
1045: memcpy(&buf[32], "0", 1);
1046:
1047: return true;
1048: }
1049:
1050: // ModeSense コマンドのデバイス固有パラメータを返す
1051: uint8
1052: SCSIDisk::GetDeviceSpecificParam() const
1053: {
1054: uint8 param = 0;
1055:
1056: // DA, MO, CD のデバイス固有パラメータは以下のようになっている。
1057: // DPOFUA はキャッシュ制御フラグのサポート(%1)かどうかなので %0 でいい。
1058: //
1059: // 7 6 5 4 3 2 1 0
1060: // +---+---+---+------+---+---+---+---+
1061: // DA |WP | 0 |DPOFUA| 0 | WP: Write Protect
1062: // +---+---+---+------+---+---+---+---+
1063: //
1064: // +---+---+---+------+---+---+---+---+
1065: // MO |WP | 0 |DPOFUA| 0 |EBC| EBC: Enable Blank Check
1066: // +---+---+---+------+---+---+---+---+
1067: //
1068: // +---+---+---+------+---+---+---+---+
1069: // CD | 0 |DPOFUA| 0 |
1070: // +---+---+---+------+---+---+---+---+
1071:
1072: if (GetDevType() == SCSI::DevType::CD) {
1073: // CD には WP ビットの定義がない
1074: } else {
1075: // DA, MO は WP ビットの定義がある。
1076: if (IsMediumLoaded() && GetWriteMode() == RW::WriteProtect) {
1077: param |= 0x80;
1078: }
1079: }
1080:
1081: return param;
1082: }
1083:
1084: bool
1085: SCSIDisk::Read(std::vector<uint8>& buf, uint64 start)
1086: {
1087: if (__predict_false((bool)image == false)) {
1088: return false;
1089: }
1.1.1.12 root 1090: return image.Read(buf.data(), start, buf.size());
1.1.1.11 root 1091: }
1092:
1093: bool
1094: SCSIDisk::Write(const std::vector<uint8>& buf, uint64 start)
1095: {
1096: if (__predict_false((bool)image == false)) {
1097: return false;
1098: }
1099:
1.1.1.12 root 1100: if (GetWriteMode() >= RW::WriteProtect) {
1.1.1.11 root 1101: // 書き込み不可
1102: return false;
1103: }
1.1.1.12 root 1104: return image.Write(buf.data(), start, buf.size());
1.1.1.11 root 1105: }
1106:
1107: off_t
1108: SCSIDisk::GetSize() const
1109: {
1110: if (__predict_false((bool)image == false)) {
1111: return 0;
1112: }
1.1.1.12 root 1113: return image.GetSize();
1.1.1.11 root 1114: }
1115:
1.1.1.2 root 1116: // 指定の場所を自由に読み出す
1117: // (エミュレータ特権アクセス)
1118: bool
1.1.1.11 root 1119: SCSIDisk::Peek(void *buf, uint64 start, uint32 len) const
1.1.1.2 root 1120: {
1.1.1.11 root 1121: if (__predict_false((bool)image == false)) {
1.1.1.2 root 1122: return false;
1123: }
1.1.1.12 root 1124: return image.Read(buf, start, len);
1.1.1.2 root 1125: }
1126:
1.1.1.11 root 1127: // メディアの取り出し可否(取り出し禁止)を設定する
1128: void
1129: SCSIDisk::PreventMediumRemoval(bool val)
1130: {
1131: // 状態が変わったところでログを表示
1132: if (medium_removal_prevented == false && val == true) {
1133: putlog(1, "Medium removal prevented");
1134: } else if (medium_removal_prevented == true && val == false) {
1135: putlog(1, "Medium removal allowed");
1136: }
1137: medium_removal_prevented = val;
1138: }
1.1.1.14 root 1139:
1140: // メディアの書き込みモードを文字列で返す
1141: const char *
1142: SCSIDisk::GetWriteModeStr() const
1143: {
1144: switch (write_mode) {
1145: case RW::ReadOnly: return "ReadOnly";
1146: case RW::WriteProtect: return "WriteProtected";
1147: case RW::Writeable: return "Writeable";
1148: case RW::WriteIgnore: return "WriteIgnore";
1149: default:
1.1.1.16 root 1150: PANIC("corrupted write_mode=%d", (int)write_mode);
1.1.1.14 root 1151: }
1152: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.