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