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

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

unix.superglobalmegacorp.com

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