|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2024 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // VirtIO SCSI デバイス
9: //
10:
11: #include "virtio_scsi.h"
12: #include "virtio_def.h"
13: #include "mainbus.h"
14: #include "memorystream.h"
15: #include "monitor.h"
16: #include "scsidev.h"
17: #include "scsidomain.h"
18:
19: // デバイス構成レイアウト
20: class VirtIOSCSIConfigWriter
21: {
22: public:
23: le32 num_queues {};
24: le32 seg_max {};
25: le32 max_sectors {};
26: le32 cmd_per_lun {};
27: le32 event_info_size {};
28: le32 sense_size {};
29: le32 cdb_size {};
30: le16 max_channel {};
31: le16 max_target {};
32: le32 max_lun {};
33:
34: public:
35: void WriteTo(uint8 *dst) const;
36: };
37:
38: // コンストラクタ
39: VirtIOSCSIDevice::VirtIOSCSIDevice(uint slot_)
40: : inherited(OBJ_VIRTIO_SCSI, slot_)
41: {
42: // 短縮形
43: AddAlias("vscsi");
44:
45: device_id = VirtIO::DEVICE_ID_SCSI;
46: vqueues.emplace_back(this, 0, "ControlQ", 8);
47: vqueues.emplace_back(this, 1, "EventQ", 8);
48: vqueues.emplace_back(this, 2, "RequestQ", 8);
49:
50: // 割り込み名
51: strlcpy(intrname, "VIOSCSI", sizeof(intrname));
52: // 完了通知メッセージ
53: msgid = MessageID::VIRTIO_SCSI_DONE;
54:
55: // モニタの行数。
56: int vqlines = 0;
57: for (const auto& q : vqueues) {
58: vqlines += 7 + q.num_max;
59: }
60:
61: monitor = gMonitorManager->Regist(ID_MONITOR_VIRTIO_SCSI, this);
62: monitor->func = ToMonitorCallback(&VirtIOSCSIDevice::MonitorUpdate);
63: monitor->SetSize(MONITOR_WIDTH, 3 + vqlines);
64: }
65:
66: // デストラクタ
67: VirtIOSCSIDevice::~VirtIOSCSIDevice()
68: {
69: }
70:
71: // 動的なコンストラクション
72: bool
73: VirtIOSCSIDevice::Create()
74: {
75: if (inherited::Create() == false) {
76: return false;
77: }
78:
79: try {
80: domain.reset(new SCSIDomain(this, "virtio-scsi"));
81: } catch (...) { }
82: if ((bool)domain == false) {
83: warnx("Failed to initialize SCSIDomain at %s", __method__);
84: return false;
85: }
86:
87: return true;
88: }
89:
90: bool
91: VirtIOSCSIDevice::Init()
92: {
93: if (inherited::Init() == false) {
94: return false;
95: }
96:
97: host = domain->GetInitiator();
98:
99: return true;
100: }
101:
102: void
103: VirtIOSCSIDevice::ResetHard(bool poweron)
104: {
105: // sense_size, cdb_size はリセットで元に戻る (5.6.4)。
106: // XXX 書き換えに対応?
107: sense_size = 96;
108: cdb_size = 32;
109:
110: // DEVICE_FEATURES と構成レイアウトを用意。
111: VirtIOSCSIConfigWriter cfg;
112: cfg.num_queues = 1;
113: cfg.seg_max = 6; // XXX ?
114: cfg.max_sectors = 256;
115: cfg.cmd_per_lun = 1;
116: cfg.sense_size = sense_size;
117: cfg.cdb_size = cdb_size;
118: cfg.max_channel = 0;
119: cfg.max_target = 7;
120: cfg.max_lun = 0;
121: cfg.WriteTo(&device_config[0]);
122: }
123:
124: void
125: VirtIOSCSIDevice::MonitorUpdate(Monitor *, TextScreen& screen)
126: {
127: int y = 0;
128:
129: screen.Clear();
130:
131: y = MonitorUpdateDev(screen, y);
132:
133: y++;
134: for (const auto& q : vqueues) {
135: y = MonitorUpdateVirtQueue(screen, y, q);
136: y++;
137: y = MonitorUpdateVirtQDesc(screen, y, q);
138: y++;
139: }
140: }
141:
142: // ディスクリプタを一つ処理する。
143: void
144: VirtIOSCSIDevice::ProcessDesc(VirtIOReq& req)
145: {
146: VirtQueue *q = req.q;
147:
148: switch (q->idx) {
149: case 0:
150: ProcessDescControl(req);
151: break;
152: case 1:
153: ProcessDescEvent(req);
154: break;
155: case 2:
156: ProcessDescRequest(req);
157: break;
158: default:
159: assert(q->idx < 3);
160: }
161: }
162:
163: // Control キューのディスクリプタを一つ処理する。
164: void
165: VirtIOSCSIDevice::ProcessDescControl(VirtIOReq& req)
166: {
167: putlog(0, "Control Queue (NOT IMPLEMENTED)");
168: }
169:
170: // Event キューのディスクリプタを一つ処理する。
171: void
172: VirtIOSCSIDevice::ProcessDescEvent(VirtIOReq& req)
173: {
174: putlog(0, "Event Queue (NOT IMPLEMENTED)");
175: }
176:
177: // Request キューのディスクリプタを一つ処理する。
178: void
179: VirtIOSCSIDevice::ProcessDescRequest(VirtIOReq& req)
180: {
181: // 8バイトの lun はこういう構成。
182: // bus は 1 から数え始める。
183: // ID が SCSI ID に相当するところ (0-255)。
184: // LUN は 0-16387。0x4000 が立っているのは何?
185: // 下位 32 ビットは論理ユニット固有のアドレスフィールド (0)。
186: // 複数バイトのフィールドは BE 順にメモリに置かれている。
187: //
188: // +0 +1 +2 +3 +4 +5 +6 +7
189: // +-----+-----+-----+-----+-----+-----+-----+-----+
190: // | bus | ID | LUN | 0 |
191: // +-----+-----+-----+-----+-----+-----+-----+-----+
192: uint32 bus = ReqReadU8(req);
193: uint32 id = ReqReadU8(req);
194: uint32 lun = ReqReadBE16(req);
195: uint32 addr = ReqReadBE32(req);
196: putlog(2, "Bus=$%02x ID=$%02x LUN=$%04x Addr=$%08x", bus, id, lun, addr);
197:
198: // 仕様ではこっちが id という名前だが紛らわしいし使わないので tag にする。
199: // QoS Tag みたいなもの?
200: uint64 tag;
201: ReqReadLE64(req, &tag);
202: putlog(3, "tagID=%08x'%08x", (uint32)(tag >> 32), (uint32)tag);
203:
204: uint32 task_attr = ReqReadU8(req);
205: uint32 prio = ReqReadU8(req);
206: uint32 crn = ReqReadU8(req);
207: putlog(3, "task_attr=%02x prio=%02x crn=%02x",
208: task_attr, prio, crn);
209:
210: // コマンド(CDB) を取り出す。
211: std::vector<uint8> cdb(cdb_size);
212: for (int i = 0; i < cdb_size; i++) {
213: cdb[i] = ReqReadU8(req);
214: }
215: if (loglevel >= 2
216: #if defined(NO_READWRITE_LOG)
217: && (cdb[0] != SCSI::Command::Read6 &&
218: cdb[0] != SCSI::Command::Read10 &&
219: cdb[0] != SCSI::Command::Write6 &&
220: cdb[0] != SCSI::Command::Write10)
221: #endif
222: ) {
223: std::string cmdbuf;
224: const char *name = SCSI::GetCommandName(cdb[0]);
225: cmdbuf += string_format("Command \"%s\"", name ?: "?");
226: for (const auto& v : cdb) {
227: cmdbuf += string_format(" %02x", v);
228: }
229: putlogn("%s", cmdbuf.c_str());
230: }
231:
232: // 読み込み側は、ここから後ろがあれば dataout[]。
233: uint32 dataout_len = req.rremain();
234: // datain[] も計算。12 は .sense_len から .response までのバイト数。
235: uint32 datain_len = req.wlen - 12 - sense_size;
236:
237: // ターゲット ID が存在するか。
238: auto *target = domain->GetTarget(id);
239: if (target == NULL) {
240: ProcessDescRequestError(req, VIRTIO_SCSI_S_BAD_TARGET,
241: dataout_len + datain_len);
242: return;
243: }
244:
245: // 両方向データ (F_INOUT) はサポートしていない
246: if (dataout_len > 0 && datain_len > 0) {
247: ProcessDescRequestError(req, VIRTIO_SCSI_S_FAILURE,
248: dataout_len + datain_len);
249: return;
250: }
251:
252: // ID と読み書き方向が分かったので、アクセスインジケータ用に記録。
253: // 少なくとも dataout[] があれば書き込み、それ以外を読み込みとする。
254: if (dataout_len > 0) {
255: access[id * 2 + 1] = req.q->last_avail_idx;
256: } else {
257: access[id * 2 + 0] = req.q->last_avail_idx;
258: }
259:
260: // ターゲットでコマンドを起動してみる。
261: SCSICmd *cmd = target->SelectCommand(cdb);
262: if (cmd == NULL) {
263: // インスタンスの生成に失敗した (未実装コマンドではない)
264: ProcessDescRequestError(req, VIRTIO_SCSI_S_FAILURE,
265: dataout_len + datain_len);
266: return;
267: }
268:
269: std::vector<uint8> databuf;
270: std::vector<uint8> statusbuf;
271: std::vector<uint8> msgbuf;
272: // 初手は必ずコマンドフェース。
273: // コマンドフェーズの次からは動的に遷移。
274: auto phase = cmd->ExecCommand(cdb);
275:
276: for (; phase != SCSI::XferPhase::End; ) {
277: SCSI::XferPhase next;
278: switch (phase) {
279: case SCSI::XferPhase::DataOut: // イニシエータ → ターゲット
280: cmd->buf.clear();
281: cmd->buf.resize(req.rremain());
282: ReqReadRegion(req, cmd->buf.data(), cmd->buf.size());
283: putlog(3, "DataOut begin (%zu bytes)", cmd->buf.size());
284: next = cmd->DoneDataOut();
285: break;
286:
287: case SCSI::XferPhase::DataIn: // イニシエータ ← ターゲット
288: next = cmd->DoneDataIn();
289: databuf = cmd->buf;
290: putlog(3, "DataIn begin (%u bytes)", (uint)databuf.size());
291: break;
292:
293: case SCSI::XferPhase::MsgOut:
294: cmd->buf.clear();
295: cmd->BeginMsgOut();
296: next = cmd->DoneMsgOut();
297: putlog(3, "MsgOut begin (%u bytes)", (uint)cmd->buf.size());
298: break;
299:
300: case SCSI::XferPhase::MsgIn:
301: cmd->BeginMsgIn();
302: next = cmd->DoneMsgIn();
303: msgbuf = cmd->buf;
304: putlog(3, "MsgIn begin (%u bytes)", (uint)msgbuf.size());
305: break;
306:
307: case SCSI::XferPhase::Status:
308: cmd->BeginStatus();
309: next = cmd->DoneStatus();
310: statusbuf = cmd->buf;
311: putlog(3, "Status begin (%u bytes)", (uint)statusbuf.size());
312: break;
313:
314: default:
315: PANIC("%s unknown phase done %d", __func__, (int)phase);
316: }
317: putlog(3, "%s done", SCSI::GetXferPhaseName(phase));
318: phase = next;
319: }
320:
321: // dataout が残っていれば読み飛ばす?
322: req.rskip();
323:
324: // sense バッファを作成。
325: // 内容は ScSICmdRequestSense::ExecCommand() のコード参照。
326: uint32 sense_len = 18;
327: std::vector<uint8> sensebuf(sense_len);
328: uint32 sensekey = cmd->GetTarget()->sensekey;
329: uint32 senseasc = cmd->GetTarget()->senseasc;
330: sensebuf[0] = 0x70; // Valid(?)
331: sensebuf[2] = sensekey;
332: sensebuf[12] = senseasc >> 8;
333: sensebuf[13] = senseasc & 0xff;
334: if (sense_size < sense_len) {
335: sense_len = sense_size;
336: }
337: sensebuf.resize(sense_size);
338:
339: // residual は未処理の"データ"バイト数?
340: uint32 residual = 0; // XXX よく分からん
341: ReqWriteLE32(req, sense_len);
342: ReqWriteLE32(req, residual);
343: ReqWriteLE16(req, 0); // status_qualifier
344: if (statusbuf.size() == 0) {
345: // ステータスフェーズが来なかった?
346: ReqWriteU8(req, 0);
347: ReqWriteU8(req, VIRTIO_SCSI_S_FAILURE);
348: return;
349: }
350:
351: ReqWriteU8(req, statusbuf[0]);
352: ReqWriteU8(req, VIRTIO_SCSI_S_OK);
353: // sense[sense_size] は常に sense_size バイト分。
354: ReqWriteRegion(req, sensebuf.data(), sensebuf.size());
355:
356: // datain ならコマンドが作成したバッファをメモリに書き出す。
357: if (databuf.empty() == false) {
358: ReqWriteRegion(req, databuf.data(), databuf.size());
359: }
360: }
361:
362: // Request キューのエラー応答を作成する。
363: // wpos は書き込みセグメントの先頭を指していること。
364: void
365: VirtIOSCSIDevice::ProcessDescRequestError(VirtIOReq& req,
366: uint32 response_, uint32 residual_)
367: {
368: // よく分からんけど readable part は読み捨てておく?
369: req.rskip();
370:
371: // writable part の先頭にいるはず。
372: assert(req.wpos() == 0);
373: ReqWriteLE32(req, 0); // sense_len
374: ReqWriteLE32(req, residual_);
375: ReqWriteLE16(req, 0); // status_qualifier
376: ReqWriteU8(req, 0); // status
377: ReqWriteU8(req, response_);
378: }
379:
380: const char *
381: VirtIOSCSIDevice::GetFeatureName(uint feature) const
382: {
383: static std::pair<uint, const char *> names[] = {
384: { VIRTIO_SCSI_F_INOUT, "INOUT" },
385: { VIRTIO_SCSI_F_HOTPLUG, "HOTPLUG" },
386: { VIRTIO_SCSI_F_CHANGE, "CHANGE" },
387: { VIRTIO_SCSI_F_T10_PI, "T10_PI" },
388: };
389:
390: for (auto& p : names) {
391: if (feature == p.first) {
392: return p.second;
393: }
394: }
395: return inherited::GetFeatureName(feature);
396: }
397:
398: // デバイス構成レイアウトを書き出す。
399: void
400: VirtIOSCSIConfigWriter::WriteTo(uint8 *dst) const
401: {
402: MemoryStreamLE mem(dst);
403:
404: mem.Write4(num_queues);
405: mem.Write4(seg_max);
406: mem.Write4(max_sectors);
407: mem.Write4(cmd_per_lun);
408: mem.Write4(event_info_size);
409: mem.Write4(sense_size);
410: mem.Write4(cdb_size);
411: mem.Write2(max_channel);
412: mem.Write2(max_target);
413: mem.Write4(max_lun);
414: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.