这个 Lab 又臭又长,我受不了,这破 intel 文档读不下去一点。因此这个 Lab 基本是抄作业,我也没有太细看 intel 文档,只针对关键部分看了下,但是我也想吐槽文档的图画得是真丑,也很不直观,受不了。
QEMU 环境问题
这个 Lab 直接编译运行失败,错误信息表明当前的 QEMU 二进制文件中没有编译进 user
模式的网络后端。需要重新编译 QEMU,并确保配置文件中包含 user
模式网络支持。
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 1 -nographic -global virtio-mmio.force-legacy=false -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -netdev user,id=net0,hostfwd=udp::26999-:2000 -object filter-dump,id=net0,netdev=net0,file=packets.pcap -device e1000,netdev=net0,bus=pcie.0 -S -gdb tcp::26000
qemu-system-riscv64: -netdev user,id=net0,hostfwd=udp::26999-:2000: network backend 'user' is not compiled into this binary
make: *** [Makefile:309: qemu-gdb] Error 1
解决办法:network backend ‘user’ is not compiled into this binary
sudo apt install libslirp-dev
cd ~/qemu-8.0.5/
./configure --target-list=riscv64-softmmu --enable-slirp
make -j$(nproc)
sudo make install
Lab7
实验中用到的 e1000 描述与寄存器
struct rx_desc
结构:
Receive Status 与实验中用到的部分:
Receive Descriptor Ring Structure,实际上 tail 指向的这个位置 software 是不能使用的,能用的位置是 (tail + 1) % RX_RING_SIZE
,原因我也不知道,手册上似乎也没讲清楚。
struct tx_desc
结构,
Transmit Command (TDESC.CMD) 与实验中用到的部分,
Transmit Status 和用到的部分,
Transmit Descriptor Ring Structure,tail 指向 software 下一个发送的位置,
TDT 寄存器,
RDT 寄存器,
代码
注意,锁只需要在 e1000_recv
中使用,e1000_recv
中不必使用锁。因为 e1000_recv
只被 e1000_intr
调用,而调用时不会发生嵌套中断,注释中有这样一句话:tell the e1000 we’ve seen this interrupt; without this the e1000 won’t raise any further interrupts.
剩余部分思路见代码注释,代码如下:
diff --git a/kernel/e1000.c b/kernel/e1000.c
index 70a2adf..82b446a 100644
--- a/kernel/e1000.c
+++ b/kernel/e1000.c
@@ -102,6 +102,27 @@ e1000_transmit(struct mbuf *m)
// the TX descriptor ring so that the e1000 sends it. Stash
// a pointer so that it can be freed after sending.
//
+ acquire(&e1000_lock);
+ int idx = regs[E1000_TDT];
+
+ // check if the next descriptor has been sent
+ if (!(tx_ring[idx].status & E1000_TXD_STAT_DD)) {
+ release(&e1000_lock);
+ return -1;
+ }
+
+ // free the old mbuf
+ if (tx_mbufs[idx]) {
+ mbuffree(tx_mbufs[idx]);
+ }
+
+ tx_mbufs[idx] = m;
+ tx_ring[idx].addr = (uint64)m->head;
+ tx_ring[idx].length = m->len;
+ tx_ring[idx].cmd = E1000_TXD_CMD_RS | E1000_TXD_CMD_EOP; // Report Status, End of Packet
+
+ regs[E1000_TDT] = (idx + 1) % TX_RING_SIZE;
+ release(&e1000_lock);
return 0;
}
@@ -115,6 +136,25 @@ e1000_recv(void)
// Check for packets that have arrived from the e1000
// Create and deliver an mbuf for each packet (using net_rx()).
//
+ while (1) {
+ int idx = (regs[E1000_RDT] + 1) % RX_RING_SIZE;
+
+ if (!(rx_ring[idx].status & E1000_RXD_STAT_DD)) {
+ break;
+ }
+
+ // update the mbuf's m->len and deliver it to the network stack
+ rx_mbufs[idx]->len = rx_ring[idx].length;
+ net_rx(rx_mbufs[idx]);
+
+ // allocate a new mbuf for the descriptor
+ rx_mbufs[idx] = mbufalloc(0);
+ // update the descriptor's address
+ rx_ring[idx].addr = (uint64)rx_mbufs[idx]->head;
+ rx_ring[idx].status = 0;
+
+ regs[E1000_RDT] = idx;
+ }
}
void
测试
References
[MIT 6.s081] Xv6 Lab8 Networking 实验记录
XV6学习(16)Lab net: Network stack