Annotation of nono/vm/virtio_block.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 ブロックデバイス
        !             9: //
        !            10: 
        !            11: #include "virtio_block.h"
        !            12: #include "virtio_def.h"
        !            13: #include "config.h"
        !            14: #include "memorystream.h"
        !            15: 
        !            16: class VirtIOBlockReq : public VirtIOReq
        !            17: {
        !            18:  public:
        !            19:        uint32 type {};                 // VIRTIO_BLK_T_*
        !            20:        uint64 sector {};               // 開始セクタ (512 バイト単位のセクタ番号)
        !            21:        uint32 statusaddr {};   // ステータスバイトのアドレス (ゲストアドレス)
        !            22: };
        !            23: 
        !            24: // デバイス構成レイアウト
        !            25: class VirtIOBlkConfigWriter
        !            26: {
        !            27:  public:
        !            28:        le64 capacity {};
        !            29:        le32 size_max {};
        !            30:        le32 seg_max {};
        !            31:        struct {
        !            32:                le16 cylinders {};
        !            33:                u8   heads {};
        !            34:                u8   sectors {};
        !            35:        } geometry;
        !            36:        le32 blk_size {};
        !            37:        struct {
        !            38:                u8   physical_block_exp {};
        !            39:                u8   alignment_offset {};
        !            40:                le16 min_io_size {};
        !            41:                le32 opt_io_size {};
        !            42:        } topology;
        !            43:        u8   writeback {};
        !            44:        le32 max_discard_sectors {};
        !            45:        le32 max_discard_seg {};
        !            46:        le32 discard_sector_alignment {};
        !            47:        le32 max_write_zeroes_sectors {};
        !            48:        le32 max_write_zeroes_seg {};
        !            49:        u8   write_zeroes_may_unmap {};
        !            50: 
        !            51:  public:
        !            52:        void WriteTo(uint8 *dst) const;
        !            53: };
        !            54: 
        !            55: // コンストラクタ
        !            56: VirtIOBlockDevice::VirtIOBlockDevice(int slot_, int id_)
        !            57:        : inherited(OBJ_VIRTIO_BLOCK(id_), slot_)
        !            58: {
        !            59:        id = id_;
        !            60: 
        !            61:        // 短縮形
        !            62:        AddAlias(string_format("vblk%d", id));
        !            63: 
        !            64:        // 割り込み名
        !            65:        snprintf(intrname, sizeof(intrname), "VIOBlk%u", id);
        !            66:        // 完了通知メッセージ
        !            67:        msgid = MessageID::VIRTIO_BLOCK_DONE(id);
        !            68: 
        !            69:        device_id = VirtIO::DEVICE_ID_BLOCK;
        !            70:        int num_max = 32;
        !            71:        vqueues.emplace_back(0, "RequestQ", num_max);
        !            72:        blocksize = 512;
        !            73: 
        !            74:        monitor.func = ToMonitorCallback(&VirtIOBlockDevice::MonitorUpdate);
        !            75:        monitor.SetSize(MONITOR_WIDTH, 2 + 4 * 1 + num_max + 3);
        !            76:        monitor.Regist(ID_MONITOR_VIRTIO_BLOCK(id));
        !            77: }
        !            78: 
        !            79: // デストラクタ
        !            80: VirtIOBlockDevice::~VirtIOBlockDevice()
        !            81: {
        !            82: }
        !            83: 
        !            84: // 初期化
        !            85: bool
        !            86: VirtIOBlockDevice::Init()
        !            87: {
        !            88:        if (inherited::Init() == false) {
        !            89:                return false;
        !            90:        }
        !            91: 
        !            92:        const std::string imgkey = string_format("virtio-block%d-image", id);
        !            93:        const std::string wikey = string_format("virtio-block%d-writeignore", id);
        !            94: 
        !            95:        // オープン前に writeignore をチェック。see scsidev.cpp
        !            96:        write_ignore = gConfig->Find(wikey).AsInt();
        !            97: 
        !            98:        // オープン (値が空ならここには来ない)
        !            99:        const ConfigItem& itemimg = gConfig->Find(imgkey);
        !           100:        assert(itemimg.AsString().empty() == false);
        !           101:        std::string path = gMainApp.NormalizePath(itemimg.AsString());
        !           102:        if (LoadDisk(path) == false) {
        !           103:                return false;
        !           104:        }
        !           105: 
        !           106:        uint seg_max = vqueues[0].num_max - 2;
        !           107: 
        !           108:        // DEVICE_FEATURES と構成レイアウトを用意。
        !           109:        VirtIOBlkConfigWriter cfg;
        !           110:        SetDeviceFeatures(VIRTIO_BLK_F_SEG_MAX), cfg.seg_max = seg_max;
        !           111:        SetDeviceFeatures(VIRTIO_BLK_F_BLK_SIZE), cfg.blk_size = blocksize;
        !           112:        if (GetWriteMode() >= SCSIDisk::RW::WriteProtect) {
        !           113:                SetDeviceFeatures(VIRTIO_BLK_F_RO);
        !           114:        }
        !           115: 
        !           116:        cfg.capacity = image.GetSize() / 512; // capacity は 512 バイト単位
        !           117:        cfg.WriteTo(&device_config[0]);
        !           118: 
        !           119:        return true;
        !           120: }
        !           121: 
        !           122: // バックエンドのディスクイメージを開く。
        !           123: // 成功すれば true、失敗すれば false を返す。
        !           124: bool
        !           125: VirtIOBlockDevice::LoadDisk(const std::string& pathname_)
        !           126: {
        !           127:        off_t size;
        !           128:        bool w_ok;
        !           129: 
        !           130:        pathname = pathname_;
        !           131:        if (image.CreateHandler(pathname) == false) {
        !           132:                return false;
        !           133:        }
        !           134: 
        !           135:        if (image.GetInfo(&size, &w_ok) == false) {
        !           136:                return false;
        !           137:        }
        !           138: 
        !           139:        // セクタ単位になっていること
        !           140:        if (size % blocksize != 0) {
        !           141:                warnx("%s: Bad image size(%ju): Not a multiple of blocksize(%d)",
        !           142:                        pathname.c_str(), (uintmax_t)size, blocksize);
        !           143:                return false;
        !           144:        }
        !           145:        // 最大サイズの制約ある?
        !           146: 
        !           147:        // 書き込みモードをここで確定
        !           148:        if (w_ok) {
        !           149:                if (write_ignore) {
        !           150:                        write_mode = SCSIDisk::RW::WriteIgnore;
        !           151:                } else {
        !           152:                        write_mode = SCSIDisk::RW::Writeable;
        !           153:                }
        !           154:        } else {
        !           155:                write_mode = SCSIDisk::RW::WriteProtect;
        !           156:        }
        !           157: 
        !           158:        // ここでオープン
        !           159:        bool read_only = (GetWriteMode() != SCSIDisk::RW::Writeable);
        !           160:        if (image.Open(read_only, write_ignore) == false) {
        !           161:                return false;
        !           162:        }
        !           163: 
        !           164:        // 書き込み無視なら一応ログ出力
        !           165:        if (GetWriteMode() == SCSIDisk::RW::WriteIgnore) {
        !           166:                putmsg(0, "write is ignored");
        !           167:        }
        !           168: 
        !           169:        return true;
        !           170: }
        !           171: 
        !           172: void
        !           173: VirtIOBlockDevice::MonitorUpdate(Monitor *, TextScreen& screen)
        !           174: {
        !           175:        int y = 0;
        !           176: 
        !           177:        screen.Clear();
        !           178: 
        !           179:        y = MonitorUpdateDev(screen, y);
        !           180:        y++;
        !           181:        y = MonitorUpdateVirtQueue(screen, y, vqueues[0]);
        !           182:        y++;
        !           183:        y = MonitorUpdateVirtQDesc(screen, y, vqueues[0]);
        !           184: }
        !           185: 
        !           186: // ディスクリプタを一つ処理する。
        !           187: void
        !           188: VirtIOBlockDevice::ProcessDesc(VirtQueue *q)
        !           189: {
        !           190:        VirtIOBlockReq req;
        !           191:        uint32 status;
        !           192: 
        !           193:        req.q = q;
        !           194:        ReadVirtIOBlockReq(req);
        !           195:        switch (req.type) {
        !           196:         case VIRTIO_BLK_T_IN:
        !           197:                status = CmdRead(req);
        !           198:                access_read = q->last_avail_idx;
        !           199:                break;
        !           200: 
        !           201:         case VIRTIO_BLK_T_OUT:
        !           202:                status = CmdWrite(req);
        !           203:                access_write = q->last_avail_idx;
        !           204:                break;
        !           205: 
        !           206:         default:
        !           207:                putlog(0, "QueueNotify req.type=%u (NOT IMPLEMENTED)", req.type);
        !           208:                status = VIRTIO_BLK_S_UNSUPP;
        !           209:                break;
        !           210:        }
        !           211:        WriteU8(req.statusaddr, status);
        !           212: 
        !           213:        CommitDesc(q, req.idx, req.len);
        !           214: }
        !           215: 
        !           216: // 読み込みコマンド (BLK_T_IN) 実行。
        !           217: uint32
        !           218: VirtIOBlockDevice::CmdRead(VirtIOBlockReq& req)
        !           219: {
        !           220:        uint64 offset = req.sector * 512;
        !           221: 
        !           222:        for (const auto seg : req.buf) {
        !           223:                std::vector<uint8> tmpbuf(seg.len);
        !           224: 
        !           225:                if (image.Read(tmpbuf.data(), offset, tmpbuf.size()) == false) {
        !           226:                        return VIRTIO_BLK_S_IOERR;
        !           227:                }
        !           228:                offset += tmpbuf.size();
        !           229: 
        !           230:                uint32 addr = seg.addr;
        !           231:                for (auto s : tmpbuf) {
        !           232:                        WriteU8(addr++, s);
        !           233:                }
        !           234:        }
        !           235:        return VIRTIO_BLK_S_OK;
        !           236: }
        !           237: 
        !           238: // 書き込みコマンド (BLK_T_OUT) 実行。
        !           239: uint32
        !           240: VirtIOBlockDevice::CmdWrite(VirtIOBlockReq& req)
        !           241: {
        !           242:        uint64 offset = req.sector * 512;
        !           243: 
        !           244:        for (const auto seg : req.buf) {
        !           245:                std::vector<uint8> tmpbuf(seg.len);
        !           246:                uint8 *d = &tmpbuf[0];
        !           247:                uint32 addr = seg.addr;
        !           248:                for (uint32 end = addr + seg.len; addr < end; ) {
        !           249:                        *d++ = ReadU8(addr++);
        !           250:                }
        !           251: 
        !           252:                if (image.Write(tmpbuf.data(), offset, tmpbuf.size()) == false) {
        !           253:                        return VIRTIO_BLK_S_IOERR;
        !           254:                }
        !           255:                offset += tmpbuf.size();
        !           256:        }
        !           257: 
        !           258:        return VIRTIO_BLK_S_OK;
        !           259: }
        !           260: 
        !           261: // リクエストを取り込む。
        !           262: void
        !           263: VirtIOBlockDevice::ReadVirtIOBlockReq(VirtIOBlockReq& req)
        !           264: {
        !           265:        // 仕様的には任意のサイズで分割していいので、本当は1バイトずつバイト数
        !           266:        // 数えながらバッファを再構築しないといけないはずだが、
        !           267:        // 実際問題そんなことするゲストドライバもおらんだろう…。
        !           268: 
        !           269:        VirtQueue *q = req.q;
        !           270:        VirtQDesc desc;
        !           271:        uint32 desc_idx;
        !           272:        uint32 reqlen = 0;
        !           273: 
        !           274:        // 1チャンク目は 16 バイトで Read-Only なヘッダのはず。
        !           275:        desc_idx = ReadLE16(q->driver + 4 + (q->last_avail_idx % q->num) * 2);
        !           276:        reqlen += ReadDesc(q, &desc, desc_idx);
        !           277:        if (__predict_false(desc.IsRead() == false)) {
        !           278:                putlog(0, "Header segment must be device-readable");
        !           279:                return;
        !           280:        }
        !           281:        if (__predict_false(desc.len < 16)) {
        !           282:                putlog(0, "Header too short (desc.len=$%x)", desc.len);
        !           283:                return;
        !           284:        }
        !           285:        req.idx = desc_idx;
        !           286:        req.type   = ReadLE32(desc.addr + 0);
        !           287:        req.sector = ReadLE64(desc.addr + 8);
        !           288:        putlog(3, "%s req.idx=%u type=%x sec=%x",
        !           289:                __func__, req.idx, req.type, (uint32)req.sector);
        !           290: 
        !           291:        // 仕様上は、T_OUT、T_DISCARD、T_WRITE_ZEROES コマンド時は
        !           292:        // 同一セグメント内で、ヘッダ(ReadOnly)とデータ(ReadOnly)が連続していても
        !           293:        // 構わないはず…。誰もやらないとは思うけど…。
        !           294:        if (__predict_false(desc.len > 16)) {
        !           295:                AddDataSegment(&req, desc.addr + 16, desc.len - 16,
        !           296:                        " (header segment)");
        !           297:        }
        !           298: 
        !           299:        // 最低でも2チャンクはあるので継続のはず。
        !           300:        if (__predict_false(desc.IsNext() == false)) {
        !           301:                putlog(0, "Subsequent buffer not found?");
        !           302:                return;
        !           303:        }
        !           304: 
        !           305:        // 2チャンク目以降はデータセグメントが任意個連続していて、
        !           306:        // 最後がステータスバイト。
        !           307:        for (;;) {
        !           308:                desc_idx = desc.next;
        !           309:                reqlen += ReadDesc(q, &desc, desc_idx);
        !           310:                if (desc.IsNext() == false) {
        !           311:                        break;
        !           312:                }
        !           313: 
        !           314:                // 最終チャンクでないので全部データ。
        !           315:                AddDataSegment(&req, desc.addr, desc.len, NULL);
        !           316:        }
        !           317: 
        !           318:        // 最終チャンク。
        !           319:        if (desc.IsWrite() == false) {
        !           320:                putlog(0, "Last segment must be device-writeable");
        !           321:                return;
        !           322:        }
        !           323:        // 仕様上は、T_IN コマンド時は同一セグメント内にデータ(WriteOnly)と
        !           324:        // ステータス(WriteOnly) が連続していても構わないはず…。
        !           325:        // 誰もやらないとは思うけど…。
        !           326:        uint32 addr = desc.addr;
        !           327:        if (__predict_false(desc.len > 1)) {
        !           328:                AddDataSegment(&req, addr, desc.len - 1, " (status segment)");
        !           329:                addr += desc.len - 1;
        !           330:        }
        !           331: 
        !           332:        req.len = reqlen;
        !           333:        req.statusaddr = addr;
        !           334:        putlog(3, "req.statusaddr=%08x len=%08x", req.statusaddr, req.len);
        !           335: }
        !           336: 
        !           337: const char *
        !           338: VirtIOBlockDevice::GetFeatureName(int feature) const
        !           339: {
        !           340:        static std::pair<uint, const char *> names[] = {
        !           341:                { VIRTIO_BLK_F_SIZE_MAX,        "SIZE_MAX" },
        !           342:                { VIRTIO_BLK_F_SEG_MAX,         "SEG_MAX" },
        !           343:                { VIRTIO_BLK_F_GEOMETRY,        "GEOMETRY" },
        !           344:                { VIRTIO_BLK_F_RO,                      "RO" },
        !           345:                { VIRTIO_BLK_F_BLK_SIZE,        "BLK_SIZE" },
        !           346:                { VIRTIO_BLK_F_FLUSH,           "FLUSH" },
        !           347:                { VIRTIO_BLK_F_TOPOLOGY,        "TOPOLOGY" },
        !           348:                { VIRTIO_BLK_F_CONFIG_WCE,      "CFG_WCE" },
        !           349:                { VIRTIO_BLK_F_DISCARD,         "DISCARD" },
        !           350:                { VIRTIO_BLK_F_WRITE_ZEROES,"WZEROES" },
        !           351:        };
        !           352: 
        !           353:        for (auto& p : names) {
        !           354:                if (feature == p.first) {
        !           355:                        return p.second;
        !           356:                }
        !           357:        }
        !           358:        return inherited::GetFeatureName(feature);
        !           359: }
        !           360: 
        !           361: // virtio_blk_config 構造体を書き出す。
        !           362: void
        !           363: VirtIOBlkConfigWriter::WriteTo(uint8 *dst) const
        !           364: {
        !           365:        MemoryStreamLE mem(dst);
        !           366: 
        !           367:        mem.Write64(capacity);
        !           368:        mem.Write32(size_max);
        !           369:        mem.Write32(seg_max);
        !           370:        mem.Write16(geometry.cylinders);
        !           371:        mem.Write8(geometry.heads);
        !           372:        mem.Write8(geometry.sectors);
        !           373:        mem.Write32(blk_size);
        !           374:        mem.Write8(topology.physical_block_exp);
        !           375:        mem.Write8(topology.alignment_offset);
        !           376:        mem.Write16(topology.min_io_size);
        !           377:        mem.Write32(topology.opt_io_size);
        !           378:        mem.Write8(writeback);
        !           379:        mem.Write8(0);
        !           380:        mem.Write8(0);
        !           381:        mem.Write8(0);
        !           382:        mem.Write32(max_discard_sectors);
        !           383:        mem.Write32(max_discard_seg);
        !           384:        mem.Write32(discard_sector_alignment);
        !           385:        mem.Write32(max_write_zeroes_sectors);
        !           386:        mem.Write32(max_write_zeroes_seg);
        !           387:        mem.Write8(write_zeroes_may_unmap);
        !           388:        mem.Write8(0);
        !           389:        mem.Write8(0);
        !           390:        mem.Write8(0);
        !           391: }

unix.superglobalmegacorp.com

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