Commit c73a36d0 authored by Stefan Lankes's avatar Stefan Lankes
Browse files

move smoltcp to libstd, add network device layer

parent 5b3db7c8
......@@ -5,5 +5,5 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
#[cfg(feature = "network")]
#[cfg(not(feature = "newlib"))]
pub mod net;
......@@ -5,148 +5,84 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
pub mod rtl8139;
//pub mod rtl8139;
pub mod uhyve;
use smoltcp::iface::EthernetInterface;
use smoltcp::phy::Device;
use smoltcp::socket::SocketSet;
use smoltcp::time::Instant;
//use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};
//use smoltcp::socket::{RawSocketBuffer, RawPacketMetadata};
//use smoltcp::dhcp::Dhcpv4Client;
use alloc::boxed::Box;
use core::ffi::c_void;
use synch::spinlock::SpinlockIrqSave;
use scheduler::task::TaskId;
use synch::semaphore::*;
static NIC: SpinlockIrqSave<Option<Box<dyn NetworkInterface>>> = SpinlockIrqSave::new(None);
static NET_SEM: Semaphore = Semaphore::new(0);
static mut NETWORK_TASK_ID: TaskId = TaskId::from(0);
pub fn init() -> Result<(), ()> {
let nic = uhyve::init()?;
*NIC.lock() = Some(nic);
pub fn get_network_task_id() -> TaskId {
unsafe { NETWORK_TASK_ID }
}
/*pub fn networkd_with_dhcp<'b, 'c, 'e, DeviceT: for<'d> Device<'d>, F>(iface: &mut EthernetInterface<'b, 'c, 'e, DeviceT>, is_polling: F) -> !
where F: Fn() -> bool {
let dhcp_rx_buffer = RawSocketBuffer::new(
[RawPacketMetadata::EMPTY; 1],
vec![0; 900]
);
let dhcp_tx_buffer = RawSocketBuffer::new(
[RawPacketMetadata::EMPTY; 1],
vec![0; 600]
);
let mut sockets = SocketSet::new(vec![]);
let boot_time = crate::arch::get_boot_time();
let mut counter: usize = 0;
let microseconds = ::arch::processor::get_timer_ticks() - boot_time;
let timestamp = Instant::from_millis(microseconds as i64 / 1000i64);
let mut dhcp = Dhcpv4Client::new(&mut sockets, dhcp_rx_buffer, dhcp_tx_buffer, timestamp);
let mut prev_cidr = Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0);
loop {
let microseconds = ::arch::processor::get_timer_ticks() - boot_time;
let timestamp = Instant::from_millis(microseconds as i64 / 1000i64);
iface.poll(&mut sockets, timestamp)
.map(|_| {trace!("receive message {}", counter); counter = counter+1;})
.unwrap_or_else(|e| info!("Poll: {:?}", e));
let config = dhcp.poll(iface, &mut sockets, timestamp)
.unwrap_or_else(|e| {
debug!("DHCP: {:?}", e);
None
});
config.map(|config| {
info!("DHCP config: {:?}", config);
match config.address {
Some(cidr) => if cidr != prev_cidr {
iface.update_ip_addrs(|addrs| {
addrs.iter_mut().nth(0)
.map(|addr| {
*addr = IpCidr::Ipv4(cidr);
});
});
prev_cidr = cidr;
info!("Assigned a new IPv4 address: {}", cidr);
}
_ => {}
}
config.router.map(|router| iface.routes_mut()
.add_default_ipv4_route(router.into())
.unwrap()
);
iface.routes_mut()
.update(|routes_map| {
routes_map.get(&IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0))
.map(|default_route| {
info!("Default gateway: {}", default_route.via_router);
});
});
info!("Network initialized!");
if config.dns_servers.iter().any(|s| s.is_some()) {
info!("DNS servers:");
for dns_server in config.dns_servers.iter().filter_map(|s| *s) {
info!("- {}", dns_server);
}
}
});
Ok(())
}
if is_polling() == false {
let mut timeout = dhcp.next_poll(timestamp);
iface.poll_delay(&sockets, timestamp)
.map(|sockets_timeout| timeout = sockets_timeout);
debug!("networkd timeout {}", timeout.millis());
pub trait NetworkInterface {
/// check if the driver in polling mode
fn is_polling(&self) -> bool;
/// set driver in polling/non-polling mode
fn set_polling(&mut self, mode: bool);
/// initialize network and returns basic network configuration
fn init(
&mut self,
sem: *const c_void,
ip: &mut [u8; 4],
gateway: &mut [u8; 4],
mac: &mut [u8; 6],
) -> i32;
/// read packet from network interface
fn read(&mut self, buf: usize, len: usize) -> usize;
/// writr packet to the network interface
fn write(&self, buf: usize, len: usize) -> usize;
}
// Calculate the absolute wakeup time in processor timer ticks out of the relative timeout in milliseconds.
let wakeup_time = if timeout.millis() > 0 {
Some(::arch::processor::get_timer_ticks() + (timeout.millis() as u64) * 1000)
} else {
Some(::arch::processor::get_timer_ticks() + 100)
};
NET_SEM.acquire(wakeup_time);
}
#[no_mangle]
pub extern "C" fn sys_network_init(
sem: *const c_void,
ip: &mut [u8; 4],
gateway: &mut [u8; 4],
mac: &mut [u8; 6],
) -> i32 {
match &mut *NIC.lock() {
Some(nic) => nic.init(sem, ip, gateway, mac),
None => -1,
}
}*/
}
pub fn networkd<'b, 'c, 'e, DeviceT: for<'d> Device<'d>, F>(
iface: &mut EthernetInterface<'b, 'c, 'e, DeviceT>,
is_polling: F,
) -> !
where
F: Fn() -> bool,
{
let mut sockets = SocketSet::new(vec![]);
let boot_time = crate::arch::get_boot_time();
let mut counter: usize = 0;
#[no_mangle]
pub extern "C" fn sys_is_polling() -> bool {
match &*NIC.lock() {
Some(nic) => nic.is_polling(),
None => false,
}
}
loop {
let microseconds = ::arch::processor::get_timer_ticks() - boot_time;
let timestamp = Instant::from_millis(microseconds as i64 / 1000i64);
#[no_mangle]
pub extern "C" fn sys_set_polling(mode: bool) {
match &mut *NIC.lock() {
Some(nic) => nic.set_polling(mode),
None => {}
}
}
iface
.poll(&mut sockets, timestamp)
.map(|_| {
trace!("receive message {}", counter);
counter += 1;
})
.unwrap_or_else(|e| debug!("Poll: {:?}", e));
#[no_mangle]
pub extern "C" fn sys_netread(buf: usize, len: usize) -> usize {
match &mut *NIC.lock() {
Some(nic) => nic.read(buf, len),
None => 0,
}
}
if !is_polling() {
let wakeup_time = match iface.poll_delay(&sockets, timestamp) {
Some(duration) => {
// Calculate the absolute wakeup time in processor timer ticks out of the relative timeout in milliseconds.
if duration.millis() > 0 {
Some(
::arch::processor::get_timer_ticks()
+ (duration.millis() as u64) * 1000,
)
} else {
Some(::arch::processor::get_timer_ticks() + 100)
}
}
None => None,
};
NET_SEM.acquire(wakeup_time);
}
#[no_mangle]
pub extern "C" fn sys_netwrite(buf: usize, len: usize) -> usize {
match &*NIC.lock() {
Some(nic) => nic.write(buf, len),
None => 0,
}
}
......@@ -5,18 +5,14 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use alloc::collections::BTreeMap;
use core::str;
use alloc::boxed::Box;
use core::ffi::c_void;
use core::ptr::read_volatile;
use core::sync::atomic::{AtomicBool, Ordering};
use core::volatile_load;
use smoltcp::iface::{EthernetInterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::{self, Device, DeviceCapabilities};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
use drivers::net::{networkd, NETWORK_TASK_ID};
use scheduler;
use core::{ptr, str};
use drivers::net::NetworkInterface;
use synch;
use syscalls::sys_sem_post;
#[cfg(target_arch = "x86_64")]
use arch::x86_64::kernel::apic;
......@@ -31,18 +27,11 @@ use arch::x86_64::mm::paging::virt_to_phys;
#[cfg(target_arch = "x86_64")]
use x86::io::*;
static POOLING: AtomicBool = AtomicBool::new(false);
fn is_pooling() -> bool {
POOLING.load(Ordering::SeqCst)
}
const UHYVE_IRQ_NET: u32 = 11;
const UHYVE_PORT_NETINFO: u16 = 0x600;
const UHYVE_PORT_NETWRITE: u16 = 0x640;
const UHYVE_PORT_NETREAD: u16 = 0x680;
//const UHYVE_PORT_NETSTAT: u16 = 0x700;
const UHYVE_MAX_MSG_SIZE: usize = 1792;
/// Data type to determine the mac address
#[derive(Debug, Default)]
......@@ -52,6 +41,93 @@ struct UhyveNetinfo {
pub mac: [u8; 18],
}
pub struct UhyveNetwork {
/// Semaphore to block IP thread
sem: *const c_void,
/// mac address
mac: [u8; 18],
/// is NIC in polling mode?
polling: AtomicBool,
}
impl UhyveNetwork {
pub const fn new(mac: &[u8; 18]) -> Self {
UhyveNetwork {
sem: ptr::null(),
mac: *mac,
polling: AtomicBool::new(false),
}
}
}
impl NetworkInterface for UhyveNetwork {
fn is_polling(&self) -> bool {
self.polling.load(Ordering::SeqCst)
}
fn set_polling(&mut self, mode: bool) {
self.polling.store(mode, Ordering::SeqCst);
if mode && !self.sem.is_null() {
sys_sem_post(self.sem as *const synch::semaphore::Semaphore);
}
}
fn init(
&mut self,
sem: *const c_void,
ip: &mut [u8; 4],
gateway: &mut [u8; 4],
mac: &mut [u8; 6],
) -> i32 {
let mac_str = str::from_utf8(&self.mac).unwrap();
let myip = get_ip();
let mygw = get_gateway();
self.sem = sem;
mac[0..].copy_from_slice(mac_str.as_bytes());
ip.copy_from_slice(&myip);
gateway.copy_from_slice(&mygw);
0
}
fn write(&self, buf: usize, len: usize) -> usize {
let uhyve_write = UhyveWrite::new(virt_to_phys(buf), len);
unsafe {
outl(
UHYVE_PORT_NETWRITE,
virt_to_phys(&uhyve_write as *const _ as usize) as u32,
);
}
let ret = uhyve_write.ret();
if ret != 0 {
error!("Unable to send message: {}", ret);
}
uhyve_write.len()
}
fn read(&mut self, buf: usize, len: usize) -> usize {
let data = UhyveRead::new(virt_to_phys(buf as usize), len);
unsafe {
outl(
UHYVE_PORT_NETREAD,
virt_to_phys(&data as *const _ as usize) as u32,
);
}
if data.ret() == 0 {
trace!("resize message to {} bytes", data.len());
data.len()
} else {
self.set_polling(false);
0
}
}
}
/// Datatype to receive packets from uhyve
#[derive(Debug)]
#[repr(C)]
......@@ -112,220 +188,42 @@ impl UhyveWrite {
}
}
extern "C" fn uhyve_thread(_arg: usize) {
debug!("Hello from network thread!");
::arch::irq::disable();
let info: UhyveNetinfo = UhyveNetinfo::default();
unsafe {
outl(
UHYVE_PORT_NETINFO,
virt_to_phys(&info as *const _ as usize) as u32,
);
}
let mac_str = str::from_utf8(&info.mac).unwrap();
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let ethernet_addr = EthernetAddress([
u8::from_str_radix(&mac_str[0..2], 16).unwrap(),
u8::from_str_radix(&mac_str[3..5], 16).unwrap(),
u8::from_str_radix(&mac_str[6..8], 16).unwrap(),
u8::from_str_radix(&mac_str[9..11], 16).unwrap(),
u8::from_str_radix(&mac_str[12..14], 16).unwrap(),
u8::from_str_radix(&mac_str[15..17], 16).unwrap(),
]);
let hcip = get_ip();
let ip_addrs = [IpCidr::new(
IpAddress::v4(hcip[0], hcip[1], hcip[2], hcip[3]),
24,
)];
let hcgw = get_gateway();
let default_gw = Ipv4Address::new(hcgw[0], hcgw[1], hcgw[2], hcgw[3]);
let mut routes_storage = [None; 1];
let mut routes = Routes::new(&mut routes_storage[..]);
routes.add_default_ipv4_route(default_gw).unwrap();
let device = UhyveNet::new();
info!("MAC address {}", ethernet_addr);
info!("Configure network interface with address {}", ip_addrs[0]);
info!("Configure gatway with address {}", default_gw);
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.routes(routes)
.finalize();
// Install interrupt handler
irq_install_handler(UHYVE_IRQ_NET, uhyve_irqhandler as usize);
::arch::irq::enable();
networkd(&mut iface, is_pooling);
}
pub fn init() -> Result<(), ()> {
pub fn init() -> Result<Box<dyn NetworkInterface>, ()> {
// does uhyve configure the network interface?
let ip = get_ip();
if ip[0] == 0xff && ip[1] == 0xff && ip[2] == 0xff && ip[3] == 0xff {
return Err(());
}
let core_scheduler = core_scheduler();
unsafe {
NETWORK_TASK_ID = core_scheduler.spawn(
uhyve_thread,
0,
scheduler::task::HIGH_PRIO,
Some(crate::arch::mm::virtualmem::task_heap_start()),
);
}
Ok(())
}
#[cfg(target_arch = "x86_64")]
extern "x86-interrupt" fn uhyve_irqhandler(_stack_frame: &mut ExceptionStackFrame) {
debug!("Receive network interrupt from uhyve");
POOLING.store(true, Ordering::SeqCst);
crate::drivers::net::NET_SEM.release();
apic::eoi();
core_scheduler().scheduler();
}
debug!("Initialize uhyve network interface!");
/// A network device for uhyve.
#[derive(Debug)]
pub struct UhyveNet {
mtu: usize,
}
impl UhyveNet {
/// Creates a network device for uhyve.
///
/// Every packet transmitted through this device will be received through it
/// in FIFO order.
pub fn new() -> UhyveNet {
UhyveNet { mtu: 1500 }
}
}
impl<'a> Device<'a> for UhyveNet {
type RxToken = RxToken;
type TxToken = TxToken;
::arch::irq::disable();
fn capabilities(&self) -> DeviceCapabilities {
let mut cap = DeviceCapabilities::default();
cap.max_transmission_unit = self.mtu;
cap
}
let nic = {
let info: UhyveNetinfo = UhyveNetinfo::default();
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
let mut rx = RxToken::new();
let data = UhyveRead::new(
virt_to_phys(rx.buffer.as_mut_ptr() as usize),
UHYVE_MAX_MSG_SIZE,
);
unsafe {
outl(
UHYVE_PORT_NETREAD,
virt_to_phys(&data as *const _ as usize) as u32,
UHYVE_PORT_NETINFO,
virt_to_phys(&info as *const _ as usize) as u32,
);
}
if data.ret() == 0 {
let tx = TxToken::new();
rx.resize(data.len());
trace!("resize message to {} bytes", rx.len());
Some((rx, tx))
} else {
POOLING.store(false, Ordering::SeqCst);
None
}
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
trace!("create TxToken to transfer data");
Some(TxToken::new())
}
}
#[doc(hidden)]
pub struct RxToken {
buffer: [u8; UHYVE_MAX_MSG_SIZE],
len: usize,
}
impl RxToken {
pub fn new() -> RxToken {
RxToken {
buffer: [0; UHYVE_MAX_MSG_SIZE],
len: UHYVE_MAX_MSG_SIZE,
}
}
pub fn resize(&mut self, len: usize) {
if len <= UHYVE_MAX_MSG_SIZE {
self.len = len;
} else {
error!("Invalid message size {}", len);
}
}
pub fn len(&self) -> usize {
self.len
}
}
impl phy::RxToken for RxToken {
fn consume<R, F>(self, _timestamp: Instant, f: F) -> smoltcp::Result<R>
where
F: FnOnce(&[u8]) -> smoltcp::Result<R>,
{
let (first, _) = self.buffer.split_at(self.len);
f(first)
}
}
#[doc(hidden)]
pub struct TxToken;
Box::new(UhyveNetwork::new(&info.mac))
};
impl TxToken {
pub const fn new() -> Self {
TxToken {}
}
fn write(&self, data: usize, len: usize) -> usize {
let uhyve_write = UhyveWrite::new(virt_to_phys(data), len);
unsafe {
outl(
UHYVE_PORT_NETWRITE,
virt_to_phys(&uhyve_write as *const _ as usize) as u32,
);
}
// Install interrupt handler
irq_install_handler(UHYVE_IRQ_NET, uhyve_irqhandler as usize);
let ret = uhyve_write.ret();
if ret != 0 {
error!("Unable to send message: {}", ret);
}
::arch::irq::enable();
uhyve_write.len()
}
Ok(nic)
}
impl phy::TxToken for TxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R>
where
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
{
let mut buffer = vec![0; len];
let result = f(&mut buffer);
if result.is_ok() {
self.write(buffer.as_ptr() as usize, len);
}
result
}
#[cfg(target_arch = "x86_64")]
extern "x86-interrupt" fn uhyve_irqhandler(_stack_frame: &mut ExceptionStackFrame) {
debug!("Receive network interrupt from uhyve");
crate::drivers::net::sys_set_polling(true);
apic::eoi();
core_scheduler().scheduler();
}
......@@ -51,8 +51,6 @@ extern crate multiboot;
extern crate x86;
#[macro_use]
extern crate log;
#[cfg(feature = "network")]
extern crate smoltcp;
#[macro_use]
mod macros;
......@@ -200,6 +198,8 @@ extern "C" fn initd(_arg: usize) {
init_uhyve_netif();
}
}
#[cfg(not(feature = "newlib"))]
let _ = drivers::net::init();
} else if !environment::is_single_kernel() {
// Initialize the mmnif interface using static IPs in the range 192.168.28.x.