Skip to content

Commit c92acc7

Browse files
committed
feat(virtio) Implement CTRL_RX support for virtio-net
1 parent 8250baf commit c92acc7

1 file changed

Lines changed: 330 additions & 2 deletions

File tree

awkernel_drivers/src/pcie/virtio/virtio_net.rs

Lines changed: 330 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const VIRTIO_NET_F_MAC: u64 = 1 << 5;
4747
const VIRTIO_NET_F_MRG_RXBUF: u64 = 1 << 15;
4848
const VIRTIO_NET_F_STATUS: u64 = 1 << 16;
4949
const VIRTIO_NET_F_CTRL_VQ: u64 = 1 << 17;
50+
const VIRTIO_NET_F_CTRL_RX: u64 = 1 << 18;
5051
const VIRTIO_NET_F_SPEED_DUPLEX: u64 = 1 << 63;
5152

5253
// Reserved Feature Bits
@@ -601,6 +602,54 @@ const _VIRTIO_NET_HDR_GSO_UDP: u8 = 3;
601602
const _VIRTIO_NET_HDR_GSO_TCPV6: u8 = 4;
602603
const _VIRTIO_NET_HDR_GSO_ECN: u8 = 0x80;
603604

605+
const VIRTIO_NET_CTRL_RX_CLASS: u8 = 0;
606+
const VIRTIO_NET_CTRL_MAC: u8 = 1;
607+
608+
const VIRTIO_NET_CTRL_RX_PROMISC: u8 = 0;
609+
const VIRTIO_NET_CTRL_RX_ALLMULTI: u8 = 1;
610+
const VIRTIO_NET_CTRL_MAC_TABLE_SET: u8 = 0;
611+
612+
const VIRTIO_NET_CTRL_OK: u8 = 0;
613+
const VIRTIO_NET_CTRL_STAT_PENDING: u8 = 0xff;
614+
const VIRTIO_NET_CTRL_WAIT_SPINS: usize = 100_000;
615+
const VIRTIO_NET_CTRL_MAC_UC_ENTRIES: usize = 1;
616+
const VIRTIO_NET_CTRL_MAC_MC_ENTRIES: usize = 64;
617+
618+
#[repr(C)]
619+
#[derive(Default, Copy, Clone)]
620+
struct VirtioNetCtrlCmd {
621+
class: u8,
622+
cmd: u8,
623+
}
624+
625+
#[repr(C)]
626+
#[derive(Default, Copy, Clone)]
627+
struct VirtioNetCtrlStatus {
628+
ack: u8,
629+
}
630+
631+
#[repr(C)]
632+
#[derive(Default, Copy, Clone)]
633+
struct VirtioNetCtrlRx {
634+
onoff: u8,
635+
}
636+
637+
#[repr(C)]
638+
#[derive(Copy, Clone)]
639+
struct VirtioNetCtrlMacTable<const N: usize> {
640+
nentries: u32,
641+
macs: [[u8; 6]; N],
642+
}
643+
644+
impl<const N: usize> Default for VirtioNetCtrlMacTable<N> {
645+
fn default() -> Self {
646+
Self {
647+
nentries: 0,
648+
macs: [[0; 6]; N],
649+
}
650+
}
651+
}
652+
604653
pub fn match_device(vendor: u16, id: u16) -> bool {
605654
vendor == pcie_id::VIRTIO_VENDOR_ID && id == VIRTIO_NET_ID
606655
}
@@ -646,6 +695,12 @@ struct VirtioNetInner {
646695
flags: NetFlags,
647696
capabilities: NetCapabilities,
648697
multicast_addrs: MulticastAddrs,
698+
ctrl_cmd: Option<DMAPool<VirtioNetCtrlCmd>>,
699+
ctrl_status: Option<DMAPool<VirtioNetCtrlStatus>>,
700+
ctrl_rx: Option<DMAPool<VirtioNetCtrlRx>>,
701+
ctrl_mac_uc: Option<DMAPool<VirtioNetCtrlMacTable<VIRTIO_NET_CTRL_MAC_UC_ENTRIES>>>,
702+
ctrl_mac_mc: Option<DMAPool<VirtioNetCtrlMacTable<VIRTIO_NET_CTRL_MAC_MC_ENTRIES>>>,
703+
ctrl_mac_table_supported: bool,
649704
virtqueues: Vec<Queue>,
650705
ctrl_vq: Option<Mutex<Virtq>>,
651706
irq_to_type: BTreeMap<u16, IRQType>,
@@ -668,6 +723,12 @@ impl VirtioNetInner {
668723
multicast_addrs: MulticastAddrs::new(),
669724
virtqueues: Vec::new(),
670725
ctrl_vq: None,
726+
ctrl_cmd: None,
727+
ctrl_status: None,
728+
ctrl_rx: None,
729+
ctrl_mac_uc: None,
730+
ctrl_mac_mc: None,
731+
ctrl_mac_table_supported: true,
671732
irq_to_type: BTreeMap::new(),
672733
pcie_int: PCIeInt::None,
673734
}
@@ -718,6 +779,7 @@ impl VirtioNetInner {
718779
self.driver_features |= VIRTIO_NET_F_MRG_RXBUF;
719780
self.driver_features |= VIRTIO_NET_F_STATUS;
720781
self.driver_features |= VIRTIO_NET_F_CTRL_VQ;
782+
self.driver_features |= VIRTIO_NET_F_CTRL_RX;
721783
self.driver_features |= VIRTIO_NET_F_SPEED_DUPLEX;
722784

723785
self.virtio_pci_negotiate_features()?;
@@ -758,6 +820,33 @@ impl VirtioNetInner {
758820
vq.virtio_start_vq_intr();
759821

760822
self.ctrl_vq = Some(Mutex::new(vq));
823+
824+
let mut ctrl_cmd: DMAPool<VirtioNetCtrlCmd> =
825+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
826+
*ctrl_cmd.as_mut() = VirtioNetCtrlCmd::default();
827+
self.ctrl_cmd = Some(ctrl_cmd);
828+
829+
let mut ctrl_status: DMAPool<VirtioNetCtrlStatus> =
830+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
831+
*ctrl_status.as_mut() = VirtioNetCtrlStatus {
832+
ack: VIRTIO_NET_CTRL_STAT_PENDING,
833+
};
834+
self.ctrl_status = Some(ctrl_status);
835+
836+
let mut ctrl_rx: DMAPool<VirtioNetCtrlRx> =
837+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
838+
*ctrl_rx.as_mut() = VirtioNetCtrlRx::default();
839+
self.ctrl_rx = Some(ctrl_rx);
840+
841+
let mut ctrl_mac_uc: DMAPool<VirtioNetCtrlMacTable<VIRTIO_NET_CTRL_MAC_UC_ENTRIES>> =
842+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
843+
*ctrl_mac_uc.as_mut() = VirtioNetCtrlMacTable::default();
844+
self.ctrl_mac_uc = Some(ctrl_mac_uc);
845+
846+
let mut ctrl_mac_mc: DMAPool<VirtioNetCtrlMacTable<VIRTIO_NET_CTRL_MAC_MC_ENTRIES>> =
847+
DMAPool::new(0, 1).ok_or(VirtioDriverErr::DMAPool)?;
848+
*ctrl_mac_mc.as_mut() = VirtioNetCtrlMacTable::default();
849+
self.ctrl_mac_mc = Some(ctrl_mac_mc);
761850
}
762851

763852
{
@@ -871,6 +960,7 @@ impl VirtioNetInner {
871960
}
872961

873962
fn virtio_pci_kick(&mut self, idx: u16) -> Result<(), VirtioDriverErr> {
963+
self.common_cfg.virtio_set_queue_select(idx)?;
874964
let queue_notify_off = self.common_cfg.virtio_get_queue_notify_off()? as usize;
875965
let notify_off_multiplier = self.notify_off_multiplier as usize;
876966
let offset = queue_notify_off * notify_off_multiplier;
@@ -927,6 +1017,15 @@ impl VirtioNetInner {
9271017
self.virtio_pci_set_msix_queue_vector(idx, vector)?;
9281018
}
9291019

1020+
if let Some(ctrl_vq) = &self.ctrl_vq {
1021+
let (idx, vector) = {
1022+
let mut node = MCSNode::new();
1023+
let ctrl_vq = ctrl_vq.lock(&mut node);
1024+
(ctrl_vq.vq_index, ctrl_vq.vq_intr_vec)
1025+
};
1026+
self.virtio_pci_set_msix_queue_vector(idx, vector)?;
1027+
}
1028+
9301029
self.virtio_pci_set_msix_config_vector(0)
9311030
}
9321031

@@ -1120,9 +1219,238 @@ impl VirtioNetInner {
11201219
Ok(())
11211220
}
11221221

1222+
fn vio_ctrl_finish(&mut self) {
1223+
if let Some(ctrl_vq) = &self.ctrl_vq {
1224+
let mut node = MCSNode::new();
1225+
let mut ctrl_vq = ctrl_vq.lock(&mut node);
1226+
while let Some((slot, _)) = ctrl_vq.virtio_dequeue() {
1227+
ctrl_vq.virtio_dequeue_commit(slot);
1228+
}
1229+
}
1230+
}
1231+
1232+
fn vio_ctrl_submit(
1233+
&mut self,
1234+
vq_index: u16,
1235+
class: u8,
1236+
cmd: u8,
1237+
) -> Result<(), VirtioDriverErr> {
1238+
self.virtio_pci_kick(vq_index)?;
1239+
1240+
for _ in 0..VIRTIO_NET_CTRL_WAIT_SPINS {
1241+
self.vio_ctrl_finish();
1242+
1243+
if let Some(status) = &self.ctrl_status {
1244+
if status.as_ref().ack != VIRTIO_NET_CTRL_STAT_PENDING {
1245+
if status.as_ref().ack == VIRTIO_NET_CTRL_OK {
1246+
return Ok(());
1247+
}
1248+
return Err(VirtioDriverErr::InitFailure);
1249+
}
1250+
} else {
1251+
return Err(VirtioDriverErr::InitFailure);
1252+
}
1253+
1254+
core::hint::spin_loop();
1255+
}
1256+
1257+
Err(VirtioDriverErr::InitFailure)
1258+
}
1259+
1260+
fn vio_ctrl_start(
1261+
&mut self,
1262+
class: u8,
1263+
cmd: u8,
1264+
nsegs: usize,
1265+
) -> Result<(usize, u16), VirtioDriverErr> {
1266+
let cmd_phy = {
1267+
let ctrl_cmd = self.ctrl_cmd.as_mut().ok_or(VirtioDriverErr::InitFailure)?;
1268+
ctrl_cmd.as_mut().class = class;
1269+
ctrl_cmd.as_mut().cmd = cmd;
1270+
ctrl_cmd.get_phy_addr().as_usize()
1271+
};
1272+
1273+
if let Some(ctrl_status) = self.ctrl_status.as_mut() {
1274+
ctrl_status.as_mut().ack = VIRTIO_NET_CTRL_STAT_PENDING;
1275+
} else {
1276+
return Err(VirtioDriverErr::InitFailure);
1277+
}
1278+
1279+
let ctrl_vq = self.ctrl_vq.as_ref().ok_or(VirtioDriverErr::InitFailure)?;
1280+
let mut node = MCSNode::new();
1281+
let mut ctrl_vq = ctrl_vq.lock(&mut node);
1282+
let slot = ctrl_vq
1283+
.virtio_enqueue_prep()
1284+
.ok_or(VirtioDriverErr::NoSlot)?;
1285+
ctrl_vq.virtio_enqueue_reserve(slot, nsegs)?;
1286+
ctrl_vq.virtio_enqueue(
1287+
slot,
1288+
cmd_phy,
1289+
core::mem::size_of::<VirtioNetCtrlCmd>(),
1290+
true,
1291+
);
1292+
1293+
Ok((slot, ctrl_vq.vq_index))
1294+
}
1295+
1296+
fn vio_ctrl_rx(&mut self, cmd: u8, onoff: bool) -> Result<(), VirtioDriverErr> {
1297+
let (rx_phy, status_phy) = {
1298+
let ctrl_rx = self.ctrl_rx.as_mut().ok_or(VirtioDriverErr::InitFailure)?;
1299+
ctrl_rx.as_mut().onoff = if onoff { 1 } else { 0 };
1300+
let rx_phy = ctrl_rx.get_phy_addr().as_usize();
1301+
1302+
let ctrl_status = self
1303+
.ctrl_status
1304+
.as_mut()
1305+
.ok_or(VirtioDriverErr::InitFailure)?;
1306+
ctrl_status.as_mut().ack = VIRTIO_NET_CTRL_STAT_PENDING;
1307+
let status_phy = ctrl_status.get_phy_addr().as_usize();
1308+
1309+
(rx_phy, status_phy)
1310+
};
1311+
1312+
let (slot, vq_index) = self.vio_ctrl_start(VIRTIO_NET_CTRL_RX_CLASS, cmd, 3)?;
1313+
1314+
{
1315+
let ctrl_vq = self.ctrl_vq.as_ref().ok_or(VirtioDriverErr::InitFailure)?;
1316+
let mut node = MCSNode::new();
1317+
let mut ctrl_vq = ctrl_vq.lock(&mut node);
1318+
1319+
ctrl_vq.virtio_enqueue(slot, rx_phy, core::mem::size_of::<VirtioNetCtrlRx>(), true);
1320+
ctrl_vq.virtio_enqueue(
1321+
slot,
1322+
status_phy,
1323+
core::mem::size_of::<VirtioNetCtrlStatus>(),
1324+
false,
1325+
);
1326+
ctrl_vq.virtio_enqueue_commit(slot);
1327+
ctrl_vq.publish_avail_idx();
1328+
membar_sync();
1329+
}
1330+
1331+
self.vio_ctrl_submit(vq_index, VIRTIO_NET_CTRL_RX_CLASS, cmd)
1332+
}
1333+
1334+
fn vio_set_rx_filter(&mut self, multicast_list: &[[u8; 6]]) -> Result<(), VirtioDriverErr> {
1335+
if !self.ctrl_mac_table_supported {
1336+
return Err(VirtioDriverErr::InitFailure);
1337+
}
1338+
1339+
let len_uc = core::mem::size_of::<u32>() + 6;
1340+
let len_mc = core::mem::size_of::<u32>() + (multicast_list.len() * 6);
1341+
1342+
let (uc_phy, mc_phy, status_phy) = {
1343+
let ctrl_status = self
1344+
.ctrl_status
1345+
.as_mut()
1346+
.ok_or(VirtioDriverErr::InitFailure)?;
1347+
ctrl_status.as_mut().ack = VIRTIO_NET_CTRL_STAT_PENDING;
1348+
let status_phy = ctrl_status.get_phy_addr().as_usize();
1349+
1350+
let ctrl_mac_uc = self
1351+
.ctrl_mac_uc
1352+
.as_mut()
1353+
.ok_or(VirtioDriverErr::InitFailure)?;
1354+
ctrl_mac_uc.as_mut().nentries = 1;
1355+
ctrl_mac_uc.as_mut().macs[0] = self.mac_addr;
1356+
let uc_phy = ctrl_mac_uc.get_phy_addr().as_usize();
1357+
1358+
let ctrl_mac_mc = self
1359+
.ctrl_mac_mc
1360+
.as_mut()
1361+
.ok_or(VirtioDriverErr::InitFailure)?;
1362+
ctrl_mac_mc.as_mut().nentries = multicast_list.len() as u32;
1363+
for (i, addr) in multicast_list.iter().enumerate() {
1364+
ctrl_mac_mc.as_mut().macs[i] = *addr;
1365+
}
1366+
for i in multicast_list.len()..VIRTIO_NET_CTRL_MAC_MC_ENTRIES {
1367+
ctrl_mac_mc.as_mut().macs[i] = [0; 6];
1368+
}
1369+
let mc_phy = ctrl_mac_mc.get_phy_addr().as_usize();
1370+
1371+
(uc_phy, mc_phy, status_phy)
1372+
};
1373+
1374+
let (slot, vq_index) =
1375+
self.vio_ctrl_start(VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET, 4)?;
1376+
1377+
{
1378+
let ctrl_vq = self.ctrl_vq.as_ref().ok_or(VirtioDriverErr::InitFailure)?;
1379+
let mut node = MCSNode::new();
1380+
let mut ctrl_vq = ctrl_vq.lock(&mut node);
1381+
1382+
ctrl_vq.virtio_enqueue(slot, uc_phy, len_uc, true);
1383+
ctrl_vq.virtio_enqueue(slot, mc_phy, len_mc, true);
1384+
ctrl_vq.virtio_enqueue(
1385+
slot,
1386+
status_phy,
1387+
core::mem::size_of::<VirtioNetCtrlStatus>(),
1388+
false,
1389+
);
1390+
ctrl_vq.virtio_enqueue_commit(slot);
1391+
ctrl_vq.publish_avail_idx();
1392+
membar_sync();
1393+
}
1394+
1395+
let ret =
1396+
self.vio_ctrl_submit(vq_index, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET);
1397+
if ret.is_err() {
1398+
self.ctrl_mac_table_supported = false;
1399+
log::info!("virtio-net: disable MAC_TABLE_SET after failure");
1400+
}
1401+
ret
1402+
}
1403+
11231404
fn vio_iff(&mut self) {
1124-
self.flags.insert(NetFlags::MULTICAST);
1125-
self.flags.insert(NetFlags::PROMISC);
1405+
self.flags.remove(NetFlags::ALLMULTI);
1406+
1407+
if !self.virtio_has_feature(VIRTIO_NET_F_CTRL_RX)
1408+
|| self.ctrl_vq.is_none()
1409+
|| self.ctrl_cmd.is_none()
1410+
|| self.ctrl_status.is_none()
1411+
|| self.ctrl_rx.is_none()
1412+
|| self.ctrl_mac_uc.is_none()
1413+
|| self.ctrl_mac_mc.is_none()
1414+
{
1415+
self.flags.insert(NetFlags::ALLMULTI);
1416+
self.flags.insert(NetFlags::PROMISC);
1417+
return;
1418+
}
1419+
1420+
let mut promisc = self.flags.contains(NetFlags::PROMISC);
1421+
let mut allmulti = false;
1422+
1423+
let multicast_list: Vec<[u8; 6]> = self.multicast_addrs.iter().copied().collect();
1424+
if !self.ctrl_mac_table_supported {
1425+
allmulti = true;
1426+
promisc = true;
1427+
self.flags.insert(NetFlags::ALLMULTI);
1428+
self.flags.insert(NetFlags::PROMISC);
1429+
} else if promisc || multicast_list.len() >= VIRTIO_NET_CTRL_MAC_MC_ENTRIES {
1430+
self.flags.insert(NetFlags::ALLMULTI);
1431+
if !promisc {
1432+
allmulti = true;
1433+
}
1434+
} else if self.vio_set_rx_filter(&multicast_list).is_err() {
1435+
allmulti = true;
1436+
self.flags.insert(NetFlags::ALLMULTI);
1437+
}
1438+
1439+
if self
1440+
.vio_ctrl_rx(VIRTIO_NET_CTRL_RX_ALLMULTI, allmulti)
1441+
.is_err()
1442+
{
1443+
promisc = true;
1444+
self.flags.insert(NetFlags::ALLMULTI);
1445+
self.flags.insert(NetFlags::PROMISC);
1446+
}
1447+
1448+
if self
1449+
.vio_ctrl_rx(VIRTIO_NET_CTRL_RX_PROMISC, promisc)
1450+
.is_err()
1451+
{
1452+
self.flags.insert(NetFlags::PROMISC);
1453+
}
11261454
}
11271455

11281456
fn vio_init(&mut self) -> Result<(), VirtioDriverErr> {

0 commit comments

Comments
 (0)