Commit 66393423 authored by Colin Finck's avatar Colin Finck
Browse files

Centrally handle environment properties and prepare support for multi-kernel...

Centrally handle environment properties and prepare support for multi-kernel mode side-by-side to Linux.

* Introduce an "environment" module to centrally determine and provide information about the environment.
  This currently includes unikernel vs. multi-kernel, whether uhyve is used, and whether proxy is used.
  It also centrally parses the command-line.

* Move all SyscallInterface implementations along with their proxy/uhyve-specific functions to a subfolder
  "librs/syscalls/interfaces".
  Rename SingleKernel to Generic to disambiguate it from other single-kernel modes.

* Fix running HermitCore Rust in unikernel mode when network initialization failed or "proxy" is not used.

* Handle multi-kernel mode wherever required.
  We currently lack init_mmnif_netif() on the lwIP side to initialize the mmnif network interface for multi-kernel mode.

* Add a kernel_message_buffer module compatible to the C implementation.
  Don't initialize a serial port and output all kernel messages to this buffer when multi-kernel mode is detected.

* Add information about the "vga" feature to Cargo.toml to make it actually possible to build with VGA support.
  This is still disabled by default.
parent b26b84e7
......@@ -9,6 +9,10 @@ authors = [
[lib]
crate-type = ["staticlib"]
[features]
#default = ["vga"]
vga = []
[dependencies]
bitflags = "1.0.1"
spin = "0.4.6"
......
......@@ -36,11 +36,11 @@ use arch::x86_64::percore::*;
use arch::x86_64::processor;
use core::sync::atomic::spin_loop_hint;
use core::{mem, ptr, str, u32};
use environment;
use mm;
use scheduler;
use x86::shared::control_regs::*;
use x86::shared::msr::*;
use utils::is_uhyve;
extern "C" {
......@@ -325,10 +325,6 @@ fn detect_from_multiprocessor_specification() -> Result<usize, ()> {
CPU_LOCAL_APIC_IDS.as_mut().unwrap()
};
let current_address_orig = current_address;
current_address = current_address_orig;
// Loop through all table entries.
for _i in 0..mp_config_header.entry_count {
// Have we crossed a page boundary in the last iteration?
......@@ -416,7 +412,7 @@ fn detect_from_multiprocessor_specification() -> Result<usize, ()> {
}
fn detect_from_uhyve() -> Result<usize, ()> {
if is_uhyve() == true {
if environment::is_uhyve() {
return Ok(0xFEE00000 as usize);
}
......@@ -468,7 +464,7 @@ pub fn init() {
calibrate_timer();
// init ioapic
if is_uhyve() == false {
if !environment::is_uhyve() {
init_ioapic();
}
}
......
......@@ -65,6 +65,7 @@ align 4
global heap_size
global header_size
global disable_x2apic
global single_kernel
global mb_info
global hbmem_base
global hbmem_size
......
......@@ -44,9 +44,9 @@ pub use arch::x86_64::gdt::get_boot_stacks;
pub use arch::x86_64::gdt::set_current_kernel_stack;
pub use arch::x86_64::percore::PERCORE;
use arch::x86_64::serial::SerialPort;
use environment;
use kernel_message_buffer;
use synch::spinlock::Spinlock;
use utils::is_uhyve;
use x86::shared::io::*;
const SERIAL_PORT_ADDRESS: u16 = 0xc110; //0x3F8;
const SERIAL_PORT_BAUDRATE: u32 = 115200;
......@@ -66,25 +66,29 @@ static COM1: SerialPort = SerialPort::new(SERIAL_PORT_ADDRESS);
// FUNCTIONS
/// forward a request to the hypervisor uhyve
pub fn uhyve_send(port: u16, data: usize)
{
unsafe {
outl(port, data as u32);
}
}
/// Earliest initialization function called by the Boot Processor.
pub fn message_output_init() {
percore::init();
COM1.init(SERIAL_PORT_BAUDRATE);
if environment::is_single_kernel() {
// We can only initialize the serial port here, because VGA requires processor
// configuration first.
COM1.init(SERIAL_PORT_BAUDRATE);
}
}
pub fn output_message_byte(byte: u8) {
COM1.write_byte(byte);
if cfg!(feature = "vga") && is_uhyve() == false {
if environment::is_single_kernel() {
// Output messages to the serial port and VGA screen in unikernel mode.
COM1.write_byte(byte);
// vga::write_byte() checks if VGA support has been initialized,
// so we don't need any additional if clause around it.
#[cfg(feature = "vga")]
vga::write_byte(byte);
} else {
// Output messages to the kernel message buffer in multi-kernel mode.
kernel_message_buffer::write_byte(byte);
}
}
......@@ -92,26 +96,33 @@ pub fn output_message_byte(byte: u8) {
pub fn boot_processor_init() {
processor::detect_features();
processor::configure();
if cfg!(feature = "vga") && is_uhyve() == false {
if cfg!(feature = "vga") && environment::is_single_kernel() && !environment::is_uhyve() {
#[cfg(feature = "vga")]
vga::init();
}
::mm::init();
::mm::print_information();
environment::init();
gdt::init();
gdt::add_current_core();
idt::install();
if is_uhyve() == false {
if !environment::is_uhyve() {
pic::init();
}
irq::install();
irq::enable();
processor::detect_frequency();
processor::print_information();
if is_uhyve() == false {
if environment::is_single_kernel() && !environment::is_uhyve() {
pci::init();
pci::print_information();
}
apic::init();
scheduler::install_timer_handler();
......
......@@ -27,18 +27,16 @@ use arch::x86_64::irq;
use arch::x86_64::percore::*;
use arch::x86_64::pic;
use arch::x86_64::pit;
use core::{fmt, slice, str, u32};
use core::{fmt, u32};
use core::sync::atomic::spin_loop_hint;
use environment;
use raw_cpuid::*;
use x86::shared::control_regs::*;
use x86::shared::msr::*;
use x86::shared::time::*;
use utils::is_uhyve;
extern "C" {
static cmdline: *const u8;
static cmdsize: usize;
static mut cpu_freq: u32;
}
......@@ -230,22 +228,14 @@ impl CpuFrequency {
}
unsafe fn detect_from_cmdline(&mut self) -> Result<(), ()> {
if cmdsize > 0 {
let slice = slice::from_raw_parts(cmdline, cmdsize);
let cmdline_str = str::from_utf8_unchecked(slice);
let freq_find = cmdline_str.find("-freq");
if freq_find.is_some() {
let cmdline_freq_str = cmdline_str.split_at(freq_find.unwrap() + "-freq".len()).1;
let mhz_str = cmdline_freq_str.split(' ').next().expect("Invalid -freq command line");
self.mhz = mhz_str.parse().expect("Could not parse -freq command line as number");
self.source = CpuFrequencySources::CommandLine;
return Ok(());
}
let mhz = environment::get_command_line_cpu_frequency();
if mhz > 0 {
self.mhz = mhz;
self.source = CpuFrequencySources::CommandLine;
Ok(())
} else {
Err(())
}
Err(())
}
unsafe fn detect_from_cpuid_frequency_info(&mut self, cpuid: &CpuId) -> Result<(), ()> {
......@@ -296,7 +286,8 @@ impl CpuFrequency {
}
fn measure_frequency(&mut self) -> Result<(), ()> {
if is_uhyve() == true {
// The PIC is not initialized for uhyve, so we cannot measure anything.
if environment::is_uhyve() {
return Err(());
}
......
......@@ -22,8 +22,8 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
use core::sync::atomic::spin_loop_hint;
use environment;
use x86::shared::io::*;
use utils::is_uhyve;
const UART_TX: u16 = 0;
const UART_IER: u16 = 1;
......@@ -58,7 +58,8 @@ impl SerialPort {
}
fn is_transmitting(&self) -> bool {
if is_uhyve() == true {
// The virtual serial port in uhyve is never blocked.
if environment::is_uhyve() {
return false;
}
......@@ -83,7 +84,8 @@ impl SerialPort {
}
pub fn init(&self, baudrate: u32) {
if is_uhyve() == true {
// The virtual serial port is always initialized in uhyve.
if environment::is_uhyve() {
return;
}
......
// Copyright (c) 2018 Colin Finck, RWTH Aachen University
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//! Determining and providing information about the environment (unikernel
//! vs. multi-kernel, hypervisor, etc.) as well as central parsing of the
//! command-line parameters.
use core::{slice, str};
extern "C" {
static cmdline: *const u8;
static cmdsize: usize;
static single_kernel: u32;
static uhyve: u32;
}
static mut COMMAND_LINE_CPU_FREQUENCY: u16 = 0;
static mut IS_PROXY: bool = false;
unsafe fn parse_command_line() {
if cmdsize == 0 {
return;
}
// Convert the command-line into a Rust string slice.
let slice = slice::from_raw_parts(cmdline, cmdsize);
let cmdline_str = str::from_utf8_unchecked(slice);
// Check for the -freq option.
if let Some(freq_index) = cmdline_str.find("-freq") {
let cmdline_freq_str = cmdline_str.split_at(freq_index + "-freq".len()).1;
let mhz_str = cmdline_freq_str.split(' ').next().expect("Invalid -freq command line");
COMMAND_LINE_CPU_FREQUENCY = mhz_str.parse().expect("Could not parse -freq command line as number");
}
// Check for the -proxy option.
IS_PROXY = cmdline_str.find("-proxy").is_some();
}
pub fn init() {
unsafe {
parse_command_line();
if uhyve > 0 {
// We are running under uhyve, which implies unikernel mode and no communication with "proxy".
IS_PROXY = false;
} else if single_kernel == 0 {
// We are running side-by-side to Linux, which implies communication with "proxy".
IS_PROXY = true;
}
}
}
/// CPU Frequency in MHz if given through the -freq command-line parameter, otherwise zero.
pub fn get_command_line_cpu_frequency() -> u16 {
unsafe { COMMAND_LINE_CPU_FREQUENCY }
}
/// Whether HermitCore shall communicate with the "proxy" application over a network interface.
/// Only valid after calling init()!
pub fn is_proxy() -> bool {
unsafe { IS_PROXY }
}
/// Whether HermitCore is running alone (true) or side-by-side to Linux in Multi-Kernel mode (false).
pub fn is_single_kernel() -> bool {
unsafe { single_kernel > 0 }
}
/// Whether HermitCore is running under the "uhyve" hypervisor.
pub fn is_uhyve() -> bool {
unsafe { uhyve > 0 }
}
// Copyright (c) 2018 Colin Finck, RWTH Aachen University
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//! Kernel Message Buffer for Multi-Kernel mode.
//! Can be read from the Linux side as no serial port is available.
use core::ptr;
use core::sync::atomic::{AtomicUsize, Ordering};
const KMSG_SIZE: usize = 0x1000;
#[repr(C)]
struct KmsgSection {
buffer: [u8; KMSG_SIZE + 1],
}
#[link_section = ".kmsg"]
static mut KMSG: KmsgSection = KmsgSection { buffer: [0; KMSG_SIZE + 1] };
static BUFFER_INDEX: AtomicUsize = AtomicUsize::new(0);
pub fn write_byte(byte: u8) {
let index = BUFFER_INDEX.fetch_add(1, Ordering::SeqCst);
unsafe {
ptr::write_volatile(&mut KMSG.buffer[index % KMSG_SIZE], byte);
}
}
......@@ -73,8 +73,9 @@ mod logging;
mod arch;
mod collections;
mod console;
mod environment;
mod errno;
mod utils;
mod kernel_message_buffer;
mod mm;
mod runtime_glue;
mod scheduler;
......@@ -89,7 +90,6 @@ use arch::percore::*;
use processor::get_frequency;
use core::ptr;
use mm::allocator;
use utils::is_uhyve;
#[global_allocator]
static ALLOCATOR: &'static allocator::HermitAllocator = &allocator::HermitAllocator;
......@@ -127,37 +127,40 @@ extern "C" fn initd(_arg: usize) {
// initialize LwIP library
unsafe { init_lwip(); }
if is_uhyve() == true {
info!("HermitCore is running on uhyve!");
// 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(); }
syscalls::init();
let argc = 0;
let argv = 0 as *mut *mut u8;
let environ = 0 as *mut *mut u8;
unsafe { libc_start(argc, argv, environ); }
} 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 {
let err;
unsafe { err = init_rtl8139_netif(get_frequency() as u32); }
syscalls::init();
if err == 0 {
let argc = 0;
let argv = 0 as *mut *mut u8;
let environ = 0 as *mut *mut u8;
// Initialize the RTL8139 interface using DHCP.
err = unsafe { init_rtl8139_netif(get_frequency() as u32) };
}
unsafe { libc_start(argc, argv, environ); }
} else {
let argc = 0;
let argv = 0 as *mut *mut u8;
let environ = 0 as *mut *mut u8;
// Check if a network interface has been initialized.
if err == 0 {
info!("Successfully initialized a network interface!");
syscalls::enable_networking();
unsafe { libc_start(argc, argv, environ); }
if environment::is_proxy() {
// TODO: Get argc, argv, environ over "proxy", call libc_start, and return.
}
} else {
warn!("Could not initialize a network interface (error code {})", err);
warn!("Starting HermitCore without network support");
}
// Start the application without any arguments and environment variables.
let argc = 0;
let argv = 0 as *mut *mut u8;
let environ = 0 as *mut *mut u8;
unsafe { libc_start(argc, argv, environ); }
}
/// Entry Point of HermitCore for the Boot Processor
......@@ -172,7 +175,8 @@ pub unsafe extern "C" fn boot_processor_main() {
arch::boot_processor_init();
scheduler::init();
scheduler::add_current_core();
if is_uhyve() == false {
if environment::is_single_kernel() && !environment::is_uhyve() {
arch::boot_application_processors();
}
......
// Copyright (c) 2018 Stefan Lankes, RWTH Aachen University
// Colin Finck, RWTH Aachen University
//
// MIT License
//
......@@ -20,10 +21,10 @@
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
extern "C" {
static uhyve: u32;
}
pub fn is_uhyve() -> bool {
unsafe { uhyve != 0 }
}
use syscalls::interfaces::SyscallInterface;
// The generic interface simply uses all default implementations of the
// SyscallInterface trait.
pub struct Generic;
impl SyscallInterface for Generic {}
// Copyright (c) 2018 Stefan Lankes, RWTH Aachen University
// Colin Finck, RWTH Aachen University
//
// MIT License
//
......@@ -21,6 +22,13 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
mod generic;
mod proxy;
mod uhyve;
pub use self::generic::*;
pub use self::proxy::*;
pub use self::uhyve::*;
use arch;
use arch::percore::*;
use console;
......@@ -28,7 +36,12 @@ use core::fmt::Write;
use core::{isize, slice, str};
use errno::*;
pub trait SyscallInterface : Send + Sync {
fn init(&self) {
// Interface-specific initialization steps.
}
fn exit(&self, arg: i32) -> ! {
core_scheduler().exit(arg);
}
......
// Copyright (c) 2018 Stefan Lankes, RWTH Aachen University
// Colin Finck, RWTH Aachen University
//
// MIT License
//
......@@ -21,19 +22,21 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
use syscalls::syscalls::SyscallInterface;
use syscalls::interfaces::SyscallInterface;
use syscalls::{LWIP_FD_BIT,LWIP_LOCK};
use syscalls::lwip::sys_lwip_get_errno;
use arch::processor::halt;
use core::mem;
extern "C" {
fn get_proxy_socket() -> i32;
fn lwip_write(fd: i32, buf: *const u8, len: usize) -> i32;
fn lwip_read(fd: i32, buf: *mut u8, len: usize) -> i32;
fn lwip_close(fd: i32) -> i32;
fn c_strlen(buf: *const u8) -> usize;
}
const HERMIT_MAGIC: i32 = 0x7E317;
const NR_EXIT: i32 = 0;
......@@ -83,7 +86,7 @@ fn proxy_write<T>(buf: *const T) {
}
}
pub fn setup_connection(fd: i32) {
fn setup_connection(fd: i32) {
info!("Setup connection to proxy!");
unsafe {
......@@ -104,6 +107,12 @@ pub fn setup_connection(fd: i32) {
//proxy_read(&mut argc as *mut i32);
}
//
// TODO: Use a big "tagged union" (aka Rust "enum") with #[repr(C)] for these structures
// once https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md
// has been implemented.
//
#[repr(C)]
struct SysWrite {
sysnr: i32,
......@@ -200,15 +209,16 @@ impl SysLseek {
}
}
pub struct Proxy;
impl Proxy {
pub const fn new() -> Proxy {
Proxy {}
impl SyscallInterface for Proxy {
fn init(&self) {
let fd = unsafe { get_proxy_socket() };
assert!(fd >= 0);
setup_connection(fd);
}
}
impl SyscallInterface for Proxy {
fn exit(&self, arg: i32) -> ! {
let guard = LWIP_LOCK.lock();
......
......@@ -21,12 +21,12 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
use syscalls::syscalls::SyscallInterface;
use syscalls::interfaces::SyscallInterface;
use syscalls::{LWIP_FD_BIT,LWIP_LOCK};
use syscalls::lwip::sys_lwip_get_errno;
use arch::mm::paging;
use arch::processor::halt;
use arch::uhyve_send;
use x86::shared::io::*;