diff --git a/.travis.yml b/.travis.yml index ebeaa3b711..541b552669 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,5 +17,5 @@ before_script: - mkdir darwin && cd darwin script: - - cmake .. -DTRAVIS=ON + - cmake .. -DTRAVIS=ON -DEXTRA_TESTS=ON - make VERBOSE=1 && ./unittests --pass diff --git a/CMakeLists.txt b/CMakeLists.txt index b104e22f8a..b22f9b6c60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ set(SCRIPTS ${CMAKE_INSTALL_PREFIX}/includeos/scripts) if(CMAKE_COMPILER_IS_GNUCC) # currently gcc is not supported due to problems cross-compiling a unikernel # (i.e., building a 32bit unikernel (only supported for now) on a 64bit system) - message(FATAL_ERROR "Building IncludeOS with gcc is not currently supported. Please clean-up build directory and configure for clang through CC and CXX environmental variables.") +# message(FATAL_ERROR "Building IncludeOS with gcc is not currently supported. Please clean-up build directory and configure for clang through CC and CXX environmental variables.") endif(CMAKE_COMPILER_IS_GNUCC) # create OS version string from git describe (used in CXX flags) @@ -23,10 +23,21 @@ execute_process(COMMAND git describe --dirty OUTPUT_VARIABLE OS_VERSION) string(STRIP ${OS_VERSION} OS_VERSION) +option(cpu_feat_vanilla "Restrict use of CPU features to vanilla" ON) +if(cpu_feat_vanilla) + include("cmake/vanilla.cmake") + set(DEFAULT_SETTINGS_CMAKE "vanilla.cmake") # for service cmake + set(DEFAULT_VM "vm.vanilla.json") # vmrunner +else() + include("cmake/cpu_feat.cmake") + set(DEFAULT_SETTINGS_CMAKE "cpu_feat.cmake") # for service cmake + set(DEFAULT_VM "vm.cpu_feat.json") # vmrunner +endif(cpu_feat_vanilla) + # create random hex string as stack protector canary string(RANDOM LENGTH 8 ALPHABET 0123456789ABCDEF STACK_PROTECTOR_VALUE) -set(CAPABS "-msse3 -fstack-protector-strong -D_STACK_GUARD_VALUE_=0x${STACK_PROTECTOR_VALUE} ") +set(CAPABS "${CAPABS} -fstack-protector-strong -D_STACK_GUARD_VALUE_=0x${STACK_PROTECTOR_VALUE}") # Various global defines # * NO_DEBUG disables output from the debug macro @@ -81,9 +92,15 @@ endif(silent) # Append optimization level set(CAPABS "${CAPABS} ${OPTIMIZE}") -# these kinda work with llvm -set(CMAKE_CXX_FLAGS "-target i686 -MMD ${CAPABS} ${WARNS} -nostdlib -nostdlibinc -c -m32 -std=c++14 -D_LIBCPP_HAS_NO_THREADS=1 -DOS_VERSION=\\\"${OS_VERSION}\\\"") -set(CMAKE_C_FLAGS "-target i686 -MMD ${CAPABS} ${WARNS} -nostdlib -nostdlibinc -c -m32 -DOS_VERSION=\"\"${OS_VERSION}\"\"") +if (CMAKE_COMPILER_IS_GNUCC) + # gcc/g++ settings + set(CMAKE_CXX_FLAGS "-m32 -MMD ${CAPABS} ${WARNS} -Wno-frame-address -nostdlib -c -std=c++14 -D_LIBCPP_HAS_NO_THREADS=1 -DOS_VERSION=\\\"${OS_VERSION}\\\"") + set(CMAKE_C_FLAGS "-m32 -MMD ${CAPABS} ${WARNS} -nostdlib -c -DOS_VERSION=\"\"${OS_VERSION}\"\"") +else() + # these kinda work with llvm + set(CMAKE_CXX_FLAGS "-target i686 -MMD ${CAPABS} ${WARNS} -nostdlib -nostdlibinc -c -m32 -std=c++14 -D_LIBCPP_HAS_NO_THREADS=1 -DOS_VERSION=\\\"${OS_VERSION}\\\"") + set(CMAKE_C_FLAGS "-target i686 -MMD ${CAPABS} ${WARNS} -nostdlib -nostdlibinc -c -m32 -DOS_VERSION=\"\"${OS_VERSION}\"\"") +endif() # either download or cross-compile needed libraries #option(from_bundle "Download and use pre-compiled libraries for cross-comilation" ON) @@ -161,10 +178,20 @@ endif(rapidjson) # # Installation # -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/etc/service.cmake DESTINATION includeos) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/etc/library.cmake DESTINATION includeos) -install(DIRECTORY vmrunner DESTINATION includeos/) + +# Install cmake files +install(FILES etc/service.cmake DESTINATION includeos) +install(FILES etc/library.cmake DESTINATION includeos) +install(FILES cmake/${DEFAULT_SETTINGS_CMAKE} DESTINATION includeos RENAME settings.cmake) # cpu_feat_vanilla opt + +# Install vmrunner +install(DIRECTORY vmrunner DESTINATION includeos) +install(FILES vmrunner/${DEFAULT_VM} DESTINATION includeos/vmrunner/ RENAME vm.default.json) # cpu_feat_vanilla opt + +# Install toolchain install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/i686-elf-toolchain.cmake DESTINATION includeos) + +# Install seed install(DIRECTORY seed/ DESTINATION includeos/seed) # Install boot util diff --git a/README.md b/README.md index 1d6f7bf11b..0201cf23ad 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ IncludeOS is free software, with "no warranties or restrictions of any kind". ### Key features * **Extreme memory footprint**: A minimal bootable image, including bootloader, operating system components and a complete C++ standard library is currently 707K when optimized for size. -* **KVM and VirtualBox support** with full virtualization, using [x86 hardware virtualization](https://en.wikipedia.org/wiki/X86_virtualization), available on any modern x86 CPUs). In principle IncludeOS should run on any x86 hardware platform, even on a physical x86 computer, given appropriate drivers. Officially, we develop for- and test on [Linux KVM](http://www.linux-kvm.org/page/Main_Page), which power the [OpenStack IaaS cloud](https://www.openstack.org/), and [VirtualBox](https://www.virtualbox.org), which means that you can run your IncludeOS service on both Linux, Microsoft Windows and macOS. +* **KVM, VirtualBox and VMWare support** with full virtualization, using [x86 hardware virtualization](https://en.wikipedia.org/wiki/X86_virtualization), available on any modern x86 CPUs). In principle IncludeOS should run on any x86 hardware platform, even on a physical x86 computer, given appropriate drivers. Officially, we develop for- and test on [Linux KVM](http://www.linux-kvm.org/page/Main_Page), which power the [OpenStack IaaS cloud](https://www.openstack.org/), and [VirtualBox](https://www.virtualbox.org), which means that you can run your IncludeOS service on both Linux, Microsoft Windows and macOS. * **C++11/14 support** * Full C++11/14 language support with [clang](http://clang.llvm.org) v3.8 and later. * Standard C++ library (STL) [libc++](http://libcxx.llvm.org) from [LLVM](http://llvm.org/). diff --git a/api/fs/common.hpp b/api/fs/common.hpp index b38e06cb4b..196f62e728 100644 --- a/api/fs/common.hpp +++ b/api/fs/common.hpp @@ -188,7 +188,7 @@ namespace fs { using on_init_func = delegate; using on_ls_func = delegate; using on_read_func = delegate; - using on_stat_func = delegate; + using on_stat_func = delegate; struct List diff --git a/api/http b/api/http index 78b70bca82..8e10ffb0df 100644 --- a/api/http +++ b/api/http @@ -17,10 +17,10 @@ // limitations under the License. #pragma once -#ifndef ___HTTP_API___ -#define ___HTTP_API___ +#ifndef INCLUDEOS_HTTP_API_HPP +#define INCLUDEOS_HTTP_API_HPP -#include "net/http/response.hpp" -#include "net/http/request.hpp" +#include "net/http/client.hpp" +#include "net/http/server.hpp" -#endif //< ___HTTP_API___ +#endif //< INCLUDEOS_HTTP_API_HPP diff --git a/api/hw/block_device.hpp b/api/hw/block_device.hpp index 4f76c8c163..49af64d754 100644 --- a/api/hw/block_device.hpp +++ b/api/hw/block_device.hpp @@ -1,6 +1,6 @@ // This file is a part of the IncludeOS unikernel - www.includeos.org // -// Copyright 2015 Oslo and Akershus University College of Applied Sciences +// Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences // and Alfred Bratterud // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,71 +16,148 @@ // limitations under the License. #pragma once -#ifndef HW_DRIVE_HPP -#define HW_DRIVE_HPP +#ifndef HW_BLOCK_DEVICE_HPP +#define HW_BLOCK_DEVICE_HPP -#include #include #include +#include -namespace hw -{ - - /** - * Abstract interface for block devices of various driver types - **/ - class Block_device { - public: - using block_t = uint64_t; //< Disk device block size - using buffer_t = std::shared_ptr; - - // Delegate for result of reading a disk sector - //using on_read_func = delegate; - using on_read_func = delegate; +namespace hw { - using Device_id = int; +/** + * This class is an abstract interface for block devices + */ +class Block_device { +public: + using block_t = uint64_t; //< Representation for a device's block size + using buffer_t = std::shared_ptr; //< Representation for a block device's buffer + using Device_id = int32_t; //< Representation for a block device's identifier + using on_read_func = delegate; //< Delegate for result of reading from a block device - static const char* device_type() - { return "Block device"; } + /** + * Method to get the type of device + * + * @return The type of device as a C-String + */ + static const char* device_type() noexcept + { return "Block device"; } - virtual std::string device_name() const = 0; + /** + * Method to get the name of the device + * + * @return The name of the device as a std::string + */ + virtual std::string device_name() const = 0; - Device_id id() const { return id_; } + /** + * Method to get the device's identifier + * + * @return The device's identifier + */ + Device_id id() const noexcept + { return id_; } - /** Human-readable name of this disk controller */ - virtual const char* driver_name() const noexcept = 0; + /** + * Get the human-readable name of this device's controller + * + * @return The human-readable name of this device's controller + */ + virtual const char* driver_name() const noexcept = 0; - /** The size of the disk in whole sectors */ - virtual block_t size() const noexcept = 0; + /** + * Get the size of the device as total number of blocks + * + * @return The size of the device as total number of blocks + */ + virtual block_t size() const noexcept = 0; - /** Returns the optimal block size for this device */ - virtual block_t block_size() const noexcept = 0; + /** + * Get the optimal block size for this device + * + * @return The optimal block size for this device + */ + virtual block_t block_size() const noexcept = 0; - /** - * Read block(s) from blk and call func with result - * A null-pointer is passed to result if something bad happened - * Validate using !buffer_t: - * if (!buffer) - * error("Device failed to read sector"); - **/ - virtual void read(block_t blk, on_read_func func) = 0; - virtual void read(block_t blk, size_t count, on_read_func) = 0; + /** + * Read a block of data asynchronously from the device + * + * @param blk + * The block of data to read from the device + * + * @param reader + * An operation to perform asynchronously + * + * @note A nullptr is passed to the reader if an error occurred + * @note Validate the reader's input + * + * @example + * if (buffer == nullptr) { + * error("Device failed to read sector"); + * } + */ + virtual void read(block_t blk, on_read_func reader) = 0; - /** read synchronously the block @blk */ - virtual buffer_t read_sync(block_t blk) = 0; - virtual buffer_t read_sync(block_t blk, size_t count) = 0; + /** + * Read blocks of data asynchronously from the device + * + * @param blk + * The starting block of data to read from the device + * + * @param count + * The number of blocks to read from the device + * + * @param reader + * An operation to perform asynchronously + * + * @note A nullptr is passed to the reader if an error occurred + * @note Validate the reader's input + * + * @example + * if (buffer == nullptr) { + * error("Device failed to read sector"); + * } + */ + virtual void read(block_t blk, size_t count, on_read_func reader) = 0; - virtual void deactivate() = 0; + /** + * Read a block of data synchronously from the device + * + * @param blk + * The block of data to read from the device + * + * @return A buffer containing the data or nullptr if an error occurred + */ + virtual buffer_t read_sync(block_t blk) = 0; - virtual ~Block_device() noexcept = default; + /** + * Read blocks of data synchronously from the device + * + * @param blk + * The starting block of data to read from the device + * + * @param count + * The number of blocks to read from the device + * + * @return A buffer containing the data or nullptr if an error occurred + */ + virtual buffer_t read_sync(block_t blk, size_t count) = 0; - protected: - Block_device(); + /** + * Method to deactivate the block device + */ + virtual void deactivate() = 0; - private: - int id_; - }; //< class Drive + /** + * Default destructor + */ + virtual ~Block_device() noexcept = default; +protected: + Block_device(); +private: + Device_id id_; +}; //< class Block_device } //< namespace hw -#endif //< HW_DRIVE_HPP +#endif //< HW_BLOCK_DEVICE_HPP diff --git a/api/net/ethernet/header.hpp b/api/net/ethernet/header.hpp index ac542ef4b9..51622454b7 100644 --- a/api/net/ethernet/header.hpp +++ b/api/net/ethernet/header.hpp @@ -14,8 +14,8 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#pragma once +#pragma once #ifndef NET_ETHERNET_HEADER_HPP #define NET_ETHERNET_HEADER_HPP @@ -40,7 +40,7 @@ class Header { const MAC::Addr& src() const noexcept { return src_; } - const Ethertype& type() const noexcept + Ethertype type() const noexcept { return type_; } void set_dest(const MAC::Addr& dest) diff --git a/api/net/inet.hpp b/api/net/inet.hpp index f38dd1c568..53fba2bb5e 100644 --- a/api/net/inet.hpp +++ b/api/net/inet.hpp @@ -179,7 +179,7 @@ namespace net { /// /** Get Maximum Transmission Unit **/ - virtual constexpr uint16_t MTU() const = 0; + virtual uint16_t MTU() const = 0; /** Provision empty anonymous packet **/ virtual Packet_ptr create_packet() = 0; diff --git a/api/net/ip4/icmp4_common.hpp b/api/net/ip4/icmp4_common.hpp index fc4c6f540a..187d19c02f 100644 --- a/api/net/ip4/icmp4_common.hpp +++ b/api/net/ip4/icmp4_common.hpp @@ -95,6 +95,8 @@ namespace net { return "TIMESTAMP REPLY (14)"; case Type::NO_REPLY: return "NO REPLY"; + default: + return "UNKNOWN"; }; }(); } diff --git a/api/net/ip4/ip4.hpp b/api/net/ip4/ip4.hpp index f775b0b8d6..2549ad35e4 100644 --- a/api/net/ip4/ip4.hpp +++ b/api/net/ip4/ip4.hpp @@ -57,7 +57,7 @@ namespace net { /* Maximum Datagram Data Size */ - constexpr uint16_t MDDS() const + uint16_t MDDS() const { return stack_.MTU() - sizeof(ip4::Header); } /** Upstream: Input from link layer */ diff --git a/api/net/ip4/udp.hpp b/api/net/ip4/udp.hpp index 1ed3dcb6c2..2c2bdd8730 100644 --- a/api/net/ip4/udp.hpp +++ b/api/net/ip4/udp.hpp @@ -133,7 +133,7 @@ namespace net { // create and transmit @num packets from sendq void process_sendq(size_t num); - inline constexpr uint16_t max_datagram_size() noexcept { + uint16_t max_datagram_size() noexcept { return stack().ip_obj().MDDS() - sizeof(header); } diff --git a/api/net/tcp/connection.hpp b/api/net/tcp/connection.hpp index 4fa0c03549..ceea39fb61 100644 --- a/api/net/tcp/connection.hpp +++ b/api/net/tcp/connection.hpp @@ -65,12 +65,13 @@ class Connection : public std::enable_shared_from_this { using WriteBuffer = Write_queue::WriteBuffer; public: - /** Called with the connection itself when it's been established. */ + /** Called with the connection itself when it's been established. May be a nullptr if the connection failed. */ using ConnectCallback = delegate; /** * @brief Event when a connection has been established. * This event lets you know when to start using the connection, * and should always be assigned. + * NOTE: The Connection_ptr will be a nullptr when an outgoing connection failed. * * @param[in] callback The callback * @@ -137,18 +138,6 @@ class Connection : public std::enable_shared_from_this { */ inline Connection& on_write(WriteCallback callback); - /** Called with the error encountered. */ - using ErrorCallback = delegate; - /** - * @brief Event when a connection has experienced an error of any kind. - * Pretty useless in it's current form, and only useful for printing. - * - * @param[in] callback The callback - * - * @return This connection - */ - inline Connection& on_error(ErrorCallback callback); - /** Called with the packet that got dropped and the reason why. */ using PacketDroppedCallback = delegate; /** @@ -231,7 +220,10 @@ class Connection : public std::enable_shared_from_this { */ Stream(Connection_ptr conn) : tcp{std::move(conn)} - {} + { + // stream for a nullptr makes no sense + Expects(tcp != nullptr); + } /** * @brief Event when the stream is connected/established/ready to use. @@ -506,7 +498,7 @@ class Connection : public std::enable_shared_from_this { * * @return True if able to send, False otherwise. */ - constexpr bool can_send() const noexcept + bool can_send() const noexcept { return (usable_window() >= SMSS()) and writeq.has_remaining_requests(); } /** @@ -764,6 +756,8 @@ class Connection : public std::enable_shared_from_this { recover = ISS; // [RFC 6582] } + uint32_t get_ts_recent() const noexcept { return TS_recent; } + bool slow_start() const noexcept { return cwnd < ssthresh; } @@ -788,6 +782,7 @@ class Connection : public std::enable_shared_from_this { /** * @brief Open the connection. * Active determines whether the connection is active or passive. + * May throw if no remote host, or state isnt valid for opening. * * @param[in] active Whether its an active (outgoing) or passive (listening) */ @@ -843,7 +838,6 @@ class Connection : public std::enable_shared_from_this { /** Callbacks */ ConnectCallback on_connect_; DisconnectCallback on_disconnect_; - ErrorCallback on_error_; PacketDroppedCallback on_packet_dropped_; RtxTimeoutCallback on_rtx_timeout_; CloseCallback on_close_; @@ -998,15 +992,15 @@ class Connection : public std::enable_shared_from_this { /* Invoke/signal the diffrent TCP events. */ - void signal_connect() - { if(on_connect_) on_connect_(shared_from_this()); } + void signal_connect(const bool success = true) + { + if(on_connect_) + (success) ? on_connect_(shared_from_this()) : on_connect_(nullptr); + } void signal_disconnect(Disconnect::Reason&& reason) { on_disconnect_(shared_from_this(), Disconnect{reason}); } - void signal_error(TCPException error) - { if(on_error_) on_error_(std::forward(error)); } - void signal_packet_dropped(const Packet& packet, Drop_reason reason) { if(on_packet_dropped_) on_packet_dropped_(packet, reason); } @@ -1123,7 +1117,7 @@ class Connection : public std::enable_shared_from_this { * * @return True if able to send one, False otherwise. */ - constexpr bool can_send_one() const + bool can_send_one() const { return send_window() >= SMSS() and writeq.has_remaining_requests(); } /** @@ -1264,7 +1258,7 @@ class Connection : public std::enable_shared_from_this { Retransmission timeout limit reached */ bool rto_limit_reached() const - { return rtx_attempt_ >= 15 or syn_rtx_ >= 5; }; + { return rtx_attempt_ >= 14 or syn_rtx_ >= 4; }; /* Remove all packets acknowledge by ACK in retransmission queue diff --git a/api/net/tcp/connection.inc b/api/net/tcp/connection.inc index 7f2a9b2eb5..16037158b6 100644 --- a/api/net/tcp/connection.inc +++ b/api/net/tcp/connection.inc @@ -24,11 +24,6 @@ inline Connection& Connection::on_write(WriteCallback cb) { return *this; } -inline Connection& Connection::on_error(ErrorCallback callback) { - on_error_ = callback; - return *this; -} - inline Connection& Connection::on_packet_dropped(PacketDroppedCallback callback) { on_packet_dropped_ = callback; return *this; diff --git a/api/net/tcp/tcp.hpp b/api/net/tcp/tcp.hpp index d4fc632b46..d4f1504774 100644 --- a/api/net/tcp/tcp.hpp +++ b/api/net/tcp/tcp.hpp @@ -354,7 +354,7 @@ namespace net { * * @return The window size */ - constexpr uint32_t window_size() const + uint32_t window_size() const { return win_size_; } /** @@ -371,7 +371,7 @@ namespace net { * * @return The wscale factor */ - constexpr uint8_t wscale() const + uint8_t wscale() const { return wscale_; } /** @@ -380,7 +380,7 @@ namespace net { * * @return Whether wscale is being used */ - constexpr bool uses_wscale() const + bool uses_wscale() const { return wscale_ > 0; } /** @@ -396,7 +396,7 @@ namespace net { * * @return Whether the TCP instance is using Timestamp Options or not */ - constexpr bool uses_timestamps() const + bool uses_timestamps() const { return timestamps_; } /** @@ -428,7 +428,7 @@ namespace net { * * @return The limit */ - constexpr uint16_t max_syn_backlog() const + uint16_t max_syn_backlog() const { return max_syn_backlog_; } /** @@ -438,7 +438,7 @@ namespace net { * * @return The MSS */ - constexpr uint16_t MSS() const + uint16_t MSS() const { return network().MDDS() - sizeof(tcp::Header); } /** diff --git a/api/net/tls/server.hpp b/api/net/tls/server.hpp index c76d2110ff..d0cf382903 100644 --- a/api/net/tls/server.hpp +++ b/api/net/tls/server.hpp @@ -38,7 +38,7 @@ class Server : public Botan::TLS::Callbacks, public tcp::Stream Server(Connection_ptr remote, Botan::RandomNumberGenerator& rng, Botan::Credentials_Manager& credman) - : tcp::Stream({remote}), + : tcp::Stream{remote}, m_creds(credman), m_session_manager(), m_tls(*this, m_session_manager, m_creds, m_policy, rng) diff --git a/api/smp b/api/smp index fe66e3a106..fb7dc1c4aa 100644 --- a/api/smp +++ b/api/smp @@ -133,7 +133,11 @@ public: }; // access a std::array of structs indexed by current CPU id +#ifdef INCLUDEOS_SINGLE_THREADED +#define PER_CPU(x) (per_cpu_help(x)) +#else #define PER_CPU(x) (per_cpu_help(x)) +#endif #ifndef ARCH_X86 typedef int spinlock_t; @@ -142,4 +146,3 @@ inline void unlock(spinlock_t&) {} #endif #endif - diff --git a/api/util/chunk.hpp b/api/util/chunk.hpp index 62acb21ee2..8d05ff984c 100644 --- a/api/util/chunk.hpp +++ b/api/util/chunk.hpp @@ -97,7 +97,7 @@ class Chunk { * * @return The byte at index */ - constexpr byte_t& operator[](index_type idx) const noexcept + byte_t& operator[](index_type idx) const noexcept { return data()[idx]; } /** @@ -193,14 +193,14 @@ class Chunk { iterator& operator=(const iterator&) noexcept = default; - constexpr reference operator*() const + reference operator*() const { Expects(chunk_ && *chunk_); return (*chunk_)[index_]; } - constexpr pointer operator->() const + pointer operator->() const { Expects(chunk_ && *chunk_); diff --git a/api/util/timer.hpp b/api/util/timer.hpp index bfce4d47b9..beaab7399e 100644 --- a/api/util/timer.hpp +++ b/api/util/timer.hpp @@ -124,7 +124,7 @@ class Timer { */ inline void _internal_timeout(id_t id); -} __attribute__((packed)); // < class Timer +}; // < class Timer inline void Timer::start(duration_t when, handler_t on_timeout) { if(!is_running()) diff --git a/cmake/cpu_feat.cmake b/cmake/cpu_feat.cmake new file mode 100644 index 0000000000..4f1b5262c1 --- /dev/null +++ b/cmake/cpu_feat.cmake @@ -0,0 +1,2 @@ +set(CAPABS "-mavx -maes -mfma -mfpmath=sse") +message(STATUS "Using extended CPU features: AVX, AES, FMA. CAPABS = ${CAPABS}") diff --git a/cmake/vanilla.cmake b/cmake/vanilla.cmake new file mode 100644 index 0000000000..028afd7db5 --- /dev/null +++ b/cmake/vanilla.cmake @@ -0,0 +1,2 @@ +set(CAPABS "-msse3 -mfpmath=sse") +message(STATUS "Using vanilla CPU features: SSE3. CAPABS = ${CAPABS}") diff --git a/etc/install_build_requirements.sh b/etc/install_build_requirements.sh index 75c5c2eb55..ac3c2266e1 100755 --- a/etc/install_build_requirements.sh +++ b/etc/install_build_requirements.sh @@ -1,35 +1,105 @@ -#!/bin/sh +#!/bin/bash # # Install dependencies -SYSTEM=$1 -RELEASE=$2 +############################################################ +# OPTIONS: +############################################################ -DEPENDENCIES="curl make clang cmake nasm bridge-utils qemu jq python-jsonschema python-psutil" +BUILD_DEPENDENCIES="curl make clang cmake nasm bridge-utils qemu jq python-jsonschema python-psutil" +TEST_DEPENDENCIES="g++ g++-multilib python-junit.xml" + +############################################################ +# COMMAND LINE PROPERTIES: +############################################################ + +# Initialize variables: +SYSTEM=0 +RELEASE=0 +CHECK_ONLY=0 +PRINT_INSTALL_STATUS=0 +DEPENDENCIES_TO_INSTALL=build + +while getopts "h?s:r:cpd:" opt; do + case "$opt" in + h|\?) + printf "%s\n" "Options:"\ + "-s System: What system to install on"\ + "-r Release: What release of said system"\ + "-c Only check: Will only check what packages are needed (will always print status as well)"\ + "-p Print install status: Flag for wheter or not to print dependency status"\ + "-d Dependencies to install: [build | test | all] are the options" + exit 0 ;; + s) SYSTEM=$OPTARG ;; + r) RELEASE=$OPTARG ;; + c) CHECK_ONLY=1 ; PRINT_INSTALL_STATUS=1;; + p) PRINT_INSTALL_STATUS=1 ;; + d) DEPENDENCIES_TO_INSTALL=$OPTARG ;; + esac +done + +# Figure out which dependencies to check +case "$DEPENDENCIES_TO_INSTALL" in + build) ALL_DEPENDENCIES=$BUILD_DEPENDENCIES ;; + test) ALL_DEPENDENCIES=$TEST_DEPENDENCIES ;; + all) ALL_DEPENDENCIES="$BUILD_DEPENDENCIES $TEST_DEPENDENCIES" ;; +esac + +############################################################ +# CHECK INSTALLED PACKAGES: +############################################################ + +if [ $PRINT_INSTALL_STATUS -eq 1 ]; then + printf "%-15s %-20s %s \n"\ + "Status" "Package" "Version"\ + "------" "-------" "-------" + for package in $ALL_DEPENDENCIES; do + dpkg-query -W $package > /dev/null 2>&1 + if [ $? -eq 0 ]; then + printf '\e[32m%-15s\e[0m %-20s %s \n'\ + "INSTALLED" $(dpkg-query -W $package) + else + printf '\e[31m%-15s\e[0m %-20s %s \n'\ + "MISSING" $package + DEPENDENCIES="$DEPENDENCIES $package" + fi + done + # Exits if CHECK_ONLY is set, exit code 1 if there are packages to install + if [ $CHECK_ONLY -eq 1 ]; then + if [ -z "$DEPENDENCIES" ]; then + exit 0 + else + exit 1 + fi + fi +else + DEPENDENCIES=$ALL_DEPENDENCIES +fi + +############################################################ +# INSTALL MISSING PACKAGES: +############################################################ case $SYSTEM in "Darwin") exit 0; ;; "Linux") - echo ">>> Installing dependencies (requires sudo):" + echo ">>> Installing missing dependencies (requires sudo):" case $RELEASE in "debian"|"ubuntu"|"linuxmint") DEPENDENCIES="$DEPENDENCIES" - echo " Packages: $DEPENDENCIES" sudo apt-get -qq update || exit 1 sudo apt-get -qqy install $DEPENDENCIES > /dev/null || exit 1 exit 0; ;; "fedora") DEPENDENCIES="$DEPENDENCIES" - echo " Packages: $DEPENDENCIES" sudo dnf install $DEPENDENCIES || exit 1 exit 0; ;; "arch") DEPENDENCIES="$DEPENDENCIES python2 python2-jsonschema python2-psutil" - echo " Packages: $DEPENDENCIES" sudo pacman -Syyu sudo pacman -S --needed $DEPENDENCIES exit 0; diff --git a/etc/library.cmake b/etc/library.cmake index 7db0b3cf04..42e4ad3552 100644 --- a/etc/library.cmake +++ b/etc/library.cmake @@ -17,10 +17,13 @@ endif(CMAKE_COMPILER_IS_GNUCC) set(CMAKE_ASM_NASM_OBJECT_FORMAT "elf") enable_language(ASM_NASM) +# defines $CAPABS depending on installation +include(${CMAKE_CURRENT_LIST_DIR}/settings.cmake) + # Various global defines # * OS_TERMINATE_ON_CONTRACT_VIOLATION provides classic assert-like output from Expects / Ensures # * _GNU_SOURCE enables POSIX-extensions in newlib, such as strnlen. ("everything newlib has", ref. cdefs.h) -set(CAPABS "-msse3 -fstack-protector-strong -DOS_TERMINATE_ON_CONTRACT_VIOLATION -D_GNU_SOURCE") +set(CAPABS "${CAPABS} -fstack-protector-strong -DOS_TERMINATE_ON_CONTRACT_VIOLATION -D_GNU_SOURCE") set(WARNS "-Wall -Wextra") #-pedantic # configure options diff --git a/etc/service.cmake b/etc/service.cmake index 71edb261a4..0e34226d59 100644 --- a/etc/service.cmake +++ b/etc/service.cmake @@ -18,19 +18,20 @@ endif() if(CMAKE_COMPILER_IS_GNUCC) # currently gcc is not supported due to problems cross-compiling a unikernel # (i.e., building a 32bit unikernel (only supported for now) on a 64bit system) - message(FATAL_ERROR "GCC is not currently supported, please clean-up build directory and configure for clang through CC and CXX environment variables") +# message(FATAL_ERROR "GCC is not currently supported, please clean-up build directory and configure for clang through CC and CXX environment variables") endif(CMAKE_COMPILER_IS_GNUCC) # Assembler set(CMAKE_ASM_NASM_OBJECT_FORMAT "elf") enable_language(ASM_NASM) -set(CAPABS "-msse3 -fstack-protector-strong") +# defines $CAPABS depending on installation +include(${CMAKE_CURRENT_LIST_DIR}/settings.cmake) # Various global defines # * OS_TERMINATE_ON_CONTRACT_VIOLATION provides classic assert-like output from Expects / Ensures # * _GNU_SOURCE enables POSIX-extensions in newlib, such as strnlen. ("everything newlib has", ref. cdefs.h) -set(CAPABS "${CAPABS} -DOS_TERMINATE_ON_CONTRACT_VIOLATION -D_GNU_SOURCE -DSERVICE=\"\\\"${BINARY}\\\"\" -DSERVICE_NAME=\"\\\"${SERVICE_NAME}\\\"\"") +set(CAPABS "${CAPABS} -fstack-protector-strong -DOS_TERMINATE_ON_CONTRACT_VIOLATION -D_GNU_SOURCE -DSERVICE=\"\\\"${BINARY}\\\"\" -DSERVICE_NAME=\"\\\"${SERVICE_NAME}\\\"\"") set(WARNS "-Wall -Wextra") #-pedantic # configure options @@ -53,9 +54,14 @@ if (debug) set(CAPABS "${CAPABS} -g") endif() -# these kinda work with llvm -set(CMAKE_CXX_FLAGS "-MMD -target i686-elf ${CAPABS} ${OPTIMIZE} ${WARNS} -nostdlib -nostdlibinc -c -m32 -std=c++14 -D_LIBCPP_HAS_NO_THREADS=1") -set(CMAKE_C_FLAGS "-MMD -target i686-elf ${CAPABS} ${OPTIMIZE} ${WARNS} -nostdlib -nostdlibinc -c -m32") +if (CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_CXX_FLAGS "-m32 -MMD ${CAPABS} ${WARNS} -nostdlib -c -std=c++14 -D_LIBCPP_HAS_NO_THREADS=1") + set(CMAKE_C_FLAGS "-m32 -MMD ${CAPABS} ${WARNS} -nostdlib -c") +else() + # these kinda work with llvm + set(CMAKE_CXX_FLAGS "-MMD -target i686-elf ${CAPABS} ${OPTIMIZE} ${WARNS} -nostdlib -nostdlibinc -c -m32 -std=c++14 -D_LIBCPP_HAS_NO_THREADS=1") + set(CMAKE_C_FLAGS "-MMD -target i686-elf ${CAPABS} ${OPTIMIZE} ${WARNS} -nostdlib -nostdlibinc -c -m32") +endif() # executable set(SERVICE_STUB "$ENV{INCLUDEOS_PREFIX}/includeos/src/service_name.cpp") @@ -184,12 +190,6 @@ set_target_properties(crti PROPERTIES IMPORTED_LOCATION $ENV{INCLUDEOS_PREFIX}/i target_link_libraries(service --whole-archive crti --no-whole-archive) -add_library(multiboot STATIC IMPORTED) -set_target_properties(multiboot PROPERTIES LINKER_LANGUAGE CXX) -set_target_properties(multiboot PROPERTIES IMPORTED_LOCATION $ENV{INCLUDEOS_PREFIX}/includeos/lib/libmultiboot.a) - -target_link_libraries(service --whole-archive multiboot --no-whole-archive) - add_library(libos STATIC IMPORTED) set_target_properties(libos PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(libos PROPERTIES IMPORTED_LOCATION $ENV{INCLUDEOS_PREFIX}/includeos/lib/libos.a) diff --git a/examples/demo_service/service.cpp b/examples/demo_service/service.cpp index 2ff012a3fa..10a845f3d3 100644 --- a/examples/demo_service/service.cpp +++ b/examples/demo_service/service.cpp @@ -34,11 +34,12 @@ std::string HTML_RESPONSE() std::stringstream stream; stream << "" << " " + << " rel='stylesheet' type='text/css'>" + << "IncludeOS Demo Service" << "

" << "IncludeOS

" - << "

The C++ Unikernel

" + << "

The C++ Unikernel

" << "

You have successfully booted an IncludeOS TCP service with simple http. " << "For a more sophisticated example, take a look at " << "Acorn.

" @@ -81,14 +82,18 @@ http::Response handle_request(const http::Request& req) void Service::start(const std::string&) { // DHCP on interface 0 - auto& inet = net::Inet4::ifconfig(10.0); - // static IP in case DHCP fails - net::Inet4::ifconfig( - { 10,0,0,42 }, // IP - { 255,255,255,0 }, // Netmask - { 10,0,0,1 }, // Gateway - { 8,8,8,8 }); // DNS - + printf("*** Waiting up to 10 sec. for DHCP... ***\n"); + auto& inet = net::Inet4::ifconfig(5.0, [](bool timeout) { + if (timeout) { + printf("*** Falling back to static network config ***\n"); + // static IP in case DHCP fails + net::Inet4::stack().network_config( + { 10,0,0,42 }, // IP + { 255,255,255,0 }, // Netmask + { 10,0,0,1 }, // Gateway + { 10,0,0,1 }); // DNS + } + }); // Print some useful netstats every 30 secs Timers::periodic(5s, 30s, [&inet] (uint32_t) { diff --git a/install.sh b/install.sh index 9a64ee6ab6..f932bf890b 100755 --- a/install.sh +++ b/install.sh @@ -113,10 +113,15 @@ if [ "Darwin" = "$SYSTEM" ]; then exit 1 fi else - if ! ./etc/install_build_requirements.sh $SYSTEM $RELEASE; then - printf "%s\n" ">>> Sorry <<<"\ - "Could not install build requirements." - exit 1 + # Will only check if build dependencies are installed at this point + if [ $INCLUDEOS_ENABLE_TEST == "ON" ]; then + dependency_level=all + else + dependency_level=build + fi + echo ">>> Dependencies required:" + if ! ./etc/install_build_requirements.sh -s $SYSTEM -r $RELEASE -c -d $dependency_level; then + missing_dependencies=1 fi fi @@ -147,6 +152,9 @@ done # Print currently set install options printf "\n\n>>> IncludeOS will be installed with the following options:\n\n" +if [ ! -z $missing_dependencies ]; then + printf ' \e[31m%-s\e[0m %-s\n\n' "[NOTICE]" "Missing dependencies will be installed" +fi printf " %-25s %-25s %s\n"\ "Env variable" "Description" "Value"\ "------------" "-----------" "-----"\ @@ -168,6 +176,15 @@ if tty -s && [ $install_yes -eq 0 ]; then esac fi +# Install dependencies if there are any missing +if [ ! -z $missing_dependencies ]; then + if ! ./etc/install_build_requirements.sh -s $SYSTEM -r $RELEASE -d $dependency_level; then + printf "%s\n" ">>> Sorry <<<"\ + "Could not install dependencies" + exit 1 + fi +fi + # Trap that cleans the cmake output file in case of exit function clean { if [ -f /tmp/cmake_output.txt ]; then diff --git a/lib/mana/src/middleware/butler.cpp b/lib/mana/src/middleware/butler.cpp index 834a8fb873..fcab87937e 100644 --- a/lib/mana/src/middleware/butler.cpp +++ b/lib/mana/src/middleware/butler.cpp @@ -65,7 +65,7 @@ void Butler::process(mana::Request_ptr req, mana::Response_ptr res, mana::Next n } // we got an index, lets send it else { - auto mime = http::ext_to_mime_type(get_extension(path)); + auto mime = http::ext_to_mime_type(this->get_extension(path)); res->header().set_field(http::header::Content_Type, mime.to_string()); return res->send_file({disk_, entry}); } @@ -101,7 +101,7 @@ void Butler::process(mana::Request_ptr req, mana::Response_ptr res, mana::Next n #ifdef VERBOSE_WEBSERVER printf(" Found file: %s (%llu B)\n", entry.name().c_str(), entry.size()); #endif - auto mime = http::ext_to_mime_type(get_extension(path)); + auto mime = http::ext_to_mime_type(this->get_extension(path)); res->header().set_field(http::header::Content_Type, mime.to_string()); res->send_file({disk_, entry}); return; diff --git a/lib/mana/src/middleware/director.cpp b/lib/mana/src/middleware/director.cpp index c2b21af952..f022f6155f 100644 --- a/lib/mana/src/middleware/director.cpp +++ b/lib/mana/src/middleware/director.cpp @@ -49,7 +49,7 @@ void Director::process( return (*next)(); } else { - res->source().add_body(create_html(entries, path)); + res->source().add_body(this->create_html(entries, path)); res->send(); } }) diff --git a/src/arch/x86/start.asm b/src/arch/x86/start.asm index 1de3cbf2b2..f7fa89d290 100644 --- a/src/arch/x86/start.asm +++ b/src/arch/x86/start.asm @@ -18,10 +18,39 @@ USE32 extern kernel_start global _start +global __xsave_enabled +global __avx_enabled +global __ecx_was + +%define MB_MAGIC 0x1BADB002 +%define MB_FLAGS 0x3 ;; ALIGN + MEMINFO + +extern _MULTIBOOT_START_ +extern _LOAD_START_ +extern _LOAD_END_ +extern _end + +ALIGN 4 +section .multiboot + dd MB_MAGIC + dd MB_FLAGS + dd -(MB_MAGIC + MB_FLAGS) + dd _MULTIBOOT_START_ + dd _LOAD_START_ + dd _LOAD_END_ + dd _end + dd _start %define data_segment 0x10 %define code_segment 0x08 +section .data +__xsave_enabled: + dw 0x0 +__avx_enabled: + dw 0x0 + +ALIGN32 section .text ;; Multiboot places boot paramters on eax and ebx. _start: @@ -49,10 +78,10 @@ rock_bottom: ;; enable SSE before we enter C/C++ land call enable_sse - ;; ... and XSAVE to get xsetbv/xgetbv working - ;; call enable_xsave - ;; ... and finally, enable AVX - ;; call enable_avx + ;; try to enable XSAVE before checking AVX + call enable_xsave + ;; enable AVX if xsave and avx supported on CPU + call enable_avx ;; Place multiboot parameters on stack push ebx @@ -74,19 +103,44 @@ enable_sse: enable_xsave: push eax + push ebx + ; check for XSAVE support + mov eax, 1 + xor ecx, ecx + cpuid + ; bit 26 ecx + and ecx, 0x04000000 + cmp ecx, 0x04000000 + jne xsave_not_supported ; enable XSAVE mov eax, cr4 or eax, 0x40000 mov cr4, eax + mov WORD [__xsave_enabled], 0x1 +xsave_not_supported: + pop ebx pop eax ret enable_avx: push eax + push ebx + ;; assuming cpuid with eax=1 supported + mov eax, 1 + xor ecx, ecx + cpuid + ;; check bits 27, 28 (xsave, avx) + and ecx, 0x18000000 + cmp ecx, 0x18000000 + jne avx_not_supported + ;; enable AVX support xor ecx, ecx xgetbv or eax, 0x7 xsetbv + mov WORD [__avx_enabled], 0x1 +avx_not_supported: + pop ebx pop eax ret @@ -96,7 +150,7 @@ gdtr: dd gdt32 ALIGN 32 gdt32: - ;; Entry 0x0: Null desriptor + ;; Entry 0x0: Null descriptor dq 0x0 ;; Entry 0x8: Code segment dw 0xffff ;Limit diff --git a/src/boot/CMakeLists.txt b/src/boot/CMakeLists.txt index f39679ed0c..abb771b212 100644 --- a/src/boot/CMakeLists.txt +++ b/src/boot/CMakeLists.txt @@ -1,5 +1,3 @@ -add_library(multiboot STATIC multiboot.cpp) -add_dependencies(multiboot PrecompiledLibraries) add_custom_command( OUTPUT bootloader @@ -10,4 +8,3 @@ add_custom_command( add_custom_target(run ALL DEPENDS bootloader) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/bootloader DESTINATION includeos/boot) -install(TARGETS multiboot DESTINATION includeos/lib) diff --git a/src/boot/multiboot.cpp b/src/boot/multiboot.cpp deleted file mode 100644 index d5ccf35987..0000000000 --- a/src/boot/multiboot.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include - -// Let the linker provide as much info as possible -extern uintptr_t _MULTIBOOT_START_; -extern uintptr_t _LOAD_START_; -extern uintptr_t _LOAD_END_; -extern uintptr_t _BSS_END_; -extern uintptr_t _TEXT_START_; -extern uintptr_t _start; - -#define MULTIBOOT_HEADER_FLAGS MULTIBOOT_MEMORY_INFO - -extern "C" volatile const multiboot_header __multiboot__ { - // magic - MULTIBOOT_HEADER_MAGIC, - // flags - MULTIBOOT_HEADER_FLAGS, - // checksum - (uint32_t) 0 - (MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS), - // header_addr - (uint32_t)&_MULTIBOOT_START_, - // load_addr - (uint32_t)&_LOAD_START_, - // load_end_addr - (uint32_t)&_LOAD_END_, - // bss_end_addr - (uint32_t)&_BSS_END_, - // entry_addr - (uint32_t)&_start, - - // Video mode - // mode_type - 0, // Unused - // width - 100, // Unused - // height - 50, // Unused - // depth - 1 - -}; diff --git a/src/crt/c_abi.c b/src/crt/c_abi.c index e11abb642e..3fe1df7b55 100644 --- a/src/crt/c_abi.c +++ b/src/crt/c_abi.c @@ -103,6 +103,13 @@ void __stack_chk_fail(void) __builtin_unreachable(); } +__attribute__((noreturn)) +void __stack_chk_fail_local(void) +{ + panic("Stack protector: Canary modified"); + __builtin_unreachable(); +} + // old function result system int errno = 0; int* __errno_location(void) diff --git a/src/drivers/virtionet.cpp b/src/drivers/virtionet.cpp index 3bb5ae5f7c..f570697858 100644 --- a/src/drivers/virtionet.cpp +++ b/src/drivers/virtionet.cpp @@ -156,7 +156,8 @@ VirtioNet::VirtioNet(hw::PCI_Device& d) } else { - assert(0 && "Legacy IRQs not supported"); + auto irq = Virtio::get_legacy_irq(); + IRQ_manager::get().subscribe(irq, {this, &VirtioNet::legacy_handler}); } #ifndef NO_DEFERRED_KICK @@ -243,6 +244,12 @@ void VirtioNet::msix_xmit_handler() } } +void VirtioNet::legacy_handler() +{ + msix_recv_handler(); + msix_xmit_handler(); +} + void VirtioNet::add_receive_buffer(uint8_t* pkt) { // offset pointer to virtionet header diff --git a/src/drivers/virtionet.hpp b/src/drivers/virtionet.hpp index c7ddbafe97..e8b8e7c32a 100644 --- a/src/drivers/virtionet.hpp +++ b/src/drivers/virtionet.hpp @@ -220,6 +220,8 @@ class VirtioNet : Virtio, public net::Link_layer { void msix_xmit_handler(); void msix_conf_handler(); + void legacy_handler(); + /** Allocate and queue buffer from bufstore_ in RX queue. */ void add_receive_buffer(uint8_t*); diff --git a/src/kernel/main_call.cpp b/src/kernel/main_call.cpp index 35e3a5d96d..6ad61732ad 100644 --- a/src/kernel/main_call.cpp +++ b/src/kernel/main_call.cpp @@ -19,8 +19,10 @@ #include #define ARGS_MAX 64 -__attribute__((weak)) -extern "C" int main(int, const char*[]); +extern "C" { + __attribute__((weak)) + int main(int, const char*[]); +} __attribute__((weak)) void Service::start(const std::string& cmd) diff --git a/src/kernel/profile.cpp b/src/kernel/profile.cpp index a59a8e9ab5..3297f25d3b 100644 --- a/src/kernel/profile.cpp +++ b/src/kernel/profile.cpp @@ -243,15 +243,13 @@ void ScopedProfiler::record() { asm volatile ("lfence\n\t" "rdtsc\n\t" - : "=A" (tick_start) - :: "%eax", "%ebx", "%ecx", "%edx"); + : "=A" (tick_start)); } else if (guard == Guard::MFENCE) { asm volatile ("mfence\n\t" "rdtsc\n\t" - : "=A" (tick_start) - :: "%eax", "%ebx", "%ecx", "%edx"); + : "=A" (tick_start)); } } @@ -267,15 +265,13 @@ ScopedProfiler::~ScopedProfiler() { asm volatile ("lfence\n\t" "rdtsc\n\t" - : "=A" (tick) - :: "%eax", "%ebx", "%ecx", "%edx"); + : "=A" (tick)); } else if (guard == Guard::MFENCE) { asm volatile ("mfence\n\t" "rdtsc\n\t" - : "=A" (tick) - :: "%eax", "%ebx", "%ecx", "%edx"); + : "=A" (tick)); } auto cycles = tick - tick_start; @@ -359,7 +355,7 @@ std::string ScopedProfiler::get_statistics() // optional name if (entry.name) ss << " (" << entry.name << ")"; - + ss << "\n"; } } diff --git a/src/kernel/timers.cpp b/src/kernel/timers.cpp index 4740bdcea1..d8451db464 100644 --- a/src/kernel/timers.cpp +++ b/src/kernel/timers.cpp @@ -8,7 +8,6 @@ #include using namespace std::chrono; -// typedef Timers::id_t id_t; typedef Timers::duration_t duration_t; typedef Timers::handler_t handler_t; @@ -56,8 +55,8 @@ struct alignas(SMP_ALIGN) timer_system uint32_t dead_timers = 0; Timers::start_func_t arch_start_func; Timers::stop_func_t arch_stop_func; - std::vector timers; - std::vector free_timers; + std::vector timers; + std::vector free_timers; // timers sorted by timestamp std::multimap scheduled; /** Stats */ @@ -66,8 +65,7 @@ struct alignas(SMP_ALIGN) timer_system uint32_t* periodic_started; uint32_t* periodic_stopped; }; - -static std::array systems; +static SMP_ARRAY systems; static inline timer_system& get() { return PER_CPU(systems); @@ -207,9 +205,9 @@ void Timers::timers_handler() while (LIKELY(!system.scheduled.empty())) { - auto it = system.scheduled.begin(); - auto when = it->first; - Timers::id_t id = it->second; + auto it = system.scheduled.begin(); + auto when = it->first; + Timers::id_t id = it->second; // remove dead timers if (system.timers[id].deferred_destruct) diff --git a/src/linker.ld b/src/linker.ld index 7e9f82164e..b46bc9bbbb 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -18,10 +18,6 @@ **/ ENTRY(_start) -PHDRS { - loadable PT_LOAD FLAGS(7); -} - SECTIONS { PROVIDE ( _ELF_START_ = . + 0x100000); @@ -29,9 +25,7 @@ SECTIONS .multiboot (_ELF_START_ + SIZEOF_HEADERS): { PROVIDE(_MULTIBOOT_START_ = .); - *multiboot.cpp.obj(.rodata); - *multiboot.cpp.o(.rodata); - *multiboot.o(.rodata); + *(.multiboot) } PROVIDE ( _TEXT_START_ = . ); .text (_TEXT_START_ ) : @@ -112,20 +106,6 @@ SECTIONS _DISK_END_ = .; } - .symtab : - { - _SYMTAB_START = .; - *(.symtab) - *(.symtab.*) - }:loadable - - .strtab : - { - _STRTAB_START = .; - *(.strtab) - *(.strtab.*) - }:loadable - .elf_symbols : { _ELF_SYM_START_ = .; LONG (0); @@ -136,9 +116,9 @@ SECTIONS _BSS_START_ = .; *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) - _BSS_END_ = .; . = ALIGN(64 / 8); } + _BSS_END_ = .; . = ALIGN(64 / 8); _end = .; diff --git a/src/net/http/client.cpp b/src/net/http/client.cpp index 242acc5f53..21239af8bc 100644 --- a/src/net/http/client.cpp +++ b/src/net/http/client.cpp @@ -155,19 +155,19 @@ namespace http { if(ip != 0) { // setup request with method and headers - auto req = create_request(method); + auto req = this->create_request(method); *req << hfields; // Set Host & path from url - populate_from_url(*req, url); + this->populate_from_url(*req, url); // Add data and content length - add_data(*req, data); + this->add_data(*req, data); // Default to port 80 if non given const uint16_t port = (url.port() != 0xFFFF) ? url.port() : 80; - send(move(req), {ip, port}, move(cb), move(opt)); + this->send(move(req), {ip, port}, move(cb), move(opt)); } else { diff --git a/src/net/http/header.cpp b/src/net/http/header.cpp index 92da16815f..345d2bd475 100644 --- a/src/net/http/header.cpp +++ b/src/net/http/header.cpp @@ -64,7 +64,7 @@ bool Header::has_field(util::csview field) const noexcept { util::sview Header::value(util::csview field) const noexcept { if (field.empty()) return field; const auto it = find(field); - return (it not_eq fields_.cend()) ? it->second : util::sview{}; + return (it not_eq fields_.cend()) ? util::csview{it->second} : util::sview(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/net/http/request.cpp b/src/net/http/request.cpp index 1f8dafdc6c..a28e93a479 100644 --- a/src/net/http/request.cpp +++ b/src/net/http/request.cpp @@ -23,46 +23,49 @@ namespace http { /// /// Configure the settings for parsing a request /// -static http_parser_settings settings +static http_parser_settings settings; + +__attribute__((constructor)) +static void _GFRGRGRgegerjiuo_() { - .on_message_begin = [](http_parser* parser) { + settings.on_message_begin = [](http_parser* parser) { auto req = reinterpret_cast(parser->data); req->set_method( http::method::code( http_method_str(static_cast(parser->method)))); return 0; - }, + }; - .on_url = [](http_parser* parser, const char* at, size_t length) { + settings.on_url = [](http_parser* parser, const char* at, size_t length) { auto req = reinterpret_cast(parser->data); req->set_uri(URI{std::string{at, length}}); return 0; - }, + }; - .on_header_field = [](http_parser* parser, const char* at, size_t length) { + settings.on_header_field = [](http_parser* parser, const char* at, size_t length) { auto req = reinterpret_cast(parser->data); req->set_private_field(at, length); return 0; - }, + }; - .on_header_value = [](http_parser* parser, const char* at, size_t length) { + settings.on_header_value = [](http_parser* parser, const char* at, size_t length) { auto req = reinterpret_cast(parser->data); req->header().set_field(req->private_field().to_string(), {at, length}); return 0; - }, + }; - .on_body = [](http_parser* parser, const char* at, size_t length) { + settings.on_body = [](http_parser* parser, const char* at, size_t length) { auto req = reinterpret_cast(parser->data); req->add_chunk({at, length}); return 0; - }, + }; - .on_headers_complete = [](http_parser* parser) { + settings.on_headers_complete = [](http_parser* parser) { auto req = reinterpret_cast(parser->data); req->set_version(Version{parser->http_major, parser->http_minor}); return 0; - } -}; + }; +} /// /// Function to parse the request data diff --git a/src/net/http/response.cpp b/src/net/http/response.cpp index 2699ebf1df..770095261c 100644 --- a/src/net/http/response.cpp +++ b/src/net/http/response.cpp @@ -23,32 +23,35 @@ namespace http { /// /// Configure the settings for parsing a response /// -static http_parser_settings settings +static http_parser_settings settings; + +__attribute__((constructor)) +static void riegfjeriugfjreiougf() { - .on_header_field = [](http_parser* parser, const char* at, size_t length) { + settings.on_header_field = [](http_parser* parser, const char* at, size_t length) { auto res = reinterpret_cast(parser->data); res->set_private_field(at, length); return 0; - }, + }; - .on_header_value = [](http_parser* parser, const char* at, size_t length) { + settings.on_header_value = [](http_parser* parser, const char* at, size_t length) { auto res = reinterpret_cast(parser->data); res->header().set_field(res->private_field().to_string(), {at, length}); return 0; - }, + }; - .on_body = [](http_parser* parser, const char* at, size_t length) { + settings.on_body = [](http_parser* parser, const char* at, size_t length) { auto res = reinterpret_cast(parser->data); res->add_chunk({at, length}); return 0; - }, + }; - .on_headers_complete = [](http_parser* parser) { + settings.on_headers_complete = [](http_parser* parser) { auto res = reinterpret_cast(parser->data); res->set_version(Version{parser->http_major, parser->http_minor}); res->set_status_code(static_cast(parser->status_code)); return 0; - } + }; }; /// diff --git a/src/net/super_stack.cpp b/src/net/super_stack.cpp index fd7e45521a..b7910e71c1 100644 --- a/src/net/super_stack.cpp +++ b/src/net/super_stack.cpp @@ -18,22 +18,25 @@ #include #include +namespace net +{ + // Specialization for IP4 template <> -net::Inet& net::Super_stack::get(int N) +Inet& Super_stack::get(int N) { if (N < 0 || N >= (int) hw::Devices::devices().size()) throw Stack_not_found{"No IP4 stack found for [" + std::to_string(N) + "] (missing driver?)"}; if (inet().ip4_stacks_[N]) return *inet().ip4_stacks_[N]; - + // create network stack auto& nic = hw::Devices::devices()[N]; - - INFO("Network", "Creating stack for %s on %s", + + INFO("Network", "Creating stack for %s on %s", nic->driver_name(), nic->device_name().c_str()); - + switch(nic->proto()) { case hw::Nic::Proto::ETH: inet().ip4_stacks_[N].reset(new Inet4(*nic)); @@ -45,7 +48,7 @@ net::Inet& net::Super_stack::get(int N) return *inet().ip4_stacks_[N]; } -net::Super_stack::Super_stack() +Super_stack::Super_stack() { if (hw::Devices::devices().empty()) INFO("Network", "No registered network interfaces found"); @@ -53,3 +56,5 @@ net::Super_stack::Super_stack() for (size_t i = 0; i < hw::Devices::devices().size(); i++) ip4_stacks_.push_back(nullptr); } + +} diff --git a/src/net/tcp/connection.cpp b/src/net/tcp/connection.cpp index 49b82a0ab5..811a34997d 100644 --- a/src/net/tcp/connection.cpp +++ b/src/net/tcp/connection.cpp @@ -82,7 +82,6 @@ void Connection::reset_callbacks() on_disconnect_ = {this, &Connection::default_on_disconnect}; on_connect_.reset(); writeq.on_write(nullptr); - on_error_.reset(); on_packet_dropped_.reset(); on_rtx_timeout_.reset(); on_close_.reset(); @@ -246,16 +245,10 @@ void Connection::writeq_reset() { rtx_timer.stop(); } -void Connection::open(bool active) { - try { - debug(" Trying to open Connection...\n"); - state_->open(*this, active); - } - // No remote host, or state isnt valid for opening. - catch (const TCPException& e) { - debug(" Cannot open Connection. \n"); - signal_error(e); - } +void Connection::open(bool active) +{ + debug(" Trying to open Connection...\n"); + state_->open(*this, active); } void Connection::close() { @@ -269,8 +262,9 @@ void Connection::close() { if(is_state(Closed::instance())) signal_close(); } - catch(const TCPException& err) { - signal_error(err); + catch(const TCPException&) { + // just ignore for now, it's kinda stupid its even throwing (i think) + // early return is_closing will probably prevent this from happening } } @@ -337,7 +331,7 @@ Packet_ptr Connection::create_outgoing_packet() { packet->set_win(std::min((cb.RCV.WND >> cb.RCV.wind_shift), (uint32_t)default_window_size)); if(cb.SND.TS_OK) - packet->add_tcp_option_aligned(host_.get_ts_value(), cb.TS_recent); + packet->add_tcp_option_aligned(host_.get_ts_value(), cb.get_ts_recent()); // Set SEQ and ACK - I think this is OK.. packet->set_seq(cb.SND.NXT).set_ack(cb.RCV.NXT); debug(" Outgoing packet created: %s \n", packet->to_string().c_str()); @@ -685,7 +679,7 @@ void Connection::retransmit() { writeq.size(), buf.length() - buf.acknowledged); fill_packet(*packet, buf.data() + writeq.acked(), buf.length() - writeq.acked()); } - + rtx_attempt_++; packet->set_seq(cb.SND.UNA); /* @@ -766,16 +760,11 @@ void Connection::rtx_timeout() { } // retransmit SND.UNA - retransmit(); + retransmit(); // increases rtx_attempt + + // "back off" timer + rttm.RTO *= 2.0; - if(cb.SND.UNA != cb.ISS) { - // "back off" timer - rttm.RTO *= 2.0; - } - // we never queue SYN packets since they don't carry data.. - else { - rttm.RTO = std::chrono::seconds(3); - } // timer need to be restarted rtx_start(); @@ -789,7 +778,7 @@ void Connection::rtx_timeout() { ssthresh = max (FlightSize / 2, 2*SMSS) */ - if(rtx_attempt_++ == 0) + if(rtx_attempt_ == 1) { // RFC 4015 /* @@ -888,14 +877,13 @@ void Connection::clean_up() { on_connect_.reset(); on_disconnect_.reset(); - on_error_.reset(); on_packet_dropped_.reset(); on_rtx_timeout_.reset(); on_close_.reset(); read_request.clean_up(); _on_cleanup_.reset(); - debug(" Succesfully cleaned up %s\n", to_string().c_str()); + debug2(" Succesfully cleaned up %s\n", to_string().c_str()); } std::string Connection::TCB::to_string() const { diff --git a/src/net/tcp/connection_states.cpp b/src/net/tcp/connection_states.cpp index f5fcd35e31..4894545a6d 100644 --- a/src/net/tcp/connection_states.cpp +++ b/src/net/tcp/connection_states.cpp @@ -890,7 +890,7 @@ State::Result Connection::SynSent::handle(Connection& tcp, Packet_ptr in) { // 2. check RST if(UNLIKELY(in->isset(RST))) { if(in->isset(ACK)) { - tcp.signal_error(TCPException{"Connection reset."}); + tcp.signal_connect(false); tcp.drop(*in, Drop_reason::RST); return CLOSED; } else { @@ -960,6 +960,13 @@ State::Result Connection::SynSent::handle(Connection& tcp, Packet_ptr in) { tcb.SND.WL2 = in->ack(); // end of correction + // [RFC 6298] p.4 (5.7) + if(UNLIKELY(tcp.syn_rtx_ > 0)) + { + tcp.syn_rtx_ = 0; + tcp.rttm.RTO = RTTM::seconds(3.0); + } + tcp.set_state(Connection::Established::instance()); const seq_t snd_nxt = tcb.SND.NXT; tcp.signal_connect(); // NOTE: User callback @@ -1061,6 +1068,13 @@ State::Result Connection::SynReceived::handle(Connection& tcp, Packet_ptr in) { tcp.handle_ack(*in); + // [RFC 6298] p.4 (5.7) + if(UNLIKELY(tcp.syn_rtx_ > 0)) + { + tcp.syn_rtx_ = 0; + tcp.rttm.RTO = RTTM::seconds(3.0); + } + tcp.signal_connect(); // NOTE: User callback // 7. proccess the segment text diff --git a/src/posix/ftw.cpp b/src/posix/ftw.cpp index 9d574fd62f..47e96fb0ec 100644 --- a/src/posix/ftw.cpp +++ b/src/posix/ftw.cpp @@ -66,7 +66,7 @@ class Walker { { //printf("Skipping %s\n", ent.name().c_str()); } - else if ((result = walk(abs_path + "/" + ent.name(), level) != 0)) + else if ((result = this->walk(abs_path + "/" + ent.name(), level) != 0)) break; } }) diff --git a/src/service_name.cpp b/src/service_name.cpp index ae22bb62b2..e21d0d9d3a 100644 --- a/src/service_name.cpp +++ b/src/service_name.cpp @@ -17,5 +17,5 @@ #include -extern "C" const char* service_name__ = SERVICE_NAME; -extern "C" const char* service_binary_name__ = SERVICE; +const char* service_name__ = SERVICE_NAME; +const char* service_binary_name__ = SERVICE; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 016688f46c..bc92d866ed 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,9 +4,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) option(COVERAGE "Build with coverage generation" OFF) option(SILENT_BUILD "Build with some warnings turned off" ON) + option(INFO "Print INFO macro output" OFF) option(DEBUG_INFO "Print debug macro output when DEBUG/DEBUG2 etc. is defined in source" OFF) option(GENERATE_SUPPORT_FILES "Generate external files required by some tests (e.g. tar)" ON) +option(EXTRA_TESTS "Build extra test" OFF) if ("${ARCH}" STREQUAL "") set (ARCH "ARCH_X86") @@ -63,8 +65,6 @@ set(TEST_SOURCES ${TEST}/kernel/unit/kprint_test.cpp ${TEST}/kernel/unit/memmap_test.cpp ${TEST}/kernel/unit/os_test.cpp - # not all CPUs have rdrand - #${TEST}/kernel/unit/rdrand_test.cpp ${TEST}/kernel/unit/service_stub_test.cpp ${TEST}/net/unit/checksum.cpp ${TEST}/net/unit/cookie_test.cpp @@ -103,12 +103,10 @@ set(TEST_SOURCES ${TEST}/util/unit/statman.cpp ${TEST}/util/unit/syslogd_test.cpp ${TEST}/util/unit/syslog_facility_test.cpp - #${TEST}/util/unit/tar_test.cpp ${TEST}/util/unit/uri_test.cpp ) set(OS_SOURCES - ${SRC}/boot/multiboot.cpp ${SRC}/fs/disk.cpp ${SRC}/fs/fat.cpp ${SRC}/fs/fat_async.cpp @@ -194,6 +192,14 @@ set(MOD_OBJECTS ${INCLUDEOS_ROOT}/mod/uzlib/src/tinfgzip.c ) +if(EXTRA_TESTS) + set(GENERATE_SUPPORT_FILES ON) + message(STATUS "Adding some extra tests") + list(APPEND TEST_SOURCES ${TEST}/kernel/unit/rdrand_test.cpp) + list(APPEND TEST_SOURCES ${TEST}/util/unit/tar_test.cpp) + +endif() + if(COVERAGE) message(STATUS "Coverage") list(REMOVE_ITEM TEST_SOURCES ${TEST}/util/unit/path_to_regex_no_options.cpp) diff --git a/test/fs/unit/memdisk_test.cpp b/test/fs/unit/memdisk_test.cpp index 491f0a3d14..d897307df5 100644 --- a/test/fs/unit/memdisk_test.cpp +++ b/test/fs/unit/memdisk_test.cpp @@ -20,16 +20,16 @@ CASE("memdisk properties") { - auto disk = fs::new_shared_memdisk(); - EXPECT(disk->empty() == true); - EXPECT(disk->device_id() == 0); - EXPECT(disk->fs_ready() == false); - EXPECT(disk->name() == "memdisk0"); - EXPECT(disk->dev().size() == 0ull); - EXPECT(disk->dev().device_type() == "Block device"); - EXPECT(disk->dev().driver_name() == "MemDisk"); + fs::Disk& disk = fs::memdisk(); + EXPECT(disk.empty() == true); + EXPECT(disk.device_id() == 0); + EXPECT(disk.fs_ready() == false); + EXPECT(disk.name() == "memdisk0"); + EXPECT(disk.dev().size() == 0ull); + EXPECT(disk.dev().device_type() == "Block device"); + EXPECT(disk.dev().driver_name() == "MemDisk"); bool enumerated_partitions {false}; - + fs::Disk::on_parts_func part_fn = [&enumerated_partitions, &lest_env] (fs::error_t err, std::vector& partitions) { @@ -39,6 +39,6 @@ CASE("memdisk properties") } }; - disk->partitions(part_fn); + disk.partitions(part_fn); EXPECT(enumerated_partitions == true); } diff --git a/test/kernel/integration/block/vm.json b/test/kernel/integration/block/vm.json index 627a5fdb70..a6c9cc19e0 100644 --- a/test/kernel/integration/block/vm.json +++ b/test/kernel/integration/block/vm.json @@ -1,4 +1,3 @@ {"image" : "test_block.img", - "time_sensitive" : "True", - "cpu" : "host" + "time_sensitive" : "True" } diff --git a/test/net/integration/tcp/service.cpp b/test/net/integration/tcp/service.cpp index 367248f9e3..59c060d873 100644 --- a/test/net/integration/tcp/service.cpp +++ b/test/net/integration/tcp/service.cpp @@ -80,17 +80,16 @@ void OUTGOING_TEST_INTERNET(const HostAddress& address) { [port](auto ip_address) { CHECK(ip_address != 0, "Resolved host"); - if(ip_address != 0) { + if(ip_address != 0) + { Inet4::stack<0>().tcp().connect({ip_address, port}) - ->on_connect([](tcp::Connection_ptr conn) { - CHECK(true, "Connected"); - conn->on_read(1024, [](tcp::buffer_t, size_t n) { - CHECK(n > 0, "Received a response"); - }); - }) - .on_error([](tcp::TCPException err) { - CHECK(false, "Error occured: %s", err.what()); - }); + ->on_connect([](tcp::Connection_ptr conn) + { + CHECKSERT(conn != nullptr, "Connected"); + conn->on_read(1024, [](tcp::buffer_t, size_t n) { + CHECK(n > 0, "Received a response"); + }); + }); } }); } @@ -102,6 +101,7 @@ void OUTGOING_TEST(tcp::Socket outgoing) { INFO("TEST", "Outgoing Connection (%s)", outgoing.to_string().c_str()); Inet4::stack<0>().tcp().connect(outgoing, [](tcp::Connection_ptr conn) { + CHECKSERT(conn != nullptr, "Connection successfully established."); conn->on_read(small.size(), [](tcp::buffer_t buffer, size_t n) { CHECKSERT(std::string((char*)buffer.get(), n) == small, "Received SMALL"); diff --git a/test/posix/integration/conf/service.cpp b/test/posix/integration/conf/service.cpp index e95ab869eb..12b7281710 100644 --- a/test/posix/integration/conf/service.cpp +++ b/test/posix/integration/conf/service.cpp @@ -29,11 +29,11 @@ extern "C" void test_sysconf(); extern "C" void test_pathconf(); extern "C" void test_pwd(); -fs::Disk_ptr& memdisk() { - static auto disk = fs::new_shared_memdisk(); +fs::Disk& memdisk() { + fs::Disk& disk = fs::memdisk(); - if (not disk->fs_ready()) { - disk->init_fs([](fs::error_t err, auto&) { + if (not disk.fs_ready()) { + disk.init_fs([](fs::error_t err, auto&) { if (err) { printf("ERROR MOUNTING DISK\n"); printf("%s\n", err.reason().c_str()); @@ -58,7 +58,7 @@ int main(int, char **) { test_sysconf(); // mount a disk with contents for testing - auto root = memdisk()->fs().stat("/"); + auto root = memdisk().fs().stat("/"); fs::mount("/etc", root, "test fs"); test_pathconf(); diff --git a/test/posix/integration/stat/ftw_tests.cpp b/test/posix/integration/stat/ftw_tests.cpp index cf002bfb70..51d6aec421 100644 --- a/test/posix/integration/stat/ftw_tests.cpp +++ b/test/posix/integration/stat/ftw_tests.cpp @@ -98,7 +98,7 @@ void ftw_tests() printf("Total size: %ld\n", total_size); } -int display_info(const char *fpath, const struct stat *sb, int flag, struct FTW *ftwbuf) +int display_info(const char *fpath, const struct stat *sb, int flag, struct FTW *) { printf("%ld\t%s (%d)\n", sb->st_size, fpath, flag); #ifdef EXTRAVERBOSE diff --git a/test/posix/integration/stat/test_stat_ftw.cpp b/test/posix/integration/stat/test_stat_ftw.cpp index 849e937d6e..024240cc8d 100644 --- a/test/posix/integration/stat/test_stat_ftw.cpp +++ b/test/posix/integration/stat/test_stat_ftw.cpp @@ -24,12 +24,12 @@ int ftw_tests(); int stat_tests(); -fs::Disk_ptr& memdisk() { - static auto disk = fs::new_shared_memdisk(); +fs::Disk& memdisk() { + fs::Disk& disk = fs::memdisk(); - if (not disk->fs_ready()) { - printf("%s\n", disk->name().c_str()); - disk->init_fs([](fs::error_t err, auto&) { + if (not disk.fs_ready()) { + printf("%s\n", disk.name().c_str()); + disk.init_fs([](fs::error_t err, auto&) { if (err) { printf("ERROR MOUNTING DISK\n"); exit(127); @@ -44,7 +44,7 @@ int main() INFO("POSIX stat", "Running tests for POSIX stat"); // mount a disk with contents for testing - auto root = memdisk()->fs().stat("/"); + auto root = memdisk().fs().stat("/"); fs::mount("/mnt/disk", root, "test root"); fs::print_tree(); diff --git a/test/util/unit/statman.cpp b/test/util/unit/statman.cpp index e3e987cbf6..8d36d7b31d 100644 --- a/test/util/unit/statman.cpp +++ b/test/util/unit/statman.cpp @@ -66,6 +66,8 @@ CASE( "Creating Statman objects" ) // Statman is both empty and full (no room for more Stat-objects) EXPECT(statman_.empty()); EXPECT(statman_.full()); + + EXPECT_THROWS(Stat& stat = statman_.create(Stat::UINT32, "some.new.stat")); } } @@ -320,6 +322,9 @@ CASE("get(\"name\") returns reference to stat with name, throws if not present") EXPECT_NO_THROW(Stat& res3 = statman_.get("very.important.stat")); EXPECT_THROWS_AS(Stat& res5 = statman_.get("some.missing.stat"), Stats_exception); + // Can't create stats with empty name + EXPECT_THROWS_AS(Stat& stat6 = statman_.create(Stat::UINT32, ""), Stats_exception); + free((void*)buffer); } diff --git a/test/util/unit/tar_test.cpp b/test/util/unit/tar_test.cpp index 8e4b9eef85..cab8be1c40 100644 --- a/test/util/unit/tar_test.cpp +++ b/test/util/unit/tar_test.cpp @@ -21,7 +21,7 @@ #include #include #include -/* + CASE("Reading single entry tar file") { tar::Reader r; @@ -126,4 +126,3 @@ CASE("Reading tar.gz inside tar file") EXPECT(inner_e.name().find(".tar.gz") == std::string::npos); close(fd); } -*/ diff --git a/vmbuild/vmbuild.cpp b/vmbuild/vmbuild.cpp index dc21e27aa0..ce12678f42 100644 --- a/vmbuild/vmbuild.cpp +++ b/vmbuild/vmbuild.cpp @@ -21,12 +21,12 @@ #include #include -#include -#include +#include +#include #include +#include #include "../api/boot/multiboot.h" -#include #include "elf.h" #include "elf_binary.hpp" @@ -72,7 +72,7 @@ string get_bootloader_path(int argc, char** argv) { int main(int argc, char** argv) { // Verify proper command usage - if (argc < 2) { + if (argc <= 2) { cout << info << usage; exit(EXIT_FAILURE); } @@ -127,7 +127,7 @@ int main(int argc, char** argv) { const decltype(binary_sectors) img_size_sect {1 + binary_sectors}; const decltype(binary_sectors) img_size_bytes {img_size_sect * SECT_SIZE}; - Expects((img_size_bytes & (SECT_SIZE-1)) == 0); + assert((img_size_bytes & (SECT_SIZE-1)) == 0); INFO("Total disk size: \t%ld bytes, => %ld sectors", img_size_bytes, img_size_sect); @@ -175,14 +175,17 @@ int main(int argc, char** argv) { INFO("Verifying multiboot header:"); INFO("Magic value: 0x%x\n" , multiboot.magic); - Expects(multiboot.magic == MULTIBOOT_HEADER_MAGIC); + if (multiboot.magic != MULTIBOOT_HEADER_MAGIC) { + printf("Multiboot magic mismatch: 0x%08x vs %#x\n", multiboot.magic, MULTIBOOT_HEADER_MAGIC); + } + assert(multiboot.magic == MULTIBOOT_HEADER_MAGIC); INFO("Flags: 0x%x" , multiboot.flags); INFO("Checksum: 0x%x" , multiboot.checksum); INFO("Checksum computed: 0x%x", multiboot.checksum + multiboot.flags + multiboot.magic); // Verify multiboot header checksum - Expects(multiboot.checksum + multiboot.flags + multiboot.magic == 0); + assert(multiboot.checksum + multiboot.flags + multiboot.magic == 0); INFO("Header addr: 0x%x" , multiboot.header_addr); INFO("Load start: 0x%x" , multiboot.load_addr); diff --git a/vmrunner/vm.cpu_feat.json b/vmrunner/vm.cpu_feat.json new file mode 100644 index 0000000000..f441944624 --- /dev/null +++ b/vmrunner/vm.cpu_feat.json @@ -0,0 +1,10 @@ +{ + "description" : "Single virtio nic with extended cpu features", + "net" : [ + {"device" : "virtio", "backend" : "tap" } + ], + "cpu" : { + "model" : "host", + "features" : ["xsave", "avx", "aes"] + } +} diff --git a/vmrunner/vm.schema.json b/vmrunner/vm.schema.json index c64d6a7cae..93436cfce7 100644 --- a/vmrunner/vm.schema.json +++ b/vmrunner/vm.schema.json @@ -1,6 +1,23 @@ { "$schema": "http://json-schema.org/schema#", "title" : "Virtual Machine Image", + + "definitions" : { + "cpu" : { + "description" : "The virtual CPU", + "type" : "object", + "properties": { + "model" : { + "enum" : ["host", "pentium", "qemu64", "kvm64"] + }, + "features" : { + "type" : "array", + "items" : { "enum" : ["xsave", "avx", "aes"] } + } + } + } + }, + "type" : "object", "properties" : { @@ -61,11 +78,7 @@ "default" : 128 }, - "cpu" : { - "description" : "The virtual CPU", - "type" : "string", - "default" : "host,+xsave,+avx,+aes" - }, + "cpu" : { "$ref": "#/definitions/cpu" }, "smp" : { "description" : "Number of virtual CPU's", diff --git a/vmrunner/vm.vanilla.json b/vmrunner/vm.vanilla.json new file mode 100644 index 0000000000..4847638a3c --- /dev/null +++ b/vmrunner/vm.vanilla.json @@ -0,0 +1,6 @@ +{ + "description" : "Single virtio nic with vanilla cpu features", + "net" : [ + {"device" : "virtio", "backend" : "tap" } + ] +} diff --git a/vmrunner/vmrunner.py b/vmrunner/vmrunner.py index 3fbb608df2..5217dbaeaf 100644 --- a/vmrunner/vmrunner.py +++ b/vmrunner/vmrunner.py @@ -17,7 +17,7 @@ if "INCLUDEOS_PREFIX" not in os.environ: def_home = "/usr/local" - print color.WARNING("WARNING:"), "Environment varialble INCLUDEOS_PREFIX is not set. Trying default", def_home + print color.WARNING("WARNING:"), "Environment variable INCLUDEOS_PREFIX is not set. Trying default", def_home if not os.path.isdir(def_home): raise Exception("Couldn't find INCLUDEOS_PREFIX") INCLUDEOS_HOME= def_home else: @@ -25,8 +25,7 @@ package_path = os.path.dirname(os.path.realpath(__file__)) -default_config = {"description" : "Single virtio nic, otherwise hypervisor defaults", - "net" : [{"device" : "virtio", "backend" : "tap" }] } +default_config = INCLUDEOS_HOME + "/includeos/vmrunner/vm.default.json" default_json = "./vm.json" @@ -261,6 +260,7 @@ def boot(self, multiboot, kernel_args = "", image_name = None): if (image_name.endswith(".img")): image_name = image_name.split(".")[0] + if not kernel_args: kernel_args = "\"\"" kernel_args = ["-kernel", image_name, "-append", kernel_args] info ( "Booting", image_name, "directly without bootloader (multiboot / -kernel args)") else: @@ -292,7 +292,11 @@ def boot(self, multiboot, kernel_args = "", image_name = None): kernel_args.extend(["-smp", str(self._config["smp"])]) if "cpu" in self._config: - kernel_args.extend(["-cpu", self._config["cpu"]]) + cpu = self._config["cpu"] + cpu_str = cpu["model"] + if "features" in cpu: + cpu_str += ",+" + ",+".join(cpu["features"]) + kernel_args.extend(["-cpu", cpu_str]) net_args = [] i = 0 @@ -401,7 +405,7 @@ def __init__(self, config = None, hyper = qemu): self._exit_msg = "" self._exit_complete = False - self._config = load_single_config(config) + self._config = load_with_default_config(True, config) self._on_success = lambda(line) : self.exit(exit_codes["SUCCESS"], nametag + " All tests passed") self._on_panic = self.panic self._on_timeout = self.timeout @@ -564,7 +568,7 @@ def cmake(self, args = []): # if everything went well, build with make and install return self.make() except Exception as e: - print "Excetption while building: ", e + print "Exception while building: ", e self.exit(exit_codes["BUILD_FAIL"], "building with cmake failed") # Clean cmake build folder @@ -689,11 +693,37 @@ def boot(self, timeout = 60, multiboot = True, kernel_args = "booted with vmrunn # If everything went well we can return return self +# Fallback to default +def load_with_default_config(use_default, path = default_json): -# Load a single vm config. Fallback to default -def load_single_config(path = default_json): + # load default config + conf = {} + if use_default == True: + info("Loading default config.") + conf = load_config(default_config) - config = default_config + # load user config (or fallback) + user_conf = load_config(path) + + if user_conf: + if use_default == False: + # return user_conf as is + return user_conf + else: + # extend (override) default config with user config + for key, value in user_conf.iteritems(): + conf[key] = value + info(str(conf)) + return conf + else: + return conf + + program_exit(73, "No config found. Try enable default config.") + +# Load a vm config. +def load_config(path): + + config = {} description = None # If path is explicitly "None", try current dir @@ -723,8 +753,6 @@ def load_single_config(path = default_json): info("No valid config found: ", e) program_exit(73, "No valid config files in " + path) - else: - info("Falling back to default config") if config.has_key("description"): description = config["description"]