--- nono/vm/virtio_base.cpp 2026/04/29 17:05:29 1.1.1.2 +++ nono/vm/virtio_base.cpp 2026/04/29 17:05:42 1.1.1.5 @@ -35,8 +35,11 @@ VirtIODevice::~VirtIODevice() bool VirtIODevice::Create() { - backend.reset(new VirtIOThread(this)); + try { + backend.reset(new VirtIOThread(this)); + } catch (...) { } if ((bool)backend == false) { + warnx("Failed to initialize VirtIOThread at %s", __method__); return false; } @@ -49,18 +52,15 @@ VirtIODevice::SetLogLevel(int loglevel_) { inherited::SetLogLevel(loglevel_); - // バックエンドにも伝達。 - backend->SetLogLevel(loglevel_); + if ((bool)backend) { + backend->SetLogLevel(loglevel_); + } } // 初期化 bool VirtIODevice::Init() { - if (inherited::Init() == false) { - return false; - } - mainbus = GetMainbusDevice(); mainram = GetMainRAMDevice(); @@ -229,8 +229,7 @@ VirtIODevice::WritePort(uint32 offset, u case VirtIO::QUEUE_READY: putlog(2, "QUEUE_READY(%u) <- $%08x", queue_sel, data); if (vq) { - vq->ready = data; - QueueReady(); + vq->SetReady(data); } break; @@ -382,7 +381,7 @@ VirtIODevice::MonitorUpdateVirtQueue(Tex y++; screen.Puts(1, y, "Ring:"); - if (__predict_true(q.ready)) { + if (__predict_true(q.GetReady())) { uint16 aidx = PeekLE16(q.driver + 2); uint16 head = PeekLE16(q.driver + 4 + q.Mod(aidx) * 2); uint16 uidx = PeekLE16(q.device + 2); @@ -458,7 +457,7 @@ VirtIODevice::MonitorUpdateVirtQDesc(Tex screen.Print(x0, y++, TA::Disable, "%02x", i); } - if (__predict_true(q.ready)) { + if (__predict_true(q.GetReady())) { uint avail_idx = PeekLE16(q.driver + 2); uint used_idx = PeekLE16(q.device + 2); screen.Puts(x1 - 3, ytop + q.Mod(avail_idx), "G>"); @@ -489,7 +488,7 @@ VirtIODevice::Reset() q.driver = 0xcccccccc; q.device = 0xcccccccc; q.num = 0; - q.ready = 0; + q.SetReady(0); q.last_avail_idx = 0; } vq = NULL; @@ -533,7 +532,7 @@ VirtIODevice::GetReg(uint32 addr) const case VirtIO::QUEUE_READY: if (vq) { - return vq->ready; + return vq->GetReady(); } else { return 0; } @@ -549,11 +548,11 @@ VirtIODevice::GetReg(uint32 addr) const } } -// QUEUE_READY 時の処理。 +// QUEUE_READY が更新された時に呼ばれる。必要ならオーバーライドする。 +// q->ready が 0 なら Not ready(初期状態)、1 なら Ready。 void -VirtIODevice::QueueReady() +VirtIODevice::QueueReadyChanged(VirtQueue *q) { - // 必要ならオーバーライドする。 } // QUEUE_NOTIFY の処理。(これは裏スレッドで実行される) @@ -598,12 +597,9 @@ VirtIODevice::StartDesc(VirtIOReq& req) uint32 flag; uint32 idx; - req.pos = 0; - req.len = 0; - // 先頭のインデックス。 idx = ReadLE16(q->driver + 4 + q->Mod(q->last_avail_idx) * 2); - if ((int32)idx<0) { + if ((int32)idx < 0) { return; } req.idx = idx; @@ -621,13 +617,17 @@ VirtIODevice::StartDesc(VirtIOReq& req) if ((flag & VIRTQ_DESC_F_INDIRECT)) { AddIndirectDesc(req, addr, slen); } else { - // R/W 属性は無視する。 - putlog(3, "req.buf[%u]: addr=$%08x off=$%08x len=$%04x", - (uint)req.buf.size(), addr, req.len, slen); - req.buf.emplace_back(addr, req.len, slen); - - // セグメントオフセットを記録した後で足す。 - req.len += slen; + if ((flag & VIRTQ_DESC_F_WRITE)) { + putlog(3, "req.wbuf[%u]: addr=$%08x len=$%04x", + (uint)req.wbuf.size(), addr, slen); + req.wbuf.emplace_back(addr, slen); + req.wlen += slen; + } else { + putlog(3, "req.rbuf[%u]: addr=$%08x len=$%04x", + (uint)req.rbuf.size(), addr, slen); + req.rbuf.emplace_back(addr, slen); + req.rlen += slen; + } } } while ((flag & VIRTQ_DESC_F_NEXT)); } @@ -656,13 +656,17 @@ VirtIODevice::AddIndirectDesc(VirtIOReq& flag = ReadLE16(descaddr + 12); idx = ReadLE16(descaddr + 14); - putlog(3, "req.buf[%u]: addr=$%08x off=$%08x len=$%04x (Indirect)", - (uint)req.buf.size(), addr, req.len, slen); - - req.buf.emplace_back(addr, req.len, slen); - - // セグメントオフセットを記録した後で足す。 - req.len += slen; + if ((flag & VIRTQ_DESC_F_WRITE)) { + putlog(3, "req.wbuf[%u]: addr=$%08x len=$%04x (Indirect)", + (uint)req.wbuf.size(), addr, slen); + req.wbuf.emplace_back(addr, slen); + req.wlen += slen; + } else { + putlog(3, "req.rbuf[%u]: addr=$%08x len=$%04x (Indirect)", + (uint)req.rbuf.size(), addr, slen); + req.rbuf.emplace_back(addr, slen); + req.rlen += slen; + } } while ((flag & VIRTQ_DESC_F_NEXT)); } @@ -675,7 +679,7 @@ VirtIODevice::CommitDesc(VirtIOReq& req) // Used Ring に記録。 uint16 used_idx = ReadLE16(q->device + 2); WriteLE32(q->device + 4 + q->Mod(used_idx) * 8 + 0, req.idx); - WriteLE32(q->device + 4 + q->Mod(used_idx) * 8 + 4, req.pos); + WriteLE32(q->device + 4 + q->Mod(used_idx) * 8 + 4, req.pos()); used_idx++; WriteLE16(q->device + 2, used_idx); @@ -688,16 +692,16 @@ VirtIODevice::CommitDesc(VirtIOReq& req) uint32 VirtIODevice::ReqReadU8(VirtIOReq& req) { - if (req.pos >= req.len) { + if (req.reof()) { return -1; } - auto seg = req.buf[req.curseg]; - int segoff = req.pos - seg.pos; - uint8 data = ReadU8(seg.addr + segoff); - - req.pos++; - if (++segoff >= seg.len) { - req.curseg++; + assert(req.roff < req.rbuf[req.rseg].len); + + uint32 data = ReadU8(req.rbuf[req.rseg].addr + req.roff); + req.roff++; + if (req.roff >= req.rbuf[req.rseg].len) { + req.rseg++; + req.roff = 0; } return data; } @@ -778,21 +782,122 @@ VirtIODevice::ReqReadLE64(VirtIOReq& req bool VirtIODevice::ReqWriteU8(VirtIOReq& req, uint32 data) { - if (req.pos >= req.len) { + if (req.weof()) { + return false; + } + assert(req.woff < req.wbuf[req.wseg].len); + + WriteU8(req.wbuf[req.wseg].addr + req.woff, data); + req.woff++; + if (req.woff >= req.wbuf[req.wseg].len) { + req.wseg++; + req.woff = 0; + } + return true; +} + +// この req に LE16 を書き出す。 +bool +VirtIODevice::ReqWriteLE16(VirtIOReq& req, uint32 data) +{ + if (ReqWriteU8(req, data & 0xff) == false) { return false; } + if (ReqWriteU8(req, (data >> 8)) == false) { + return false; + } + return true; +} - const auto seg = req.buf[req.curseg]; - int segoff = req.pos - seg.pos; - WriteU8(seg.addr + segoff, data); - - req.pos++; - if (++segoff >= seg.len) { - req.curseg++; +// この req に LE32 を書き出す。 +bool +VirtIODevice::ReqWriteLE32(VirtIOReq& req, uint32 data) +{ + if (ReqWriteLE16(req, data & 0xffff) == false) { + return false; + } + if (ReqWriteLE16(req, (data >> 16)) == false) { + return false; } return true; } +// この req から dst に dstlen バイト読み込む。 +// dstlen のうち読み込めなかったバイト数を返す (すべて読み込めたら 0 を返す)。 +uint32 +VirtIODevice::ReqReadRegion(VirtIOReq& req, uint8 *dst, uint32 dstlen) +{ + uint8 *d = dst; + + while (dstlen != 0) { + if (req.reof()) { + break; + } + + // 今回読み込むバイト数と読み出し元 VM アドレス。 + uint32 len = std::min(req.rbuf[req.rseg].len - req.roff, dstlen); + uint32 addr = req.rbuf[req.rseg].addr + req.roff; + // 指定範囲は通常全域 RAM のはずなので mainram を直接呼ぶ。 + // 指定範囲が RAM からはみ出たりしていたら (通常まずありえない) + // mainbus 経由でアクセスする。 + bool ok = mainram->ReadMem(addr, d, len); + if (__predict_true(ok)) { + d += len; + } else { + for (uint32 end = addr + len; addr < end; ) { + *d++ = ReadU8(addr++); + } + } + // この加算でセグメント長を超えることはない。 + req.roff += len; + if (req.roff == req.rbuf[req.rseg].len) { + req.rseg++; + req.roff = 0; + } + dstlen -= len; + } + + return dstlen; +} + +// src から req に srclen バイト書き出す。 +// srclen のうち書き出せなかったバイト数を返す (すべて書き出せたら 0 を返す)。 +uint32 +VirtIODevice::ReqWriteRegion(VirtIOReq& req, const uint8 *src, uint32 srclen) +{ + const uint8 *s = src; + + while (srclen != 0) { + if (req.weof()) { + break; + } + + // 今回書き出すバイト数と書き込み先 VM アドレス。 + uint32 len = std::min(req.wbuf[req.wseg].len - req.woff, srclen); + uint32 addr = req.wbuf[req.wseg].addr + req.woff; + // 指定範囲は通常全域 RAM のはずなので mainram を直接呼ぶ。 + // 指定範囲が RAM からはみ出たりしていたら (通常まずありえない) + // mainbus 経由でアクセスする。 + bool ok = mainram->WriteMem(addr, s, len); + if (__predict_true(ok)) { + s += len; + } else { + for (uint32 end = addr + len; addr < end; ) { + WriteU8(addr++, *s); + } + } + // この加算でセグメント長を超えることはない。 + req.woff += len; + if (req.woff == req.wbuf[req.wseg].len) { + req.wseg++; + req.woff = 0; + } + srclen -= len; + } + + return srclen; +} + // 裏スレッドからの処理完了メッセージ通知。(VM スレッドで実行される) // arg はキュー番号。 void @@ -1031,6 +1136,10 @@ VirtIOThread::VirtIOThread(VirtIODevice { parent = parent_; SetName(parent->GetName()); + + // コンストラクト後ただちにログ出力できるようここで一度追従しておく。 + // 以降は親クラス(表スレッド)の SetLogLevel() で変更する。 + loglevel = parent->loglevel; } // デストラクタ @@ -1098,9 +1207,56 @@ VirtIOThread::ThreadRun() // // コンストラクタ -VirtQueue::VirtQueue(int idx_, const std::string& name_, int num_max_) +VirtQueue::VirtQueue(VirtIODevice *parent_, int idx_, const std::string& name_, + int num_max_) { + parent = parent_; idx = idx_; name = name_; num_max = num_max_; } + +// QUEUE_READY の代入。 +void +VirtQueue::SetReady(uint32 val) +{ + if (val != ready) { + ready = val; + parent->QueueReadyChanged(this); + } +} + + +// +// VirtIOReq +// + +// 読み込みセグメントの先頭からの現在位置を返す。 +uint32 +VirtIOReq::rpos() const +{ + uint32 totalpos = 0; + + for (int i = 0; i < rseg; i++) { + totalpos += rbuf[i].len; + } + if (rseg < rbuf.size()) { + totalpos += roff; + } + return totalpos; +} + +// 書き込みセグメントの先頭からの現在位置を返す。 +uint32 +VirtIOReq::wpos() const +{ + uint32 totalpos = 0; + + for (int i = 0; i < wseg; i++) { + totalpos += wbuf[i].len; + } + if (wseg < wbuf.size()) { + totalpos += woff; + } + return totalpos; +}