|
|
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.22! root 65: SetName(string_format("(SCSIHost.%s)", domain_->GetConfigName()));
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);
571:
572: // コンストラクタの初期化子では devtypename を組み込みにくいので、
573: // コンストラクタ本文でログ名などを設定する。その時の作法に
574: // 従っているのですこし面倒な手法が必要。
575: // lib/object.h 参照。
1.1.1.18 root 576: SetName(string_format("SCSI%s%u", devtypename, id));
1.1.1.11 root 577: ClearAlias();
1.1.1.18 root 578: AddAlias(string_format("SCSI%s%u", devtypename, id));
579: AddAlias(string_format("SCSI%u", id));
580: AddAlias(string_format("%s%u", devtypename, id));
1.1.1.11 root 581:
582: switch (devtype) {
583: case SCSI::DevType::HD:
584: removable_device = false;
585: writeable_device = true;
586: blocksize = 512;
587: break;
588: case SCSI::DevType::CD:
589: removable_device = true;
590: writeable_device = false;
591: blocksize = 2048;
592: break;
593: case SCSI::DevType::MO:
594: removable_device = true;
595: writeable_device = true;
596: blocksize = 512;
597: break;
598: default:
1.1.1.18 root 599: PANIC("corrupted devtype=%u", (uint)devtype);
1.1.1.11 root 600: }
1.1.1.10 root 601:
1.1 root 602: }
603:
604: // デストラクタ
1.1.1.11 root 605: SCSIDisk::~SCSIDisk()
1.1 root 606: {
607: }
608:
609: // 初期化
610: bool
1.1.1.11 root 611: SCSIDisk::Init()
1.1 root 612: {
1.1.1.18 root 613: const std::string keybody = string_format("%s-id%u-",
1.1.1.20 root 614: domain->GetConfigName(), myid);
1.1 root 615: const std::string imgkey = keybody + "image";
1.1.1.11 root 616: const std::string wikey = keybody + "writeignore";
1.1.1.5 root 617: const std::string stkey = keybody + "seektime";
1.1 root 618:
1.1.1.11 root 619: // デバイス種別(v[0])とパス(v[1])に分解。
620: // デバイス種別はすでにチェックしてあるのでここでは不要。
1.1.1.3 root 621: const ConfigItem& itemimg = gConfig->Find(imgkey);
1.1.1.11 root 622: const std::string& imageval = itemimg.AsString();
623: auto v = string_split(imageval, ',', 2);
624: std::string filename;
625: if (v.size() > 1) {
626: filename = string_trim(v[1]);
627: }
628:
629: // 固定デバイス(HD)ならファイル名は省略不可
630: if (!IsRemovableDevice() && filename.empty()) {
1.1.1.2 root 631: itemimg.Err("imagefile not specified");
1.1 root 632: return false;
633: }
634:
1.1.1.14 root 635: // リムーバブルデバイスのみ UI からの通知を受け取るイベントを用意
636: if (IsRemovableDevice()) {
1.1.1.16 root 637: scheduler->ConnectMessage(MessageID::SCSIDEV_LOAD(myid), this,
1.1.1.17 root 638: ToMessageCallback(&SCSIDisk::LoadMessage));
1.1.1.16 root 639: scheduler->ConnectMessage(MessageID::SCSIDEV_UNLOAD(myid), this,
1.1.1.17 root 640: ToMessageCallback(&SCSIDisk::UnloadMessage));
1.1.1.14 root 641: }
642:
1.1.1.11 root 643: // オープン前に writeignore をチェック。
644: // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
645: // 行わないといけない。うーん。
1.1.1.20 root 646: write_ignore = gConfig->Find(wikey).AsInt();
1.1 root 647:
1.1.1.21 root 648: // writeignore が決まったので状態を初期化。
649: Clear();
650:
1.1.1.11 root 651: // 設定時点でファイルが指定されていればオープン
652: if (filename.empty() == false) {
1.1.1.17 root 653: std::string path = gMainApp.NormalizePath(filename);
1.1.1.11 root 654: if (LoadDisk(path) == false) {
655: return false;
656: }
1.1 root 657: }
658:
1.1.1.20 root 659: // 平均シークタイム。設定は [msec]、変数は [nsec]。
660: // virtio-scsi にはない。
661: if (strncmp(stkey.c_str(), "virtio", 6) != 0) {
662: seektime = gConfig->Find(stkey).AsInt();
663: seektime *= 1_msec;
664: }
1.1.1.5 root 665:
1.1 root 666: return true;
667: }
668:
1.1.1.11 root 669: // 電源オン
1.1.1.14 root 670: void
671: SCSIDisk::ResetHard(bool poweron)
1.1.1.11 root 672: {
1.1.1.14 root 673: inherited::ResetHard(poweron);
1.1.1.11 root 674:
1.1.1.14 root 675: if (poweron) {
676: // メディアの取り出し禁止を解除
677: PreventMediumRemoval(false);
678: }
1.1.1.11 root 679: }
680:
1.1.1.18 root 681: // バスリセットされた (SCSIBus から呼ばれる)
1.1.1.11 root 682: void
1.1.1.18 root 683: SCSIDisk::OnBusReset()
1.1.1.11 root 684: {
685: // メディアの取り出し禁止を解除
686: PreventMediumRemoval(false);
687:
1.1.1.18 root 688: inherited::OnBusReset();
1.1.1.11 root 689: }
690:
691: // バックエンドのディスクイメージを開く。
692: // 成功すれば true、失敗すれば false を返す。(ロードされれば true を返す
693: // のではないが、medium_loaded が同じ値になるので使いまわしている)
694: bool
695: SCSIDisk::LoadDisk(const std::string& pathname_)
696: {
1.1.1.15 root 697: bool unloaded;
1.1.1.11 root 698: off_t size;
1.1.1.18 root 699: int w_ok;
1.1.1.11 root 700: bool read_only;
701:
702: // すでにあればクローズ (ここでは通知は行わない)
1.1.1.15 root 703: unloaded = UnloadDisk_internal();
1.1.1.11 root 704:
705: // 新しいイメージをオープン(する準備)
706: pathname = pathname_;
1.1.1.12 root 707: if (image.CreateHandler(pathname) == false) {
1.1.1.11 root 708: goto done;
709: }
710:
1.1.1.18 root 711: w_ok = image.IsWriteable();
712: if (w_ok < 0) {
1.1.1.11 root 713: goto done;
714: }
715:
1.1.1.22! root 716: // 書き込みモードをここで確定。
! 717: //
! 718: // デバイス特性 イメージ write_ignore write_mode
! 719: // ------------ ---------- ------------ : ----------
! 720: // 書込み可 書込み可 no Writeable
! 721: // 〃 〃 yes WriteIgnore
! 722: // 〃 書込み不可 no WriteProtect (A)
! 723: // 〃 〃 yes WriteIgnore (B)
! 724: // 書込み不可 * * ReadOnly
! 725: //
! 726: // (A) は「ライトプロテクトした MO を入れた」状態。
! 727: // (B) は「書き換えたくないので書き込み権を落としてあるイメージファイルを
! 728: // あたかも書き込めるように見せたい」。(WI マークが出る)
1.1.1.18 root 729: if (IsWriteableDevice()) {
730: // 書き込み可能デバイスの場合
1.1.1.22! root 731: if (write_ignore) {
! 732: write_mode = RW::WriteIgnore;
! 733: } else {
! 734: if (w_ok) {
1.1.1.18 root 735: write_mode = RW::Writeable;
1.1.1.22! root 736: } else {
! 737: write_mode = RW::WriteProtect;
1.1.1.18 root 738: }
739: }
740: } else {
741: // 読み込み専用デバイスの場合
742: write_mode = RW::ReadOnly;
743: }
744:
745: // ここでオープン
746: read_only = (GetWriteMode() != RW::Writeable);
747: if (image.Open(read_only, write_ignore) == false) {
748: goto done;
749: }
750:
751: size = image.GetSize();
752:
1.1.1.11 root 753: // セクタ単位になっていること
754: if (size % blocksize != 0) {
1.1.1.18 root 755: warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%u)",
1.1.1.11 root 756: pathname.c_str(), (uintmax_t)size, blocksize);
757: goto done;
758: }
759: // メディアごとの最大サイズを越えていないこと
760: switch (GetDevType()) {
761: case SCSI::DevType::HD:
762: // LBA (32bit) * 512 byte/sector
763: if (size >= 0x200'0000'0000ULL) {
764: warnx("%s: Bad image size(%ju): Too large",
765: pathname.c_str(), (uintmax_t)size);
766: goto done;
767: }
768: break;
769: case SCSI::DevType::CD:
770: // https://cdrfaq.org/faq07.html#S7-6
771: // 63min = 283,500 [sect] = 553.7MB CD-ROM = 635.9MB CD-DA
772: // 74min = 333,000 [sect] = 650.3MB CD-ROM = 746.9MB CD-DA
773: // 80min = 360,000 [sect] = 703.1MB CD-ROM = 807.4MB CD-DA
774: // 90min = 405,000 [sect] = 791.0MB CD-ROM = 908.4MB CD-DA
775: // 99min = 445,500 [sect] = 870.1MB CD-ROM = 999.3MB CD-DA
776: if (size >= 445500 * 2048) {
777: warnx("%s: Bad image size(%ju): Too Large",
778: pathname.c_str(), (uintmax_t)size);
779: goto done;
780: }
781: break;
782: case SCSI::DevType::MO:
783: // 128M は 25 [sect] * 10,000 [sect/track] * 512 [byte/sect]
784: // 230M は 25 [sect] * 17,853 [sect/track] * 512 [byte/sect]
785: // 540M は 25 [sect] * 41,660 [sect/track] * 512 [byte/sect]
786: // 640M は 17 [sect] * 18,256 [sect/track] * 2048 [byte/sect]
787: // 1.3G は 17 [sect] * 35,638 [sect/track] * 2048 [byte/sect]
788: // 2.3G は 17 [sect] * 62,538 [sect/track] * 2048 [byte/sect]
789: //
790: // 今の所 512 byte/sector フォーマットのみ対応しておく。
791: if (size >= 25 * 41660 * 512) {
792: warnx("%s: Bad image size(%ju): 640M or larger MO not supported",
793: pathname.c_str(), (uintmax_t)size);
794: goto done;
795: }
796: break;
797: default:
1.1.1.18 root 798: PANIC("corrupted devtype=%u", (uint)devtype);
1.1.1.11 root 799: }
800:
801: // 書き込み無視なら一応ログ出力
802: if (GetWriteMode() == RW::WriteIgnore) {
803: putmsg(0, "write is ignored");
804: }
805:
806: medium_loaded = true;
1.1.1.15 root 807: // リムーバブルデバイスでだけログを出す
808: if (IsRemovableDevice()) {
809: putlog(1, "Medium loaded");
1.1.1.11 root 810: }
1.1.1.15 root 811:
812: done:
1.1.1.11 root 813: if (medium_loaded == false) {
814: // ここで状態をクリアする
815: Clear();
816: }
1.1.1.15 root 817:
818: // 変化があれば通知
819: if (unloaded || medium_loaded) {
820: MediaChanged();
821: }
1.1.1.11 root 822: return medium_loaded;
823: }
824:
825: // バックエンドのディスクイメージを閉じる。
826: // オープンされてなければ何もしない。
827: void
828: SCSIDisk::UnloadDisk(bool force)
829: {
830: if (IsMediumRemovalAllowed() || force) {
831: if (UnloadDisk_internal()) {
832: // 実際に閉じたら通知
833: MediaChanged();
834: }
835: }
836: }
837:
838: // バックエンドのディスクイメージを閉じる (内部用)。
839: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
840: // ここでは通知は行わない。
841: bool
842: SCSIDisk::UnloadDisk_internal()
843: {
844: if ((bool)image == false) {
845: return false;
846: }
847:
848: Clear();
849:
850: // 取り出し禁止はされてても解除する
851: if (IsMediumRemovalPrevented()) {
852: // XXX PreventMediumRemoval() を呼ぶかどうするか
853: medium_removal_prevented = false;
854: }
855:
856: putlog(1, "Medium unloaded");
857: return true;
858: }
859:
860: // ディスク状態をクリアする
861: // (メディアを取り外した時など)
862: void
863: SCSIDisk::Clear()
864: {
865: medium_loaded = false;
1.1.1.12 root 866: image.Close();
1.1.1.11 root 867: pathname.clear();
1.1.1.21 root 868:
869: if (IsWriteableDevice()) {
870: if (write_ignore) {
871: write_mode = RW::WriteIgnore;
872: } else {
873: write_mode = RW::Writeable;
874: }
875: } else {
876: write_mode = RW::ReadOnly;
877: }
1.1.1.11 root 878: }
879:
880: // メディア状態変更を UI に通知する
881: void
882: SCSIDisk::MediaChanged() const
883: {
1.1.1.22! root 884: gMainApp.GetUIMessage()->Post(UIMessage::SCSI_MEDIA_CHANGE, GetMyID());
1.1.1.11 root 885: }
886:
887: // メディアを挿入する。
888: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
889: void
890: SCSIDisk::LoadDiskUI(const std::string& pathname_)
891: {
892: new_pathname = pathname_;
1.1.1.16 root 893: scheduler->SendMessage(MessageID::SCSIDEV_LOAD(GetMyID()));
1.1.1.11 root 894: }
895:
896: // メディアを排出する。
897: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
898: void
899: SCSIDisk::UnloadDiskUI(bool force)
900: {
1.1.1.16 root 901: scheduler->SendMessage(MessageID::SCSIDEV_UNLOAD(GetMyID()), force);
1.1.1.11 root 902: }
903:
1.1.1.14 root 904: // メディア挿入メッセージコールバック
1.1.1.11 root 905: void
1.1.1.17 root 906: SCSIDisk::LoadMessage(MessageID msgid, uint32 arg)
1.1.1.11 root 907: {
908: if (LoadDisk(new_pathname) == false) {
909: // 失敗したら UI に通知
1.1.1.22! root 910: gMainApp.GetUIMessage()->Post(UIMessage::SCSI_MEDIA_FAILED);
1.1.1.11 root 911: }
912: }
913:
1.1.1.14 root 914: // メディア排出メッセージコールバック
1.1.1.11 root 915: void
1.1.1.17 root 916: SCSIDisk::UnloadMessage(MessageID msgid, uint32 arg)
1.1.1.11 root 917: {
1.1.1.14 root 918: bool force = arg;
1.1.1.11 root 919: UnloadDisk(force);
920: }
921:
1.1.1.20 root 922: // SCSI コマンドのディスパッチャ。
923: // C++ の例外は呼び出し側で捕捉している。
1.1 root 924: void
1.1.1.11 root 925: SCSIDisk::DispatchCmd()
1.1 root 926: {
927: switch (cmdseq[0]) {
928: case SCSI::Command::Read6: // 0x08
1.1.1.11 root 929: case SCSI::Command::Read10: // 0x28
930: cmd.reset(new SCSICmdRead(this));
1.1 root 931: break;
932:
933: case SCSI::Command::Write6: // 0x0a
1.1.1.11 root 934: case SCSI::Command::Write10: // 0x2a
935: if (IsWriteableDevice()) {
936: cmd.reset(new SCSICmdWrite(this));
937: } else {
938: cmd.reset(new SCSICmdNotSupportedCommand(this));
939: }
1.1 root 940: break;
941:
1.1.1.4 root 942: case SCSI::Command::StartStopUnit: // 0x1b
943: cmd.reset(new SCSICmdStartStopUnit(this));
944: break;
945:
1.1.1.11 root 946: case SCSI::Command::PreventAllowMediumRemoval: // 0x1e
947: if (IsRemovableDevice() == false) {
948: // HD は取り出せないので、とりあえず。
949: // XXX 要調査
950: cmd.reset(new SCSICmdNotSupportedCommand(this));
951: return;
952: }
953: cmd.reset(new SCSICmdPreventAllowMediumRemoval(this));
954: break;
955:
1.1 root 956: case SCSI::Command::ReadCapacity: // 0x25
1.1.1.11 root 957: // DA デバイスでは Read Capacity (0x25)、
958: // CD-ROM デバイスでは Read CDROM Capacity (0x25) だが
959: // 今の所同じ動作なので区別せず処理する。
1.1 root 960: cmd.reset(new SCSICmdReadCapacity(this));
961: break;
962:
1.1.1.11 root 963: case SCSI::Command::SynchronizeCache: // 0x35
964: // このクラスは、物理ドライブにあるようなディスクとの間の
965: // キャッシュ機構は持っていないので、そのキャッシュをフラッシュ
966: // しろという指示に対しては、黙って成功するだけでいい気がする。
967: // このコマンドはオプションだが NetBSD カーネルが使う。
968: //
969: // CD-ROM でキャッシュの同期とは読み出し側のことだろうか?
970: // とりあえず黙って成功しておく。
971: cmd.reset(new SCSICmd(this));
1.1 root 972: break;
973:
1.1.1.11 root 974: case SCSI::Command::ReadTOC: // 0x43
975: if (devtype == SCSI::DevType::CD) {
976: cmd.reset(new SCSICmdReadTOC(this));
977: } else {
978: cmd.reset(new SCSICmdNotSupportedCommand(this));
979: }
980: break;
981:
982: case SCSI::Command::ReadFormatCapacities: // 0x23
983: // SCSI-2 規格でないので詳細不明。
984: // ディスク用か共通かも分からないけど、とりあえず
985: case SCSI::Command::ReadDiscInformation: // 0x51
986: // CD-ROM デバイスのオプションコマンドで
987: // CD-R ライター用(?)らしいので無視する。
988: cmd.reset(new SCSICmdNotSupportedCommand(this));
1.1.1.7 root 989: break;
990:
1.1 root 991: default:
992: // 見付からなければ基本クラス側に任せる
993: inherited::DispatchCmd();
994: break;
995: }
996: }
997:
1.1.1.11 root 998: // Inquiry データを返す
999: bool
1.1.1.18 root 1000: SCSIDisk::Inquiry(std::vector<uint8>& buf, uint lun)
1.1.1.11 root 1001: {
1002: uint8 inqtype;
1003: const char *prodname;
1004:
1005: switch (devtype) {
1006: case SCSI::DevType::HD:
1007: inqtype = SCSI::InquiryDeviceType::DirectAccess;
1008: prodname = "SCSIHD";
1009: break;
1010: case SCSI::DevType::CD:
1011: inqtype = SCSI::InquiryDeviceType::CDROM;
1012: prodname = "SCSICD";
1013: break;
1014: case SCSI::DevType::MO:
1015: inqtype = SCSI::InquiryDeviceType::MO;
1016: prodname = "SCSIMO";
1017: break;
1018: default:
1.1.1.18 root 1019: PANIC("Unexpected devtype=%u", (uint)devtype);
1.1.1.11 root 1020: }
1021:
1022: buf[0] = inqtype;
1023: if (lun != 0) {
1024: buf[0] |= SCSI::InquiryQualifier::LU_NotPreseted;
1025: }
1026: buf[1] = 0;
1027: if (IsRemovableDevice()) {
1028: buf[1] |= 0x80;
1029: }
1030: buf[2] = 2; // SCSI-2
1031: buf[3] = 2; // Response Data Format = SCSI-2
1032: buf[4] = buf.size() - 4; // 追加データ長
1033:
1034: // 文字列はゼロ終端ではなく、余りは空白で埋める
1035: memset(&buf[8], ' ', 36 - 8);
1036: memcpy(&buf[8], "NONO", 4);
1037: memcpy(&buf[16], prodname, strlen(prodname));
1038: memcpy(&buf[32], "0", 1);
1039:
1040: return true;
1041: }
1042:
1043: // ModeSense コマンドのデバイス固有パラメータを返す
1044: uint8
1045: SCSIDisk::GetDeviceSpecificParam() const
1046: {
1047: uint8 param = 0;
1048:
1049: // DA, MO, CD のデバイス固有パラメータは以下のようになっている。
1050: // DPOFUA はキャッシュ制御フラグのサポート(%1)かどうかなので %0 でいい。
1051: //
1052: // 7 6 5 4 3 2 1 0
1053: // +---+---+---+------+---+---+---+---+
1054: // DA |WP | 0 |DPOFUA| 0 | WP: Write Protect
1055: // +---+---+---+------+---+---+---+---+
1056: //
1057: // +---+---+---+------+---+---+---+---+
1058: // MO |WP | 0 |DPOFUA| 0 |EBC| EBC: Enable Blank Check
1059: // +---+---+---+------+---+---+---+---+
1060: //
1061: // +---+---+---+------+---+---+---+---+
1062: // CD | 0 |DPOFUA| 0 |
1063: // +---+---+---+------+---+---+---+---+
1064:
1065: if (GetDevType() == SCSI::DevType::CD) {
1066: // CD には WP ビットの定義がない
1067: } else {
1068: // DA, MO は WP ビットの定義がある。
1069: if (IsMediumLoaded() && GetWriteMode() == RW::WriteProtect) {
1070: param |= 0x80;
1071: }
1072: }
1073:
1074: return param;
1075: }
1076:
1077: bool
1078: SCSIDisk::Read(std::vector<uint8>& buf, uint64 start)
1079: {
1080: if (__predict_false((bool)image == false)) {
1081: return false;
1082: }
1.1.1.12 root 1083: return image.Read(buf.data(), start, buf.size());
1.1.1.11 root 1084: }
1085:
1086: bool
1087: SCSIDisk::Write(const std::vector<uint8>& buf, uint64 start)
1088: {
1089: if (__predict_false((bool)image == false)) {
1090: return false;
1091: }
1092:
1.1.1.12 root 1093: if (GetWriteMode() >= RW::WriteProtect) {
1.1.1.11 root 1094: // 書き込み不可
1095: return false;
1096: }
1.1.1.12 root 1097: return image.Write(buf.data(), start, buf.size());
1.1.1.11 root 1098: }
1099:
1100: off_t
1101: SCSIDisk::GetSize() const
1102: {
1103: if (__predict_false((bool)image == false)) {
1104: return 0;
1105: }
1.1.1.12 root 1106: return image.GetSize();
1.1.1.11 root 1107: }
1108:
1.1.1.2 root 1109: // 指定の場所を自由に読み出す
1110: // (エミュレータ特権アクセス)
1111: bool
1.1.1.11 root 1112: SCSIDisk::Peek(void *buf, uint64 start, uint32 len) const
1.1.1.2 root 1113: {
1.1.1.11 root 1114: if (__predict_false((bool)image == false)) {
1.1.1.2 root 1115: return false;
1116: }
1.1.1.12 root 1117: return image.Read(buf, start, len);
1.1.1.2 root 1118: }
1119:
1.1.1.11 root 1120: // メディアの取り出し可否(取り出し禁止)を設定する
1121: void
1122: SCSIDisk::PreventMediumRemoval(bool val)
1123: {
1124: // 状態が変わったところでログを表示
1125: if (medium_removal_prevented == false && val == true) {
1126: putlog(1, "Medium removal prevented");
1127: } else if (medium_removal_prevented == true && val == false) {
1128: putlog(1, "Medium removal allowed");
1129: }
1130: medium_removal_prevented = val;
1131: }
1.1.1.14 root 1132:
1133: // メディアの書き込みモードを文字列で返す
1134: const char *
1135: SCSIDisk::GetWriteModeStr() const
1136: {
1137: switch (write_mode) {
1138: case RW::ReadOnly: return "ReadOnly";
1139: case RW::WriteProtect: return "WriteProtected";
1140: case RW::Writeable: return "Writeable";
1141: case RW::WriteIgnore: return "WriteIgnore";
1142: default:
1.1.1.16 root 1143: PANIC("corrupted write_mode=%d", (int)write_mode);
1.1.1.14 root 1144: }
1145: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.