Nothing to see here, move along meow
1#![no_std]
2#![no_main]
3
4mod net;
5mod virtio;
6mod virtqueue;
7
8use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter};
9use lancer_user::show;
10use lancer_user::syscall;
11use net::VirtioNetHdr;
12use virtio::RX_BUF_SIZE;
13
14const IRQ_CAP_SLOT: u64 = 3;
15const NOTIF_CAP_SLOT: u64 = 4;
16#[allow(dead_code)]
17const ENDPOINT_SLOT: u64 = 1;
18const PACKET_RING_BASE_SLOT: u64 = 64;
19const PACKET_RING_FRAME_COUNT: u64 = 16;
20const NETSTACK_NOTIF_SLOT: u64 = 7;
21const RING_BASE_VADDR: u64 = 0x4020_0000;
22const RING_HALF_SIZE: usize = 32768;
23
24fn print_mac(mac: &[u8; 6]) {
25 show!(
26 virtio,
27 "mac {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
28 mac[0],
29 mac[1],
30 mac[2],
31 mac[3],
32 mac[4],
33 mac[5]
34 );
35}
36
37#[unsafe(no_mangle)]
38pub extern "C" fn lancer_main() -> ! {
39 show!(virtio, "driver starting");
40
41 let mut dev = virtio::init();
42
43 print_mac(&dev.mac);
44
45 let ring_ok = (0..PACKET_RING_FRAME_COUNT)
46 .all(|i| syscall::frame_map(PACKET_RING_BASE_SLOT + i, RING_BASE_VADDR + i * 4096, 1) >= 0);
47 if !ring_ok {
48 show!(virtio, error, "packet ring frame_map failed");
49 syscall::exit();
50 }
51
52 let rx_ring_base = RING_BASE_VADDR as *mut u8;
53 let tx_ring_base = (RING_BASE_VADDR + RING_HALF_SIZE as u64) as *mut u8;
54
55 let rx_writer = unsafe { PacketRingWriter::init(rx_ring_base, RING_HALF_SIZE, 2048) };
56 let tx_reader = unsafe { PacketRingReader::attach(tx_ring_base, RING_HALF_SIZE) };
57
58 show!(virtio, "ready");
59 syscall::irq_ack(IRQ_CAP_SLOT);
60
61 loop {
62 let (status, _bits) = syscall::notify_wait(NOTIF_CAP_SLOT);
63 if status < 0 {
64 continue;
65 }
66
67 let mut rx_completed = [(0u16, 0u32); crate::virtqueue::QUEUE_SIZE as usize];
68 let rx_count = core::iter::from_fn(|| dev.rx_queue.poll_used())
69 .take(crate::virtqueue::QUEUE_SIZE as usize)
70 .enumerate()
71 .fold(0usize, |_, (i, entry)| {
72 rx_completed[i] = entry;
73 i + 1
74 });
75
76 let mut pushed_any = false;
77 let mut recycle_bufs = [0u16; crate::virtqueue::QUEUE_SIZE as usize];
78 let recycle_count = rx_completed[..rx_count].iter().enumerate().fold(
79 0usize,
80 |count, (i, &(desc_idx, total_len))| {
81 let buf_idx = match (desc_idx as usize) < dev.rx_desc_to_buf.len() {
82 true => dev.rx_desc_to_buf[desc_idx as usize] as usize,
83 false => return count,
84 };
85 let buf_virt = dev.rx_buf_virt_base + buf_idx * RX_BUF_SIZE;
86
87 let hdr_size = VirtioNetHdr::SIZE;
88 let clamped_len = (total_len as usize).min(RX_BUF_SIZE);
89 if clamped_len > hdr_size {
90 let payload_len = clamped_len - hdr_size;
91 let payload = unsafe {
92 core::slice::from_raw_parts((buf_virt + hdr_size) as *const u8, payload_len)
93 };
94 if rx_writer.try_push(payload) {
95 pushed_any = true;
96 }
97 }
98
99 recycle_bufs[i] = buf_idx as u16;
100 count + 1
101 },
102 );
103
104 recycle_bufs[..recycle_count].iter().for_each(|&buf_idx| {
105 let buf_phys = dev.rx_buf_phys(buf_idx as usize);
106 if let Some(new_desc) = dev
107 .rx_queue
108 .add_buf_single(buf_phys, RX_BUF_SIZE as u32, true)
109 {
110 dev.rx_desc_to_buf[new_desc as usize] = buf_idx;
111 }
112 });
113
114 if rx_count > 0 {
115 dev.notify_queue(0);
116 }
117
118 if pushed_any {
119 syscall::notify_signal(NETSTACK_NOTIF_SLOT, 1);
120 }
121
122 let mut sent_any;
123 {
124 let hdr_size = VirtioNetHdr::SIZE;
125 let zeroed_hdr = VirtioNetHdr::zeroed();
126 let hdr_bytes: &[u8] = unsafe {
127 core::slice::from_raw_parts(
128 &zeroed_hdr as *const VirtioNetHdr as *const u8,
129 hdr_size,
130 )
131 };
132
133 sent_any = false;
134
135 core::iter::from_fn(|| dev.tx_queue.poll_used())
136 .take(crate::virtqueue::QUEUE_SIZE as usize)
137 .count();
138
139 let mut payload_buf = [0u8; 1514];
140 core::iter::from_fn(|| match tx_reader.try_pop(&mut payload_buf) {
141 Some(payload_len) => match dev.acquire_tx_buf() {
142 Some((_buf_idx, buf_phys, buf_virt)) => {
143 let total = hdr_size + payload_len;
144 let dst = buf_virt as *mut u8;
145 unsafe {
146 core::ptr::copy_nonoverlapping(hdr_bytes.as_ptr(), dst, hdr_size);
147 core::ptr::copy_nonoverlapping(
148 payload_buf.as_ptr(),
149 dst.add(hdr_size),
150 payload_len,
151 );
152 }
153 dev.tx_queue.add_buf_single(buf_phys, total as u32, false);
154 sent_any = true;
155 Some(())
156 }
157 None => None,
158 },
159 None => None,
160 })
161 .take(virtio::TX_BUF_COUNT)
162 .count();
163
164 if sent_any {
165 dev.notify_queue(1);
166 syscall::notify_signal(NETSTACK_NOTIF_SLOT, 2);
167 }
168 }
169
170 core::iter::from_fn(|| dev.tx_queue.poll_used())
171 .take(crate::virtqueue::QUEUE_SIZE as usize)
172 .count();
173
174 syscall::irq_ack(IRQ_CAP_SLOT);
175 }
176}