Skip to content

feat(drivers/net): Add loopback network driver #1834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ fuse = ["pci", "dep:fuse-abi", "fuse-abi/num_enum"]
gem-net = ["tcp", "dep:tock-registers"]
idle-poll = []
log-target = []
net = []
mmap = []
newlib = []
nostd = []
Expand Down
74 changes: 74 additions & 0 deletions src/drivers/net/loopback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use alloc::collections::vec_deque::VecDeque;
use alloc::vec::Vec;

use hermit_sync::InterruptTicketMutex;

use crate::drivers::net::NetworkDriver;
use crate::drivers::{Driver, InterruptLine};
use crate::executor::device::{RxToken, TxToken};
use crate::mm::device_alloc::DeviceAlloc;

pub(crate) struct LoopbackDriver(VecDeque<Vec<u8, DeviceAlloc>>);

impl LoopbackDriver {
pub(crate) const fn new() -> Self {
Self(VecDeque::new())
}
}

impl Driver for LoopbackDriver {
fn get_interrupt_number(&self) -> InterruptLine {
// This is called by mmio / pci specific code, this driver
// is using neither.
unimplemented!()
}

fn get_name(&self) -> &'static str {
"loopback"
}
}

impl NetworkDriver for LoopbackDriver {
fn get_mac_address(&self) -> [u8; 6] {
// This matches Linux' behavior
[0; 6]
}

fn get_mtu(&self) -> u16 {
// Technically Linux uses 2^16, which we cannot use until we switch
// to u32 for MTU
u16::MAX
}

fn receive_packet(&mut self) -> Option<(RxToken, TxToken)> {
self.0
.pop_front()
.map(move |buffer| (RxToken::new(buffer), TxToken::new()))
}

fn send_packet<R, F>(&mut self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let mut buffer = Vec::with_capacity_in(len, DeviceAlloc);
buffer.resize(len, 0);
let result = f(&mut buffer);
self.0.push_back(buffer);
result
}

fn has_packet(&self) -> bool {
!self.0.is_empty()
}

fn set_polling_mode(&mut self, _value: bool) {
// no-op
}

fn handle_interrupt(&mut self) {
// no-op
}
}

pub(crate) static LOOPBACK: InterruptTicketMutex<LoopbackDriver> =
InterruptTicketMutex::new(LoopbackDriver::new());
34 changes: 34 additions & 0 deletions src/drivers/net/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#[cfg(all(target_arch = "riscv64", feature = "gem-net"))]
pub mod gem;
#[cfg(all(
not(any(
all(target_arch = "riscv64", feature = "gem-net", not(feature = "pci")),
all(target_arch = "x86_64", feature = "rtl8139"),
)),
feature = "net"
))]
pub mod loopback;
#[cfg(all(target_arch = "x86_64", feature = "rtl8139"))]
pub mod rtl8139;
#[cfg(not(all(target_arch = "x86_64", feature = "rtl8139")))]
Expand Down Expand Up @@ -56,3 +64,29 @@ pub(crate) fn mtu() -> u16 {
DEFAULT_MTU
}
}

cfg_if::cfg_if! {
if #[cfg(all(
not(feature = "pci"),
not(all(
feature = "net",
not(all(target_arch = "riscv64", feature = "gem-net"))
))
))] {
pub(crate) use crate::arch::kernel::mmio::get_network_driver;
} else if #[cfg(all(
feature = "pci",
not(all(
feature = "net",
not(all(target_arch = "x86_64", feature = "rtl8139"))
))
))] {
pub(crate) use crate::drivers::pci::get_network_driver;
} else {
use hermit_sync::InterruptTicketMutex;

pub(crate) fn get_network_driver() -> Option<&'static InterruptTicketMutex<loopback::LoopbackDriver>> {
Some(&loopback::LOOPBACK)
}
}
}
17 changes: 5 additions & 12 deletions src/executor/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};

use super::network::{NetworkInterface, NetworkState};
use crate::arch;
#[cfg(not(feature = "pci"))]
use crate::arch::kernel::mmio as hardware;
use crate::drivers::net::NetworkDriver;
#[cfg(feature = "pci")]
use crate::drivers::pci as hardware;
use crate::drivers::net::{NetworkDriver, get_network_driver};
use crate::mm::device_alloc::DeviceAlloc;

/// Data type to determine the mac address
Expand All @@ -42,7 +38,7 @@ impl HermitNet {
impl<'a> NetworkInterface<'a> {
#[cfg(feature = "dhcpv4")]
pub(crate) fn create() -> NetworkState<'a> {
let (mtu, mac, checksums) = if let Some(driver) = hardware::get_network_driver() {
let (mtu, mac, checksums) = if let Some(driver) = get_network_driver() {
let guard = driver.lock();
(
guard.get_mtu(),
Expand Down Expand Up @@ -98,7 +94,7 @@ impl<'a> NetworkInterface<'a> {

#[cfg(not(feature = "dhcpv4"))]
pub(crate) fn create() -> NetworkState<'a> {
let (mtu, mac, checksums) = if let Some(driver) = hardware::get_network_driver() {
let (mtu, mac, checksums) = if let Some(driver) = get_network_driver() {
let guard = driver.lock();
(
guard.get_mtu(),
Expand Down Expand Up @@ -182,7 +178,7 @@ impl Device for HermitNet {
}

fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if let Some(driver) = hardware::get_network_driver() {
if let Some(driver) = get_network_driver() {
driver.lock().receive_packet()
} else {
None
Expand Down Expand Up @@ -231,9 +227,6 @@ impl phy::TxToken for TxToken {
where
F: FnOnce(&mut [u8]) -> R,
{
hardware::get_network_driver()
.unwrap()
.lock()
.send_packet(len, f)
get_network_driver().unwrap().lock().send_packet(len, f)
}
}
6 changes: 1 addition & 5 deletions src/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@ use hermit_sync::without_interrupts;
use smoltcp::time::Instant;

use crate::arch::core_local::*;
#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "pci")))]
use crate::drivers::mmio::get_network_driver;
#[cfg(any(feature = "tcp", feature = "udp"))]
use crate::drivers::net::NetworkDriver;
#[cfg(all(any(feature = "tcp", feature = "udp"), feature = "pci"))]
use crate::drivers::pci::get_network_driver;
use crate::drivers::net::{NetworkDriver, get_network_driver};
use crate::errno::Errno;
use crate::executor::task::AsyncTask;
use crate::io;
Expand Down