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

first steps to replace LwIP by smoltcp

parent 18eba6f3
......@@ -32,12 +32,12 @@ if(NOT BOOTSTRAP)
## Tests and benchmarks
build_external(tests ${HERMIT_ROOT}/usr/tests hermit)
build_external(openmpbench ${HERMIT_ROOT}/usr/openmpbench hermit)
#build_external(openmpbench ${HERMIT_ROOT}/usr/openmpbench hermit)
# These are currently x86_64-specific
if("${HERMIT_ARCH}" STREQUAL "x86_64")
build_external(benchmarks ${HERMIT_ROOT}/usr/benchmarks hermit)
endif()
#if("${HERMIT_ARCH}" STREQUAL "x86_64")
# build_external(benchmarks ${HERMIT_ROOT}/usr/benchmarks hermit)
#endif()
endif()
## relocate the local prefix to our install destination
......
......@@ -36,34 +36,17 @@ add_custom_target(doc
${CMAKE_CURRENT_LIST_DIR})
# Now define "kernel modules", which are built into object libraries.
# It is important that ASM and C sources are not mixed in a single module and ASM modules are suffixed with "_asm"!
add_definitions(-D__KERNEL__)
# LwIP
set(LWIP_SRC ${CMAKE_SOURCE_DIR}/lwip/src)
file(GLOB LWIP_SOURCES
"${LWIP_SRC}/api/*.c"
"${LWIP_SRC}/arch/*.c"
"${LWIP_SRC}/core/*.c"
"${LWIP_SRC}/core/ipv4/*.c"
"${LWIP_SRC}/core/ipv6/*.c"
"${LWIP_SRC}/netif/*.c")
add_library(lwip OBJECT ${LWIP_SOURCES})
target_compile_options(lwip PRIVATE ${HERMIT_KERNEL_FLAGS} -w)
target_include_directories(lwip PRIVATE ${HERMIT_KERNEL_INCLUDES})
# arch
if("${HERMIT_ARCH}" STREQUAL "x86_64")
add_subdirectory(src/arch/x86_64/kernel)
endif()
set(KERNEL_OBJECTS
${ARCH_OBJECTS}
$<TARGET_OBJECTS:lwip>)
${ARCH_OBJECTS})
# Build all kernel modules into a single static library.
add_library(hermit-bootstrap STATIC ${KERNEL_OBJECTS})
set_target_properties(hermit-bootstrap PROPERTIES LINKER_LANGUAGE C)
add_dependencies(hermit-bootstrap hermit_rs)
set_target_properties(hermit-bootstrap PROPERTIES ARCHIVE_OUTPUT_NAME hermit)
......
......@@ -38,8 +38,7 @@ pub use arch::aarch64::kernel::stubs::{switch,set_oneshot_timer,wakeup_core};
#[cfg(target_arch="aarch64")]
pub use arch::aarch64::kernel::{application_processor_init,boot_application_processors,
network_adapter_init,output_message_byte,message_output_init,boot_processor_init,
get_processor_count};
output_message_byte,message_output_init,boot_processor_init,get_processor_count};
#[cfg(target_arch="aarch64")]
use arch::aarch64::kernel::percore::core_scheduler;
......@@ -64,7 +63,7 @@ pub use arch::x86_64::*;
#[cfg(target_arch="x86_64")]
pub use arch::x86_64::kernel::{get_processor_count,application_processor_init,
boot_application_processors,message_output_init,network_adapter_init,
boot_application_processors,message_output_init,
output_message_byte,boot_processor_init};
#[cfg(target_arch="x86_64")]
pub use arch::x86_64::kernel::apic::{set_oneshot_timer,wakeup_core};
......
......@@ -92,14 +92,30 @@ static mut KERNEL_HEADER: KernelHeader = KernelHeader {
boot_stack: [0xCD; KERNEL_STACK_SIZE]
};
extern "C" {
fn init_rtl8139_netif(freq: u32) -> i32;
}
static COM1: SerialPort = SerialPort::new(0x3f8);
// FUNCTIONS
pub fn get_ip() -> [u8; 4] {
let mut ip: [u8; 4] = [0,0,0,0];
for i in 0..4 {
ip[i] = unsafe { ptr::read_volatile(&KERNEL_HEADER.hcip[i]) as u8 };
}
ip
}
pub fn get_gateway() -> [u8; 4] {
let mut gw: [u8; 4] = [0,0,0,0];
for i in 0..4 {
gw[i] = unsafe { ptr::read_volatile(&KERNEL_HEADER.hcgateway[i]) as u8 };
}
gw
}
pub fn get_image_size() -> usize {
unsafe { ptr::read_volatile(&KERNEL_HEADER.image_size) as usize }
}
......@@ -243,8 +259,3 @@ fn finish_processor_init() {
let _ = intrinsics::atomic_xadd(&mut KERNEL_HEADER.cpu_online as *mut u32, 1);
}
}
pub fn network_adapter_init() -> i32 {
// Try initializing the RTL8139 interface using DHCP.
unsafe { init_rtl8139_netif(processor::get_frequency() as u32) }
}
......@@ -184,6 +184,7 @@ impl TaskFrame for Task {
extern "x86-interrupt" fn timer_handler(_stack_frame: &mut irq::ExceptionStackFrame) {
core_scheduler().blocked_tasks.lock().handle_waiting_tasks();
apic::eoi();
core_scheduler().scheduler();
}
pub fn install_timer_handler() {
......
// Copyright (c) 2019 Stefan Lankes, RWTH Aachen University
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
pub mod net;
\ No newline at end of file
// Copyright (c) 2019 Stefan Lankes, RWTH Aachen University
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
pub mod uhyve;
use synch::semaphore::*;
use scheduler::task::TaskId;
static NET_SEM: Semaphore = Semaphore::new(0);
static mut NETWORK_TASK_ID: TaskId = TaskId::from(0);
pub fn get_network_task_id() -> TaskId {
unsafe {
NETWORK_TASK_ID
}
}
\ No newline at end of file
// Copyright (c) 2019 Stefan Lankes, RWTH Aachen University
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use alloc::vec::Vec;
use alloc::collections::BTreeMap;
use core::ptr::read_volatile;
use core::str;
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes};
use smoltcp::socket::SocketSet;
//use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::Result;
use smoltcp::phy::{self, Device, DeviceCapabilities};
use scheduler;
use drivers::net::NETWORK_TASK_ID;
#[cfg(target_arch="x86_64")]
use crate::arch::x86_64::kernel::percore::core_scheduler;
#[cfg(target_arch="x86_64")]
use crate::arch::x86_64::kernel::{get_ip,get_gateway};
#[cfg(target_arch="x86_64")]
use crate::arch::x86_64::kernel::irq::*;
#[cfg(target_arch="x86_64")]
use crate::arch::x86_64::kernel::apic;
#[cfg(target_arch="x86_64")]
use crate::arch::x86_64::mm::paging::virt_to_phys;
#[cfg(target_arch="x86_64")]
use x86::io::*;
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_MSG_SIZE: usize = 1792;
/// Data type to determine the mac address
#[derive(Debug, Default)]
#[repr(C)]
struct UhyveNetinfo {
/// mac address
pub mac: [u8; 18]
}
/// Datatype to receive packets from uhyve
#[derive(Debug)]
#[repr(C)]
struct UhyveRead {
/// address to the received data
pub data: usize,
/// length of the buffer
pub len: usize,
/// amount of received data (in bytes)
pub ret: i32
}
impl UhyveRead {
pub fn new(data: usize, len: usize) -> Self {
UhyveRead {
data: data,
len: len,
ret: 0
}
}
pub fn len(&self) -> usize {
unsafe {
read_volatile(&self.len)
}
}
pub fn ret(&self) -> i32 {
unsafe {
read_volatile(&self.ret)
}
}
}
/// Datatype to forward packets to uhyve
#[derive(Debug)]
#[repr(C)]
struct UhyveWrite {
/// address to the data
pub data: usize,
/// length of the data
pub len: usize,
/// return value, transfered bytes
pub ret: i32
}
impl UhyveWrite {
pub fn new(data: usize, len: usize) -> Self {
UhyveWrite {
data: data,
len: len,
ret: 0
}
}
pub fn ret(&self) -> i32 {
unsafe {
read_volatile(&self.ret)
}
}
}
extern "C" fn uhyve_thread(_arg: usize) {
info!("Hello form network thread!");
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();
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.routes(routes)
.finalize();
info!("MAC address {}", ethernet_addr);
info!("Configure network interface with address {}", ip_addrs[0]);
info!("Configure gatway with address {}", default_gw);
let mut sockets = SocketSet::new(vec![]);
let boot_time = crate::arch::get_boot_time();
let mut counter: usize = 0;
loop {
let microseconds = crate::arch::processor::get_timer_ticks() - boot_time;
let timestamp = Instant::from_millis(microseconds as i64 / 1000i64);
match iface.poll(&mut sockets, timestamp) {
Ok(ready) => {
if ready == true {
trace!("receive message {}", counter);
} else {
crate::drivers::net::NET_SEM.acquire(None);
}
},
Err(e) => {
trace!("poll error: {}", e);
}
}
counter = counter+1;
}
}
pub fn init() {
irq_install_handler(UHYVE_IRQ_NET, uhyve_irqhandler as usize);
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())
);
}
}
#[cfg(target_arch="x86_64")]
extern "x86-interrupt" fn uhyve_irqhandler(_stack_frame: &mut ExceptionStackFrame) {
trace!("Receive network interrupt from uhyve");
crate::drivers::net::NET_SEM.release();
apic::eoi();
core_scheduler().scheduler();
}
/// 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;
fn capabilities(&self) -> DeviceCapabilities {
let mut cap = DeviceCapabilities::default();
cap.max_transmission_unit = self.mtu;
cap
}
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_MSG_SIZE);
unsafe {
outl(UHYVE_PORT_NETREAD, virt_to_phys(&data as *const _ as usize) as u32);
}
if data.ret() == 0 {
let tx = TxToken { };
rx.resize(data.len());
Some((rx, tx))
} else {
None
}
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
info!("transmit");
Some(TxToken {})
}
}
#[doc(hidden)]
pub struct RxToken {
buffer: [u8; UHYVE_MSG_SIZE],
len: usize
}
impl RxToken {
pub fn new() -> RxToken {
RxToken {
buffer: [0; UHYVE_MSG_SIZE],
len: UHYVE_MSG_SIZE
}
}
pub fn resize(&mut self, len: usize) {
if len <= UHYVE_MSG_SIZE {
self.len = len;
} else {
warn!("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) -> Result<R>
where F: FnOnce(&[u8]) -> Result<R>
{
let (first, _) = self.buffer.split_at(self.len);
f(first)
}
}
#[doc(hidden)]
pub struct TxToken;
impl 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);
}
let ret = uhyve_write.ret() as usize;
if ret != len {
debug!("Incorrect return value: {} != {}", ret, len);
}
ret
}
}
impl phy::TxToken for TxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
{
let mut buffer = Vec::with_capacity(len);
let result = f(&mut buffer);
self.write(buffer.as_ptr() as usize, len);
result
}
}
\ No newline at end of file
......@@ -29,21 +29,18 @@
include!(concat!(env!("CARGO_TARGET_DIR"), "/config.rs"));
// EXTERNAL CRATES
#[macro_use]
extern crate alloc;
#[macro_use]
extern crate bitflags;
#[cfg(target_arch = "x86_64")]
extern crate hermit_multiboot;
extern crate spin;
#[cfg(target_arch = "x86_64")]
extern crate x86;
#[macro_use]
extern crate log;
extern crate smoltcp;
// MODULES
#[macro_use]
......@@ -63,6 +60,7 @@ mod runtime_glue;
mod scheduler;
mod synch;
mod syscalls;
mod drivers;
// IMPORTS
pub use arch::*;
......@@ -122,8 +120,6 @@ extern "C" {
static kernel_start: u8;
fn libc_start(argc: i32, argv: *mut *mut u8, env: *mut *mut u8) -> !;
fn init_lwip();
fn init_uhyve_netif() -> i32;
}
// FUNCTIONS
......@@ -137,37 +133,25 @@ unsafe fn sections_init() {
}
extern "C" fn initd(_arg: usize) {
// initialize LwIP library
unsafe { init_lwip(); }
// Initialize the specific network interface.
let mut err = 0;
if environment::is_uhyve() {
// Initialize the uhyve-net interface using the IP and gateway addresses specified in hcip, hcmask, hcgateway.
info!("HermitCore is running on uhyve!");
unsafe { init_uhyve_netif(); }
drivers::net::uhyve::init();
} else if !environment::is_single_kernel() {
// Initialize the mmnif interface using static IPs in the range 192.168.28.x.
info!("HermitCore is running side-by-side to Linux!");
//unsafe { init_mmnif_netif(); }
} else {
} /*else {
err = arch::network_adapter_init();
}
// Check if a network interface has been initialized.
if err == 0 {
info!("Successfully initialized a network interface!");
} else {
warn!("Could not initialize a network interface (error code {})", err);
warn!("Starting HermitCore without network support");
}
}*/
syscalls::init();
// Get the application arguments and environment variables.
let (argc, argv, environ) = syscalls::get_application_parameters();
// give the IP thread time to initialize the network interface
core_scheduler().scheduler();
unsafe {
// Initialize .bss sections for the application.
ptr::write_bytes(
......@@ -203,7 +187,7 @@ pub fn boot_processor_main() -> ! {
core_scheduler.spawn(
initd,
0,
scheduler::task::HIGH_PRIO,
scheduler::task::NORMAL_PRIO,
Some(arch::mm::virtualmem::task_heap_start())
);
......
......@@ -96,10 +96,12 @@ unsafe fn alloc_bootstrap(layout: Layout) -> *mut u8 {
/// An allocation using the initialized System Allocator.
fn alloc_system(layout: Layout) -> *mut u8 {
trace!("Allocating {} bytes using the System Allocator", layout.size());
let size = align_up!(layout.size(), BasePageSize::SIZE);
mm::allocate(size, true) as *mut u8
let virtual_address = mm::allocate(size, true);
trace!("Allocated {} bytes at {:#X} using the System Allocator", layout.size(), virtual_address);
virtual_address as *mut u8
}
/// A deallocation using the initialized System Allocator.
......
......@@ -25,7 +25,6 @@ extern "C" {
pub static MM_LOCK: MmLock = MmLock::new();
pub static mut POOL: NodePool = NodePool::new();
/// Physical and virtual address of the first 2 MiB page that maps the kernel.
/// Can be easily accessed through kernel_start_address()
static mut KERNEL_START_ADDRESS: usize = 0;
......
......@@ -20,7 +20,7 @@ use core::sync::atomic::{AtomicI32, AtomicU32, AtomicUsize, Ordering};
use scheduler::task::*;
use synch::spinlock::*;
use syscalls::*;
use drivers::net::get_network_task_id;
/// Time slice of a task in microseconds.
/// When this time has elapsed and the scheduler is called, it may switch to another ready task.
......@@ -247,14 +247,14 @@ impl PerCoreScheduler {
(borrowed.id, borrowed.last_stack_pointer)
};
// If this is the Boot Processor and only the lwIP TCP/IP task is left, it's time to shut down the OS.
if core_id() == 0 && new_id.into() == get_lwip_tcpip_task_id() && NO_TASKS.load(Ordering::SeqCst) == 1 {
debug!("Only lwIP TCP/IP task is left");
// If this is the Boot Processor and only the network task is left, it's time to shut down the OS.
if core_id() == 0 && new_id == get_network_task_id() && NO_TASKS.load(Ordering::SeqCst) == 1 {
debug!("Only network task is left");
sys_shutdown();
}
// Tell the scheduler about the new task.