|
|
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_net.h" ! 12: #include "virtio_def.h" ! 13: #include "config.h" ! 14: #include "ethernet.h" ! 15: #include "macaddr.h" ! 16: #include "memorystream.h" ! 17: #include "scheduler.h" ! 18: ! 19: class VirtIONetReq : public VirtIOReq ! 20: { ! 21: public: ! 22: virtio_net_hdr hdr; ! 23: }; ! 24: ! 25: // デバイス構成レイアウト ! 26: class VirtIONetConfigWriter ! 27: { ! 28: public: ! 29: std::array<u8, 6> mac {}; ! 30: le16 status {}; ! 31: le16 max_virtqueue_pairs {}; ! 32: le16 mtu {}; ! 33: ! 34: public: ! 35: void WriteTo(uint8 *dst) const; ! 36: }; ! 37: ! 38: // コンストラクタ ! 39: VirtIONetDevice::VirtIONetDevice(int slot_) ! 40: : inherited(OBJ_VIRTIO_NET, slot_) ! 41: { ! 42: // 短縮形 ! 43: AddAlias("VNet"); ! 44: AddAlias("Ethernet"); ! 45: ! 46: // 割り込み名 ! 47: strlcpy(intrname, "VIONet", sizeof(intrname)); ! 48: // 完了通知メッセージ ! 49: msgid = MessageID::VIRTIO_NET_DONE; ! 50: ! 51: device_id = VirtIO::DEVICE_ID_NET; ! 52: vqueues.emplace_back(0, "ReceiveQ1", 16); ! 53: vqueues.emplace_back(1, "TransmitQ1", 8); ! 54: ! 55: // time は都度設定する ! 56: event.func = ToEventCallback(&VirtIONetDevice::RxEvent); ! 57: event.Regist("VirtIONet RX"); ! 58: ! 59: // モニタの行数。 ! 60: int vqlines = 0; ! 61: for (const auto& q : vqueues) { ! 62: vqlines += 7 + q.num_max; ! 63: } ! 64: ! 65: monitor.func = ToMonitorCallback(&VirtIONetDevice::MonitorUpdate); ! 66: monitor.SetSize(MONITOR_WIDTH, 3 + vqlines); ! 67: monitor.Regist(ID_MONITOR_VIRTIO_NET); ! 68: } ! 69: ! 70: // デストラクタ ! 71: VirtIONetDevice::~VirtIONetDevice() ! 72: { ! 73: // EthernetDevice と同じ…。 ! 74: if ((bool)hostnet) { ! 75: hostnet->SetRxCallback(NULL); ! 76: } ! 77: } ! 78: ! 79: // 動的なコンストラクション ! 80: bool ! 81: VirtIONetDevice::Create() ! 82: { ! 83: if (inherited::Create() == false) { ! 84: return false; ! 85: } ! 86: ! 87: // EthernetDevice とほぼ同じ…。 ! 88: hostnet.reset(new HostNetDevice(this, 0)); ! 89: ! 90: hostnet->SetRxCallback(ToDeviceCallback(&VirtIONetDevice::HostRxCallback)); ! 91: ! 92: return true; ! 93: } ! 94: ! 95: // 初期化 ! 96: bool ! 97: VirtIONetDevice::Init() ! 98: { ! 99: if (inherited::Init() == false) { ! 100: return false; ! 101: } ! 102: ! 103: // MAC アドレスを取得。 ! 104: macaddr_t macaddr; ! 105: if (EthernetDevice::GetConfigMacAddr(0, &macaddr, false) == false) { ! 106: // エラーメッセージ表示済み。 ! 107: return false; ! 108: } ! 109: // この先設定するタイミングがないのでここで設定する? ! 110: hostnet->SetMyAddr(macaddr); ! 111: // 表示用。 ! 112: macaddr_str = macaddr.ToString(':'); ! 113: ! 114: // DEVICE_FEATURES と構成レイアウトを用意。 ! 115: VirtIONetConfigWriter cfg; ! 116: SetDeviceFeatures(VIRTIO_NET_F_CSUM); ! 117: SetDeviceFeatures(VIRTIO_NET_F_GUEST_CSUM); ! 118: SetDeviceFeatures(VIRTIO_NET_F_MTU), cfg.mac = macaddr; ! 119: SetDeviceFeatures(VIRTIO_NET_F_MAC), cfg.mtu = 1500; ! 120: SetDeviceFeatures(VIRTIO_NET_F_MRG_RXBUF); ! 121: cfg.WriteTo(&device_config[0]); ! 122: ! 123: // ホストからの受信コールバックを登録 ! 124: scheduler->ConnectMessage(MessageID::HOSTNET_RX(0), this, ! 125: ToMessageCallback(&VirtIONetDevice::RxMessage)); ! 126: ! 127: return true; ! 128: } ! 129: ! 130: void ! 131: VirtIONetDevice::MonitorUpdate(Monitor *, TextScreen& screen) ! 132: { ! 133: int y = 0; ! 134: ! 135: screen.Clear(); ! 136: ! 137: y = MonitorUpdateDev(screen, y); ! 138: ! 139: screen.Puts(0, y, "MACAddress:"); ! 140: screen.Puts(12, y, macaddr_str.c_str()); ! 141: y++; ! 142: ! 143: y++; ! 144: for (const auto& q : vqueues) { ! 145: y = MonitorUpdateVirtQueue(screen, y, q); ! 146: y++; ! 147: y = MonitorUpdateVirtQDesc(screen, y, q); ! 148: y++; ! 149: } ! 150: } ! 151: ! 152: void ! 153: VirtIONetDevice::QueueReady() ! 154: { ! 155: if (vq->idx == 0) { ! 156: // ここで受信開始だろうか。 ! 157: hostnet->EnableRx(true); ! 158: } ! 159: } ! 160: ! 161: // ディスクリプタを一つ処理する。 ! 162: void ! 163: VirtIONetDevice::ProcessDesc(VirtQueue *q) ! 164: { ! 165: VirtIONetReq req; ! 166: VirtQDesc desc; ! 167: uint32 desc_idx; ! 168: uint32 reqlen = 0; ! 169: ! 170: if (q->idx == 0) { ! 171: putlog(0, "QUEUE_NOTIFY on rx queue (VQ%u)?", q->idx); ! 172: return; ! 173: } ! 174: ! 175: // 1チャンク目は先頭 12 バイトが Read-Only なヘッダのはず。 ! 176: desc_idx = ReadLE16(q->driver + 4 + (q->last_avail_idx % q->num) * 2); ! 177: reqlen += ReadDesc(q, &desc, desc_idx); ! 178: if (__predict_false(desc.IsRead() == false)) { ! 179: putlog(0, "NetHdr segment must be device-readable"); ! 180: return; ! 181: } ! 182: // 仕様上は1バイト単位でチェインされてもいいのだが、面倒なので無視。 ! 183: if (desc.len < 12) { ! 184: putlog(0, "NetHdr segment too short (desc.len=$%x)", desc.len); ! 185: return; ! 186: } ! 187: req.idx = desc_idx; ! 188: req.hdr.flags = ReadU8(desc.addr + 0); ! 189: req.hdr.gso_type = ReadU8(desc.addr + 1); ! 190: req.hdr.hdr_len = ReadLE16(desc.addr + 2); ! 191: req.hdr.gso_size = ReadLE16(desc.addr + 4); ! 192: req.hdr.csum_start = ReadLE16(desc.addr + 6); ! 193: req.hdr.csum_offset = ReadLE16(desc.addr + 8); ! 194: req.hdr.num_buffers = ReadLE16(desc.addr + 10); ! 195: putlog(2, "req.hdr $%08x: flags=%02x gso=%02x hdr_len=%04x gso_size=%04x " ! 196: "csum_start=%04x offset=%04x num=%04x", ! 197: desc.addr, ! 198: req.hdr.flags, ! 199: req.hdr.gso_type, ! 200: req.hdr.hdr_len, ! 201: req.hdr.gso_size, ! 202: req.hdr.csum_start, ! 203: req.hdr.csum_offset, ! 204: req.hdr.num_buffers); ! 205: ! 206: // 同一セグメント内にデータ(ReadOnly) が連続していても構わない。 ! 207: const auto HDRSZ = sizeof(req.hdr); ! 208: if (desc.len > HDRSZ) { ! 209: AddDataSegment(&req, desc.addr + HDRSZ, desc.len - HDRSZ, ! 210: " (header segment)"); ! 211: } ! 212: ! 213: while (desc.IsNext()) { ! 214: desc_idx = desc.next; ! 215: reqlen += ReadDesc(q, &desc, desc_idx); ! 216: AddDataSegment(&req, desc.addr, desc.len, NULL); ! 217: } ! 218: putlog(3, "reqlen=%08x", reqlen); ! 219: ! 220: NetPacket tx_packet; ! 221: for (const auto seg : req.buf) { ! 222: uint32 addr = seg.addr; ! 223: for (uint32 end = addr + seg.len; addr < end; ) { ! 224: tx_packet.Append(ReadU8(addr++)); ! 225: } ! 226: } ! 227: hostnet->Tx(tx_packet); ! 228: ! 229: CommitDesc(q, req.idx, reqlen); ! 230: } ! 231: ! 232: // これは Host スレッドから呼ばれる ! 233: void ! 234: VirtIONetDevice::HostRxCallback() ! 235: { ! 236: // スレッドを超えるためにメッセージを投げる ! 237: scheduler->SendMessage(MessageID::HOSTNET_RX(0)); ! 238: } ! 239: ! 240: // パケット受信通知 ! 241: void ! 242: VirtIONetDevice::RxMessage(MessageID msgid_, uint32 arg) ! 243: { ! 244: // 受信イベントが止まっていれば動かす。 ! 245: // 受信イベントがすでに動いていれば、1パケット受信完了後に ! 246: // ホストキューを確認するので、ここでは何もしなくてよい。 ! 247: if (event.IsRunning() == false) { ! 248: event.time = 0; ! 249: scheduler->StartEvent(event); ! 250: } ! 251: } ! 252: ! 253: // 受信ループイベント。 ! 254: void ! 255: VirtIONetDevice::RxEvent(Event& ev) ! 256: { ! 257: VirtQueue *q = &vqueues[0]; ! 258: ! 259: // 空きディスクリプタがなければ、空くまでポーリングで待ち続ける。 ! 260: uint16 avail_idx = ReadLE16(q->driver + 2); ! 261: if (q->last_avail_idx == avail_idx) { ! 262: event.time = 500_usec; ! 263: putlog(2, "%s: wait %u usec", __func__, (uint)(event.time / 1_usec)); ! 264: scheduler->RestartEvent(event); ! 265: return; ! 266: } ! 267: ! 268: // ディスクリプタに空きが出来たので、ホストキューから1パケット取り出す。 ! 269: assert(rx_packet.length == 0); ! 270: if (hostnet->Rx(&rx_packet) == false) { ! 271: // 取り出せなくなったら、ここでイベントループを終了。 ! 272: rx_packet.Clear(); ! 273: putlog(2, "Rx no more rx_packet"); ! 274: return; ! 275: } ! 276: ! 277: putlog(2, "%s: %u bytes from host queue", __func__, rx_packet.length); ! 278: ! 279: // rx_packet が埋まって、空きディスクリプタがあるので受信処理へ。 ! 280: Rx(q); ! 281: ! 282: rx_packet.Clear(); ! 283: event.time = 500_usec; ! 284: scheduler->RestartEvent(event); ! 285: } ! 286: ! 287: // 受信。(rx_packet が埋まっていてかつ空きディスクリプタがある状態で呼ぶこと) ! 288: void ! 289: VirtIONetDevice::Rx(VirtQueue *q) ! 290: { ! 291: VirtIONetReq req; ! 292: VirtQDesc desc; ! 293: uint32 desc_idx; ! 294: uint32 nseg = 0; // ゲストが指定したセグメント数 ! 295: uint32 buflen = 0; // ゲストが指定したバッファのバイト数 ! 296: uint32 datalen; // 実際に書き込んだバイト数 ! 297: ! 298: assert(rx_packet.length != 0); ! 299: assert(q->last_avail_idx != ReadLE16(q->driver + 2)); ! 300: ! 301: // XXX どこでやるのがいいか ! 302: // VirtIO は FCS を含まない 1514 バイトが上限と決まっているが、 ! 303: // HostNet の下の受信ドライバが FCS を含めているかどうかが分からない。 ! 304: // 仕方ないので 1514 バイトを超える時だけ取り除く…。 ! 305: if (rx_packet.length > 1514) { ! 306: rx_packet.length = 1514; ! 307: } ! 308: ! 309: desc_idx = ReadLE16(q->driver + 4 + (q->last_avail_idx % q->num) * 2); ! 310: req.idx = desc_idx; ! 311: do { ! 312: buflen += ReadDesc(q, &desc, desc_idx); ! 313: if (__predict_false(desc.IsWrite() == false)) { ! 314: putlog(0, "Segments in rxq must be device-writeable"); ! 315: return; ! 316: } ! 317: nseg++; ! 318: AddDataSegment(&req, desc.addr, desc.len, NULL); ! 319: ! 320: desc_idx = desc.next; ! 321: } while (desc.IsNext()); ! 322: putlog(3, "buflen=%08x", buflen); ! 323: ! 324: memset(&req.hdr, 0, sizeof(req.hdr)); ! 325: req.hdr.hdr_len = htole16(sizeof(req.hdr)); ! 326: // XXX nseg は実際に使ったセグメントをカウントしないといけない ! 327: req.hdr.num_buffers = htole16(nseg); ! 328: ! 329: // 書き込む。 ! 330: const uint8 *s = (const uint8 *)&req.hdr; ! 331: for (int i = 0; i < sizeof(req.hdr); i++) { ! 332: ReqAppendByte(&req, *s++); ! 333: } ! 334: datalen = sizeof(req.hdr); ! 335: ! 336: s = rx_packet.data(); ! 337: for (int i = 0; i < rx_packet.length; i++) { ! 338: if (ReqAppendByte(&req, rx_packet[i]) == false) { ! 339: // どうする? ! 340: putlog(0, "buffer too small: buflen=%u rx_packet=%u", ! 341: buflen, rx_packet.length); ! 342: } ! 343: datalen++; ! 344: } ! 345: ! 346: CommitDesc(q, req.idx, datalen); ! 347: ! 348: // ここは VM スレッドなのでそのまま割り込みを上げる。 ! 349: Done(q); ! 350: } ! 351: ! 352: const char * ! 353: VirtIONetDevice::GetFeatureName(int feature) const ! 354: { ! 355: static std::pair<uint, const char *> names[] = { ! 356: { VIRTIO_NET_F_CSUM, "CSUM" }, ! 357: { VIRTIO_NET_F_GUEST_CSUM, "G_CSUM" }, ! 358: { VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, "C_G_OFFL" }, ! 359: { VIRTIO_NET_F_MTU, "MTU" }, ! 360: { VIRTIO_NET_F_MAC, "MAC" }, ! 361: { VIRTIO_NET_F_GUEST_TSO4, "G_TSO4" }, ! 362: { VIRTIO_NET_F_GUEST_TSO6, "G_TSO6" }, ! 363: { VIRTIO_NET_F_GUEST_ECN, "G_ECN" }, ! 364: { VIRTIO_NET_F_GUEST_UFO, "G_UFO" }, ! 365: { VIRTIO_NET_F_HOST_TSO4, "H_TSO4" }, ! 366: { VIRTIO_NET_F_HOST_TSO6, "H_TSO6" }, ! 367: { VIRTIO_NET_F_HOST_ECN, "H_ECN" }, ! 368: { VIRTIO_NET_F_HOST_UFO, "H_UFO" }, ! 369: { VIRTIO_NET_F_MRG_RXBUF, "MRG_RX" }, ! 370: { VIRTIO_NET_F_STATUS, "STATUS" }, ! 371: { VIRTIO_NET_F_CTRL_VQ, "CTL_VQ" }, ! 372: { VIRTIO_NET_F_CTRL_RX, "CTL_RX" }, ! 373: { VIRTIO_NET_F_CTRL_VLAN, "CTL_VLAN" }, ! 374: { VIRTIO_NET_F_GUEST_ANNOUNCE, "G_ANN" }, ! 375: { VIRTIO_NET_F_MQ, "MQ" }, ! 376: { VIRTIO_NET_F_CTRL_MAC_ADDR, "CTL_MAC" }, ! 377: { VIRTIO_NET_F_RSC_EXT, "RSC_EXT" }, ! 378: { VIRTIO_NET_F_STANDBY, "STANDBY" }, ! 379: }; ! 380: ! 381: for (auto& p : names) { ! 382: if (feature == p.first) { ! 383: return p.second; ! 384: } ! 385: } ! 386: return inherited::GetFeatureName(feature); ! 387: } ! 388: ! 389: // デバイス構成レイアウトを書き出す。 ! 390: void ! 391: VirtIONetConfigWriter::WriteTo(uint8 *dst) const ! 392: { ! 393: MemoryStreamLE mem(dst); ! 394: ! 395: for (auto c : mac) { ! 396: mem.Write8(c); ! 397: } ! 398: mem.Write16(status); ! 399: mem.Write16(max_virtqueue_pairs); ! 400: mem.Write16(mtu); ! 401: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.