Files
Deshab-OS/CODE/DKM/virtio_net/virtio_net.c
T
Linehu 583262f5f0 feat: SKEL skeleton instrumentation - designed-but-unimplemented paths panic
- hypercall.c: 5 Phase 1.3 stubs -> HCALL-E01..E05 panic (no NOSYS return)
- drr_stub.c: snapshot alloc fail -> DRR-E01; new skeletons DRR-E02 root key
  seal, DRR-E03 task kill crypto erase, DRR-E04/E05 segment/system rollback,
  DRR-E06/E07 driver recovery ops dispatch
- sched.c: enable fail -> SCH-E02 panic (no silent demo skip)
- dkm/kernel_api.c: D1 hot-unload DKM-E01, D2 bare code loader DKM-E02,
  SIPI AP wake DKM-E03 skeletons
- virtio_net tx/rx stubs -> VNET-E01/E02; ath9k tx stub -> ATH-E01;
  xhci port reset -> XHC-E01; nvme MSI/MSI-X fail -> NVME-E01..E03 (no
  silent polling downgrade); e1000 MAC fallback -> E1K-E01
- pe_shim unimpl API -> PE-E01; PE ordinal import -> PE-E02
- kernel/main.c: lib sync / vscode install fail -> LNX-E01/E02
- NEW brand/brand.c (E1 boot anim BRAND-E01, E2 time egg BRAND-E02) and
  pkg/pkg.c (pacman skeleton PKG-E01..03), wired into UTSM MAKEFILE
- Deliberate design-internal fallbacks kept and documented (VMX skip,
  bzImage skip, KDE fallback, boot-module fallback, apic->PIC route)
2026-10-03 00:30:54 +08:00

952 lines
36 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* DKM virtio-net Driver — virtio network device (legacy/transitional PCI)
* Stage 3 optional network driver, depends on "pci" and "irq", provides "netdev".
*
* 数据路径实现(virtio spec 1.0 legacy interface,transitional device 1AF4:1001):
* - PCI 探测 → BAR0 legacy I/O 窗口
* - reset → ACKNOWLEDGE → DRIVER → feature negotiation(MAC/STATUS 子集)
* → FEATURES_OK → virtqueue 建立(RXQ0/TXQ1, legacy split-ring 布局)
* → DRIVER_OK
* - RX: 预填 32×2048B buffer 到 RXQ avail;rx_poll 收割 used ring,
* 剥 10B virtio_net_hdr 后把以太网帧交给上层协议栈,buffer 回收重填
* - TX: 8×2048B buffer 池(desc 索引与 buffer 1:1 映射),hdr+帧单描述符,
* QueueNotify kick,与 e1000 一致的同步等待完成(TSC 超时兜底)
* - boot 自测: 经真实 QEMU user-net 后端向 10.0.2.2 DHCP server 发 DISCOVER、
* 收 OFFER,一次跑通证明 TX/RX 全链路(DMA/notify/used ring)
* - IRQ 按 IDT vector(irq_line+0x20)注册做 ISR 统计;PIC 未解屏蔽时
* 自动退化为纯轮询,不依赖中断正确性
*/
#include "../dkm_shared.h"
/* virtio-specific PCI IDs and capability types (driver-local) */
#define PCI_CLASS_REG 0x08 /* 32-bit class/subclass/prog_if/revision */
#define PCI_VENDOR_VIRTIO 0x1AF4u
/* PCI device ID 约定见 virtio_is_net_device(): transitional net=0x1000, modern net=0x1041 */
#define VIRTIO_PCI_CAP_COMMON_CFG 1u
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u
#define VIRTIO_PCI_CAP_ISR_CFG 3u
#define VIRTIO_PCI_CAP_DEVICE_CFG 4u
#define VIRTIO_PCI_CAP_PCI_CFG 5u
/* ---- legacy I/O 寄存器偏移(virtio spec 1.0 §4.1.4, BAR0 I/O space) ---- */
#define VP_HOST_FEATURES 0x00u /* u32 RO: device feature bits 0..31 */
#define VP_GUEST_FEATURES 0x04u /* u32 WO: driver 接受的 feature 子集 */
#define VP_QUEUE_PFN 0x08u /* u32 WO: 队列物理地址 >> 12 */
#define VP_QUEUE_NUM 0x0Cu /* u16 RO: 队列容量(2 的幂) */
#define VP_QUEUE_SEL 0x0Eu /* u16 WO: 选择操作的队列 */
#define VP_QUEUE_NOTIFY 0x10u /* u16 WO: kick 指定队列 */
#define VP_STATUS 0x12u /* u8 RW: device status */
#define VP_ISR 0x13u /* u8 RO: ISR,读即清除 */
#define VP_NET_CFG_MAC 0x14u /* 6B RO: MAC(device config 区起始) */
/* ---- device status bits ---- */
#define VS_ACKNOWLEDGE 0x01u
#define VS_DRIVER 0x02u
#define VS_DRIVER_OK 0x04u
#define VS_FEATURES_OK 0x08u
/* ---- virtio-net feature bits(spec §5.1.6),只取最小子集 ---- */
#define VNET_F_MAC (1u << 5) /* device config 提供 MAC */
#define VNET_F_STATUS (1u << 16) /* device config 提供 link status */
/* ---- vring ---- */
#define VRING_DESC_F_WRITE 2u /* descriptor 由 device 写(RX) */
#define VNET_HDR_SIZE 10u /* 未协商 MRG_RXBUF → virtio_net_hdr 为 10 字节 */
#define RX_BUF_COUNT 32u /* RX 预填 buffer 数(不必填满整个队列) */
#define TX_BUF_COUNT 8u /* TX buffer 池大小 */
#define VNET_BUF_SIZE 2048u /* 10B hdr + 最大 1518B 以太网帧 */
/* split-ring 结构(legacy,x86 上字段为 guest 原生小端序) */
struct vring_desc {
u64 addr;
u32 len;
u16 flags;
u16 next;
};
struct vring_used_elem {
u32 id;
u32 len;
};
struct vring_avail {
u16 flags;
u16 idx;
u16 ring[];
};
struct vring_used {
u16 flags;
u16 idx;
struct vring_used_elem ring[];
};
/* 单个 virtqueue 的运行时视图。
* legacy 布局(spec §2.4.2):
* base+0 : descriptor table(16B × size)
* base+16*size : avail ring(4 + 2×size + 2B used_event)
* align4K(16*size + 6 + 2*size): used ring(4 + 8×size + 2B avail_event)
* 整段必须物理连续且 4KiB 对齐(QueuePFN 只接受页帧号)。 */
struct virtq {
u16 qsel; /* queue select 索引(0=RX, 1=TX) */
u16 size; /* device 报告的队列容量 */
u64 phys; /* 队列内存物理基址(页对齐) */
struct vring_desc *desc;
struct vring_avail *avail;
struct vring_used *used;
u16 last_used_idx; /* driver 侧 used ring 收割游标 */
};
static const char *const g_depends[] = { "pci", "irq" };
static const char *const g_provides[] = { "netdev" };
__attribute__((visibility("default"), used))
const struct dkm_driver_desc driver_desc = {
.magic = DKM_DRIVER_MAGIC,
.abi_version = DKM_ABI_VERSION,
.desc_size = sizeof(struct dkm_driver_desc),
.name = "virtio_net",
.version = "0.2.0",
.vendor = "Deshab",
.driver_class = 9, /* DKM_CLASS_NET */
.stage = 3,
.flags = 0,
.priority = 0,
.depends = g_depends,
.depends_count = 2,
.provides = g_provides,
.provides_count = 1,
.min_kernel_abi = 1,
.feature_bits = 0,
.reserved0 = 0,
.reserved1 = 0
};
static const struct dkm_log_api *g_log;
/* ---- 全局设备状态 ---- */
static u16 g_io_base; /* legacy I/O BAR 基址,0=未初始化 */
static struct virtq g_rxq;
static struct virtq g_txq;
static u8 *g_rx_buf[RX_BUF_COUNT];
static u64 g_rx_buf_phys[RX_BUF_COUNT];
static u8 *g_tx_buf[TX_BUF_COUNT];
static u64 g_tx_buf_phys[TX_BUF_COUNT];
static u8 g_tx_free[TX_BUF_COUNT]; /* 1=空闲可提交 */
static int g_ready; /* 数据路径就绪标志 */
static u8 g_mac[6];
/* ---- 统计计数器(串口可观测) ---- */
static u64 g_stat_tx_ok;
static u64 g_stat_tx_err;
static u64 g_stat_tx_timeout;
static u64 g_stat_rx_ok;
static u64 g_stat_rx_err;
static u64 g_stat_rx_overflow;
static u64 g_stat_irq_count;
/* ---- 自测缓冲区(静态 bss,避免内核栈压力) ---- */
static u8 g_st_frame[360]; /* DHCP DISCOVER 帧(342B) */
static u8 g_st_buf[2048]; /* RX 收割暂存 */
/* ---- 16-bit I/O port 访问(dkm_shared.h 只提供 8/32 位) ---- */
static __inline__ void vn_outw(u16 port, u16 value) {
__asm__ volatile ("outw %0, %1" :: "a"(value), "Nd"(port));
}
static __inline__ u16 vn_inw(u16 port) {
u16 value;
__asm__ volatile ("inw %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
/* device DMA 内存屏障:描述符/ring 写入必须先于 idx 更新对设备可见。
* x86 store-store 有序,compiler fence + mfence 双保险(控制路径,开销可忽略)。 */
static __inline__ void vn_fence(void) {
__asm__ volatile("mfence" ::: "memory");
}
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read(bus, dev, func, reg);
}
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
dkm_pci_write(bus, dev, func, reg, value);
}
static u8 pci_read8(u8 bus, u8 dev, u8 func, u8 reg) {
return dkm_pci_read8(bus, dev, func, reg);
}
static void copy_bytes(void *dst, const void *src, u32 len) {
u8 *d = (u8 *)dst;
const u8 *s = (const u8 *)src;
for (u32 i = 0; i < len; i++) d[i] = s[i];
}
static void zero_bytes(void *dst, u32 len) {
u8 *d = (u8 *)dst;
for (u32 i = 0; i < len; i++) d[i] = 0;
}
static void log_hex(const char *prefix, u64 value) {
static const char hex[] = "0123456789abcdef";
char buf[19];
u32 pos = 0;
buf[pos++] = '0';
buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
buf[pos++] = hex[(value >> (i * 4)) & 0xf];
}
buf[pos] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static u32 dec_u32(u32 v, char *out) {
char tmp[10];
u32 n = 0;
if (v == 0) { out[0] = '0'; return 1; }
while (v && n < 10) { tmp[n++] = (char)('0' + (v % 10)); v /= 10; }
for (u32 i = 0; i < n; i++) out[i] = tmp[n - 1 - i];
return n;
}
/* 打印 IPv4 地址。ip 为主机序(高字节 = 第一个 octet,如 0x0A00020F = 10.0.2.15)。 */
static void log_ipv4(const char *prefix, u32 ip) {
char buf[24];
u32 p = 0;
for (u32 i = 0; i < 4; i++) {
u8 octet = (u8)(ip >> ((3 - i) * 8));
p += dec_u32(octet, buf + p);
if (i < 3) buf[p++] = '.';
}
buf[p] = 0;
g_log->info(prefix);
g_log->info(buf);
}
static void log_mac(const char *prefix, const u8 mac[6]) {
static const char hex[] = "0123456789abcdef";
char buf[18];
u32 p = 0;
for (u32 i = 0; i < 6; i++) {
buf[p++] = hex[(mac[i] >> 4) & 0xf];
buf[p++] = hex[mac[i] & 0xf];
if (i < 5) buf[p++] = ':';
}
buf[p] = 0;
g_log->info(prefix);
g_log->info(buf);
}
/* virtio-net PCI device ID 判定。
* QEMU 编号约定(virtio 1.0 spec §4.1.2.1 + QEMU hw/virtio/virtio-pci.c):
* - transitional/legacy: 0x1000=net, 0x1001=blk, 0x1002=balloon, ...
* (历史 table,不是 0x1000+virtio_id;virtio device id 由 PCI Subsystem ID 携带)
* - modern-only: 0x1040+virtio_id → net = 0x1041
* class code (0x02=Network) 作为第二判据,与 virtio-blk(0x1001/Storage) 区分。 */
static int virtio_is_net_device(u16 device_id) {
return (device_id == 0x1000u || device_id == 0x1041u) ? 1 : 0;
}
static int virtio_net_find(u8 *out_bus, u8 *out_dev, u8 *out_func) {
for (u8 bus = 0; bus < 16; bus++) {
for (u8 dev = 0; dev < 32; dev++) {
u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID);
if ((vd & 0xffff) == 0xffff) continue;
u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16);
u8 func_count = (header & 0x80) ? 8 : 1;
for (u8 func = 0; func < func_count; func++) {
u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID);
u16 vendor = (u16)(vd2 & 0xffff);
u16 device = (u16)(vd2 >> 16);
if (vendor != PCI_VENDOR_VIRTIO) continue;
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
u8 class_code = (u8)(class_reg >> 24);
if (virtio_is_net_device(device) && class_code == 0x02) {
*out_bus = bus;
*out_dev = dev;
*out_func = func;
return 0;
}
}
}
}
return -1;
}
static const char *virtio_cap_name(u8 cfg_type) {
if (cfg_type == VIRTIO_PCI_CAP_COMMON_CFG) return "common_cfg";
if (cfg_type == VIRTIO_PCI_CAP_NOTIFY_CFG) return "notify_cfg";
if (cfg_type == VIRTIO_PCI_CAP_ISR_CFG) return "isr_cfg";
if (cfg_type == VIRTIO_PCI_CAP_DEVICE_CFG) return "device_cfg";
if (cfg_type == VIRTIO_PCI_CAP_PCI_CFG) return "pci_cfg";
return "unknown_cfg";
}
static u32 read_bar_raw(u8 bus, u8 dev, u8 func, u8 bar) {
return pci_read(bus, dev, func, (u8)(PCI_BAR0 + bar * 4));
}
static void log_bars(u8 bus, u8 dev, u8 func) {
for (u8 bar = 0; bar < 6; bar++) {
u32 raw = read_bar_raw(bus, dev, func, bar);
if (raw == 0 || raw == 0xffffffffu) continue;
log_hex("[virtio_net] BAR index=", bar);
log_hex("[virtio_net] raw=", raw);
if (raw & 1u) {
log_hex("[virtio_net] io base=", raw & 0xfffffffcU);
} else {
log_hex("[virtio_net] mmio base=", raw & 0xfffffff0U);
}
}
}
static void log_virtio_caps(u8 bus, u8 dev, u8 func) {
u8 cap = pci_read8(bus, dev, func, PCI_CAP_PTR) & 0xfc;
u32 guard = 0;
u32 found = 0;
while (cap && guard++ < 32) {
u8 cap_id = pci_read8(bus, dev, func, cap + 0);
u8 next = pci_read8(bus, dev, func, cap + 1) & 0xfc;
if (cap_id == 0x09) {
u8 cfg_type = pci_read8(bus, dev, func, cap + 3);
u8 bar = pci_read8(bus, dev, func, cap + 4);
u32 offset = pci_read(bus, dev, func, cap + 8);
u32 length = pci_read(bus, dev, func, cap + 12);
found++;
g_log->info("[virtio_net] virtio capability");
g_log->info(virtio_cap_name(cfg_type));
log_hex("[virtio_net] cap_offset=", cap);
log_hex("[virtio_net] cfg_type=", cfg_type);
log_hex("[virtio_net] bar=", bar);
log_hex("[virtio_net] offset=", offset);
log_hex("[virtio_net] length=", length);
if (cfg_type == VIRTIO_PCI_CAP_NOTIFY_CFG) {
u32 mult = pci_read(bus, dev, func, cap + 16);
log_hex("[virtio_net] notify_off_multiplier=", mult);
}
}
cap = next;
}
log_hex("[virtio_net] virtio cap count=", found);
if (!found) {
g_log->warn("[virtio_net] no modern virtio capabilities; transitional IO path expected");
}
}
/* ================= legacy device 初始化序列 ================= */
/* device status 寄存器读写(8 位) */
static void vdev_set_status(u8 status) {
dkm_outb((u16)(g_io_base + VP_STATUS), status);
}
static u8 vdev_get_status(void) {
return dkm_inb((u16)(g_io_base + VP_STATUS));
}
/* reset → ACKNOWLEDGE → DRIVER → feature 协商 → FEATURES_OK。
* 返回 0 成功;失败返回负数(调用方回退 stub 注册)。 */
static int vdev_negotiate(void) {
/* 1. reset: 写 0 并等待设备读回 0(spec §4.1.4.3 要求轮询确认) */
vdev_set_status(0);
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
while (vdev_get_status() != 0) {
if (dkm_rdtsc() > deadline) {
g_log->error("[virtio_net] device reset timeout");
return -1;
}
__asm__ volatile("pause");
}
/* 2/3. ACKNOWLEDGE → DRIVER(status 位按序累积写入) */
vdev_set_status(VS_ACKNOWLEDGE);
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER);
/* 4. feature negotiation: 只取 MAC + STATUS 子集。
* 不接受 MRG_RXBUF/CTRL_VQ/CSUM/GSO 等复杂特性 →
* virtio_net_hdr 固定 10 字节、无控制队列、无卸载,数据路径最简。 */
u32 dev_feat = dkm_inl((u16)(g_io_base + VP_HOST_FEATURES));
log_hex("[virtio_net] device features=", dev_feat);
u32 subset = dev_feat & (VNET_F_MAC | VNET_F_STATUS);
dkm_outl((u16)(g_io_base + VP_GUEST_FEATURES), subset);
log_hex("[virtio_net] guest features=", subset);
/* 5. FEATURES_OK 并回读确认(spec §3.1.1)。
* 纯 legacy(pre-1.0)设备没有该位概念;QEMU transitional 实现 1.0 语义会置位。
* 未置位时按 legacy 兼容策略 warn 后继续(与 Linux legacy 驱动一致)。 */
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER | VS_FEATURES_OK);
u8 st = vdev_get_status();
log_hex("[virtio_net] status after FEATURES_OK=", st);
if (!(st & VS_FEATURES_OK)) {
g_log->warn("[virtio_net] FEATURES_OK not echoed; continuing as pre-1.0 legacy device");
} else {
g_log->info("[virtio_net] FEATURES_OK confirmed");
}
return 0;
}
/* QueuePFN = 物理地址 >> 12(legacy 队列地址寄存器语义) */
static void vn_outl_pfn(const struct virtq *q) {
dkm_outl((u16)(g_io_base + VP_QUEUE_PFN), (u32)(q->phys >> 12));
}
/* 建立单个 virtqueue:读容量 → 计算 legacy 布局 → 分配连续页 → 写 QueuePFN。 */
static int vq_setup(const struct dkm_dma_api *dma, struct virtq *q, u16 qsel) {
q->qsel = qsel;
q->last_used_idx = 0;
vn_outw((u16)(g_io_base + VP_QUEUE_SEL), qsel);
q->size = vn_inw((u16)(g_io_base + VP_QUEUE_NUM));
if (q->size == 0) {
g_log->error("[virtio_net] queue size is 0");
return -1;
}
/* legacy 布局(spec §2.4.2):
* desc @ 0,avail @ 16*size,used @ 4KiB 对齐边界,整段物理连续。 */
u64 avail_off = 16ULL * q->size;
u64 used_off = (avail_off + 6ULL + 2ULL * q->size + 4095ULL) & ~4095ULL;
u64 total = used_off + 6ULL + 8ULL * q->size;
u64 pages = (total + 4095ULL) / 4096ULL;
struct dkm_dma_buffer buf;
/* 低 4G + 页对齐:QueuePFN 只接受 phys>>12,alloc_pages 返回已清零页 */
if (dma->alloc_pages(pages, 4096, 0x100000000ULL, &buf) != 0) {
g_log->error("[virtio_net] queue memory alloc failed");
return -2;
}
q->phys = buf.phys;
q->desc = (struct vring_desc *)buf.virt;
q->avail = (struct vring_avail *)((u8 *)buf.virt + avail_off);
q->used = (struct vring_used *)((u8 *)buf.virt + used_off);
vn_outl_pfn(q);
return 0;
}
/* kick 队列:写 queue index 到 QueueNotify */
static void vq_notify(const struct virtq *q) {
vn_outw((u16)(g_io_base + VP_QUEUE_NOTIFY), q->qsel);
}
/* 把描述符挂到 avail ring 并推进 idx。
* 顺序约束:desc 字段必须先写好(调用方责任),ring 写入 → fence → idx 推进。 */
static void vq_push_avail(struct virtq *q, u16 desc_idx) {
u16 slot = (u16)(q->avail->idx % q->size);
q->avail->ring[slot] = desc_idx;
vn_fence();
q->avail->idx = (u16)(q->avail->idx + 1u);
vn_fence();
}
/* 从 used ring 取一个完成项。返回 0=取到,1=空。 */
static int vq_pop_used(struct virtq *q, u16 *out_id, u32 *out_len) {
vn_fence();
if (q->used->idx == q->last_used_idx) return 1;
struct vring_used_elem *e = &q->used->ring[q->last_used_idx % q->size];
*out_id = (u16)e->id;
*out_len = e->len;
vn_fence();
q->last_used_idx = (u16)(q->last_used_idx + 1u);
return 0;
}
/* ================= RX 路径 ================= */
/* 回收 RX buffer:重置描述符并重新挂回 avail ring,保持 RX 水位。 */
static void rx_requeue(u16 id) {
g_rxq.desc[id].addr = g_rx_buf_phys[id];
g_rxq.desc[id].len = VNET_BUF_SIZE;
g_rxq.desc[id].flags = VRING_DESC_F_WRITE;
g_rxq.desc[id].next = 0;
vq_push_avail(&g_rxq, id);
vq_notify(&g_rxq);
}
/* 预填全部 RX buffer(单描述符覆盖 hdr+帧,device 可写)。 */
static int rx_prefill(const struct dkm_dma_api *dma) {
for (u32 i = 0; i < RX_BUF_COUNT; i++) {
struct dkm_dma_buffer b;
if (dma->alloc_pages(1, 4096, 0x100000000ULL, &b) != 0) {
g_log->error("[virtio_net] rx buffer alloc failed");
log_hex("[virtio_net] filled=", i);
return -1;
}
g_rx_buf[i] = (u8 *)b.virt;
g_rx_buf_phys[i] = b.phys;
g_rxq.desc[i].addr = b.phys;
g_rxq.desc[i].len = VNET_BUF_SIZE;
g_rxq.desc[i].flags = VRING_DESC_F_WRITE;
g_rxq.desc[i].next = 0;
g_rxq.avail->ring[i] = (u16)i;
}
vn_fence();
g_rxq.avail->idx = (u16)RX_BUF_COUNT;
vn_fence();
log_hex("[virtio_net] rx buffers prefilled=", RX_BUF_COUNT);
return 0;
}
/* netdev rx_poll 回调(契约与 e1000 一致):
* 返回 0 且 *out_length>0 = 收到一帧;1 = 无数据;负数 = 错误。 */
static int virtio_net_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx;
if (out_length) *out_length = 0;
if (!g_ready || !buffer || !out_length) return -1;
u16 id;
u32 total;
if (vq_pop_used(&g_rxq, &id, &total) != 0) return 1; /* 无完成项 */
/* 防御:id 越界或总长小于 hdr → 丢帧(id 无效时无法回收,只计数) */
if (id >= RX_BUF_COUNT) {
g_stat_rx_err++;
return -3;
}
if (total < VNET_HDR_SIZE) {
g_stat_rx_err++;
rx_requeue(id);
return -3;
}
u32 frame_len = total - VNET_HDR_SIZE; /* 剥掉 virtio_net_hdr */
if (frame_len > capacity) {
g_stat_rx_overflow++;
rx_requeue(id);
return -2;
}
copy_bytes(buffer, g_rx_buf[id] + VNET_HDR_SIZE, frame_len);
*out_length = frame_len;
rx_requeue(id);
g_stat_rx_ok++;
return 0;
}
/* ================= TX 路径 ================= */
/* 收割 TX used ring,把完成的 buffer 标回空闲。 */
static void tx_reclaim(void) {
u16 id;
u32 len;
while (vq_pop_used(&g_txq, &id, &len) == 0) {
(void)len; /* TX 的 used.len 无语义,仅 id 用于回收 */
if (id < TX_BUF_COUNT) g_tx_free[id] = 1;
}
}
static int tx_buffers_alloc(const struct dkm_dma_api *dma) {
for (u32 i = 0; i < TX_BUF_COUNT; i++) {
struct dkm_dma_buffer b;
if (dma->alloc_pages(1, 4096, 0x100000000ULL, &b) != 0) {
g_log->error("[virtio_net] tx buffer alloc failed");
return -1;
}
g_tx_buf[i] = (u8 *)b.virt;
g_tx_buf_phys[i] = b.phys;
g_tx_free[i] = 1;
}
return 0;
}
/* netdev tx 回调:hdr(10B 全零,无卸载) + 帧单描述符提交,kick 后同步等待完成。
* 与 e1000 行为一致:TSC 1 秒超时兜底,避免上层协议栈无限阻塞。 */
static int virtio_net_tx(void *ctx, const void *packet, u32 length) {
(void)ctx;
if (!g_ready || !packet || length == 0 || length + VNET_HDR_SIZE > VNET_BUF_SIZE) {
g_stat_tx_err++;
return -1;
}
tx_reclaim();
int slot = -1;
for (u32 i = 0; i < TX_BUF_COUNT; i++) {
if (g_tx_free[i]) { slot = (int)i; break; }
}
if (slot < 0) {
g_stat_tx_err++;
g_log->warn("[virtio_net] tx ring full");
return -2;
}
u8 *buf = g_tx_buf[slot];
zero_bytes(buf, VNET_HDR_SIZE); /* virtio_net_hdr: flags/gso 全 0 */
copy_bytes(buf + VNET_HDR_SIZE, packet, length);
/* desc 索引与 buffer 1:1 映射,回收时 used.id 即 buffer 索引 */
g_txq.desc[slot].addr = g_tx_buf_phys[slot];
g_txq.desc[slot].len = length + VNET_HDR_SIZE;
g_txq.desc[slot].flags = 0; /* device 只读 */
g_txq.desc[slot].next = 0;
g_tx_free[slot] = 0;
vq_push_avail(&g_txq, (u16)slot);
vq_notify(&g_txq);
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000;
while (dkm_rdtsc() < deadline) {
tx_reclaim();
if (g_tx_free[slot]) {
g_stat_tx_ok++;
return 0;
}
__asm__ volatile("pause");
}
g_stat_tx_timeout++;
g_log->warn("[virtio_net] tx timeout");
log_hex("[virtio_net] txq used idx=", g_txq.used->idx);
log_hex("[virtio_net] txq last_used=", g_txq.last_used_idx);
return -3;
}
/* ================= IRQ(辅助统计,数据路径不依赖) ================= */
static int virtio_net_irq(u8 irq) {
(void)irq;
if (!g_io_base) return 0;
/* ISR 读即清除;bit0=queue used buffer 通知,bit1=config change */
u8 isr = dkm_inb((u16)(g_io_base + VP_ISR));
if (isr & 0x1u) g_stat_irq_count++;
return 0; /* 不 suppress EOI,由 IDT 发 PIC EOI */
}
/* ================= boot 自测:DHCP DISCOVER/OFFER ================= */
#define ST_XID 0x4B1D3A5Fu
#define ST_FRAME_LEN 342u /* 14 eth + 20 ip + 8 udp + 300 bootp */
/* 构造 DHCP DISCOVER(全手工大端字段,避免 packed struct 字节序歧义)。
* BOOTP 载荷补齐到 300 字节(RFC 1542 最小值,slirp DHCP server 容忍度最高)。 */
static void st_build_discover(const u8 mac[6]) {
u8 *f = g_st_frame;
zero_bytes(f, ST_FRAME_LEN);
/* Ethernet: 广播 DISCOVER */
for (u32 i = 0; i < 6; i++) f[i] = 0xff;
for (u32 i = 0; i < 6; i++) f[6 + i] = mac[i];
f[12] = 0x08; f[13] = 0x00; /* IPv4 */
/* IPv4 @14 */
u8 *ip = f + 14;
ip[0] = 0x45; /* v4, ihl=5 */
ip[1] = 0x00;
ip[2] = 0x01; ip[3] = 0x48; /* total len = 328 */
ip[4] = 0x00; ip[5] = 0x00; /* id */
ip[6] = 0x00; ip[7] = 0x00; /* flags/frag */
ip[8] = 64; /* ttl */
ip[9] = 17; /* UDP */
ip[10] = 0x00; ip[11] = 0x00; /* cksum 占位 */
/* src 0.0.0.0 @12..15 保持 0 */
ip[16] = 0xff; ip[17] = 0xff; ip[18] = 0xff; ip[19] = 0xff;
/* IP header checksum(标准 1's complement,大端语义直接按字节对累加) */
u32 sum = 0;
for (u32 i = 0; i < 20; i += 2) sum += ((u32)ip[i] << 8) | ip[i + 1];
while (sum >> 16) sum = (sum & 0xffffu) + (sum >> 16);
u16 ck = (u16)(~sum & 0xffffu);
ip[10] = (u8)(ck >> 8); ip[11] = (u8)ck;
/* UDP @34: 68 → 67,校验和置 0(IPv4 下合法) */
u8 *udp = f + 34;
udp[0] = 0x00; udp[1] = 68;
udp[2] = 0x00; udp[3] = 67;
udp[4] = 0x01; udp[5] = 0x34; /* len = 308 */
udp[6] = 0x00; udp[7] = 0x00;
/* BOOTP @42(固定 236B + magic 4B + options,pad 到 300B) */
u8 *bp = f + 42;
bp[0] = 1; /* op = BOOTREQUEST */
bp[1] = 1; /* htype = Ethernet */
bp[2] = 6; /* hlen */
bp[3] = 0; /* hops */
bp[4] = (u8)(ST_XID >> 24);
bp[5] = (u8)(ST_XID >> 16);
bp[6] = (u8)(ST_XID >> 8);
bp[7] = (u8)ST_XID;
/* secs @8..9 = 0 */
bp[10] = 0x80; bp[11] = 0x00; /* flags = broadcast */
/* ciaddr/yiaddr/siaddr/giaddr @12..27 = 0 */
for (u32 i = 0; i < 6; i++) bp[28 + i] = mac[i]; /* chaddr(16B,后 10B 为 0) */
/* sname @44..107, file @108..235 = 0 */
bp[236] = 0x63; bp[237] = 0x82; bp[238] = 0x53; bp[239] = 0x63; /* magic cookie */
u8 *opt = bp + 240;
opt[0] = 53; opt[1] = 1; opt[2] = 1; /* DHCP msg type = DISCOVER */
opt[3] = 55; opt[4] = 4; /* param request list */
opt[5] = 1; opt[6] = 3; opt[7] = 6; opt[8] = 15; /* subnet/router/dns/domain */
opt[9] = 255; /* end */
}
/* 解析帧是否为匹配 XID 的 DHCP OFFER。
* 返回 2=OFFER(*out_yiaddr 有效,主机序),5=ACK,0=无关帧。 */
static int st_parse_reply(const u8 *f, u32 len, u32 *out_yiaddr) {
if (len < 14 + 20 + 8 + 240) return 0;
if (f[12] != 0x08 || f[13] != 0x00) return 0; /* 非 IPv4 */
u32 ihl = (u32)(f[14] & 0x0f) * 4u;
if (ihl < 20) return 0;
if (f[14 + 9] != 17) return 0; /* 非 UDP */
u32 udp = 14 + ihl;
if (len < udp + 8 + 240) return 0;
u16 dport = (u16)(((u16)f[udp + 2] << 8) | f[udp + 3]);
if (dport != 68) return 0; /* 非 DHCP 回包 */
u32 bp = udp + 8;
if (f[bp] != 2) return 0; /* 非 BOOTREPLY */
u32 xid = ((u32)f[bp + 4] << 24) | ((u32)f[bp + 5] << 16) |
((u32)f[bp + 6] << 8) | f[bp + 7];
if (xid != ST_XID) return 0; /* XID 不匹配 */
if (f[bp + 236] != 0x63 || f[bp + 237] != 0x82 ||
f[bp + 238] != 0x53 || f[bp + 239] != 0x63) return 0;
u32 yiaddr = ((u32)f[bp + 16] << 24) | ((u32)f[bp + 17] << 16) |
((u32)f[bp + 18] << 8) | f[bp + 19];
/* options 区扫描 msg type(53) */
u32 i = bp + 240;
while (i < len) {
u8 o = f[i];
if (o == 255) break;
if (o == 0) { i++; continue; }
if (i + 1 >= len) break;
u8 olen = f[i + 1];
if (o == 53 && olen == 1 && i + 2 < len) {
u8 mtype = f[i + 2];
if (mtype == 2 || mtype == 5) {
*out_yiaddr = yiaddr;
return mtype;
}
return 0;
}
i += 2u + olen;
}
return 0;
}
/* boot 自测:经真实网络后端验证 TX(desc+notify+used 回收)与
* RX(avail 预填 + used 收割 + hdr 剥离)全链路。
* 任何失败只记日志,绝不阻塞/崩溃启动流程。 */
static void virtio_net_selftest(void) {
if (!g_ready) return;
g_log->info("[virtio_net] selftest begin: DHCP DISCOVER -> expect OFFER from backend");
st_build_discover(g_mac);
int txrc = virtio_net_tx(0, g_st_frame, ST_FRAME_LEN);
log_hex("[virtio_net] selftest tx rc=", (u64)(i64)txrc);
if (txrc != 0) {
g_log->warn("[virtio_net] selftest: TX path FAILED");
return;
}
g_log->info("[virtio_net] selftest: TX path ok (descriptor consumed by device)");
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 2000;
u32 frames = 0;
while (dkm_rdtsc() < deadline) {
u32 flen = 0;
int rc = virtio_net_rx_poll(0, g_st_buf, sizeof(g_st_buf), &flen);
if (rc == 0 && flen > 0) {
frames++;
u32 ethertype = ((u32)g_st_buf[12] << 8) | g_st_buf[13];
log_hex("[virtio_net] selftest rx frame len=", flen);
log_hex("[virtio_net] ethertype=", ethertype);
u32 yiaddr = 0;
int t = st_parse_reply(g_st_buf, flen, &yiaddr);
if (t == 2) {
log_ipv4("[virtio_net] selftest: DHCP OFFER yiaddr=", yiaddr);
g_log->info("[virtio_net] selftest PASS: RX path ok (frame from real backend)");
return;
}
}
__asm__ volatile("pause");
}
g_log->warn("[virtio_net] selftest: no DHCP OFFER within 2000ms");
log_hex("[virtio_net] selftest rx frames seen=", frames);
}
/* ================= stub(初始化失败时的安全占位) =================
* 严格错误策略插桩:占位不再静默丢包/静默无数据——一旦被上层调用
* (即有人真的把流量交给未协商 virtqueue 的设备),立即莲花 panic。
* 正常路径 netman 因 flags 缺 LINK_UP 不会选中本驱动,stub 不触发。 */
static int virtio_net_tx_stub(void *ctx, const void *packet, u32 length) {
(void)ctx; (void)packet;
log_hex("[virtio_net] tx stub hit, len=", length);
g_log->panic("VNET-E01 TX VIRTQUEUE NOT IMPLEMENTED (stub tx called)");
return 0;
}
static int virtio_net_rx_poll_stub(void *ctx, void *buffer, u32 capacity, u32 *out_length) {
(void)ctx; (void)buffer; (void)capacity;
g_log->panic("VNET-E02 RX VIRTQUEUE NOT IMPLEMENTED (stub rx called)");
if (out_length) *out_length = 0;
return 0;
}
/* ================= driver 入口 ================= */
__attribute__((visibility("default")))
int driver_init(const struct dkm_kernel_api *api,
struct dkm_driver_handle *handle) {
(void)handle;
if (!api || !api->log) return -1;
g_log = api->log;
/* 实机要求: 在驱动初始化开头校准 TSC, 为后续时序提供准确计时 */
dkm_tsc_calibrate();
g_log->info("[virtio_net] init begin");
u8 bus = 0;
u8 dev = 0;
u8 func = 0;
if (virtio_net_find(&bus, &dev, &func) != 0) {
g_log->warn("[virtio_net] virtio-net device not found");
g_log->info("[virtio_net] driver ready");
return 0;
}
g_log->info("[virtio_net] virtio-net device found");
log_hex("[virtio_net] bus=", bus);
log_hex("[virtio_net] dev=", dev);
log_hex("[virtio_net] func=", func);
u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID);
u16 vendor = (u16)(vd & 0xffff);
u16 device = (u16)(vd >> 16);
u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG);
u8 revision = (u8)(class_reg & 0xff);
log_hex("[virtio_net] vendor=", vendor);
log_hex("[virtio_net] device=", device);
log_hex("[virtio_net] revision=", revision);
u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff;
command |= PCI_CMD_IO | PCI_CMD_MEM | PCI_CMD_BUSM;
pci_write(bus, dev, func, PCI_COMMAND, command);
log_hex("[virtio_net] PCI command=", command);
u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff;
log_hex("[virtio_net] PCI IRQ line=", irq_line);
log_bars(bus, dev, func);
log_virtio_caps(bus, dev, func);
/* ---- legacy I/O BAR 检查:transitional 设备 BAR0 必须是 I/O space ---- */
u32 bar0 = pci_read(bus, dev, func, PCI_BAR0);
int datapath_ok = 0;
if (!(bar0 & 1u)) {
g_log->warn("[virtio_net] BAR0 is not I/O space (modern-only device?); legacy path unavailable");
} else if (!api->dma || !api->dma->alloc_pages) {
g_log->warn("[virtio_net] dma API unavailable");
} else {
g_io_base = (u16)(bar0 & 0xfffcU);
log_hex("[virtio_net] legacy io_base=", g_io_base);
if (vdev_negotiate() != 0) {
g_log->warn("[virtio_net] feature negotiation failed");
} else if (vq_setup(api->dma, &g_rxq, 0) != 0) {
g_log->warn("[virtio_net] RXQ setup failed");
} else if (vq_setup(api->dma, &g_txq, 1) != 0) {
g_log->warn("[virtio_net] TXQ setup failed");
} else if (rx_prefill(api->dma) != 0) {
g_log->warn("[virtio_net] RX prefill failed");
} else if (tx_buffers_alloc(api->dma) != 0) {
g_log->warn("[virtio_net] TX buffer alloc failed");
} else {
log_hex("[virtio_net] rxq size=", g_rxq.size);
log_hex("[virtio_net] rxq phys=", g_rxq.phys);
log_hex("[virtio_net] txq size=", g_txq.size);
log_hex("[virtio_net] txq phys=", g_txq.phys);
/* MAC: device config @ io_base+0x14(协商 MAC feature 后有效) */
for (u32 i = 0; i < 6; i++) {
g_mac[i] = dkm_inb((u16)(g_io_base + VP_NET_CFG_MAC + i));
}
u8 mac_sum = 0;
for (u32 i = 0; i < 6; i++) mac_sum |= g_mac[i];
if (mac_sum == 0 || (g_mac[0] & 1u)) {
/* 全 0 或组播位异常 → fallback */
g_mac[0] = 0x52; g_mac[1] = 0x54; g_mac[2] = 0x00;
g_mac[3] = 0x12; g_mac[4] = 0x34; g_mac[5] = 0x57;
g_log->warn("[virtio_net] invalid device MAC, using fallback");
}
log_mac("[virtio_net] MAC=", g_mac);
/* DRIVER_OK:设备开始处理队列;随后 kick RXQ 让设备吃掉预填 buffer */
vdev_set_status(VS_ACKNOWLEDGE | VS_DRIVER | VS_FEATURES_OK | VS_DRIVER_OK);
vq_notify(&g_rxq);
g_ready = 1;
datapath_ok = 1;
g_log->info("[virtio_net] DRIVER_OK set; virtqueues live");
}
}
/* ---- IRQ 注册(辅助统计;数据路径为纯轮询,不依赖中断) ----
* 注意: kernel irq_register 按 IDT vector 索引(PIC IRQn → vector 0x20+n),
* 传裸 IRQ 号会落到异常向量槽位永不触发(e1000 当前如此)。
* PIC 未解屏蔽该线时自动退化为纯轮询。 */
if (datapath_ok && api->irq_register && irq_line > 0 && irq_line < 16) {
api->irq_register((u8)(irq_line + 0x20u), (void *)(uintptr_t)virtio_net_irq);
g_log->info("[virtio_net] IRQ handler registered (vector = irq + 0x20)");
} else {
g_log->info("[virtio_net] IRQ not registered (polling mode)");
}
/* ---- 注册 netdev ---- */
if (api->net && api->net->register_device) {
struct dkm_net_device_desc netdev;
zero_bytes(&netdev, sizeof(netdev));
netdev.name = "virtio-net";
for (u32 i = 0; i < 6; i++) netdev.mac[i] = g_mac[i];
netdev.ctx = 0;
if (datapath_ok) {
netdev.flags = DKM_NET_F_LINK_UP | DKM_NET_F_TX_READY | DKM_NET_F_RX_READY;
netdev.tx = virtio_net_tx;
netdev.rx_poll = virtio_net_rx_poll;
} else {
/* 安全占位:flags 仅 LINK_UP,netman 不会选它跑 DHCP(与历史行为一致) */
netdev.flags = DKM_NET_F_LINK_UP;
netdev.tx = virtio_net_tx_stub;
netdev.rx_poll = virtio_net_rx_poll_stub;
}
int net_index = api->net->register_device(&netdev);
log_hex("[virtio_net] netdev registered, index=", (u64)(i64)net_index);
} else {
g_log->warn("[virtio_net] net API unavailable; netdev not registered");
}
/* ---- boot 自测(证明 TX/RX 全链路,失败仅记日志) ---- */
virtio_net_selftest();
log_hex("[virtio_net] stat tx_ok=", g_stat_tx_ok);
log_hex("[virtio_net] stat rx_ok=", g_stat_rx_ok);
g_log->info("[virtio_net] driver ready");
return 0;
}
__attribute__((visibility("default")))
int driver_exit(struct dkm_driver_handle *handle) {
(void)handle;
/* reset 设备:停止一切 DMA 与队列活动 */
if (g_io_base) {
vdev_set_status(0);
}
g_ready = 0;
g_io_base = 0;
return 0;
}