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