|
|
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.