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