|
|
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, §or) == 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.