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