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