Annotation of nono/vm/virtio_scsi.cpp, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.