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

unix.superglobalmegacorp.com

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