|
|
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) {
1.1.1.4 root 198: putlog(0, "Cannot read type in header");
1.1.1.2 root 199: return;
200: }
201: ReqReadLE32(req); // skip
202: if (ReqReadLE64(req, §or) == false) {
1.1.1.4 root 203: putlog(0, "Cannot read sector in header");
1.1.1.2 root 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;
1.1.1.4 root 213: if (req.totallen() >= 16 + 1) {
214: datalen = req.totallen() - (16 + 1);
1.1.1.2 root 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:
1.1.1.5 ! root 230: case VIRTIO_BLK_T_GET_ID:
! 231: status = CmdGetID(req, datalen);
! 232: break;
! 233:
1.1 root 234: default:
1.1.1.2 root 235: putlog(0, "%s req.type=%u (NOT IMPLEMENTED)", __func__, type);
1.1 root 236: status = VIRTIO_BLK_S_UNSUPP;
237: break;
238: }
1.1.1.2 root 239: ReqWriteU8(req, status);
1.1 root 240: }
241:
242: // 読み込みコマンド (BLK_T_IN) 実行。
243: uint32
1.1.1.2 root 244: VirtIOBlockDevice::CmdRead(VirtIOReq& req, uint64 sector, uint32 datalen)
1.1 root 245: {
1.1.1.4 root 246: std::vector<uint8> databuf(datalen);
1.1 root 247:
1.1.1.4 root 248: if (image.Read(databuf.data(), sector * 512, databuf.size()) == false) {
249: return VIRTIO_BLK_S_IOERR;
250: }
251: if (ReqWriteRegion(req, databuf.data(), datalen) != 0) {
252: return VIRTIO_BLK_S_IOERR;
1.1 root 253: }
1.1.1.4 root 254:
1.1 root 255: return VIRTIO_BLK_S_OK;
256: }
257:
258: // 書き込みコマンド (BLK_T_OUT) 実行。
259: uint32
1.1.1.2 root 260: VirtIOBlockDevice::CmdWrite(VirtIOReq& req, uint64 sector, uint32 datalen)
1.1 root 261: {
1.1.1.4 root 262: std::vector<uint8> databuf(datalen);
1.1 root 263:
1.1.1.4 root 264: if (ReqReadRegion(req, databuf.data(), datalen) != 0) {
265: return VIRTIO_BLK_S_IOERR;
266: }
267: if (image.Write(databuf.data(), sector * 512, databuf.size()) == false) {
268: return VIRTIO_BLK_S_IOERR;
1.1 root 269: }
270:
1.1.1.2 root 271: return VIRTIO_BLK_S_OK;
1.1 root 272: }
273:
1.1.1.5 ! root 274: // ID 取得コマンド (BLK_T_GET_ID) 実行。
! 275: uint32
! 276: VirtIOBlockDevice::CmdGetID(VirtIOReq& req, uint32 datalen)
! 277: {
! 278: std::vector<uint8> databuf(datalen);
! 279:
! 280: // Device ID string を返すらしいが、どういう文字列か分からん。
! 281: // 20バイトで余りはゼロ埋め。20文字なら終端文字なしで 20文字有効。
! 282: snprintf((char *)databuf.data(), datalen, "virtio-block%u", id);
! 283:
! 284: if (ReqWriteRegion(req, databuf.data(), datalen) != 0) {
! 285: return VIRTIO_BLK_S_IOERR;
! 286: }
! 287:
! 288: return VIRTIO_BLK_S_OK;
! 289: }
! 290:
1.1 root 291: const char *
1.1.1.2 root 292: VirtIOBlockDevice::GetFeatureName(uint feature) const
1.1 root 293: {
294: static std::pair<uint, const char *> names[] = {
295: { VIRTIO_BLK_F_SIZE_MAX, "SIZE_MAX" },
296: { VIRTIO_BLK_F_SEG_MAX, "SEG_MAX" },
297: { VIRTIO_BLK_F_GEOMETRY, "GEOMETRY" },
298: { VIRTIO_BLK_F_RO, "RO" },
299: { VIRTIO_BLK_F_BLK_SIZE, "BLK_SIZE" },
300: { VIRTIO_BLK_F_FLUSH, "FLUSH" },
301: { VIRTIO_BLK_F_TOPOLOGY, "TOPOLOGY" },
302: { VIRTIO_BLK_F_CONFIG_WCE, "CFG_WCE" },
303: { VIRTIO_BLK_F_DISCARD, "DISCARD" },
304: { VIRTIO_BLK_F_WRITE_ZEROES,"WZEROES" },
305: };
306:
307: for (auto& p : names) {
308: if (feature == p.first) {
309: return p.second;
310: }
311: }
312: return inherited::GetFeatureName(feature);
313: }
314:
315: // virtio_blk_config 構造体を書き出す。
316: void
317: VirtIOBlkConfigWriter::WriteTo(uint8 *dst) const
318: {
319: MemoryStreamLE mem(dst);
320:
1.1.1.2 root 321: mem.Write8(capacity);
322: mem.Write4(size_max);
323: mem.Write4(seg_max);
324: mem.Write2(geometry.cylinders);
325: mem.Write1(geometry.heads);
326: mem.Write1(geometry.sectors);
327: mem.Write4(blk_size);
328: mem.Write1(topology.physical_block_exp);
329: mem.Write1(topology.alignment_offset);
330: mem.Write2(topology.min_io_size);
331: mem.Write4(topology.opt_io_size);
332: mem.Write1(writeback);
333: mem.Write1(0);
334: mem.Write1(0);
335: mem.Write1(0);
336: mem.Write4(max_discard_sectors);
337: mem.Write4(max_discard_seg);
338: mem.Write4(discard_sector_alignment);
339: mem.Write4(max_write_zeroes_sectors);
340: mem.Write4(max_write_zeroes_seg);
341: mem.Write1(write_zeroes_may_unmap);
342: mem.Write1(0);
343: mem.Write1(0);
344: mem.Write1(0);
1.1 root 345: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.