net: Basic UDP/ICMP over IPv4 networking using virtio-net

This commit is contained in:
Mark Poliakov 2024-01-20 19:40:27 +02:00
parent 9bd29970f8
commit 5d8067991d
40 changed files with 4054 additions and 17 deletions

1055
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@ device-api-macros = { path = "lib/device-api/macros" }
# Drivers
ygg_driver_block = { path = "driver/block/core" }
ygg_driver_net_core = { path = "driver/net/core" }
kernel-fs = { path = "driver/fs/kernel-fs" }
memfs = { path = "driver/fs/memfs" }
@ -57,6 +58,7 @@ xhci_lib = { git = "https://github.com/rust-osdev/xhci.git", package = "xhci" }
ygg_driver_pci = { path = "driver/bus/pci" }
ygg_driver_nvme = { path = "driver/block/nvme" }
ygg_driver_ahci = { path = "driver/block/ahci" }
ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
[features]
default = ["fb_console"]

View File

@ -11,12 +11,48 @@ use yggdrasil_abi::error::Error;
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
use crate::PciBaseAddress;
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
fn from_space_offset(space: &'s S, offset: usize) -> Self;
fn space(&self) -> &'s S;
fn offset(&self) -> usize;
fn bar_index(&self) -> Option<usize> {
let value = self.space().read_u8(self.offset() + 4);
(value <= 0x5).then_some(value as _)
}
fn bar_offset(&self) -> usize {
let value = self.space().read_u32(self.offset() + 8);
value as _
}
fn length(&self) -> usize {
let value = self.space().read_u32(self.offset() + 12);
value as _
}
}
pub trait VirtioCapability {
const CFG_TYPE: u8;
const MIN_LEN: usize = 0;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
}
/// MSI-X capability query
pub struct MsiXCapability;
/// MSI capability query
pub struct MsiCapability;
// VirtIO-over-PCI capabilities
/// VirtIO PCI configuration access
pub struct VirtioDeviceConfigCapability;
/// VirtIO common configuration
pub struct VirtioCommonConfigCapability;
/// VirtIO notify configuration
pub struct VirtioNotifyConfigCapability;
/// Represents an entry in MSI-X vector table
#[repr(C)]
pub struct MsiXEntry {
@ -44,6 +80,39 @@ pub struct MsiData<'s, S: PciConfigurationSpace + ?Sized + 's> {
offset: usize,
}
pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioCommonConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioNotifyConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
impl<T: VirtioCapability> PciCapability for T {
const ID: PciCapabilityId = PciCapabilityId::VendorSpecific;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = T::Output<'a, S>;
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
let cfg_type = space.read_u8(offset + 3);
cfg_type == T::CFG_TYPE && len >= T::MIN_LEN
}
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
T::Output::from_space_offset(space, offset)
}
}
impl PciCapability for MsiXCapability {
const ID: PciCapabilityId = PciCapabilityId::MsiX;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiXData<'a, S>;
@ -51,6 +120,7 @@ impl PciCapability for MsiXCapability {
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
MsiXData { space, offset }
}
@ -63,11 +133,82 @@ impl PciCapability for MsiCapability {
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
MsiData { space, offset }
}
}
impl VirtioCapability for VirtioDeviceConfigCapability {
const CFG_TYPE: u8 = 0x04;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioDeviceConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioCommonConfigCapability {
const CFG_TYPE: u8 = 0x01;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioCommonConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioCommonConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioNotifyConfigCapability {
const CFG_TYPE: u8 = 0x02;
const MIN_LEN: usize = 0x14;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioNotifyConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioNotifyConfigData<'s, S> {
pub fn offset_multiplier(&self) -> usize {
self.space.read_u32(self.offset + 16) as usize
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioNotifyConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
// TODO use pending bits as well
/// Maps and returns the vector table associated with the device's MSI-X capability

View File

@ -73,6 +73,8 @@ pub enum PciBaseAddress {
pub enum PciCapabilityId {
/// MSI (32-bit or 64-bit)
Msi = 0x05,
/// Vendor-specific capability
VendorSpecific = 0x09,
/// MSI-X
MsiX = 0x11,
/// Unknown capability missing from this list
@ -86,10 +88,15 @@ pub trait PciCapability {
/// Wrapper for accessing the capability data structure
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a>;
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
true
}
/// Constructs an access wrapper for this capability with given offset
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
len: usize,
) -> Self::CapabilityData<'s, S>;
}
@ -104,6 +111,7 @@ pub struct PciDeviceInfo {
pub enum PciMatch {
Generic(fn(&PciDeviceInfo) -> bool),
Vendor(u16, u16),
Class(u8, Option<u8>, Option<u8>),
}
@ -134,6 +142,15 @@ pub struct PciBusManager {
segments: Vec<PciBusSegment>,
}
impl PciBaseAddress {
pub fn as_memory(self) -> usize {
match self {
Self::Memory(address) => address,
_ => panic!("Not a memory BAR"),
}
}
}
impl PciBusSegment {
fn probe_config_space(&self, address: PciAddress) -> Result<Option<PciConfigSpace>, Error> {
match self.ecam_phys_base {
@ -344,6 +361,9 @@ impl PciMatch {
pub fn check_device(&self, info: &PciDeviceInfo, class: u8, subclass: u8, prog_if: u8) -> bool {
match self {
Self::Generic(f) => f(info),
&Self::Vendor(vendor_, device_) => {
info.config_space.vendor_id() == vendor_ && info.config_space.device_id() == device_
}
&Self::Class(class_, Some(subclass_), Some(prog_if_)) => {
class_ == class && subclass_ == subclass && prog_if_ == prog_if
}
@ -367,6 +387,19 @@ pub fn register_class_driver(
});
}
pub fn register_vendor_driver(
name: &'static str,
vendor_id: u16,
device_id: u16,
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
) {
PCI_DRIVERS.lock().push(PciDriver {
name,
check: PciMatch::Vendor(vendor_id, device_id),
probe,
});
}
pub fn register_generic_driver(
name: &'static str,
check: fn(&PciDeviceInfo) -> bool,

View File

@ -79,12 +79,13 @@ pub struct CapabilityIterator<'s, S: PciConfigurationSpace + ?Sized> {
}
impl<'s, S: PciConfigurationSpace + ?Sized> Iterator for CapabilityIterator<'s, S> {
type Item = (PciCapabilityId, usize);
type Item = (PciCapabilityId, usize, usize);
fn next(&mut self) -> Option<Self::Item> {
let offset = self.current? & !0x3;
let id = unsafe { core::mem::transmute(self.space.read_u8(offset)) };
let len = self.space.read_u8(offset + 2);
let next_pointer = self.space.read_u8(offset + 1);
self.current = if next_pointer != 0 {
@ -93,7 +94,7 @@ impl<'s, S: PciConfigurationSpace + ?Sized> Iterator for CapabilityIterator<'s,
None
};
Some((id, offset))
Some((id, offset, len as usize))
}
}
@ -301,9 +302,9 @@ pub trait PciConfigurationSpace {
/// Locates a capability within this configuration space
fn capability<C: PciCapability>(&self) -> Option<C::CapabilityData<'_, Self>> {
self.capability_iter().find_map(|(id, offset)| {
if id == C::ID {
Some(C::data(self, offset))
self.capability_iter().find_map(|(id, offset, len)| {
if id == C::ID && C::check(self, offset, len) {
Some(C::data(self, offset, len))
} else {
None
}

View File

@ -1,9 +1,15 @@
//! Device virtual file system
use core::sync::atomic::{AtomicUsize, Ordering};
use core::{
net::IpAddr,
sync::atomic::{AtomicUsize, Ordering},
};
use alloc::{format, string::String};
use alloc::{format, string::String, sync::Arc};
use kernel_util::util::OneTimeInit;
use vfs::{impls::MemoryDirectory, CharDevice, Node, NodeFlags, NodeRef};
use vfs::{
impls::{mdir, FnValueNode, MemoryDirectory, ReadOnlyFnValueNode},
CharDevice, Node, NodeFlags, NodeRef,
};
use ygg_driver_block::BlockDevice;
use yggdrasil_abi::error::Error;
@ -16,11 +22,28 @@ pub enum CharDeviceType {
TtySerial,
}
/// Describes the kind of a network device
#[derive(Debug)]
pub enum NetworkDeviceType {
/// Ethernet device
Ethernet,
}
pub trait NetworkConfigInterface: Send + Sync {
fn address(&self) -> Result<IpAddr, Error>;
fn set_address(&self, addr: IpAddr) -> Result<(), Error>;
fn mac(&self) -> [u8; 6];
}
static DEVFS_ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
static NET_ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
/// Sets up the device filesystem
pub fn init() {
let root = MemoryDirectory::empty();
let net = MemoryDirectory::empty();
NET_ROOT.init(net.clone());
let root = mdir([("net", net)]);
DEVFS_ROOT.init(root);
}
@ -55,6 +78,59 @@ pub fn add_named_block_device<S: Into<String>>(
DEVFS_ROOT.get().add_child(name, node)
}
pub fn add_network_config<S: Into<String>>(name: S, node: NodeRef) -> Result<(), Error> {
let name = name.into();
NET_ROOT.get().add_child(name, node)
}
pub fn add_named_network_device<S: Into<String>>(
name: S,
dev: Arc<dyn NetworkConfigInterface>,
) -> Result<(), Error> {
let name = name.into();
log::info!("Add network device: {}", name);
let read_dev = dev.clone();
let write_dev = dev.clone();
let address = FnValueNode::new(
move || read_dev.address(),
move |value| write_dev.set_address(value),
);
let read_dev = dev.clone();
let mac = ReadOnlyFnValueNode::new(move || {
let bytes = read_dev.mac();
let mut out = String::new();
for (i, byte) in bytes.into_iter().enumerate() {
if i != 0 {
out.push(':');
}
out.push_str(&format!("{:02X}", byte));
}
Ok(out)
});
let dev_dir = mdir([("address", address), ("mac", mac)]);
NET_ROOT.get().add_child(name, dev_dir)
}
pub fn add_network_device(
dev: Arc<dyn NetworkConfigInterface>,
kind: NetworkDeviceType,
) -> Result<(), Error> {
static ETH_COUNT: AtomicUsize = AtomicUsize::new(0);
let (count, prefix) = match kind {
NetworkDeviceType::Ethernet => (&ETH_COUNT, "eth"),
};
let value = count.fetch_add(1, Ordering::AcqRel);
let name = format!("{}{}", prefix, value);
add_named_network_device(name, dev)
}
pub fn add_block_device_partition<S: Into<String>>(
base_name: S,
index: usize,

View File

@ -0,0 +1,16 @@
[package]
name = "ygg_driver_net_core"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
kernel-util = { path = "../../../lib/kernel-util" }
vfs = { path = "../../../lib/vfs" }
kernel-fs = { path = "../../fs/kernel-fs" }
log = "0.4.20"
bytemuck = { version = "1.14.0", features = ["derive"] }

View File

@ -0,0 +1,100 @@
use core::{fmt, mem::size_of};
use bytemuck::{Pod, Zeroable};
use kernel_util::mem::PageBox;
use yggdrasil_abi::error::Error;
use crate::{
interface::{self, NetworkInterface},
l3,
types::{MacAddress, NetValue, Value},
};
pub struct L2Packet {
pub interface_id: u32,
pub source_address: MacAddress,
pub destination_address: MacAddress,
pub l2_offset: usize,
pub l3_offset: usize,
pub data: PageBox<[u8]>,
}
impl L2Packet {
pub fn ethernet_frame(&self) -> &EthernetFrame {
bytemuck::from_bytes(
&self.data[self.l2_offset..self.l2_offset + size_of::<EthernetFrame>()],
)
}
pub fn l2_data(&self) -> &[u8] {
&self.data[self.l3_offset..]
}
}
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct EthernetFrame {
pub destination_mac: MacAddress,
pub source_mac: MacAddress,
pub ethertype: Value<EtherType>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Pod, Zeroable)]
#[repr(transparent)]
pub struct EtherType(u16);
impl EtherType {
pub const ARP: Self = Self(0x0806);
pub const IPV4: Self = Self(0x0800);
}
impl NetValue for EtherType {
fn to_network_order(self) -> Value<Self> {
Value(Self(self.0.to_be()))
}
fn from_network_order(value: Value<Self>) -> Self {
Self(u16::from_be(value.0 .0))
}
}
pub fn send_l2<T: Pod>(
interface: &NetworkInterface,
source_mac: MacAddress,
destination_mac: MacAddress,
ethertype: EtherType,
l2_data: &T,
) -> Result<(), Error> {
let l2_frame = EthernetFrame {
source_mac,
destination_mac,
ethertype: ethertype.to_network_order(),
};
log::debug!(
"send_l2: {} -> {}",
l2_frame.source_mac,
l2_frame.destination_mac
);
interface.send_l2(&l2_frame, bytemuck::bytes_of(l2_data))
}
pub fn handle(packet: L2Packet) {
let frame = packet.ethernet_frame();
let ty = EtherType::from_network_order(frame.ethertype);
match ty {
EtherType::ARP => l3::arp::handle_packet(packet),
EtherType::IPV4 => l3::ip::handle_v4_packet(packet),
p => {
log::debug!(
"Unrecognized L2 protocol: {:#06x}",
bytemuck::cast::<_, u16>(p)
);
}
}
}

View File

@ -0,0 +1,116 @@
use core::{
mem::size_of,
net::{IpAddr, Ipv4Addr},
sync::atomic::{AtomicU32, Ordering},
};
use alloc::{collections::BTreeMap, sync::Arc, vec};
use bytemuck::Pod;
use kernel_fs::devfs::{self, NetworkConfigInterface, NetworkDeviceType};
// TODO: link state management?
use kernel_util::{
mem::PageBox,
sync::{mutex::Mutex, spin_rwlock::IrqSafeRwLock},
};
use yggdrasil_abi::error::Error;
use crate::{
ethernet::EthernetFrame,
l3::{self, arp::ARP_TABLE, IpFrame, Route},
types::{MacAddress, SubnetAddress, SubnetV4Address},
};
pub trait NetworkDevice: Sync {
fn transmit(&self, packet: PageBox<[u8]>) -> Result<(), Error>;
fn packet_prefix_size(&self) -> usize;
fn read_hardware_address(&self) -> MacAddress;
}
pub struct NetworkInterface {
pub(crate) device: &'static dyn NetworkDevice,
pub(crate) mac: MacAddress,
pub(crate) address: IrqSafeRwLock<Option<IpAddr>>,
pub(crate) id: u32,
}
impl NetworkConfigInterface for NetworkInterface {
fn address(&self) -> Result<IpAddr, Error> {
if let Some(address) = self.address.read().as_ref() {
Ok(*address)
} else {
Err(Error::InvalidOperation)
}
}
fn set_address(&self, addr: IpAddr) -> Result<(), Error> {
// Remove old ARP entry, if exists
ARP_TABLE.write().remove_ip(self.id, addr);
*self.address.write() = Some(addr);
ARP_TABLE.write().insert_ip(self.id, self.mac, addr, true);
match addr {
IpAddr::V4(v4) => {
l3::add_route(Route {
subnet: SubnetAddress::V4(SubnetV4Address::from_address_mask(v4, 24)),
interface: self.id,
// TODO manual configuration for this
gateway: Some(IpAddr::V4(Ipv4Addr::new(11, 0, 0, 1))),
});
}
IpAddr::V6(_) => todo!(),
}
Ok(())
}
fn mac(&self) -> [u8; 6] {
self.mac.into()
}
}
static ETH_INTERFACES: IrqSafeRwLock<BTreeMap<u32, Arc<NetworkInterface>>> =
IrqSafeRwLock::new(BTreeMap::new());
static ETH_INTERFACE_ID: AtomicU32 = AtomicU32::new(1);
impl NetworkInterface {
pub fn get(id: u32) -> Result<Arc<Self>, Error> {
ETH_INTERFACES
.read()
.get(&id)
.cloned()
.ok_or(Error::DoesNotExist)
}
pub fn send_l2(&self, l2_frame: &EthernetFrame, l2_data: &[u8]) -> Result<(), Error> {
let l2_offset = self.device.packet_prefix_size();
let l2_data_offset = l2_offset + size_of::<EthernetFrame>();
let mut packet = PageBox::new_slice(0, l2_data_offset + l2_data.len())?;
packet[l2_offset..l2_data_offset].copy_from_slice(bytemuck::bytes_of(l2_frame));
packet[l2_data_offset..].copy_from_slice(l2_data);
self.device.transmit(packet)
}
}
pub fn register_interface(dev: &'static dyn NetworkDevice) -> u32 {
let mac = dev.read_hardware_address();
let id = ETH_INTERFACE_ID.fetch_add(1, Ordering::SeqCst);
let iface = NetworkInterface {
device: dev,
mac,
address: IrqSafeRwLock::new(None),
id,
};
log::info!("Registered network interface #{}: {}", id, mac);
let interface = Arc::new(iface);
devfs::add_network_device(interface.clone(), NetworkDeviceType::Ethernet).unwrap();
ETH_INTERFACES.write().insert(id, interface);
id
}

View File

@ -0,0 +1,142 @@
use core::{
borrow::Borrow,
mem::size_of,
net::{IpAddr, Ipv4Addr},
};
use alloc::{collections::BTreeMap, sync::Arc};
use bytemuck::{Pod, Zeroable};
use kernel_util::{mem::PageBox, sync::spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::error::Error;
use crate::{
ethernet::{self, EtherType, EthernetFrame},
interface::NetworkInterface,
types::{MacAddress, NetValue, Value},
L2Packet,
};
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C, packed)]
struct ArpFrame {
pub hardware_type: Value<u16>,
pub protocol: Value<EtherType>,
pub hardware_size: u8,
pub protocol_size: u8,
pub opcode: Value<u16>,
pub sender_mac: MacAddress,
pub sender_ip: Value<u32>,
pub target_mac: MacAddress,
// TODO handle IPv6
pub target_ip: Value<u32>,
}
pub struct ArpTable {
entries_v4: BTreeMap<(u32, Ipv4Addr), (MacAddress, bool)>,
reverse_v4: BTreeMap<(u32, MacAddress), (Ipv4Addr, bool)>,
}
impl ArpTable {
pub const fn new() -> Self {
Self {
reverse_v4: BTreeMap::new(),
entries_v4: BTreeMap::new(),
}
}
// TODO multiple IPs per MAC?
pub fn insert_v4(&mut self, interface: u32, mac: MacAddress, ip: Ipv4Addr, own: bool) {
if !self.reverse_v4.contains_key(&(interface, mac)) {
log::debug!("Insert {} <-> {}", mac, ip);
}
self.reverse_v4.insert((interface, mac), (ip, own));
self.entries_v4.insert((interface, ip), (mac, own));
}
pub fn insert_ip(&mut self, interface: u32, mac: MacAddress, ip: IpAddr, own: bool) {
match ip {
IpAddr::V4(v4) => self.insert_v4(interface, mac, v4, own),
IpAddr::V6(_) => todo!(),
}
}
pub fn remove_ip(&mut self, interface: u32, ip: IpAddr) {
match ip {
IpAddr::V4(v4) => {
let entry = self.entries_v4.remove(&(interface, v4));
if let Some((mac, _)) = entry {
self.reverse_v4.remove(&(interface, mac)).unwrap();
}
}
IpAddr::V6(_) => todo!(),
}
}
pub fn get_v4(&self, interface: u32, ip: Ipv4Addr) -> Option<(MacAddress, bool)> {
self.entries_v4.get(&(interface, ip)).cloned()
}
}
pub(crate) static ARP_TABLE: IrqSafeRwLock<ArpTable> = IrqSafeRwLock::new(ArpTable::new());
pub fn lookup(interface: u32, ip: IpAddr) -> Option<MacAddress> {
// TODO send a request if MAC is not yet in the table
match ip {
IpAddr::V4(v4) => Some(ARP_TABLE.read().get_v4(interface, v4)?.0),
IpAddr::V6(_) => todo!(),
}
}
fn send_reply(interface_id: u32, arp: &ArpFrame, target_mac: MacAddress) -> Result<(), Error> {
let interface = NetworkInterface::get(interface_id)?;
let reply = ArpFrame {
protocol: arp.protocol,
hardware_type: arp.hardware_type,
hardware_size: arp.hardware_size,
protocol_size: arp.protocol_size,
opcode: 2u16.to_network_order(),
sender_mac: target_mac,
sender_ip: arp.target_ip,
target_ip: arp.sender_ip,
target_mac: arp.sender_mac,
};
ethernet::send_l2(
&interface,
target_mac,
arp.sender_mac,
EtherType::ARP,
&reply,
)
}
pub fn handle_packet(packet: L2Packet) {
let arp: &ArpFrame = bytemuck::from_bytes(&packet.l2_data()[..size_of::<ArpFrame>()]);
let proto = EtherType::from_network_order(arp.protocol);
let opcode = u16::from_network_order(arp.opcode);
let (target_address, sender_address) = match proto {
EtherType::IPV4 => (
Ipv4Addr::from(u32::from_network_order(arp.target_ip)),
Ipv4Addr::from(u32::from_network_order(arp.sender_ip)),
),
_ => {
log::warn!("TODO: unhandled ARP proto: {:#x?}", proto);
return;
}
};
let mut table = ARP_TABLE.write();
table.insert_v4(packet.interface_id, arp.sender_mac, sender_address, false);
if opcode == 1 {
// Don't answer with non-owned addresses
if let Some((mac, true)) = table.get_v4(packet.interface_id, target_address) {
// Reply with own address
send_reply(packet.interface_id, arp, mac).ok();
}
}
}

View File

@ -0,0 +1,148 @@
use core::{
fmt,
mem::size_of,
net::{IpAddr, Ipv4Addr},
};
use alloc::sync::Arc;
use bytemuck::{Pod, Zeroable};
use yggdrasil_abi::error::Error;
use crate::{
ethernet::EthernetFrame,
interface::NetworkInterface,
l3,
l4::udp,
types::{InetChecksum, NetValue, Value},
L2Packet, L3Packet, ACCEPT_QUEUE,
};
use super::IpFrame;
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct Ipv4Frame {
pub version_length: u8,
pub dscp_flags: u8,
pub total_length: Value<u16>,
pub id: Value<u16>,
pub flags_frag: Value<u16>,
pub ttl: u8,
pub protocol: Protocol,
pub header_checksum: Value<u16>,
pub source_address: Value<u32>,
pub destination_address: Value<u32>,
}
#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct Protocol(pub u8);
impl Protocol {
pub const ICMP: Self = Self(1);
pub const UDP: Self = Self(17);
}
impl fmt::Debug for Protocol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::ICMP => f.write_str("ICMP"),
Self::UDP => f.write_str("UDP"),
_ => f.write_str("<unknown-l4-proto>"),
}
}
}
impl Ipv4Frame {
fn header_length(&self) -> usize {
core::cmp::min(
(self.version_length & 0xF) << 2,
size_of::<Ipv4Frame>() as u8,
) as usize
}
fn total_length(&self) -> usize {
u16::from_network_order(self.total_length) as usize
}
}
impl IpFrame for Ipv4Frame {
fn destination_ip(&self) -> IpAddr {
IpAddr::V4(Ipv4Addr::from(u32::from_network_order(
self.destination_address,
)))
}
fn source_ip(&self) -> IpAddr {
IpAddr::V4(Ipv4Addr::from(u32::from_network_order(self.source_address)))
}
fn data_length(&self) -> usize {
self.total_length()
.checked_sub(self.header_length())
.unwrap_or(0)
}
}
pub fn handle_v4_packet(packet: L2Packet) {
let Ok(interface) = NetworkInterface::get(packet.interface_id) else {
log::debug!("Invalid interface ID in L2 packet");
return;
};
let l2_data = packet.l2_data();
let l3_frame: &Ipv4Frame = bytemuck::from_bytes(&l2_data[..size_of::<Ipv4Frame>()]);
let header_length = l3_frame.header_length();
let l3_data = &l2_data[size_of::<Ipv4Frame>()..];
let is_input = interface
.address
.read()
.map(|address| address == l3_frame.destination_ip())
.unwrap_or(false);
if is_input {
// Extract ports from L4 proto
let (source_port, destination_port) = match l3_frame.protocol {
Protocol::UDP => {
// TODO check size
let l4_frame: &udp::UdpFrame =
bytemuck::from_bytes(&l3_data[..size_of::<udp::UdpFrame>()]);
(
Some(u16::from_network_order(l4_frame.source_port)),
Some(u16::from_network_order(l4_frame.destination_port)),
)
}
Protocol::ICMP => (None, None),
_ => (None, None),
};
let l3_packet = L3Packet {
interface_id: packet.interface_id,
protocol: l3_frame.protocol,
source_address: l3_frame.source_ip(),
destination_address: l3_frame.destination_ip(),
source_port,
destination_port,
l2_offset: packet.l2_offset,
l3_offset: packet.l3_offset,
l4_offset: packet.l3_offset + header_length,
data_length: l3_frame.data_length(),
data: packet.data,
};
ACCEPT_QUEUE.receive_packet(l3_packet).ok();
} else {
// TODO forwarding
log::debug!(
"Dropped forwarded IPv4: {} -> {}",
l3_frame.source_ip(),
l3_frame.destination_ip()
);
}
}

View File

@ -0,0 +1,187 @@
use core::{
fmt,
mem::size_of,
net::{IpAddr, Ipv4Addr},
str::FromStr,
};
use alloc::{sync::Arc, vec::Vec};
use bytemuck::Pod;
use kernel_util::{mem::PageBox, sync::spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::error::Error;
use crate::{
ethernet::{EtherType, EthernetFrame},
interface::NetworkInterface,
l3::ip::Ipv4Frame,
l4,
types::{InetChecksum, NetValue, SubnetAddress},
};
use self::ip::Protocol;
pub mod arp;
pub mod ip;
pub struct L3Packet {
pub interface_id: u32,
pub protocol: Protocol,
pub source_address: IpAddr,
pub destination_address: IpAddr,
pub source_port: Option<u16>,
pub destination_port: Option<u16>,
pub l2_offset: usize,
pub l3_offset: usize,
pub l4_offset: usize,
pub data_length: usize,
pub data: PageBox<[u8]>,
}
pub trait IpFrame: Pod {
fn destination_ip(&self) -> IpAddr;
fn source_ip(&self) -> IpAddr;
fn data_length(&self) -> usize;
}
// TODO use range map for this?
pub struct Route {
pub subnet: SubnetAddress,
pub interface: u32,
pub gateway: Option<IpAddr>,
}
static ROUTES: IrqSafeRwLock<Vec<Route>> = IrqSafeRwLock::new(Vec::new());
impl L3Packet {
pub fn l3_data(&self) -> &[u8] {
&self.data[self.l4_offset..]
}
}
pub fn lookup_route(address: IpAddr) -> Option<(u32, Option<IpAddr>)> {
let routes = ROUTES.read();
for route in routes.iter() {
if route.subnet.contains(&address) {
return Some((route.interface, route.gateway));
}
}
None
}
pub fn list_routes<F: FnMut(&Route)>(mut f: F) {
let routes = ROUTES.read();
for route in routes.iter() {
f(route);
}
}
pub fn add_route(route: Route) -> Result<(), Error> {
// TODO check for conflicts
log::debug!("Add route: {}", route);
ROUTES.write().push(route);
Ok(())
}
impl fmt::Display for Route {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ", self.subnet)?;
if let Some(gw) = self.gateway {
write!(f, " via {}", gw)?;
}
Ok(())
}
}
pub fn handle_accepted(l3_packet: L3Packet) -> Result<(), Error> {
match l3_packet.protocol {
Protocol::UDP => l4::udp::handle(l3_packet),
Protocol::ICMP => l4::icmp::handle(l3_packet),
_ => todo!(),
}
}
pub fn send_l4_ip<L4: Pod>(
destination_ip: IpAddr,
protocol: ip::Protocol,
l4_frame: &L4,
l4_data: &[u8],
) -> Result<(), Error> {
send_l4_ip_opt(destination_ip, protocol, l4_frame, &[], l4_data)
}
pub fn send_l4_ip_opt<L4: Pod>(
destination_ip: IpAddr,
protocol: ip::Protocol,
l4_frame: &L4,
l4_options: &[u8],
l4_data: &[u8],
) -> Result<(), Error> {
let IpAddr::V4(destination_v4) = destination_ip else {
log::debug!("Destination: {}", destination_ip);
todo!();
};
// Lookup route to destination
let (iface_id, gateway) = lookup_route(destination_ip).unwrap();
let iface = NetworkInterface::get(iface_id).unwrap();
let Some(IpAddr::V4(gateway_v4)) = gateway else {
todo!();
};
let Some(IpAddr::V4(source_v4)) = *iface.address.read() else {
todo!();
};
// Lookup gateway MAC
let gateway_mac = arp::lookup(iface.id, IpAddr::V4(gateway_v4)).unwrap();
let l2_offset = iface.device.packet_prefix_size();
let l3_offset = l2_offset + size_of::<EthernetFrame>();
let l4_offset = l3_offset + size_of::<Ipv4Frame>();
let l4_data_offset = l4_offset + size_of::<L4>();
let mut packet = PageBox::new_slice(0, l4_data_offset + l4_data.len())?;
let l2_frame: &mut EthernetFrame = bytemuck::from_bytes_mut(&mut packet[l2_offset..l3_offset]);
l2_frame.source_mac = iface.mac;
l2_frame.destination_mac = gateway_mac;
l2_frame.ethertype = EtherType::IPV4.to_network_order();
let l3_frame: &mut Ipv4Frame = bytemuck::from_bytes_mut(&mut packet[l3_offset..l4_offset]);
l3_frame.source_address = u32::to_network_order(u32::from(source_v4));
l3_frame.destination_address = u32::to_network_order(u32::from(destination_v4));
l3_frame.protocol = protocol;
l3_frame.version_length = 0x45;
l3_frame.dscp_flags = 0;
l3_frame.total_length = u16::to_network_order(
(size_of::<Ipv4Frame>() + size_of::<L4>() + l4_data.len())
.try_into()
.unwrap(),
);
// Disable fragmentation
l3_frame.flags_frag = u16::to_network_order(0x4000);
l3_frame.id = u16::to_network_order(0);
l3_frame.header_checksum = u16::to_network_order(0);
l3_frame.ttl = 64;
let l3_frame_bytes = &packet[l3_offset..l4_offset];
let mut ip_checksum = InetChecksum::new();
ip_checksum.add_bytes(l3_frame_bytes, true);
let ip_checksum = ip_checksum.finish();
let l3_frame: &mut Ipv4Frame = bytemuck::from_bytes_mut(&mut packet[l3_offset..l4_offset]);
l3_frame.header_checksum = u16::to_network_order(ip_checksum);
packet[l4_offset..l4_data_offset].copy_from_slice(bytemuck::bytes_of(l4_frame));
packet[l4_data_offset..l4_data_offset + l4_options.len()].copy_from_slice(l4_options);
packet[l4_data_offset + l4_options.len()..].copy_from_slice(l4_data);
iface.device.transmit(packet)
}

View File

@ -0,0 +1,104 @@
use core::{
mem::size_of,
net::{IpAddr, Ipv4Addr},
};
use bytemuck::{Pod, Zeroable};
use yggdrasil_abi::error::Error;
use crate::{
l3::{self, ip::Protocol},
types::{InetChecksum, NetValue, Value},
L3Packet,
};
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct IcmpV4Frame {
ty: u8,
code: u8,
checksum: Value<u16>,
rest: Value<u32>,
}
fn send_v4_reply(
destination_ip: Ipv4Addr,
icmp_frame: &IcmpV4Frame,
icmp_data: &[u8],
) -> Result<(), Error> {
let mut reply_frame = IcmpV4Frame {
ty: 0,
code: 0,
checksum: u16::to_network_order(0),
rest: icmp_frame.rest,
};
if icmp_data.len() % 2 != 0 {
todo!();
}
let l4_bytes = bytemuck::bytes_of(&reply_frame);
let mut checksum = InetChecksum::new();
checksum.add_bytes(l4_bytes, true);
checksum.add_bytes(icmp_data, true);
reply_frame.checksum = checksum.finish().to_network_order();
l3::send_l4_ip(
IpAddr::V4(destination_ip),
Protocol::ICMP,
&reply_frame,
icmp_data,
)
}
fn handle_v4(source_address: Ipv4Addr, l3_packet: L3Packet) -> Result<(), Error> {
if l3_packet.data_length < size_of::<IcmpV4Frame>() {
log::debug!("Truncated ICMPv4 packet");
return Err(Error::MissingData);
}
if l3_packet.data_length - size_of::<IcmpV4Frame>() > 576 {
log::debug!("ICMPv4 packet too large");
return Err(Error::MissingData);
}
let l3_data = l3_packet.l3_data();
let icmp_frame: &IcmpV4Frame = bytemuck::from_bytes(&l3_data[..size_of::<IcmpV4Frame>()]);
let icmp_data = &l3_data[size_of::<IcmpV4Frame>()..l3_packet.data_length];
match (icmp_frame.ty, icmp_frame.code) {
(8, 0) => send_v4_reply(source_address, icmp_frame, icmp_data),
_ => {
log::debug!(
"Ignoring unknown ICMPv4 type:code: {}:{}",
icmp_frame.ty,
icmp_frame.code
);
return Ok(());
}
}
}
pub fn handle(l3_packet: L3Packet) -> Result<(), Error> {
match l3_packet.source_address {
IpAddr::V4(v4) => handle_v4(v4, l3_packet),
IpAddr::V6(_) => todo!(),
}
}
// fn send_icmp_v4_reply(
// l3_frame: &Ipv4Frame,
// icmp_frame: &IcmpV4Frame,
// icmp_data: &[u8],
// ) -> Result<(), Error> {
//
// l3::send_l4_ip(l3_frame.source_ip(), Protocol::ICMP, &l4_frame, icmp_data)
// }
//
// pub fn handle_icmp_v4_packet(
// interface: Arc<NetworkInterface>,
// l3_frame: &Ipv4Frame,
// l3_data: &[u8],
// ) {
// }

View File

@ -0,0 +1,2 @@
pub mod icmp;
pub mod udp;

View File

@ -0,0 +1,79 @@
use core::{
mem::size_of,
net::{IpAddr, SocketAddr},
};
use bytemuck::{Pod, Zeroable};
use yggdrasil_abi::error::Error;
use crate::{
l3::{self, ip::Protocol, IpFrame},
socket::{self, UdpSocket},
types::{NetValue, Value},
L3Packet,
};
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct UdpFrame {
pub source_port: Value<u16>,
pub destination_port: Value<u16>,
pub length: Value<u16>,
pub checksum: Value<u16>,
}
impl UdpFrame {
fn data_length(&self) -> usize {
(u16::from_network_order(self.length) as usize)
.checked_sub(0)
.unwrap_or(0)
}
}
pub fn send(
source_port: u16,
destination_ip: IpAddr,
destination_port: u16,
data: &[u8],
) -> Result<(), Error> {
let length: u16 = (data.len() + size_of::<UdpFrame>()).try_into().unwrap();
let udp_frame = UdpFrame {
source_port: source_port.to_network_order(),
destination_port: destination_port.to_network_order(),
length: length.to_network_order(),
checksum: 0u16.to_network_order(),
};
l3::send_l4_ip(destination_ip, Protocol::UDP, &udp_frame, data)
}
pub fn handle(l3_packet: L3Packet) -> Result<(), Error> {
if l3_packet.data_length < size_of::<UdpFrame>() {
log::warn!("Truncated UDP frame received");
return Err(Error::MissingData);
}
let l3_data = l3_packet.l3_data();
let udp_frame: &UdpFrame = bytemuck::from_bytes(&l3_data[..size_of::<UdpFrame>()]);
let data_size = core::cmp::min(
udp_frame.data_length(),
l3_packet.data_length - size_of::<UdpFrame>(),
);
let udp_data = &l3_data[size_of::<UdpFrame>()..data_size + size_of::<UdpFrame>()];
let source = SocketAddr::new(
l3_packet.source_address,
u16::from_network_order(udp_frame.source_port),
);
let destination = SocketAddr::new(
l3_packet.destination_address,
u16::from_network_order(udp_frame.destination_port),
);
if let Some(socket) = UdpSocket::get(&destination) {
socket.packet_received(source, udp_data);
}
Ok(())
}

139
driver/net/core/src/lib.rs Normal file
View File

@ -0,0 +1,139 @@
#![feature(map_try_insert)]
#![no_std]
extern crate alloc;
use core::{
fmt,
future::Future,
mem::size_of,
net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4},
pin::Pin,
str::FromStr,
sync::atomic::{AtomicU32, Ordering},
task::{Context, Poll},
};
use alloc::{collections::BTreeMap, format, string::String, vec::Vec};
use ethernet::{EtherType, EthernetFrame, L2Packet};
use interface::{NetworkDevice, NetworkInterface};
use kernel_fs::devfs;
use kernel_util::{
mem::PageBox,
runtime::{self, QueueWaker},
sync::{mutex::Mutex, spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
util::ring::RingBuffer,
};
use l3::{ip::Protocol, L3Packet, Route};
use queue::Queue;
use socket::UdpSocket;
use types::{MacAddress, NetValue, SubnetAddress, Value};
use vfs::{impls::FnValueNode, PacketSocket};
use yggdrasil_abi::error::Error;
use crate::types::SubnetV4Address;
pub mod ethernet;
pub mod l3;
pub mod l4;
pub mod socket;
pub mod interface;
pub mod queue;
pub mod types;
pub use interface::register_interface;
pub struct Packet {
// TODO info about "received" interface
buffer: PageBox<[u8]>,
offset: usize,
iface: u32,
}
impl Packet {
#[inline]
pub fn new(buffer: PageBox<[u8]>, offset: usize, iface: u32) -> Self {
Self {
buffer,
offset,
iface,
}
}
}
static PACKET_QUEUE: Queue<Packet> = Queue::new();
static ACCEPT_QUEUE: Queue<L3Packet> = Queue::new();
#[inline]
pub fn receive_packet(packet: Packet) -> Result<(), Error> {
PACKET_QUEUE.receive_packet(packet)
}
pub fn start_network_tasks() -> Result<(), Error> {
devfs::add_network_config(
"routes",
FnValueNode::new(
|| {
let mut output = String::new();
l3::list_routes(|route| {
output.push_str(&format!("{}\n", route));
});
Ok(output)
},
|_value| Err(Error::NotImplemented),
),
);
runtime::spawn(l2_packet_handler_worker())?;
for _ in 0..1 {
runtime::spawn(l3_accept_worker())?;
}
Ok(())
}
async fn l2_packet_handler_worker() {
loop {
let packet = PACKET_QUEUE.wait().await;
let eth_frame: &EthernetFrame = bytemuck::from_bytes(
&packet.buffer[packet.offset..packet.offset + size_of::<EthernetFrame>()],
);
let l2_packet = L2Packet {
interface_id: packet.iface,
source_address: eth_frame.source_mac,
destination_address: eth_frame.destination_mac,
l2_offset: packet.offset,
l3_offset: packet.offset + size_of::<EthernetFrame>(),
data: packet.buffer,
};
ethernet::handle(l2_packet);
}
}
async fn l3_accept_worker() {
loop {
let l3_packet = ACCEPT_QUEUE.wait().await;
log::debug!(
"INPUT {:?} {}:{:?} -> {}:{:?}: ACCEPT",
l3_packet.protocol,
l3_packet.source_address,
l3_packet.source_port,
l3_packet.destination_address,
l3_packet.destination_port
);
if let Err(error) = l3::handle_accepted(l3_packet) {
log::error!("L3 handle error: {:?}", error);
}
}
}

View File

@ -0,0 +1,53 @@
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use kernel_util::{runtime::QueueWaker, sync::IrqSafeSpinlock, util::ring::RingBuffer};
use yggdrasil_abi::error::Error;
pub struct Queue<T> {
queue: IrqSafeSpinlock<RingBuffer<T>>,
notify: QueueWaker,
}
impl<T> Queue<T> {
pub const fn new() -> Self {
Self {
queue: IrqSafeSpinlock::new(RingBuffer::with_capacity(1024)),
notify: QueueWaker::new(),
}
}
pub fn receive_packet(&self, packet: T) -> Result<(), Error> {
self.queue.lock().write(packet);
self.notify.wake_one();
// TODO notify of dropped packets
Ok(())
}
pub fn wait(&self) -> impl Future<Output = T> + '_ {
struct F<'f, T> {
queue: &'f Queue<T>,
}
impl<'f, T> Future for F<'f, T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.queue.notify.register(cx.waker());
let mut lock = self.queue.queue.lock();
if lock.is_readable() {
self.queue.notify.remove(cx.waker());
Poll::Ready(unsafe { lock.read_single_unchecked() })
} else {
Poll::Pending
}
}
}
F { queue: self }
}
}

View File

@ -0,0 +1,198 @@
use core::{
future::Future,
net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4},
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll},
};
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use kernel_util::{
block,
runtime::QueueWaker,
sync::{
mutex::{Mutex, MutexGuard},
spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard},
IrqSafeSpinlock, LockMethod,
},
util::ring::RingBuffer,
};
use vfs::{FileReadiness, PacketSocket, Socket};
use yggdrasil_abi::error::Error;
use crate::{l4, queue::Queue};
pub struct UdpSocket {
local: SocketAddr,
remote: Option<SocketAddr>,
// TODO just place packets here for one less copy?
receive_queue: Mutex<RingBuffer<(SocketAddr, Vec<u8>)>>,
receive_notify: QueueWaker,
}
pub struct SocketTable<T: Socket> {
inner: BTreeMap<SocketAddr, Arc<T>>,
}
impl<T: Socket> SocketTable<T> {
pub const fn new() -> Self {
Self {
inner: BTreeMap::new(),
}
}
pub fn insert(&mut self, address: SocketAddr, socket: Arc<T>) -> Result<(), Error> {
match self.inner.try_insert(address, socket) {
Ok(_) => Ok(()),
Err(_) => return Err(Error::AlreadyExists),
}
}
pub fn remove(&mut self, local: SocketAddr) -> Result<(), Error> {
match self.inner.remove(&local) {
Some(_) => Ok(()),
None => Err(Error::DoesNotExist),
}
}
pub fn get_exact(&self, local: &SocketAddr) -> Option<Arc<T>> {
self.inner.get(local).cloned()
}
pub fn get(&self, local: &SocketAddr) -> Option<Arc<T>> {
if let Some(socket) = self.inner.get(local) {
return Some(socket.clone());
}
match local {
SocketAddr::V4(v4) => {
let unspec_v4 = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, local.port());
self.inner.get(&SocketAddr::V4(unspec_v4)).cloned()
}
SocketAddr::V6(_) => todo!(),
}
}
}
static UDP_SOCKETS: IrqSafeRwLock<SocketTable<UdpSocket>> = IrqSafeRwLock::new(SocketTable::new());
impl UdpSocket {
pub fn bind(address: SocketAddr) -> Result<Arc<UdpSocket>, Error> {
let mut sockets = UDP_SOCKETS.write();
if sockets.get(&address).is_some() {
// TODO use network-specific error
return Err(Error::AlreadyExists);
}
let socket = Arc::new(Self {
local: address,
remote: None,
receive_queue: Mutex::new(RingBuffer::try_with_capacity(128)?),
receive_notify: QueueWaker::new(),
});
sockets.insert(address, socket.clone());
log::debug!("UDP socket opened: {}", address);
Ok(socket)
}
pub fn connect(address: SocketAddr) -> Result<Arc<UdpSocket>, Error> {
todo!()
}
pub fn get(local: &SocketAddr) -> Option<Arc<UdpSocket>> {
UDP_SOCKETS.read().get(local)
}
fn poll_receive(
&self,
cx: &mut Context<'_>,
) -> Poll<Result<MutexGuard<RingBuffer<(SocketAddr, Vec<u8>)>>, Error>> {
self.receive_notify.register(cx.waker());
let mut lock = self.receive_queue.lock()?;
if lock.is_readable() {
self.receive_notify.remove(cx.waker());
Poll::Ready(Ok(lock))
} else {
Poll::Pending
}
}
pub fn receive_raw<'a>(
&'a self,
) -> impl Future<Output = Result<(SocketAddr, Vec<u8>), Error>> + 'a {
struct F<'f> {
socket: &'f UdpSocket,
}
impl<'f> Future for F<'f> {
type Output = Result<(SocketAddr, Vec<u8>), Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.socket.poll_receive(cx)? {
Poll::Ready(mut lock) => {
let (source, data) = unsafe { lock.read_single_unchecked() };
Poll::Ready(Ok((source, data)))
}
Poll::Pending => Poll::Pending,
}
}
}
F { socket: self }
}
pub fn packet_received(&self, source: SocketAddr, data: &[u8]) -> Result<(), Error> {
let mut lock = self.receive_queue.lock()?;
lock.write((source, Vec::from(data)));
self.receive_notify.wake_one();
Ok(())
}
}
impl FileReadiness for UdpSocket {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
self.poll_receive(cx).map_ok(|_| ())
}
}
impl PacketSocket for UdpSocket {
fn send(&self, destination: SocketAddr, data: &[u8]) -> Result<usize, Error> {
// TODO check that destnation family matches self family
l4::udp::send(
self.local.port(),
destination.ip(),
destination.port(),
data,
)?;
Ok(data.len())
}
fn receive(&self, buffer: &mut [u8]) -> Result<(SocketAddr, usize), Error> {
let (source, data) = block!(self.receive_raw().await)??;
if data.len() > buffer.len() {
todo!()
}
buffer[..data.len()].copy_from_slice(&data);
Ok((source, data.len()))
}
}
impl Socket for UdpSocket {
fn local_address(&self) -> SocketAddr {
self.local
}
fn remote_address(&self) -> Option<SocketAddr> {
self.remote
}
fn close(&self) -> Result<(), Error> {
log::debug!("UDP socket closed: {}", self.local);
UDP_SOCKETS.write().remove(self.local)
}
}

View File

@ -0,0 +1,225 @@
use core::{
fmt,
net::{IpAddr, Ipv4Addr},
str::FromStr,
};
use bytemuck::{Pod, Zeroable};
use yggdrasil_abi::error::Error;
#[macro_export]
macro_rules! wrap_value {
($vis:vis struct $name:ident($ty_vis:vis $ty:ty)) => {
#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
$vis struct $name($ty_vis $ty);
impl $crate::types::NetValue for $name {
fn from_network_order(value: $crate::types::Value<Self>) -> Self {
todo!()
}
fn to_network_order(self) -> $crate::types::Value<Self> {
todo!()
}
}
};
}
pub trait NetValue: Copy + Eq + Pod + Zeroable {
fn from_network_order(value: Value<Self>) -> Self;
fn to_network_order(self) -> Value<Self>;
}
#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct Value<T: NetValue>(pub T);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Pod, Zeroable)]
#[repr(transparent)]
pub struct MacAddress([u8; 6]);
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct SubnetV4Address {
pub root: Ipv4Addr,
pub mask_bits: u8,
pub mask: u32,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SubnetAddress {
V4(SubnetV4Address),
}
impl<T: NetValue + fmt::Debug> fmt::Debug for Value<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = T::from_network_order(*self);
fmt::Debug::fmt(&value, f)
}
}
impl NetValue for u16 {
fn from_network_order(value: Value<Self>) -> Self {
Self::from_be(value.0)
}
fn to_network_order(self) -> Value<Self> {
Value(self.to_be())
}
}
impl NetValue for u32 {
fn from_network_order(value: Value<Self>) -> Self {
Self::from_be(value.0)
}
fn to_network_order(self) -> Value<Self> {
Value(self.to_be())
}
}
impl fmt::Display for MacAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use fmt::Write;
for (i, &v) in self.0.iter().enumerate() {
if i != 0 {
write!(f, ":")?;
}
write!(f, "{:02X}", v)?;
}
Ok(())
}
}
impl From<MacAddress> for [u8; 6] {
fn from(value: MacAddress) -> Self {
value.0
}
}
impl From<[u8; 6]> for MacAddress {
fn from(value: [u8; 6]) -> Self {
Self(value)
}
}
impl SubnetV4Address {
pub const UNSPECIFIED: Self = Self {
root: Ipv4Addr::new(0, 0, 0, 0),
mask: 0,
mask_bits: 0,
};
pub fn from_address_mask(root: Ipv4Addr, mask_bits: u8) -> Self {
let root = u32::from(root);
let mask = Self::make_mask(mask_bits);
// Clear masked bits
let root = root & mask;
Self {
root: Ipv4Addr::from(root),
mask_bits,
mask,
}
}
pub fn contains(&self, address: &Ipv4Addr) -> bool {
let root = u32::from(self.root);
let address = u32::from(*address);
(address & self.mask) == root
}
fn make_mask(bits: u8) -> u32 {
((1 << bits) - 1) << (32 - bits)
}
}
impl fmt::Display for SubnetV4Address {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.root, self.mask_bits)
}
}
impl FromStr for SubnetV4Address {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (addr, mask) = s.split_once('/').ok_or(Error::InvalidArgument)?;
let addr = Ipv4Addr::from_str(addr).map_err(|_| Error::InvalidArgument)?;
let mask = u8::from_str(mask).map_err(|_| Error::InvalidArgument)?;
Ok(Self::from_address_mask(addr, mask))
}
}
impl SubnetAddress {
pub const UNSPECIFIED_V4: Self = Self::V4(SubnetV4Address::UNSPECIFIED);
pub fn contains(&self, address: &IpAddr) -> bool {
match (self, address) {
(Self::V4(subnet), IpAddr::V4(address)) => subnet.contains(address),
_ => false,
}
}
pub fn is_v4(&self) -> bool {
true
}
}
impl From<SubnetV4Address> for SubnetAddress {
fn from(value: SubnetV4Address) -> Self {
Self::V4(value)
}
}
impl FromStr for SubnetAddress {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// TODO v6
let v4 = SubnetV4Address::from_str(s)?;
Ok(Self::from(v4))
}
}
impl fmt::Display for SubnetAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::V4(v4) => fmt::Display::fmt(v4, f),
}
}
}
pub struct InetChecksum {
state: u32,
}
impl InetChecksum {
pub fn new() -> Self {
Self { state: 0 }
}
pub fn add_bytes(&mut self, bytes: &[u8], reorder: bool) {
let len = bytes.len();
for i in 0..len / 2 {
let word = if reorder {
((bytes[i * 2] as u16) << 8) | (bytes[i * 2 + 1] as u16)
} else {
(bytes[i * 2] as u16) | ((bytes[i * 2 + 1] as u16) << 8)
};
self.state = self.state.wrapping_add(word as u32);
}
if len % 2 != 0 {
self.state += self.state.wrapping_add(bytes[len - 1] as u32);
}
}
pub fn finish(self) -> u16 {
let sum = (self.state >> 16) + (self.state & 0xFFFF);
(!(sum & 0xFFFF)) as u16
}
}

View File

@ -0,0 +1,21 @@
[package]
name = "ygg_driver_virtio_core"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
kernel-util = { path = "../../../lib/kernel-util" }
device-api = { path = "../../../lib/device-api", features = ["derive"] }
ygg_driver_pci = { path = "../../bus/pci", optional = true }
log = "0.4.20"
bitflags = "2.4.2"
tock-registers = "0.8.1"
[features]
default = []
pci = ["ygg_driver_pci"]

View File

@ -0,0 +1,20 @@
#[derive(Debug)]
pub enum Error {
OsError(yggdrasil_abi::error::Error),
InvalidPciConfiguration,
NoCommonConfigCapability,
NoNotifyConfigCapability,
NoDeviceConfigCapability,
QueueTooLarge,
InvalidQueueSize,
EmptyTransaction,
QueueFull,
QueueEmpty,
WrongToken,
}
impl From<yggdrasil_abi::error::Error> for Error {
fn from(value: yggdrasil_abi::error::Error) -> Self {
Self::OsError(value)
}
}

View File

@ -0,0 +1,54 @@
#![no_std]
extern crate alloc;
pub mod error;
pub mod queue;
pub mod transport;
use bitflags::bitflags;
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
register_structs! {
pub CommonConfiguration {
(0x00 => device_feature_select: ReadWrite<u32>),
(0x04 => device_feature: ReadOnly<u32>),
(0x08 => driver_feature_select: ReadWrite<u32>),
(0x0C => driver_feature: ReadWrite<u32>),
(0x10 => msix_config: ReadWrite<u16>),
(0x12 => num_queues: ReadOnly<u16>),
(0x14 => device_status: ReadWrite<u8>),
(0x15 => config_generation: ReadOnly<u8>),
(0x16 => queue_select: ReadWrite<u16>),
(0x18 => queue_size: ReadWrite<u16>),
(0x1A => queue_msix_vector: ReadWrite<u16>),
(0x1C => queue_enable: ReadWrite<u16>),
(0x1E => queue_notify_off: ReadWrite<u16>),
(0x20 => queue_desc: ReadWrite<u64>),
(0x28 => queue_driver: ReadWrite<u64>),
(0x30 => queue_device: ReadWrite<u64>),
(0x38 => @END),
}
}
bitflags! {
#[derive(Clone, Copy)]
pub struct DeviceStatus: u8 {
const ACKNOWLEDGE = 1 << 0;
const DRIVER = 1 << 1;
const DRIVER_OK = 1 << 2;
const FEATURES_OK = 1 << 3;
const DEVICE_NEEDS_RESET = 1 << 6;
const FAILED = 1 << 7;
}
}
impl DeviceStatus {
pub const RESET_VALUE: Self = Self::empty();
}

View File

@ -0,0 +1,344 @@
//! VirtIO queue implementation.
//!
//! # Note
//!
//! The code is poorly borrowed from `virtio-drivers` crate. I want to rewrite it properly myself.
use core::{
mem::MaybeUninit,
sync::atomic::{fence, Ordering},
};
use alloc::boxed::Box;
use kernel_util::mem::{
address::{AsPhysicalAddress, IntoRaw},
PageBox,
};
use crate::{error::Error, transport::Transport};
#[derive(Debug)]
#[repr(C)]
struct Descriptor {
address: u64,
len: u32,
flags: u16,
next: u16,
}
// Layout:
// {
// flags: u16,
// idx: u16,
// ring: [u16; QUEUE_SIZE],
// used_event: u16
// }
struct AvailableRing {
data: PageBox<[MaybeUninit<u16>]>,
}
// Layout:
// {
// flags: u16,
// idx: u16,
// ring: [UsedElem; QUEUE_SIZE],
// avail_event: u16,
// _pad: u16
// }
struct UsedRing {
data: PageBox<[MaybeUninit<u32>]>,
used_count: usize,
}
pub struct VirtQueue {
descriptor_table: PageBox<[MaybeUninit<Descriptor>]>,
available: AvailableRing,
used: UsedRing,
capacity: usize,
queue_index: u16,
free_head: u16,
avail_idx: u16,
last_used_idx: u16,
msix_vector: u16,
}
impl AvailableRing {
pub fn with_capacity(capacity: usize) -> Result<Self, Error> {
let mut data = PageBox::new_uninit_slice(capacity + 3)?;
data[1].write(0);
Ok(Self { data })
}
pub fn set_head(&mut self, slot: u16, head: u16) {
self.data[slot as usize + 2].write(head);
}
pub fn set_index(&mut self, index: u16) {
self.data[1].write(index);
}
}
impl UsedRing {
pub fn with_capacity(capacity: usize) -> Result<Self, Error> {
let mut data = PageBox::new_uninit_slice(capacity * 2 + 2)?;
data[0].write(0);
Ok(Self {
data,
used_count: 0,
})
}
pub fn read_slot(&self, index: u16) -> (u32, u32) {
let index = unsafe { self.data[1 + index as usize * 2].assume_init() };
let len = unsafe { self.data[2 + index as usize * 2].assume_init() };
(index, len)
}
pub fn index(&self) -> u16 {
unsafe { (self.data[0].assume_init() >> 16) as u16 }
}
}
impl VirtQueue {
pub fn with_capacity<T: Transport>(
transport: &mut T,
index: u16,
capacity: usize,
msix_vector: Option<u16>,
) -> Result<Self, Error> {
// TODO check if queue is already set up
let max_capacity = transport.max_queue_size(index);
if !capacity.is_power_of_two() || capacity > u16::MAX.into() {
return Err(Error::InvalidQueueSize);
}
if capacity > max_capacity as usize {
return Err(Error::QueueTooLarge);
}
let descriptor_table = PageBox::new_uninit_slice(capacity)?;
let available = AvailableRing::with_capacity(capacity)?;
let used = UsedRing::with_capacity(capacity)?;
transport.set_queue(
index,
capacity as u16,
unsafe { descriptor_table.as_physical_address() },
unsafe { available.data.as_physical_address() },
unsafe { used.data.as_physical_address() },
msix_vector,
);
Ok(Self {
descriptor_table,
available,
used,
capacity,
queue_index: index,
free_head: 0,
avail_idx: 0,
last_used_idx: 0,
msix_vector: msix_vector.unwrap_or(0xFFFF),
})
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn with_max_capacity<T: Transport>(
transport: &mut T,
index: u16,
capacity: usize,
msix_vector: Option<u16>,
) -> Result<Self, Error> {
let max_capacity = transport.max_queue_size(index);
let capacity = capacity.min(max_capacity as usize);
Self::with_capacity(transport, index, capacity, msix_vector)
}
pub unsafe fn add<'a, 'b>(
&mut self,
input: &'a [&'b mut PageBox<[u8]>],
output: &'a [&'b PageBox<[u8]>],
) -> Result<u16, Error> {
if input.is_empty() && output.is_empty() {
return Err(Error::EmptyTransaction);
}
let n_desc = input.len() + output.len();
if self.used.used_count + 1 > self.capacity || self.used.used_count + n_desc > self.capacity
{
return Err(Error::QueueFull);
}
let head = self.add_direct(input, output);
let avail_slot = self.avail_idx % self.capacity as u16;
self.available.set_head(avail_slot, head);
fence(Ordering::SeqCst);
self.avail_idx = self.avail_idx.wrapping_add(1);
self.available.set_index(self.avail_idx);
fence(Ordering::SeqCst);
Ok(head)
}
unsafe fn add_direct<'a, 'b>(
&mut self,
input: &'a [&'b mut PageBox<[u8]>],
output: &'a [&'b PageBox<[u8]>],
) -> u16 {
let head = self.free_head;
let mut last = self.free_head;
for item in input {
assert_ne!(item.len(), 0);
let desc = &mut self.descriptor_table[usize::from(self.free_head)];
let next = (self.free_head + 1) % self.capacity as u16;
let desc = desc.write(Descriptor {
address: item.as_physical_address().into_raw(),
len: item.len().try_into().unwrap(),
// TODO MAGIC
flags: (1 << 0) | (1 << 1),
next,
});
last = self.free_head;
self.free_head = next;
}
for item in output {
assert_ne!(item.len(), 0);
let desc = &mut self.descriptor_table[usize::from(self.free_head)];
let next = (self.free_head + 1) % self.capacity as u16;
let desc = desc.write(Descriptor {
address: item.as_physical_address().into_raw(),
len: item.len().try_into().unwrap(),
// TODO
flags: (1 << 0),
next,
});
last = self.free_head;
self.free_head = next;
}
{
let last_desc = self.descriptor_table[last as usize].assume_init_mut();
// TODO
last_desc.flags &= !(1 << 0);
}
self.used.used_count += (input.len() + output.len());
head
}
pub fn add_notify_wait_pop<'a, 'b, T: Transport>(
&mut self,
input: &'a [&'b mut PageBox<[u8]>],
output: &'a [&'b PageBox<[u8]>],
transport: &mut T,
) -> Result<u32, Error> {
let token = unsafe { self.add(input, output) }?;
transport.notify(self.queue_index);
while self.is_used_empty() {
core::hint::spin_loop();
}
unsafe { self.pop_used(token) }
}
pub fn is_used_empty(&self) -> bool {
fence(Ordering::SeqCst);
self.last_used_idx == self.used.index()
}
pub unsafe fn pop_last_used(&mut self) -> Option<(u16, u32)> {
let token = self.peek_used()?;
let len = self.pop_used(token).unwrap();
Some((token, len))
}
pub unsafe fn peek_used(&mut self) -> Option<u16> {
if !self.is_used_empty() {
let last_used = self.last_used_idx % self.capacity as u16;
Some(self.used.read_slot(last_used).0 as u16)
} else {
None
}
}
pub unsafe fn pop_used(&mut self, token: u16) -> Result<u32, Error> {
if self.is_used_empty() {
return Err(Error::QueueEmpty);
}
let last_used_slot = self.last_used_idx % self.capacity as u16;
let (index, len) = self.used.read_slot(last_used_slot);
if index != token as u32 {
return Err(Error::WrongToken);
}
let freed_count = self.free_descriptor_chain(token);
self.last_used_idx = self.last_used_idx.wrapping_add(1);
Ok(len)
}
unsafe fn free_descriptor_chain(&mut self, head: u16) -> usize {
let mut current_node = Some(self.descriptor_table[usize::from(head)].assume_init_mut());
let mut count = 0;
while let Some(current) = current_node {
assert_ne!(current.len, 0);
let next_head = (current.flags & (1 << 0) != 0).then_some(current.next);
current.address = 0;
current.flags = 0;
current.next = 0;
current.len = 0;
self.used.used_count -= 1;
count += 1;
current_node =
next_head.map(|head| self.descriptor_table[usize::from(head)].assume_init_mut());
}
self.free_head = head;
count
}
pub fn msix_vector(&self) -> u16 {
self.msix_vector
}
}

View File

@ -0,0 +1,92 @@
use core::mem::size_of;
use kernel_util::mem::{
address::{IntoRaw, PhysicalAddress},
device::DeviceMemoryIo,
};
use tock_registers::{
interfaces::{Readable, Writeable},
registers::{ReadWrite, WriteOnly},
};
use crate::{CommonConfiguration, DeviceStatus};
pub mod pci;
pub trait Transport {
fn common_cfg(&self) -> &CommonConfiguration;
fn notify_cfg(&self) -> &[WriteOnly<u16>];
fn notify_off_mul(&self) -> usize;
fn supports_msix(&self) -> bool;
fn device_cfg(&self) -> Option<&DeviceMemoryIo<[u8]>>;
fn read_device_features(&mut self) -> u64 {
let cfg = self.common_cfg();
cfg.device_feature_select.set(0);
let low = cfg.device_feature.get();
cfg.device_feature_select.set(1);
let high = cfg.device_feature.get();
(low as u64) | ((high as u64) << 32)
}
fn write_driver_features(&mut self, value: u64) {
let cfg = self.common_cfg();
cfg.driver_feature_select.set(0);
cfg.driver_feature.set(value as u32);
cfg.driver_feature_select.set(1);
cfg.driver_feature.set((value >> 32) as u32);
}
fn read_device_status(&mut self) -> DeviceStatus {
let cfg = self.common_cfg();
DeviceStatus::from_bits_retain(cfg.device_status.get())
}
fn write_device_status(&mut self, value: DeviceStatus) {
let cfg = self.common_cfg();
cfg.device_status.set(value.bits());
}
fn max_queue_size(&mut self, queue: u16) -> u32 {
let cfg = self.common_cfg();
cfg.queue_select.set(queue);
cfg.queue_size.get().into()
}
fn set_queue(
&mut self,
queue: u16,
capacity: u16,
descriptor_table_phys: PhysicalAddress,
available_ring_phys: PhysicalAddress,
used_ring_phys: PhysicalAddress,
msix_vector: Option<u16>,
) {
let cfg = self.common_cfg();
cfg.queue_select.set(queue);
cfg.queue_size.set(capacity);
cfg.queue_desc.set(descriptor_table_phys.into_raw());
cfg.queue_driver.set(available_ring_phys.into_raw());
cfg.queue_device.set(used_ring_phys.into_raw());
if self.supports_msix() {
cfg.queue_msix_vector.set(msix_vector.unwrap_or(0xFFFF));
}
cfg.queue_enable.set(1);
}
fn unset_queue(&mut self, queue: u16) {
todo!()
}
fn notify(&mut self, queue: u16) {
let cfg = self.common_cfg();
let notify = self.notify_cfg();
cfg.queue_select.set(queue);
let notify_off = cfg.queue_notify_off.get() as usize;
let index = (notify_off * self.notify_off_mul()) / size_of::<u16>();
notify[index].set(queue);
}
}

View File

@ -0,0 +1,119 @@
use kernel_util::mem::{
address::{FromRaw, PhysicalAddress},
device::DeviceMemoryIo,
};
use tock_registers::registers::WriteOnly;
use ygg_driver_pci::{
capability::{
MsiXCapability, VirtioCapabilityData, VirtioCommonConfigCapability,
VirtioDeviceConfigCapability, VirtioNotifyConfigCapability,
},
PciCommandRegister, PciConfigurationSpace,
};
use crate::{error::Error, CommonConfiguration};
use super::Transport;
pub struct PciTransport {
common_cfg: DeviceMemoryIo<'static, CommonConfiguration>,
device_cfg: DeviceMemoryIo<'static, [u8]>,
notify_cfg: DeviceMemoryIo<'static, [WriteOnly<u16>]>,
notify_cfg_mul: usize,
}
impl Transport for PciTransport {
fn common_cfg(&self) -> &CommonConfiguration {
&self.common_cfg
}
fn notify_cfg(&self) -> &[WriteOnly<u16>] {
&self.notify_cfg
}
fn notify_off_mul(&self) -> usize {
self.notify_cfg_mul
}
fn supports_msix(&self) -> bool {
true
}
fn device_cfg(&self) -> Option<&DeviceMemoryIo<[u8]>> {
Some(&self.device_cfg)
}
}
impl PciTransport {
pub fn from_config_space<S: PciConfigurationSpace + ?Sized>(space: &S) -> Result<Self, Error> {
// Transitional devices MUST have a PCI Revision ID of 0.
// Transitional devices MUST have the PCI Subsystem Device ID
// matching the Virtio Device ID, as indicated in section 5.
// Transitional devices MUST have the Transitional PCI
// Device ID in the range 0x1000 to 0x103f.
// TODO check PCI subsystem ID
if space.rev_id() != 0 {
return Err(Error::InvalidPciConfiguration);
}
let mut cmd = PciCommandRegister::from_bits_retain(space.command());
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
space.set_command(cmd.bits());
// Extract capabilities
let common_cfg_cap = space
.capability::<VirtioCommonConfigCapability>()
.ok_or(Error::NoCommonConfigCapability)?;
// TODO this is not mandatory
let device_cfg_cap = space
.capability::<VirtioDeviceConfigCapability>()
.ok_or(Error::NoDeviceConfigCapability)?;
let notify_cfg_cap = space
.capability::<VirtioNotifyConfigCapability>()
.ok_or(Error::NoNotifyConfigCapability)?;
// TODO MSI/MSI-X
// Map the regions
let common_cfg_base = space
.bar(common_cfg_cap.bar_index().unwrap())
.unwrap()
.as_memory()
+ common_cfg_cap.bar_offset();
let device_cfg_base = space
.bar(device_cfg_cap.bar_index().unwrap())
.unwrap()
.as_memory()
+ device_cfg_cap.bar_offset();
let device_cfg_len = device_cfg_cap.length();
let notify_cfg_base = space
.bar(notify_cfg_cap.bar_index().unwrap())
.unwrap()
.as_memory()
+ notify_cfg_cap.bar_offset();
let notify_cfg_len = notify_cfg_cap.length();
let notify_cfg_mul = notify_cfg_cap.offset_multiplier();
let common_cfg_base = PhysicalAddress::from_raw(common_cfg_base);
let device_cfg_base = PhysicalAddress::from_raw(device_cfg_base);
let notify_cfg_base = PhysicalAddress::from_raw(notify_cfg_base);
assert_eq!(notify_cfg_len % 2, 0);
let common_cfg = unsafe { DeviceMemoryIo::map(common_cfg_base) }.unwrap();
let device_cfg =
unsafe { DeviceMemoryIo::map_slice(device_cfg_base, device_cfg_len) }.unwrap();
let notify_cfg =
unsafe { DeviceMemoryIo::map_slice(notify_cfg_base, notify_cfg_len / 2) }.unwrap();
Ok(Self {
common_cfg,
device_cfg,
notify_cfg,
notify_cfg_mul,
})
}
}

View File

@ -0,0 +1,24 @@
[package]
name = "ygg_driver_virtio_net"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
kernel-util = { path = "../../../lib/kernel-util" }
device-api = { path = "../../../lib/device-api", features = ["derive"] }
ygg_driver_virtio_core = { path = "../core" }
ygg_driver_net_core = { path = "../../net/core" }
ygg_driver_pci = { path = "../../bus/pci", optional = true }
log = "0.4.20"
bitflags = "2.4.2"
tock-registers = "0.8.1"
bytemuck = { version = "1.14.0", features = ["derive"] }
[features]
default = []
pci = ["ygg_driver_pci", "ygg_driver_virtio_core/pci"]

View File

@ -0,0 +1,316 @@
// TODO use more fancy features of virtio-net, TCP/IP checksum offloading would be nice
#![feature(strict_provenance)]
#![no_std]
extern crate alloc;
use core::{
future::Future,
mem::size_of,
pin::Pin,
ptr::NonNull,
task::{Context, Poll},
};
use alloc::{boxed::Box, collections::BTreeMap};
use bitflags::Flags;
use bytemuck::{Pod, Zeroable};
use device_api::{
interrupt::{InterruptAffinity, InterruptHandler, MsiHandler},
Device,
};
use kernel_util::{
block,
mem::{
self,
address::{FromRaw, IntoRaw, PhysicalAddress},
device::{DeviceMemoryIo, RawDeviceMemoryMapping},
PageBox,
},
message_interrupt_controller,
runtime::{self, QueueWaker},
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock, IrqSafeSpinlockGuard},
util::{ring::RingBuffer, OneTimeInit},
};
use ygg_driver_net_core::{interface::NetworkDevice, types::MacAddress, Packet};
use ygg_driver_pci::{
capability::{
MsiXCapability, MsiXVectorTable, VirtioCapabilityData, VirtioCommonConfigCapability,
VirtioDeviceConfigCapability, VirtioNotifyConfigCapability,
},
PciBaseAddress, PciCommandRegister, PciConfigurationSpace, PciDeviceInfo,
};
use ygg_driver_virtio_core::{
queue::VirtQueue,
transport::{pci::PciTransport, Transport},
DeviceStatus,
};
use yggdrasil_abi::error::Error;
struct Queues {
receive: IrqSafeSpinlock<VirtQueue>,
transmit: IrqSafeSpinlock<VirtQueue>,
configuration_vector: usize,
receive_vector: usize,
}
pub struct VirtioNet<T: Transport> {
transport: IrqSafeSpinlock<T>,
queues: OneTimeInit<Queues>,
interface_id: OneTimeInit<u32>,
mac: IrqSafeRwLock<MacAddress>,
pending_packets: IrqSafeRwLock<BTreeMap<u16, PageBox<[u8]>>>,
pending_tx_packets: IrqSafeRwLock<BTreeMap<u16, PageBox<[u8]>>>,
vector_table: IrqSafeRwLock<MsiXVectorTable<'static>>,
}
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C)]
struct VirtioPacketHeader {
flags: u8,
gso_type: u8,
hdr_len: u16,
gso_size: u16,
csum_start: u16,
csum_offset: u16,
}
impl Queues {
pub fn try_receive(&self, _index: usize) -> Option<(u16, IrqSafeSpinlockGuard<VirtQueue>)> {
let mut queue = self.receive.lock();
let (token, _) = unsafe { queue.pop_last_used() }?;
Some((token, queue))
}
}
impl<T: Transport> VirtioNet<T> {
const PACKET_SIZE: usize = 4096;
pub fn new(mut transport: T, vector_table: MsiXVectorTable<'static>) -> Self {
// Read MAC from device config
let device_cfg = transport
.device_cfg()
.expect("virtio-net must have device-specific configuration section");
let mut mac_bytes = [0; 6];
mac_bytes.copy_from_slice(&device_cfg[..6]);
let mac = MacAddress::from(mac_bytes);
Self {
transport: IrqSafeSpinlock::new(transport),
queues: OneTimeInit::new(),
interface_id: OneTimeInit::new(),
mac: IrqSafeRwLock::new(mac),
pending_packets: IrqSafeRwLock::new(BTreeMap::new()),
pending_tx_packets: IrqSafeRwLock::new(BTreeMap::new()),
vector_table: IrqSafeRwLock::new(vector_table),
}
}
pub fn listen(&self, buffers: usize) {
let queues = self.queues.get();
let mut queue = queues.receive.lock();
let mut packets = self.pending_packets.write();
for _ in 0..buffers {
let mut packet = PageBox::new_slice(0, Self::PACKET_SIZE).unwrap();
let token = unsafe { queue.add(&[&mut packet], &[]).unwrap() };
packets.insert(token, packet);
}
}
fn handle_receive_interrupt(&self, queue: usize) -> bool {
let queues = self.queues.get();
let interface_id = *self.interface_id.get();
let mut count = 0;
while let Some((token, mut queue)) = queues.try_receive(queue) {
let mut pending_packets = self.pending_packets.write();
let packet = pending_packets.remove(&token).unwrap();
let mut buffer = PageBox::new_slice(0, Self::PACKET_SIZE).unwrap();
let token = unsafe { queue.add(&[&mut buffer], &[]).unwrap() };
pending_packets.insert(token, buffer);
let packet = Packet::new(packet, size_of::<VirtioPacketHeader>(), interface_id);
ygg_driver_net_core::receive_packet(packet).unwrap();
count += 1
}
if count != 0 {
self.transport.lock().notify(1);
}
count != 0
}
fn begin_init(&self) -> Result<DeviceStatus, Error> {
let mut transport = self.transport.lock();
let mut status = DeviceStatus::RESET_VALUE;
log::debug!("Reset device");
transport.write_device_status(status);
status |= DeviceStatus::ACKNOWLEDGE;
transport.write_device_status(status);
status |= DeviceStatus::DRIVER;
transport.write_device_status(status);
let device_features = transport.read_device_features();
// TODO blah blah blah
transport.write_driver_features(0);
status |= DeviceStatus::FEATURES_OK;
transport.write_device_status(status);
if !transport
.read_device_status()
.contains(DeviceStatus::FEATURES_OK)
{
return Err(Error::InvalidOperation);
}
Ok(status)
}
fn finish_init(&self, status: DeviceStatus) {
let mut transport = self.transport.lock();
transport.write_device_status(status | DeviceStatus::DRIVER_OK);
}
unsafe fn setup_queues(
&'static self,
receive_count: usize,
transmit_count: usize,
) -> Result<(), Error> {
// TODO multiqueue capability
assert_eq!(receive_count, 1);
assert_eq!(transmit_count, 1);
let mut transport = self.transport.lock();
let mut vt = self.vector_table.write();
let msix_range = vt.register_range(
0,
1 + receive_count,
message_interrupt_controller(),
InterruptAffinity::Any,
self,
)?;
// TODO set the configuration vector in virtio common cfg
let receive_vector: u16 = msix_range[1].vector.try_into().unwrap();
// Setup the virtqs
let rx = VirtQueue::with_max_capacity(&mut *transport, 0, 128, Some(receive_vector))
.map_err(cvt_error)?;
let tx = VirtQueue::with_max_capacity(&mut *transport, 1, 128, None).map_err(cvt_error)?;
self.queues.init(Queues {
receive: IrqSafeSpinlock::new(rx),
transmit: IrqSafeSpinlock::new(tx),
configuration_vector: msix_range[0].vector,
receive_vector: msix_range[1].vector,
});
Ok(())
}
}
impl<T: Transport + 'static> NetworkDevice for VirtioNet<T> {
fn transmit(&self, mut packet: PageBox<[u8]>) -> Result<(), Error> {
let queues = self.queues.get();
let mut tx = queues.transmit.lock();
let mut transport = self.transport.lock();
packet[..size_of::<VirtioPacketHeader>()].fill(0);
let len = tx
.add_notify_wait_pop(&[], &[&packet], &mut *transport)
.unwrap();
Ok(())
}
fn read_hardware_address(&self) -> MacAddress {
*self.mac.read()
}
fn packet_prefix_size(&self) -> usize {
size_of::<VirtioPacketHeader>()
}
}
impl<T: Transport + 'static> MsiHandler for VirtioNet<T> {
fn handle_msi(&self, vector: usize) -> bool {
let Some(queues) = self.queues.try_get() else {
return false;
};
if vector == queues.receive_vector {
self.handle_receive_interrupt(0)
} else {
false
}
}
}
impl<T: Transport + 'static> Device for VirtioNet<T> {
fn display_name(&self) -> &'static str {
"VirtIO Network Device"
}
unsafe fn init(&'static self) -> Result<(), Error> {
let status = self.begin_init()?;
// TODO multiqueue
self.setup_queues(1, 1)?;
self.finish_init(status);
let id = ygg_driver_net_core::register_interface(self);
self.interface_id.init(id);
self.listen(64);
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
Ok(())
}
}
fn cvt_error(error: ygg_driver_virtio_core::error::Error) -> Error {
use ygg_driver_virtio_core::error::Error as VirtioError;
match error {
VirtioError::OsError(err) => err,
_ => Error::InvalidOperation,
}
}
pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
let space = &info.config_space;
let mut msix = space.capability::<MsiXCapability>().unwrap();
let mut vt = msix.vector_table()?;
// TODO is this really needed? PCI spec says this is masked on reset, though I'm not sure if
// firmware puts it back in masked state after loading the kernel
vt.mask_all();
msix.set_function_mask(false);
msix.set_enabled(true);
let transport = PciTransport::from_config_space(space).unwrap();
let device = VirtioNet::new(transport, vt);
let device = Box::leak(Box::new(device));
Ok(device)
}

View File

@ -27,6 +27,8 @@ extern "Rust" {
pub fn __virtualize(phys: u64) -> usize;
pub fn __physicalize(virt: usize) -> u64;
pub fn __translate_kernel(virt: usize) -> Option<PhysicalAddress>;
pub fn __map_device_pages(
base: PhysicalAddress,
count: usize,

View File

@ -2,6 +2,7 @@ use core::{
alloc::Layout,
mem::size_of,
ops::{Deref, DerefMut},
ptr::NonNull,
};
use alloc::sync::Arc;
@ -66,6 +67,39 @@ impl RawDeviceMemoryMapping {
core::mem::forget(self);
address
}
pub fn into_raw_parts(self) -> (usize, usize, usize, usize) {
let address = self.address;
let base_address = self.base_address;
let page_count = self.page_count;
let page_size = self.page_size;
core::mem::forget(self);
(address, base_address, page_count, page_size)
}
pub unsafe fn from_raw_parts(
address: usize,
base_address: usize,
page_count: usize,
page_size: usize,
) -> Self {
Self {
address,
base_address,
page_count,
page_size,
}
}
/// "Casts" the mapping to a specific type T and returns a [NonNull] pointer to it
pub unsafe fn as_non_null<T>(&self) -> NonNull<T> {
if self.page_size * self.page_count < size_of::<T>() {
panic!();
}
NonNull::new_unchecked(self.address as *mut T)
}
}
impl Drop for RawDeviceMemoryMapping {

View File

@ -5,11 +5,12 @@ use core::{
ops::{Deref, DerefMut},
};
use alloc::boxed::Box;
use yggdrasil_abi::error::Error;
use crate::api::{self, __allocate_contiguous_pages, __free_page, __physicalize};
use self::address::{AsPhysicalAddress, PhysicalAddress};
use self::address::{AsPhysicalAddress, FromRaw, PhysicalAddress};
pub mod address;
pub mod device;
@ -258,3 +259,11 @@ unsafe impl<T: ?Sized + Sync> Sync for PageBox<T> {}
pub fn allocate_page() -> Result<PhysicalAddress, Error> {
unsafe { api::__allocate_page() }
}
pub fn allocate_contiguous_pages(count: usize) -> Result<PhysicalAddress, Error> {
unsafe { api::__allocate_contiguous_pages(count) }
}
pub fn translate_kernel_address(virt: usize) -> Option<PhysicalAddress> {
unsafe { api::__translate_kernel(virt) }
}

View File

@ -89,12 +89,9 @@ impl<T> RingBuffer<T> {
///
/// The caller must perform the necessary checks to avoid reading beyond the write head.
#[inline]
pub unsafe fn read_single_unchecked(&mut self) -> T
where
T: Copy,
{
pub unsafe fn read_single_unchecked(&mut self) -> T {
let data = self.data.as_ref().unwrap();
let res = data[self.rd].assume_init();
let res = data[self.rd].assume_init_read();
self.rd = (self.rd + 1) % self.capacity;
res
}

View File

@ -2,6 +2,7 @@ use core::{
any::Any,
fmt,
mem::MaybeUninit,
net::SocketAddr,
task::{Context, Poll},
};
@ -24,8 +25,10 @@ use crate::{
channel::ChannelDescriptor,
device::{BlockDeviceWrapper, CharDeviceWrapper},
node::NodeRef,
socket::PacketSocketWrapper,
traits::{Read, Seek, Write},
FdPoll, FileReadiness, PseudoTerminal, PseudoTerminalMaster, PseudoTerminalSlave, SharedMemory,
FdPoll, FileReadiness, PacketSocket, PseudoTerminal, PseudoTerminalMaster, PseudoTerminalSlave,
SharedMemory, Socket,
};
use self::{
@ -63,6 +66,8 @@ pub enum File {
Block(BlockFile),
Char(CharFile),
PacketSocket(Arc<PacketSocketWrapper>),
AnonymousPipe(PipeEnd),
Poll(FdPoll),
Channel(ChannelDescriptor),
@ -115,6 +120,11 @@ impl File {
))
}
/// Constructs a [File] from a [PacketSocket]
pub fn from_packet_socket(socket: Arc<dyn PacketSocket>) -> Arc<Self> {
Arc::new(Self::PacketSocket(Arc::new(PacketSocketWrapper(socket))))
}
pub(crate) fn directory(node: NodeRef, position: DirectoryOpenPosition) -> Arc<Self> {
let position = IrqSafeSpinlock::new(position.into());
Arc::new(Self::Directory(DirectoryFile { node, position }))
@ -261,6 +271,39 @@ impl File {
Err(Error::InvalidOperation)
}
}
/// Interprets the file as a packet-based socket
pub fn as_packet_socket(&self) -> Result<&PacketSocketWrapper, Error> {
if let Self::PacketSocket(sock) = self {
Ok(sock)
} else {
Err(Error::InvalidOperation)
}
}
/// Sends data to a socket
pub fn send_to(&self, buffer: &[u8], recepient: Option<SocketAddr>) -> Result<usize, Error> {
match recepient {
Some(recepient) => self.as_packet_socket()?.send(recepient, buffer),
None => todo!(),
}
}
/// Receives data from a socket
pub fn receive_from(
&self,
buffer: &mut [u8],
remote: &mut MaybeUninit<SocketAddr>,
) -> Result<usize, Error> {
match self {
Self::PacketSocket(socket) => {
let (addr, len) = socket.receive(buffer)?;
remote.write(addr);
Ok(len)
}
_ => Err(Error::InvalidOperation),
}
}
}
impl PageProvider for File {
@ -295,6 +338,7 @@ impl Read for File {
// TODO maybe allow reading messages from Channels?
Self::Channel(_) => Err(Error::InvalidOperation),
Self::SharedMemory(_) => Err(Error::InvalidOperation),
Self::PacketSocket(_) => Err(Error::InvalidOperation),
Self::Directory(_) => Err(Error::IsADirectory),
}
}
@ -314,6 +358,7 @@ impl Write for File {
// TODO maybe allow writing messages to Channels?
Self::Channel(_) => Err(Error::InvalidOperation),
Self::SharedMemory(_) => Err(Error::InvalidOperation),
Self::PacketSocket(_) => Err(Error::InvalidOperation),
Self::Directory(_) => Err(Error::IsADirectory),
}
}
@ -366,6 +411,11 @@ impl fmt::Debug for File {
Self::SharedMemory(_) => f.debug_struct("SharedMemory").finish_non_exhaustive(),
Self::PtySlave(_) => f.debug_struct("PtySlave").finish_non_exhaustive(),
Self::PtyMaster(_) => f.debug_struct("PtyMaster").finish_non_exhaustive(),
Self::PacketSocket(sock) => f
.debug_struct("PacketSocket")
.field("local", &sock.local_address())
.field("remote", &sock.remote_address())
.finish_non_exhaustive(),
}
}
}

View File

@ -19,6 +19,7 @@ pub(crate) mod path;
pub(crate) mod poll;
pub(crate) mod pty;
pub(crate) mod shared_memory;
pub(crate) mod socket;
pub(crate) mod traits;
pub use channel::MessagePayload;
@ -32,4 +33,5 @@ pub use node::{
pub use poll::FdPoll;
pub use pty::{PseudoTerminal, PseudoTerminalMaster, PseudoTerminalSlave};
pub use shared_memory::SharedMemory;
pub use socket::{ConnectionSocket, ListenerSocket, PacketSocket, Socket};
pub use traits::{FileReadiness, Read, Seek, Write};

View File

@ -292,6 +292,10 @@ where
let instance = instance.unwrap().downcast_ref::<FnNodeData>().unwrap();
Ok(instance.as_write()?.lock().write_slice(pos as _, buf))
}
fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
Ok(())
}
}
// Byte read-only node

53
lib/vfs/src/socket.rs Normal file
View File

@ -0,0 +1,53 @@
use core::{
net::SocketAddr,
ops::Deref,
task::{Context, Poll},
};
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
use crate::FileReadiness;
/// Interface for interacting with network sockets
pub trait Socket: FileReadiness + Send {
/// Socket listen/receive address
fn local_address(&self) -> SocketAddr;
/// Socket remote address
fn remote_address(&self) -> Option<SocketAddr>;
/// Closes a socket
fn close(&self) -> Result<(), Error>;
}
/// Stateless/packet-based socket interface
pub trait PacketSocket: Socket {
/// Receives a packet into provided buffer. Will return an error if packet cannot be placed
/// within the buffer.
fn receive(&self, buffer: &mut [u8]) -> Result<(SocketAddr, usize), Error>;
/// Sends provided data to the recepient specified by `destination`
fn send(&self, destination: SocketAddr, data: &[u8]) -> Result<usize, Error>;
}
/// Connection-based client socket interface
pub trait ConnectionSocket: Socket {}
/// Connection-based listener socket interface
pub trait ListenerSocket: Socket {}
pub struct PacketSocketWrapper(pub Arc<dyn PacketSocket + 'static>);
impl Deref for PacketSocketWrapper {
type Target = dyn PacketSocket;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl Drop for PacketSocketWrapper {
fn drop(&mut self) {
self.0.close().ok();
}
}

View File

@ -122,6 +122,9 @@ pub trait Architecture {
/// Converts a virtual address created by [Architecture::virtualize] back to its physical form
fn physicalize(address: usize) -> u64;
/// Translates a kernel virtual address into its physical page
fn translate_kernel_address(address: usize) -> Option<PhysicalAddress>;
// Architecture intrinsics
/// Suspends CPU until an interrupt is received
@ -356,3 +359,8 @@ fn __monotonic_timestamp() -> Result<Duration, Error> {
fn __message_interrupt_controller() -> &'static dyn MessageInterruptController {
ARCHITECTURE.message_interrupt_controller()
}
#[no_mangle]
fn __translate_kernel(address: usize) -> Option<PhysicalAddress> {
ArchitectureImpl::translate_kernel_address(address)
}

View File

@ -311,6 +311,23 @@ fn clone_kernel_tables(dst: &mut PageTable<L0>) {
}
}
pub(super) fn translate_kernel_address(address: usize) -> Option<PhysicalAddress> {
let l0i = address.page_index::<L0>();
let l1i = address.page_index::<L1>();
let l2i = address.page_index::<L2>();
let l3i = address.page_index::<L3>();
match l0i {
KERNEL_L0_INDEX => match l1i {
HEAP_MAPPING_L1I => Some(PhysicalAddress::from_raw(address - HEAP_MAPPING_OFFSET)),
DEVICE_MAPPING_L1I => todo!(),
_ => todo!(),
},
RAM_MAPPING_L0I => todo!(),
_ => None,
}
}
/// Sets up the following memory map:
/// ...: KERNEL_TABLES.l0:
/// * 0xFFFFFF0000000000 .. 0xFFFFFFFF8000000000 : RAM_MAPPING_L1

View File

@ -275,6 +275,11 @@ impl Architecture for X86_64 {
(address - RAM_MAPPING_OFFSET) as _
}
#[inline]
fn translate_kernel_address(address: usize) -> Option<PhysicalAddress> {
mem::translate_kernel_address(address)
}
fn external_interrupt_controller(
&'static self,
) -> &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
@ -394,6 +399,12 @@ impl X86_64 {
Some(0x01),
ygg_driver_ahci::probe,
);
ygg_driver_pci::register_vendor_driver(
"Virtio PCI Network Device",
0x1AF4,
0x1000,
ygg_driver_virtio_net::probe,
);
match self.boot_data.get() {
&BootData::YBoot(data) => {

View File

@ -28,6 +28,8 @@ fn setup_root() -> Result<NodeRef, Error> {
pub fn kinit() -> Result<(), Error> {
infoln!("In main");
ygg_driver_net_core::start_network_tasks()?;
#[cfg(feature = "fb_console")]
{
use crate::device::display::console::update_consoles_task;

View File

@ -1,5 +1,5 @@
//! System function call handlers
use core::{mem::MaybeUninit, time::Duration};
use core::{mem::MaybeUninit, net::SocketAddr, time::Duration};
use abi::{
error::Error,
@ -9,6 +9,7 @@ use abi::{
TerminalOptions, TerminalSize,
},
mem::MappingSource,
net::SocketType,
process::{
ExecveOptions, ExitCode, MutexOperation, Signal, SpawnOption, SpawnOptions,
ThreadSpawnOptions,
@ -18,6 +19,7 @@ use abi::{
use alloc::{borrow::ToOwned, boxed::Box, sync::Arc, vec::Vec};
use kernel_util::{block, mem::table::EntryLevelExt, runtime, sync::IrqSafeSpinlockGuard};
use vfs::{File, IoContext, MessagePayload, NodeRef, Read, Seek, Write};
use ygg_driver_net_core::socket::UdpSocket;
use yggdrasil_abi::{error::SyscallResult, io::MountOptions};
use crate::{
@ -658,6 +660,45 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
Ok(0)
}
// Networking
SyscallFunction::BindSocket => {
let listen = arg_user_ref::<SocketAddr>(args[0] as usize)?;
let ty = SocketType::try_from(args[1] as u32).map_err(|_| Error::InvalidArgument)?;
run_with_io(process, |mut io| {
let socket = match ty {
SocketType::UdpPacket => UdpSocket::bind(*listen)?,
_ => todo!(),
};
let file = File::from_packet_socket(socket);
let fd = io.files.place_file(file, true)?;
Ok(fd.0 as usize)
})
}
SyscallFunction::ConnectSocket => todo!(),
SyscallFunction::SendTo => {
let socket_fd = RawFd::from(args[0] as u32);
let buffer = arg_buffer_ref(args[1] as usize, args[2] as usize)?;
let recepient = arg_user_ref::<Option<SocketAddr>>(args[3] as usize)?;
run_with_io(process, |mut io| {
let file = io.files.file(socket_fd)?;
file.send_to(buffer, *recepient)
})
}
SyscallFunction::ReceiveFrom => {
let socket_fd = RawFd::from(args[0] as u32);
let buffer = arg_buffer_mut(args[1] as usize, args[2] as usize)?;
let remote = arg_user_mut::<MaybeUninit<SocketAddr>>(args[3] as usize)?;
run_with_io(process, |mut io| {
let file = io.files.file(socket_fd)?;
file.receive_from(buffer, remote)
})
}
SyscallFunction::Fork => unreachable!(),
}
}