|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2024 nono project ! 4: // Licensed under nono-license.txt ! 5: // ! 6: ! 7: // ! 8: // VirtIO SCSI デバイス ! 9: // ! 10: ! 11: #include "virtio_scsi.h" ! 12: #include "virtio_def.h" ! 13: #include "mainbus.h" ! 14: #include "memorystream.h" ! 15: #include "monitor.h" ! 16: #include "scsidev.h" ! 17: #include "scsidomain.h" ! 18: ! 19: // デバイス構成レイアウト ! 20: class VirtIOSCSIConfigWriter ! 21: { ! 22: public: ! 23: le32 num_queues {}; ! 24: le32 seg_max {}; ! 25: le32 max_sectors {}; ! 26: le32 cmd_per_lun {}; ! 27: le32 event_info_size {}; ! 28: le32 sense_size {}; ! 29: le32 cdb_size {}; ! 30: le16 max_channel {}; ! 31: le16 max_target {}; ! 32: le32 max_lun {}; ! 33: ! 34: public: ! 35: void WriteTo(uint8 *dst) const; ! 36: }; ! 37: ! 38: // コンストラクタ ! 39: VirtIOSCSIDevice::VirtIOSCSIDevice(uint slot_) ! 40: : inherited(OBJ_VIRTIO_SCSI, slot_) ! 41: { ! 42: // 短縮形 ! 43: AddAlias("vscsi"); ! 44: ! 45: device_id = VirtIO::DEVICE_ID_SCSI; ! 46: vqueues.emplace_back(this, 0, "ControlQ", 8); ! 47: vqueues.emplace_back(this, 1, "EventQ", 8); ! 48: vqueues.emplace_back(this, 2, "RequestQ", 8); ! 49: ! 50: // 割り込み名 ! 51: strlcpy(intrname, "VIOSCSI", sizeof(intrname)); ! 52: // 完了通知メッセージ ! 53: msgid = MessageID::VIRTIO_SCSI_DONE; ! 54: ! 55: // モニタの行数。 ! 56: int vqlines = 0; ! 57: for (const auto& q : vqueues) { ! 58: vqlines += 7 + q.num_max; ! 59: } ! 60: ! 61: monitor = gMonitorManager->Regist(ID_MONITOR_VIRTIO_SCSI, this); ! 62: monitor->func = ToMonitorCallback(&VirtIOSCSIDevice::MonitorUpdate); ! 63: monitor->SetSize(MONITOR_WIDTH, 3 + vqlines); ! 64: } ! 65: ! 66: // デストラクタ ! 67: VirtIOSCSIDevice::~VirtIOSCSIDevice() ! 68: { ! 69: } ! 70: ! 71: // 動的なコンストラクション ! 72: bool ! 73: VirtIOSCSIDevice::Create() ! 74: { ! 75: if (inherited::Create() == false) { ! 76: return false; ! 77: } ! 78: ! 79: try { ! 80: domain.reset(new SCSIDomain(this, "virtio-scsi")); ! 81: } catch (...) { } ! 82: if ((bool)domain == false) { ! 83: warnx("Failed to initialize SCSIDomain at %s", __method__); ! 84: return false; ! 85: } ! 86: ! 87: return true; ! 88: } ! 89: ! 90: bool ! 91: VirtIOSCSIDevice::Init() ! 92: { ! 93: if (inherited::Init() == false) { ! 94: return false; ! 95: } ! 96: ! 97: host = domain->GetInitiator(); ! 98: ! 99: return true; ! 100: } ! 101: ! 102: void ! 103: VirtIOSCSIDevice::ResetHard(bool poweron) ! 104: { ! 105: // sense_size, cdb_size はリセットで元に戻る (5.6.4)。 ! 106: // XXX 書き換えに対応? ! 107: sense_size = 96; ! 108: cdb_size = 32; ! 109: ! 110: // DEVICE_FEATURES と構成レイアウトを用意。 ! 111: VirtIOSCSIConfigWriter cfg; ! 112: cfg.num_queues = 1; ! 113: cfg.seg_max = 6; // XXX ? ! 114: cfg.max_sectors = 256; ! 115: cfg.cmd_per_lun = 1; ! 116: cfg.sense_size = sense_size; ! 117: cfg.cdb_size = cdb_size; ! 118: cfg.max_channel = 0; ! 119: cfg.max_target = 7; ! 120: cfg.max_lun = 0; ! 121: cfg.WriteTo(&device_config[0]); ! 122: } ! 123: ! 124: void ! 125: VirtIOSCSIDevice::MonitorUpdate(Monitor *, TextScreen& screen) ! 126: { ! 127: int y = 0; ! 128: ! 129: screen.Clear(); ! 130: ! 131: y = MonitorUpdateDev(screen, y); ! 132: ! 133: y++; ! 134: for (const auto& q : vqueues) { ! 135: y = MonitorUpdateVirtQueue(screen, y, q); ! 136: y++; ! 137: y = MonitorUpdateVirtQDesc(screen, y, q); ! 138: y++; ! 139: } ! 140: } ! 141: ! 142: // ディスクリプタを一つ処理する。 ! 143: void ! 144: VirtIOSCSIDevice::ProcessDesc(VirtIOReq& req) ! 145: { ! 146: VirtQueue *q = req.q; ! 147: ! 148: switch (q->idx) { ! 149: case 0: ! 150: ProcessDescControl(req); ! 151: break; ! 152: case 1: ! 153: ProcessDescEvent(req); ! 154: break; ! 155: case 2: ! 156: ProcessDescRequest(req); ! 157: break; ! 158: default: ! 159: assert(q->idx < 3); ! 160: } ! 161: } ! 162: ! 163: // Control キューのディスクリプタを一つ処理する。 ! 164: void ! 165: VirtIOSCSIDevice::ProcessDescControl(VirtIOReq& req) ! 166: { ! 167: putlog(0, "Control Queue (NOT IMPLEMENTED)"); ! 168: } ! 169: ! 170: // Event キューのディスクリプタを一つ処理する。 ! 171: void ! 172: VirtIOSCSIDevice::ProcessDescEvent(VirtIOReq& req) ! 173: { ! 174: putlog(0, "Event Queue (NOT IMPLEMENTED)"); ! 175: } ! 176: ! 177: // Request キューのディスクリプタを一つ処理する。 ! 178: void ! 179: VirtIOSCSIDevice::ProcessDescRequest(VirtIOReq& req) ! 180: { ! 181: // 8バイトの lun はこういう構成。 ! 182: // bus は 1 から数え始める。 ! 183: // ID が SCSI ID に相当するところ (0-255)。 ! 184: // LUN は 0-16387。0x4000 が立っているのは何? ! 185: // 下位 32 ビットは論理ユニット固有のアドレスフィールド (0)。 ! 186: // 複数バイトのフィールドは BE 順にメモリに置かれている。 ! 187: // ! 188: // +0 +1 +2 +3 +4 +5 +6 +7 ! 189: // +-----+-----+-----+-----+-----+-----+-----+-----+ ! 190: // | bus | ID | LUN | 0 | ! 191: // +-----+-----+-----+-----+-----+-----+-----+-----+ ! 192: uint32 bus = ReqReadU8(req); ! 193: uint32 id = ReqReadU8(req); ! 194: uint32 lun = ReqReadBE16(req); ! 195: uint32 addr = ReqReadBE32(req); ! 196: putlog(2, "Bus=$%02x ID=$%02x LUN=$%04x Addr=$%08x", bus, id, lun, addr); ! 197: ! 198: // 仕様ではこっちが id という名前だが紛らわしいし使わないので tag にする。 ! 199: // QoS Tag みたいなもの? ! 200: uint64 tag; ! 201: ReqReadLE64(req, &tag); ! 202: putlog(3, "tagID=%08x'%08x", (uint32)(tag >> 32), (uint32)tag); ! 203: ! 204: uint32 task_attr = ReqReadU8(req); ! 205: uint32 prio = ReqReadU8(req); ! 206: uint32 crn = ReqReadU8(req); ! 207: putlog(3, "task_attr=%02x prio=%02x crn=%02x", ! 208: task_attr, prio, crn); ! 209: ! 210: // コマンド(CDB) を取り出す。 ! 211: std::vector<uint8> cdb(cdb_size); ! 212: for (int i = 0; i < cdb_size; i++) { ! 213: cdb[i] = ReqReadU8(req); ! 214: } ! 215: if (loglevel >= 2 ! 216: #if defined(NO_READWRITE_LOG) ! 217: && (cdb[0] != SCSI::Command::Read6 && ! 218: cdb[0] != SCSI::Command::Read10 && ! 219: cdb[0] != SCSI::Command::Write6 && ! 220: cdb[0] != SCSI::Command::Write10) ! 221: #endif ! 222: ) { ! 223: std::string cmdbuf; ! 224: const char *name = SCSI::GetCommandName(cdb[0]); ! 225: cmdbuf += string_format("Command \"%s\"", name ?: "?"); ! 226: for (const auto& v : cdb) { ! 227: cmdbuf += string_format(" %02x", v); ! 228: } ! 229: putlogn("%s", cmdbuf.c_str()); ! 230: } ! 231: ! 232: // 読み込み側は、ここから後ろがあれば dataout[]。 ! 233: uint32 dataout_len = req.rremain(); ! 234: // datain[] も計算。12 は .sense_len から .response までのバイト数。 ! 235: uint32 datain_len = req.wlen - 12 - sense_size; ! 236: ! 237: // ターゲット ID が存在するか。 ! 238: auto *target = domain->GetTarget(id); ! 239: if (target == NULL) { ! 240: ProcessDescRequestError(req, VIRTIO_SCSI_S_BAD_TARGET, ! 241: dataout_len + datain_len); ! 242: return; ! 243: } ! 244: ! 245: // 両方向データ (F_INOUT) はサポートしていない ! 246: if (dataout_len > 0 && datain_len > 0) { ! 247: ProcessDescRequestError(req, VIRTIO_SCSI_S_FAILURE, ! 248: dataout_len + datain_len); ! 249: return; ! 250: } ! 251: ! 252: // ID と読み書き方向が分かったので、アクセスインジケータ用に記録。 ! 253: // 少なくとも dataout[] があれば書き込み、それ以外を読み込みとする。 ! 254: if (dataout_len > 0) { ! 255: access[id * 2 + 1] = req.q->last_avail_idx; ! 256: } else { ! 257: access[id * 2 + 0] = req.q->last_avail_idx; ! 258: } ! 259: ! 260: // ターゲットでコマンドを起動してみる。 ! 261: SCSICmd *cmd = target->SelectCommand(cdb); ! 262: if (cmd == NULL) { ! 263: // インスタンスの生成に失敗した (未実装コマンドではない) ! 264: ProcessDescRequestError(req, VIRTIO_SCSI_S_FAILURE, ! 265: dataout_len + datain_len); ! 266: return; ! 267: } ! 268: ! 269: std::vector<uint8> databuf; ! 270: std::vector<uint8> statusbuf; ! 271: std::vector<uint8> msgbuf; ! 272: // 初手は必ずコマンドフェース。 ! 273: // コマンドフェーズの次からは動的に遷移。 ! 274: auto phase = cmd->ExecCommand(cdb); ! 275: ! 276: for (; phase != SCSI::XferPhase::End; ) { ! 277: SCSI::XferPhase next; ! 278: switch (phase) { ! 279: case SCSI::XferPhase::DataOut: // イニシエータ → ターゲット ! 280: cmd->buf.clear(); ! 281: cmd->buf.resize(req.rremain()); ! 282: ReqReadRegion(req, cmd->buf.data(), cmd->buf.size()); ! 283: putlog(3, "DataOut begin (%zu bytes)", cmd->buf.size()); ! 284: next = cmd->DoneDataOut(); ! 285: break; ! 286: ! 287: case SCSI::XferPhase::DataIn: // イニシエータ ← ターゲット ! 288: next = cmd->DoneDataIn(); ! 289: databuf = cmd->buf; ! 290: putlog(3, "DataIn begin (%u bytes)", (uint)databuf.size()); ! 291: break; ! 292: ! 293: case SCSI::XferPhase::MsgOut: ! 294: cmd->buf.clear(); ! 295: cmd->BeginMsgOut(); ! 296: next = cmd->DoneMsgOut(); ! 297: putlog(3, "MsgOut begin (%u bytes)", (uint)cmd->buf.size()); ! 298: break; ! 299: ! 300: case SCSI::XferPhase::MsgIn: ! 301: cmd->BeginMsgIn(); ! 302: next = cmd->DoneMsgIn(); ! 303: msgbuf = cmd->buf; ! 304: putlog(3, "MsgIn begin (%u bytes)", (uint)msgbuf.size()); ! 305: break; ! 306: ! 307: case SCSI::XferPhase::Status: ! 308: cmd->BeginStatus(); ! 309: next = cmd->DoneStatus(); ! 310: statusbuf = cmd->buf; ! 311: putlog(3, "Status begin (%u bytes)", (uint)statusbuf.size()); ! 312: break; ! 313: ! 314: default: ! 315: PANIC("%s unknown phase done %d", __func__, (int)phase); ! 316: } ! 317: putlog(3, "%s done", SCSI::GetXferPhaseName(phase)); ! 318: phase = next; ! 319: } ! 320: ! 321: // dataout が残っていれば読み飛ばす? ! 322: req.rskip(); ! 323: ! 324: // sense バッファを作成。 ! 325: // 内容は ScSICmdRequestSense::ExecCommand() のコード参照。 ! 326: uint32 sense_len = 18; ! 327: std::vector<uint8> sensebuf(sense_len); ! 328: uint32 sensekey = cmd->GetTarget()->sensekey; ! 329: uint32 senseasc = cmd->GetTarget()->senseasc; ! 330: sensebuf[0] = 0x70; // Valid(?) ! 331: sensebuf[2] = sensekey; ! 332: sensebuf[12] = senseasc >> 8; ! 333: sensebuf[13] = senseasc & 0xff; ! 334: if (sense_size < sense_len) { ! 335: sense_len = sense_size; ! 336: } ! 337: sensebuf.resize(sense_size); ! 338: ! 339: // residual は未処理の"データ"バイト数? ! 340: uint32 residual = 0; // XXX よく分からん ! 341: ReqWriteLE32(req, sense_len); ! 342: ReqWriteLE32(req, residual); ! 343: ReqWriteLE16(req, 0); // status_qualifier ! 344: if (statusbuf.size() == 0) { ! 345: // ステータスフェーズが来なかった? ! 346: ReqWriteU8(req, 0); ! 347: ReqWriteU8(req, VIRTIO_SCSI_S_FAILURE); ! 348: return; ! 349: } ! 350: ! 351: ReqWriteU8(req, statusbuf[0]); ! 352: ReqWriteU8(req, VIRTIO_SCSI_S_OK); ! 353: // sense[sense_size] は常に sense_size バイト分。 ! 354: ReqWriteRegion(req, sensebuf.data(), sensebuf.size()); ! 355: ! 356: // datain ならコマンドが作成したバッファをメモリに書き出す。 ! 357: if (databuf.empty() == false) { ! 358: ReqWriteRegion(req, databuf.data(), databuf.size()); ! 359: } ! 360: } ! 361: ! 362: // Request キューのエラー応答を作成する。 ! 363: // wpos は書き込みセグメントの先頭を指していること。 ! 364: void ! 365: VirtIOSCSIDevice::ProcessDescRequestError(VirtIOReq& req, ! 366: uint32 response_, uint32 residual_) ! 367: { ! 368: // よく分からんけど readable part は読み捨てておく? ! 369: req.rskip(); ! 370: ! 371: // writable part の先頭にいるはず。 ! 372: assert(req.wpos() == 0); ! 373: ReqWriteLE32(req, 0); // sense_len ! 374: ReqWriteLE32(req, residual_); ! 375: ReqWriteLE16(req, 0); // status_qualifier ! 376: ReqWriteU8(req, 0); // status ! 377: ReqWriteU8(req, response_); ! 378: } ! 379: ! 380: const char * ! 381: VirtIOSCSIDevice::GetFeatureName(uint feature) const ! 382: { ! 383: static std::pair<uint, const char *> names[] = { ! 384: { VIRTIO_SCSI_F_INOUT, "INOUT" }, ! 385: { VIRTIO_SCSI_F_HOTPLUG, "HOTPLUG" }, ! 386: { VIRTIO_SCSI_F_CHANGE, "CHANGE" }, ! 387: { VIRTIO_SCSI_F_T10_PI, "T10_PI" }, ! 388: }; ! 389: ! 390: for (auto& p : names) { ! 391: if (feature == p.first) { ! 392: return p.second; ! 393: } ! 394: } ! 395: return inherited::GetFeatureName(feature); ! 396: } ! 397: ! 398: // デバイス構成レイアウトを書き出す。 ! 399: void ! 400: VirtIOSCSIConfigWriter::WriteTo(uint8 *dst) const ! 401: { ! 402: MemoryStreamLE mem(dst); ! 403: ! 404: mem.Write4(num_queues); ! 405: mem.Write4(seg_max); ! 406: mem.Write4(max_sectors); ! 407: mem.Write4(cmd_per_lun); ! 408: mem.Write4(event_info_size); ! 409: mem.Write4(sense_size); ! 410: mem.Write4(cdb_size); ! 411: mem.Write2(max_channel); ! 412: mem.Write2(max_target); ! 413: mem.Write4(max_lun); ! 414: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.