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