Annotation of nono/vm/virtio_block.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 ブロックデバイス
                      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.