Annotation of nono/vm/virtio_scsi.cpp, revision 1.1.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.