Annotation of nono/vm/virtio_block.cpp, revision 1.1.1.6

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

unix.superglobalmegacorp.com

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