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

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

unix.superglobalmegacorp.com

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