├── .gitignore ├── .rustfmt.toml ├── CMakeLists.txt ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md ├── _config.yml ├── aarch64-unknown-hermit-kernel.json ├── build.rs ├── img ├── macro_benchmark.png ├── memory_rayout.png └── write_bytes.png ├── index.md ├── loader ├── .cargo │ └── config ├── .gitignore ├── Cargo.toml ├── Makefile ├── build.rs ├── src │ ├── arch │ │ ├── mod.rs │ │ └── x86_64 │ │ │ ├── bootinfo.rs │ │ │ ├── entry.asm │ │ │ ├── link.ld │ │ │ ├── mod.rs │ │ │ ├── paging.rs │ │ │ ├── processor.rs │ │ │ └── serial.rs │ ├── console.rs │ ├── elf.rs │ ├── lib.rs │ ├── macros.rs │ ├── main.rs │ ├── physicalmem.rs │ └── runtime_glue.rs └── x86_64-unknown-hermit-loader.json ├── patches ├── rust.patch └── uhyve.patch ├── rust-toolchain ├── src ├── arch │ ├── aarch64 │ │ ├── entry.S │ │ ├── kernel │ │ │ ├── irq.rs │ │ │ ├── mod.rs │ │ │ ├── percore.rs │ │ │ ├── processor.rs │ │ │ ├── scheduler.rs │ │ │ ├── serial.rs │ │ │ ├── stubs.rs │ │ │ └── systemtime.rs │ │ ├── mm │ │ │ ├── mod.rs │ │ │ ├── paging.rs │ │ │ ├── physicalmem.rs │ │ │ └── virtualmem.rs │ │ └── mod.rs │ ├── mod.rs │ └── x86_64 │ │ ├── kernel │ │ ├── CMakeLists.txt │ │ ├── acpi.rs │ │ ├── apic.rs │ │ ├── boot.asm │ │ ├── copy_safe.rs │ │ ├── gdt.rs │ │ ├── idt.rs │ │ ├── irq.rs │ │ ├── mod.rs │ │ ├── pci.ids │ │ ├── pci.rs │ │ ├── pci_ids.rs │ │ ├── percore.rs │ │ ├── pic.rs │ │ ├── pit.rs │ │ ├── processor.rs │ │ ├── scheduler.rs │ │ ├── serial.rs │ │ ├── smp_boot_code.rs │ │ ├── start.rs │ │ ├── switch.rs │ │ ├── systemtime.rs │ │ └── vga.rs │ │ ├── mm │ │ ├── mod.rs │ │ ├── mpk.rs │ │ ├── paging.rs │ │ ├── physicalmem.rs │ │ └── virtualmem.rs │ │ └── mod.rs ├── collections │ ├── doublylinkedlist.rs │ └── mod.rs ├── config.rs ├── console.rs ├── drivers │ ├── mod.rs │ └── net │ │ ├── mod.rs │ │ ├── rtl8139.rs │ │ └── uhyve.rs ├── environment.rs ├── errno.rs ├── kernel_message_buffer.rs ├── lib.rs ├── logging.rs ├── macros.rs ├── mm │ ├── allocator.rs │ ├── freelist.rs │ ├── hole.rs │ ├── mod.rs │ └── test.rs ├── runtime_glue.rs ├── scheduler │ ├── mod.rs │ └── task.rs ├── synch │ ├── mod.rs │ ├── recmutex.rs │ ├── semaphore.rs │ └── spinlock.rs └── syscalls │ ├── condvar.rs │ ├── interfaces │ ├── generic.rs │ ├── mod.rs │ └── uhyve.rs │ ├── lwip.rs │ ├── mod.rs │ ├── processor.rs │ ├── random.rs │ ├── recmutex.rs │ ├── semaphore.rs │ ├── spinlock.rs │ ├── system.rs │ ├── tasks.rs │ └── timer.rs ├── tests ├── Cargo.toml ├── Makefile └── src │ ├── linker.ld │ ├── main.rs │ └── tests │ ├── laplace.rs │ ├── matmul.rs │ └── mod.rs └── x86_64-unknown-hermit-kernel.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Gitignore files from GitHub 2 | # See https://github.com/github/gitignore 3 | hello_world/ 4 | 5 | ### Rust 6 | # Generated by Cargo 7 | # will have compiled files and executables 8 | /target/ 9 | /tests/target/ 10 | 11 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 12 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 13 | Cargo.lock 14 | 15 | # These are backup files generated by rustfmt 16 | **/*.rs.bk 17 | 18 | # Dump file 19 | dump 20 | 21 | # Script 22 | *.sh 23 | 24 | ### CMake 25 | CMakeLists.txt.user 26 | CMakeCache.txt 27 | CMakeFiles 28 | CMakeScripts 29 | Testing 30 | cmake_install.cmake 31 | install_manifest.txt 32 | compile_commands.json 33 | CTestTestfile.cmake 34 | _deps 35 | build/ 36 | 37 | ## swp file 38 | *.swp 39 | 40 | ### Visual Studio Code 41 | .vscode 42 | vscode/* 43 | !.vscode/settings.json 44 | !.vscode/tasks.json 45 | !.vscode/launch.json 46 | !.vscode/extensions.json 47 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(librs C) 2 | 3 | # Add the Cargo project to build the Rust library. 4 | set(HERMIT_RS "${CMAKE_BINARY_DIR}/hermit_rs/${HERMIT_ARCH}-unknown-hermit-kernel/${CARGO_BUILDTYPE_OUTPUT}/libhermit.a") 5 | add_custom_target(hermit_rs 6 | COMMAND 7 | ${CMAKE_COMMAND} -E env CARGO_TARGET_DIR=${CMAKE_BINARY_DIR}/hermit_rs RUST_TARGET_PATH=${HERMIT_ROOT}/librs 8 | cargo xbuild ${CARGO_BUILDTYPE_PARAMETER} --target ${HERMIT_ARCH}-unknown-hermit-kernel.json --features newlib 9 | WORKING_DIRECTORY 10 | ${CMAKE_CURRENT_LIST_DIR}) 11 | 12 | # Add a documentation target for the Cargo project. 13 | add_custom_target(doc 14 | COMMAND 15 | ${CMAKE_COMMAND} -E env CARGO_TARGET_DIR=${CMAKE_BINARY_DIR}/hermit_rs 16 | cargo rustdoc -- --no-defaults --passes collapse-docs --passes unindent-comments 17 | WORKING_DIRECTORY 18 | ${CMAKE_CURRENT_LIST_DIR}) 19 | 20 | # arch 21 | if("${HERMIT_ARCH}" STREQUAL "aarch64") 22 | add_subdirectory(src/arch/aarch64) 23 | #elseif("${HERMIT_ARCH}" STREQUAL "x86_64") 24 | # add_subdirectory(src/arch/x86_64/kernel) 25 | endif() 26 | 27 | set(LWIP_SRC /home/mincheol/hermit-playground/lwip/src) 28 | #add_kernel_module_sources("lwip" "${LWIP_SRC}/api/*.c") 29 | #add_kernel_module_sources("lwip" "${LWIP_SRC}/arch/*.c") 30 | #add_kernel_module_sources("lwip" "${LWIP_SRC}/core/*.c") 31 | #add_kernel_module_sources("lwip" "${LWIP_SRC}/core/ipv4/*.c") 32 | #add_kernel_module_sources("lwip" "${LWIP_SRC}/core/ipv6/*.c") 33 | #add_kernel_module_sources("lwip" "${LWIP_SRC}/netif/*.c") 34 | 35 | get_kernel_modules(KERNEL_MODULES) 36 | foreach(MODULE ${KERNEL_MODULES}) 37 | get_kernel_module_sources(SOURCES ${MODULE}) 38 | 39 | # maintain list of all objects that will end up in libhermit.a 40 | list(APPEND KERNEL_OBJECTS $) 41 | 42 | add_library(${MODULE} OBJECT ${SOURCES}) 43 | 44 | # this is kernel code 45 | target_compile_definitions(${MODULE} 46 | PRIVATE -D__KERNEL__) 47 | 48 | target_compile_definitions(${MODULE} 49 | PRIVATE -DMAX_ARGC_ENVC=${MAX_ARGC_ENVC}) 50 | 51 | target_compile_options(${MODULE} 52 | PRIVATE ${HERMIT_KERNEL_FLAGS}) 53 | 54 | target_include_directories(${MODULE} 55 | PUBLIC ${HERMIT_KERNEL_INCLUDES}) 56 | 57 | # suppress all LwIP compiler warnings. Not our code, so we cannot fix 58 | if("${MODULE}" STREQUAL "lwip") 59 | target_compile_options(${MODULE} 60 | PRIVATE -w) 61 | endif() 62 | 63 | endforeach() 64 | 65 | # Build all kernel modules into a single static library. 66 | add_library(hermit-bootstrap STATIC ${KERNEL_OBJECTS}) 67 | set_target_properties(hermit-bootstrap PROPERTIES LINKER_LANGUAGE C) 68 | add_dependencies(hermit-bootstrap hermit_rs) 69 | set_target_properties(hermit-bootstrap PROPERTIES ARCHIVE_OUTPUT_NAME hermit) 70 | 71 | # Post-process the static library. 72 | add_custom_command( 73 | TARGET hermit-bootstrap POST_BUILD 74 | 75 | # Merge the Rust library into this static library. 76 | COMMAND 77 | ${CMAKE_AR} x ${HERMIT_RS} 78 | COMMAND 79 | ${CMAKE_AR} rcs $ *.o 80 | COMMAND 81 | ${CMAKE_COMMAND} -E remove *.o 82 | 83 | # Convert the combined library to osabi "Standalone" 84 | COMMAND 85 | ${CMAKE_ELFEDIT} --output-osabi Standalone $ 86 | 87 | # Copy libhermit.a into local prefix directory so that all subsequent 88 | # targets can link against the freshly built version (as opposed to 89 | # linking against the one supplied by the toolchain) 90 | COMMAND 91 | ${CMAKE_COMMAND} -E make_directory ${LOCAL_PREFIX_ARCH_LIB_DIR} 92 | COMMAND 93 | ${CMAKE_COMMAND} -E copy_if_different $ ${LOCAL_PREFIX_ARCH_LIB_DIR}/ 94 | 95 | # and also copy headers into local prefix 96 | COMMAND 97 | ${CMAKE_COMMAND} -E make_directory ${LOCAL_PREFIX_ARCH_INCLUDE_DIR}/hermit 98 | COMMAND 99 | ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/include/hermit/*.h ${LOCAL_PREFIX_ARCH_INCLUDE_DIR}/hermit/) 100 | 101 | # Deploy libhermit.a and headers for package creation 102 | install(TARGETS hermit-bootstrap 103 | DESTINATION ${HERMIT_ARCH}-hermit/lib 104 | COMPONENT bootstrap) 105 | 106 | install(DIRECTORY include/hermit 107 | DESTINATION ${HERMIT_ARCH}-hermit/include/ 108 | COMPONENT bootstrap 109 | FILES_MATCHING PATTERN *.h) 110 | 111 | # Provide custom target to only install libhermit without its runtimes which is 112 | # needed during the compilation of the cross toolchain 113 | add_custom_target(hermit-bootstrap-install 114 | DEPENDS 115 | hermit-bootstrap 116 | COMMAND 117 | ${CMAKE_COMMAND} 118 | -DCMAKE_INSTALL_COMPONENT=bootstrap 119 | -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} 120 | -P cmake_install.cmake) 121 | 122 | # The target 'hermit' includes the HermitCore kernel and several runtimes. 123 | # Applications should depend on this target if they link against HermitCore. 124 | add_custom_target(hermit 125 | DEPENDS hermit-bootstrap) 126 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rusty-hermit" 3 | version = "0.3.11" 4 | authors = [ 5 | "Stefan Lankes ", 6 | "Colin Finck " 7 | ] 8 | build = "build.rs" 9 | license = "MIT/Apache-2.0" 10 | readme = "README.md" 11 | keywords = ["unikernel", "libos"] 12 | categories = ["os"] 13 | description = """ 14 | RustyHermit - A Rust-based, lightweight unikernel 15 | """ 16 | exclude = ["/img/*", "/loader/*", "/tests/*", "./Makefile", "./CMakeLists.txt", "/.travis.yml", "/.gitlab-ci.yml", ".gitignore", "/.devcontainer/*", "/.vscode/*"] 17 | 18 | [badges] 19 | travis-ci = { repository = "hermitcore/libhermit-rs" } 20 | 21 | [lib] 22 | crate-type = ["staticlib"] 23 | name = "hermit" 24 | 25 | [features] 26 | vga = [] 27 | newlib = [] 28 | shm = [] 29 | rustc-dep-of-std = ['core', 'compiler_builtins/rustc-dep-of-std'] 30 | 31 | [dependencies] 32 | core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } 33 | compiler_builtins = { version = '0.1.10', optional = true } 34 | bitflags = "1.2" 35 | raw-cpuid = "7.0.3" 36 | #lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 37 | #cfg-if = "0.1" 38 | #byteorder = { version = "1.0", default-features = false } 39 | 40 | [dependencies.log] 41 | version = "0.4" 42 | default-features = false 43 | #features = ["release_max_level_info"] 44 | 45 | [target.'cfg(target_arch = "x86_64")'.dependencies.multiboot] 46 | version = "0.*" 47 | 48 | [target.'cfg(target_arch = "x86_64")'.dependencies.x86] 49 | version = "0.*" 50 | default-features = false 51 | 52 | [package.metadata.cargo-xbuild] 53 | memcpy = true 54 | 55 | # The development profile, used for `cargo build`. 56 | [profile.dev] 57 | opt-level = 1 # controls the `--opt-level` the compiler builds with 58 | debug = true # controls whether the compiler passes `-C debuginfo` 59 | # a value of `true` is equivalent to `2` 60 | rpath = false # controls whether the compiler passes `-C rpath` 61 | lto = false # controls `-C lto` for binaries and staticlibs 62 | debug-assertions = true # controls whether debug assertions are enabled 63 | panic = "abort" # Call abort on panic https://github.com/rust-lang/rust/pull/32900 64 | 65 | # The release profile, used for `cargo build --release`. 66 | [profile.release] 67 | opt-level = 3 68 | debug = false 69 | rpath = false 70 | lto = false 71 | debug-assertions = false 72 | panic = "abort" 73 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | arch ?= x86_64 2 | target ?= $(arch)-unknown-hermit 3 | release ?= 0 4 | 5 | opt := 6 | rdir := debug 7 | 8 | ifeq ($(release), 1) 9 | opt := --release 10 | rdir := release 11 | endif 12 | 13 | 14 | RN := 15 | ifdef COMSPEC 16 | RM := del 17 | else 18 | RM := rm -rf 19 | endif 20 | 21 | .PHONY: all loader qemu tests clippy clean lib docs 22 | 23 | default: lib 24 | make arch=$(arch) release=$(release) -C tests 25 | 26 | all: loader lib 27 | make arch=$(arch) release=$(release) -C tests 28 | 29 | clean: 30 | $(RM) target/x86_64-unknown-hermit-kernel 31 | make -C tests clean 32 | make -C loader clean 33 | 34 | loader: 35 | make -C loader release=$(release) 36 | 37 | qemu: 38 | qemu-system-x86_64 -display none -smp 1 -m 64M -serial stdio -kernel loader/target/$(target)-loader/$(rdir)/hermit-loader -initrd tests/target/$(target)/$(rdir)/rusty_tests -cpu qemu64,apic,fsgsbase,pku,rdtscp,xsave,fxsr 39 | 40 | docs: 41 | @echo DOC 42 | @cargo doc --no-deps 43 | 44 | clippy: 45 | @echo Run clippy... 46 | @RUST_TARGET_PATH=$(CURDIR) cargo clippy --target $(target) 47 | 48 | lib: 49 | @echo Build libhermit 50 | @RUST_TARGET_PATH=$(CURDIR) cargo xbuild $(opt) --target $(target)-kernel 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libhermitMPK: Intra-Unikernel Isolation with Intel Memory Protection Keys 2 | 3 | For general information about its design principles and implementation, please read the [paper](https://www.ssrg.ece.vt.edu/papers/vee20-mpk.pdf). 4 | 5 | This work was implemented on the top of RustyHermit (https://github.com/hermitcore/libhermit-rs) 6 | 7 | ## Prerequisites 8 | 1. Install Rust toolchain with RustyHertmit extensions. 9 | ```sh 10 | $ git clone git@github.com:ssrg-vt/rust.git 11 | $ cd rust 12 | $ git checkout hermit 13 | ``` 14 | To build the toolchain, you need the configuration file `config.toml` in the root directory of the repository. 15 | A template `config.toml.example` is part of the repository. 16 | However, default `config.toml` in this repository, which already enable the support of RustyHermit is recommended. 17 | You have only to change the installation path (see variable `prefix` in `config.toml`). 18 | 19 | Afterwards you are able to build the toolchain with following command `./x.py install`. 20 | This will take a while (at least 45 minutes). 21 | Afterwards you have to set the environment variable `XARGO_RUST_SRC` to `/installation_path/lib/rustlib/src/rust/src/`. 22 | Please replace installation_path to the location, where you install the toolchain. 23 | 24 | 2. Install `uhyve` 25 | ```sh 26 | # Get our source code. 27 | $ git clone git@github.com:hermitcore/uhyve.git 28 | $ cd uhyve 29 | 30 | # Get a copy of the Rust source code so we can rebuild core 31 | # for a bare-metal target. 32 | $ cargo build 33 | 34 | # To avoid *sudo* to start uhyve 35 | $ sudo chmod a+rw /dev/kvm 36 | ``` 37 | ## Build 38 | ```sh 39 | # Go to the libhermitMPK repo 40 | $ cd libhermitMPK 41 | 42 | # build a debug version 43 | $ make 44 | 45 | # build a release version 46 | $ make release=1 47 | ``` 48 | 49 | ## Run an application 50 | ```sh 51 | # To start the rusty_tests application 52 | $ /path_to_uhyve/uhyve -v /path_to_the_unikernel/rusty_tests 53 | ``` 54 | ## License 55 | 56 | Licensed under either of 57 | 58 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 59 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 60 | 61 | at your option. 62 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal 2 | -------------------------------------------------------------------------------- /aarch64-unknown-hermit-kernel.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "aarch64-unknown-none", 3 | "linker-flavor": "gcc", 4 | "target-endian": "little", 5 | "target-pointer-width": "64", 6 | "target-c-int-width" : "32", 7 | "os": "none", 8 | "arch": "aarch64", 9 | "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", 10 | "pre-link-args": { 11 | "gcc": ["-m64"] 12 | }, 13 | "features": "+a53,+strict-align,-fp-armv8", 14 | "disable-redzone": true, 15 | "eliminate-frame-pointer": true, 16 | "linker-is-gnu": true, 17 | "no-compiler-rt": true, 18 | "archive-format": "gnu", 19 | "code-model": "large", 20 | "relocation-model": "static", 21 | "panic-strategy": "abort" 22 | } 23 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | fn main() { 4 | // create boot code for application processors 5 | /*let _ = Command::new("nasm").args(&["-f", "bin", "-o", "src/arch/x86_64/kernel/boot.bin", "src/arch/x86_64/kernel/boot.asm"]).output().unwrap(); 6 | let _ = Command::new("sh").args(&["-c", "echo -n \"pub static SMP_BOOT_CODE: [u8; \" > src/arch/x86_64/kernel/smp_boot_code.rs"]).output().unwrap(); 7 | let _ = Command::new("sh").args(&["-c", "stat -c %s src/arch/x86_64/kernel/boot.bin >> src/arch/x86_64/kernel/smp_boot_code.rs"]).output().unwrap(); 8 | let _ = Command::new("sh").args(&["-c", "echo -n \"] = [\" >> src/arch/x86_64/kernel/smp_boot_code.rs"]).output().unwrap(); 9 | let _ = Command::new("sh").args(&["-c", "hexdump -v -e \"1/1 \\\"0x%02X,\\\"\" src/arch/x86_64/kernel/boot.bin >> src/arch/x86_64/kernel/smp_boot_code.rs"]).output().unwrap(); 10 | let _ = Command::new("sh").args(&["-c", "echo -n \"];\" >> src/arch/x86_64/kernel/smp_boot_code.rs"]).output().unwrap(); 11 | // build pci ids as rust file 12 | let _ = Command::new("pci_ids_parser").args(&["src/arch/x86_64/kernel/pci.ids", "src/arch/x86_64/kernel/pci_ids.rs"]).output().unwrap();*/ 13 | 14 | // determine git revision 15 | let output = Command::new("git") 16 | .args(&["rev-parse", "HEAD"]) 17 | .output() 18 | .unwrap(); 19 | let git_hash = String::from_utf8(output.stdout).unwrap(); 20 | println!("cargo:rustc-env=GIT_HASH={}", git_hash); 21 | } 22 | -------------------------------------------------------------------------------- /img/macro_benchmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssrg-vt/libhermitMPK/5b1bf9c4b1349728f454f975de3603038e172570/img/macro_benchmark.png -------------------------------------------------------------------------------- /img/memory_rayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssrg-vt/libhermitMPK/5b1bf9c4b1349728f454f975de3603038e172570/img/memory_rayout.png -------------------------------------------------------------------------------- /img/write_bytes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ssrg-vt/libhermitMPK/5b1bf9c4b1349728f454f975de3603038e172570/img/write_bytes.png -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | * * * 2 | **News** 3 | - Our paper is presented in VEE 2020. Check the conference talk [video](https://youtu.be/JFJxpZVSJCc)! 4 | - Our libhermitMPK [paper](https://www.ssrg.ece.vt.edu/papers/vee20-mpk.pdf) is accepted at [VEE 2020](https://conf.researchr.org/home/vee-2020)! 5 | 6 | * * * 7 | 8 | libhermitMPK is a new design bringing memory isolation inside a unikernel instance while keeping a *single address space*. 9 | By leveraging Intel Memory Protection Keys ([MPK](https://lwn.net/Articles/643797)), intra-unikernel isolation can be provided without impacting the lightweightness and performance benefits of unikernels. 10 | libhermitMPK provides isolation between trusted and untrusted components: (1) safe from unsafe [Rust](https://www.rust-lang.org) kernel code and (2) kernel from user code. 11 | 12 |

13 | 14 |

15 | 16 | libhermitMPK is implemented on top of [RustyHermit](https://github.com/hermitcore/libhermit-rs). 17 | 18 | *For more details see our [VEE'20 paper](https://www.ssrg.ece.vt.edu/papers/vee20-mpk.pdf).* 19 | 20 | ### Trying it out 21 | libhermitMPK is open source and all the code and instructions are on GitHub: 22 | - [https://github.com/ssrg-vt/libhermitMPK](https://github.com/ssrg-vt/libhermitMPK) 23 | 24 | ### Design Principles 25 | libhermitMPK follows the design objectives: (1) preservation of a single address space, (2) isolation of various memory areas, and (3) negligible cost. Below are evaluation results to demonstrate the design principles. 26 | 27 |

28 | 29 |

30 | 31 | `write_bytes` is an unsafe kernel function writing byte to an arbitrary address. The isolated `write_bytes` 32 | introduces a 6% slowdown compared to the vanilla `write_bytes` when writing 4KB at a time. 33 | 34 |

35 | 36 |

37 | 38 | The results of memory/compute intensive benchmarks from various suites including [NPB](http://aces.snu.ac.kr/software/snu-npb), [PARSEC](https://parsec.cs.princeton.edu), and [Phoenix](https://github.com/kozyraki/phoenix) illustrate 39 | that the average slowdown imposed by the intra-unikernel isolation compared with the vanilla unikernel is only 0.6% 40 | 41 | For a detailed description please read libhermitMPK’s VEE 2020 [paper](https://www.ssrg.ece.vt.edu/papers/vee20-mpk.pdf). 42 | 43 | 44 | ### Contact 45 | - [Mincheol Sung](https://mincheolsung.com), Virginia Tech: mincheol *at* vt.edu 46 | - [Pierre Olivier](https://sites.google.com/view/pierreolivier), The University of Manchester: pierre.olivier *at* manchester.ac.uk 47 | - [Binoy Ravindran](https://ece.vt.edu/people/profile/ravindran), Virginia Tech: binoy *at* vt.edu 48 | 49 | We also have a Slack [channel](https://hermitcore.slack.com/archives/CTUDKSBAP) for libhermitMPK. 50 | 51 | * * * 52 | 53 | libhermitMPK is an open-source project of the [Systems Software Research Group](https://www.ssrg.ece.vt.edu/) at [Virginia Tech](https://vt.edu/). 54 | 55 | libhermitMPK is supported in part by the US Office of Naval Research under grants N00014-18-1-2022, N00014-16-1-2104, and N00014-16-1-2711. Any opinions, findings, and conclusions or recommendations expressed in this site are those of the author(s) and do not necessarily reflect the views of ONR. 56 | -------------------------------------------------------------------------------- /loader/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = [ 3 | "-C", "link-arg=-Tsrc/arch/x86_64/link.ld" 4 | ] 5 | -------------------------------------------------------------------------------- /loader/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /loader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermit-loader" 3 | version = "0.2.5" 4 | authors = ["Stefan Lankes ", "Colin Finck "] 5 | license = "MIT/Apache-2.0" 6 | publish = false 7 | 8 | [dependencies] 9 | bitflags = "1.0.*" 10 | 11 | [target.'cfg(target_arch = "x86_64")'.dependencies.multiboot] 12 | version = "0.*" 13 | 14 | [target.'cfg(target_arch = "x86_64")'.dependencies.x86] 15 | version = "0.*" 16 | default-features = false 17 | 18 | [package.metadata.cargo-xbuild] 19 | memcpy = true 20 | 21 | # The release profile, used for `cargo build --release`. 22 | [profile.dev] 23 | opt-level = 1 # controls the `--opt-level` the compiler builds with 24 | debug = true # controls whether the compiler passes `-C debuginfo` 25 | # a value of `true` is equivalent to `2` 26 | rpath = false # controls whether the compiler passes `-C rpath` 27 | lto = false # controls `-C lto` for binaries and staticlibs 28 | debug-assertions = true # controls whether debug assertions are enabled 29 | 30 | [profile.release] 31 | opt-level = 3 32 | debug = false 33 | rpath = false 34 | lto = true 35 | debug-assertions = false 36 | codegen-units = 1 37 | -------------------------------------------------------------------------------- /loader/Makefile: -------------------------------------------------------------------------------- 1 | arch ?= x86_64 2 | target ?= $(arch)-unknown-hermit 3 | release ?= 0 4 | 5 | opt := 6 | rdir := debug 7 | 8 | ifeq ($(release), 1) 9 | opt := --release 10 | rdir := release 11 | endif 12 | 13 | RN := 14 | ifdef COMSPEC 15 | RM := del 16 | else 17 | RM := rm -rf 18 | endif 19 | 20 | .PHONY: all loader clippy clean docs 21 | 22 | all: loader 23 | 24 | clean: 25 | @$(RM) target 26 | 27 | docs: 28 | @echo DOC 29 | @cargo doc 30 | clippy: 31 | @echo Run clippy... 32 | @RUST_TARGET_PATH=$(CURDIR) cargo clippy --target $(target) 33 | 34 | loader: 35 | @echo Build loader 36 | @RUST_TARGET_PATH=$(CURDIR) cargo xbuild $(opt) --target $(target)-loader 37 | @objcopy -O elf32-i386 target/$(target)-loader/$(rdir)/hermit-loader 38 | -------------------------------------------------------------------------------- /loader/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::Path; 3 | use std::process::Command; 4 | 5 | fn main() { 6 | let out_dir = env::var("OUT_DIR").unwrap(); 7 | 8 | Command::new("nasm") 9 | .args(&["src/arch/x86_64/entry.asm", "-felf64", "-o"]) 10 | .arg(&format!("{}/entry.o", out_dir)) 11 | .status() 12 | .unwrap(); 13 | Command::new("ar") 14 | .args(&["crus", "libentry.a", "entry.o"]) 15 | .current_dir(&Path::new(&out_dir)) 16 | .status() 17 | .unwrap(); 18 | 19 | println!("cargo:rustc-link-search=native={}", out_dir); 20 | println!("cargo:rustc-link-lib=static=entry"); 21 | } 22 | -------------------------------------------------------------------------------- /loader/src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | #[cfg(target_arch = "x86_64")] 9 | pub use arch::x86_64::*; 10 | 11 | #[cfg(target_arch = "x86_64")] 12 | pub mod x86_64; 13 | -------------------------------------------------------------------------------- /loader/src/arch/x86_64/bootinfo.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch::x86_64::SERIAL_PORT_ADDRESS; 9 | use core::fmt; 10 | 11 | #[repr(C)] 12 | #[derive(Clone, Copy)] 13 | pub struct BootInfo { 14 | pub magic_number: u32, 15 | pub version: u32, 16 | pub base: u64, 17 | pub limit: u64, 18 | pub image_size: u64, 19 | pub tls_start: u64, 20 | pub tls_filesz: u64, 21 | pub tls_memsz: u64, 22 | pub current_stack_address: u64, 23 | pub current_percore_address: u64, 24 | pub host_logical_addr: u64, 25 | pub boot_gtod: u64, 26 | pub mb_info: u64, 27 | pub cmdline: u64, 28 | pub cmdsize: u64, 29 | pub cpu_freq: u32, 30 | pub boot_processor: u32, 31 | pub cpu_online: u32, 32 | pub possible_cpus: u32, 33 | pub current_boot_id: u32, 34 | pub uartport: u16, 35 | pub single_kernel: u8, 36 | pub uhyve: u8, 37 | pub hcip: [u8; 4], 38 | pub hcgateway: [u8; 4], 39 | pub hcmask: [u8; 4], 40 | } 41 | 42 | impl BootInfo { 43 | pub const fn new() -> Self { 44 | BootInfo { 45 | magic_number: 0xC0DE_CAFEu32, 46 | version: 1, 47 | base: 0, 48 | limit: 0, 49 | tls_start: 0, 50 | tls_filesz: 0, 51 | tls_memsz: 0, 52 | image_size: 0, 53 | current_stack_address: 0, 54 | current_percore_address: 0, 55 | host_logical_addr: 0, 56 | boot_gtod: 0, 57 | mb_info: 0, 58 | cmdline: 0, 59 | cmdsize: 0, 60 | cpu_freq: 0, 61 | boot_processor: !0, 62 | cpu_online: 0, 63 | possible_cpus: 0, 64 | current_boot_id: 0, 65 | uartport: SERIAL_PORT_ADDRESS, 66 | single_kernel: 1, 67 | uhyve: 0, 68 | hcip: [255, 255, 255, 255], 69 | hcgateway: [255, 255, 255, 255], 70 | hcmask: [255, 255, 255, 0], 71 | } 72 | } 73 | } 74 | 75 | impl fmt::Debug for BootInfo { 76 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 77 | writeln!(f, "magic_number 0x{:x}", self.magic_number)?; 78 | writeln!(f, "version 0x{:x}", self.version)?; 79 | writeln!(f, "base 0x{:x}", self.base)?; 80 | writeln!(f, "limit 0x{:x}", self.limit)?; 81 | writeln!(f, "image_size 0x{:x}", self.image_size)?; 82 | writeln!( 83 | f, 84 | "current_stack_address 0x{:x}", 85 | self.current_stack_address 86 | )?; 87 | writeln!( 88 | f, 89 | "current_percore_address 0x{:x}", 90 | self.current_percore_address 91 | )?; 92 | writeln!(f, "host_logical_addr 0x{:x}", self.host_logical_addr)?; 93 | writeln!(f, "boot_gtod 0x{:x}", self.boot_gtod)?; 94 | writeln!(f, "mb_info 0x{:x}", self.mb_info)?; 95 | writeln!(f, "cmdline 0x{:x}", self.cmdline)?; 96 | writeln!(f, "cmdsize 0x{:x}", self.cmdsize)?; 97 | writeln!(f, "cpu_freq {}", self.cpu_freq)?; 98 | writeln!(f, "boot_processor {}", self.boot_processor)?; 99 | writeln!(f, "cpu_online {}", self.cpu_online)?; 100 | writeln!(f, "possible_cpus {}", self.possible_cpus)?; 101 | writeln!(f, "current_boot_id {}", self.current_boot_id)?; 102 | writeln!(f, "uartport 0x{:x}", self.uartport)?; 103 | writeln!(f, "single_kernel {}", self.single_kernel)?; 104 | writeln!(f, "uhyve {}", self.uhyve) 105 | } 106 | } -------------------------------------------------------------------------------- /loader/src/arch/x86_64/link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf64-x86-64") 2 | OUTPUT_ARCH("i386:x86-64") 3 | ENTRY(_start) 4 | phys = 0x000000100000; 5 | 6 | SECTIONS 7 | { 8 | kernel_start = phys; 9 | .mboot phys : AT(ADDR(.mboot)) { 10 | *(.mboot) 11 | } 12 | .text ALIGN(4096) : AT(ADDR(.text)) { 13 | *(.text) 14 | *(.text.*) 15 | } 16 | .rodata ALIGN(4096) : AT(ADDR(.rodata)) { 17 | *(.rodata) 18 | *(.rodata.*) 19 | } 20 | .data ALIGN(4096) : AT(ADDR(.data)) { 21 | *(.data) 22 | *(.data.*) 23 | } 24 | .bss ALIGN(4096) : AT(ADDR(.bss)) { 25 | bss_start = .; 26 | *(.bss) 27 | *(.bss.*) 28 | } 29 | bss_end = .; 30 | kernel_end = .; 31 | } 32 | -------------------------------------------------------------------------------- /loader/src/arch/x86_64/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | pub mod bootinfo; 9 | pub mod paging; 10 | pub mod processor; 11 | pub mod serial; 12 | 13 | use arch::x86_64::paging::{BasePageSize, LargePageSize, PageSize, PageTableEntryFlags}; 14 | use arch::x86_64::serial::SerialPort; 15 | pub use self::bootinfo::*; 16 | use core::{mem, slice}; 17 | use elf::*; 18 | use multiboot::Multiboot; 19 | use physicalmem; 20 | 21 | extern "C" { 22 | static mb_info: usize; 23 | } 24 | 25 | // CONSTANTS 26 | pub const ELF_ARCH: u16 = ELF_EM_X86_64; 27 | 28 | const KERNEL_STACK_SIZE: usize = 32_768; 29 | const SERIAL_PORT_ADDRESS: u16 = 0x3F8; 30 | const SERIAL_PORT_BAUDRATE: u32 = 115200; 31 | 32 | // VARIABLES 33 | static COM1: SerialPort = SerialPort::new(SERIAL_PORT_ADDRESS); 34 | pub static mut BOOT_INFO: BootInfo = BootInfo::new(); 35 | 36 | fn paddr_to_slice<'a>(p: multiboot::PAddr, sz: usize) -> Option<&'a [u8]> { 37 | unsafe { 38 | let ptr = mem::transmute(p); 39 | Some(slice::from_raw_parts(ptr, sz)) 40 | } 41 | } 42 | 43 | // FUNCTIONS 44 | pub fn message_output_init() { 45 | COM1.init(SERIAL_PORT_BAUDRATE); 46 | } 47 | 48 | pub fn output_message_byte(byte: u8) { 49 | COM1.write_byte(byte); 50 | } 51 | 52 | pub unsafe fn find_kernel() -> usize { 53 | // Identity-map the Multiboot information. 54 | assert!(mb_info > 0, "Could not find Multiboot information"); 55 | loaderlog!("Found Multiboot information at {:#X}", mb_info); 56 | let page_address = align_down!(mb_info, BasePageSize::SIZE); 57 | paging::map::(page_address, page_address, 1, PageTableEntryFlags::empty()); 58 | 59 | // Load the Multiboot information and identity-map the modules information. 60 | let multiboot = Multiboot::new(mb_info as u64, paddr_to_slice).unwrap(); 61 | let modules_address = multiboot 62 | .modules() 63 | .expect("Could not find a memory map in the Multiboot information") 64 | .next() 65 | .expect("Could not first map address") 66 | .start as usize; 67 | let page_address = align_down!(modules_address, BasePageSize::SIZE); 68 | paging::map::(page_address, page_address, 1, PageTableEntryFlags::empty()); 69 | 70 | // Iterate through all modules. 71 | // Collect the start address of the first module and the highest end address of all modules. 72 | let modules = multiboot.modules().unwrap(); 73 | let mut found_module = false; 74 | let mut start_address = 0; 75 | let mut end_address = 0; 76 | 77 | for m in modules { 78 | found_module = true; 79 | 80 | if start_address == 0 { 81 | start_address = m.start as usize; 82 | } 83 | 84 | if m.end as usize > end_address { 85 | end_address = m.end as usize; 86 | } 87 | } 88 | 89 | loaderlog!( 90 | "Found module: [0x{:x} - 0x{:x}]", 91 | start_address, 92 | end_address 93 | ); 94 | 95 | // Memory after the highest end address is unused and available for the physical memory manager. 96 | // However, we want to move the HermitCore Application to the next 2 MB boundary. 97 | // So add this boundary and align up the address to be on the safe side. 98 | end_address += LargePageSize::SIZE; 99 | physicalmem::init(align_up!(end_address, LargePageSize::SIZE)); 100 | 101 | // Identity-map the ELF header of the first module. 102 | assert!( 103 | found_module, 104 | "Could not find a single module in the Multiboot information" 105 | ); 106 | assert!(start_address > 0); 107 | loaderlog!("Found an ELF module at {:#X}", start_address); 108 | let page_address = align_down!(start_address, BasePageSize::SIZE); 109 | paging::map::(page_address, page_address, 1, PageTableEntryFlags::empty()); 110 | 111 | start_address 112 | } 113 | 114 | pub unsafe fn move_kernel( 115 | physical_address: usize, 116 | virtual_address: usize, 117 | mem_size: usize, 118 | file_size: usize, 119 | ) -> usize { 120 | // We want to move the application to realize a identify mapping 121 | let page_count = align_up!(mem_size, LargePageSize::SIZE) / LargePageSize::SIZE; 122 | loaderlog!("Use {} large pages for the application.", page_count); 123 | 124 | paging::map::( 125 | virtual_address, 126 | virtual_address, 127 | page_count, 128 | PageTableEntryFlags::WRITABLE, 129 | ); 130 | 131 | for i in (0..align_up!(file_size, BasePageSize::SIZE) / BasePageSize::SIZE).rev() { 132 | let tmp = 0x2000; 133 | paging::map::( 134 | tmp, 135 | align_down!(physical_address, BasePageSize::SIZE) + i * BasePageSize::SIZE, 136 | 1, 137 | PageTableEntryFlags::WRITABLE, 138 | ); 139 | 140 | for j in 0..BasePageSize::SIZE { 141 | *((virtual_address + i * BasePageSize::SIZE + j) as *mut u8) = 142 | *((tmp + j) as *const u8); 143 | } 144 | } 145 | 146 | // clear rest of the kernel 147 | let start = file_size; 148 | let end = mem_size; 149 | loaderlog!("Clear BSS from 0x{:x} to 0x{:x}", virtual_address+start, virtual_address+end); 150 | for i in start..end { 151 | *((virtual_address + i) as *mut u8) = 0; 152 | } 153 | 154 | virtual_address 155 | } 156 | 157 | pub unsafe fn boot_kernel( 158 | new_physical_address: usize, 159 | virtual_address: usize, 160 | mem_size: usize, 161 | entry_point: usize, 162 | ) { 163 | // Supply the parameters to the HermitCore application. 164 | BOOT_INFO.base = new_physical_address as u64; 165 | BOOT_INFO.image_size = mem_size as u64; 166 | BOOT_INFO.mb_info = mb_info as u64; 167 | BOOT_INFO.current_stack_address = (virtual_address - KERNEL_STACK_SIZE) as u64; 168 | 169 | // map stack in the address space 170 | paging::map::( 171 | virtual_address - KERNEL_STACK_SIZE, 172 | virtual_address - KERNEL_STACK_SIZE, 173 | KERNEL_STACK_SIZE / BasePageSize::SIZE, 174 | PageTableEntryFlags::WRITABLE, 175 | ); 176 | 177 | loaderlog!("BootInfo located at 0x{:x}", &BOOT_INFO as *const _ as u64); 178 | loaderlog!("Use stack address 0x{:x}", BOOT_INFO.current_stack_address); 179 | 180 | let multiboot = Multiboot::new(mb_info as u64, paddr_to_slice).unwrap(); 181 | if let Some(cmdline) = multiboot.command_line() { 182 | let address = cmdline.as_ptr(); 183 | 184 | // Identity-map the command line. 185 | let page_address = align_down!(address as usize, BasePageSize::SIZE); 186 | paging::map::(page_address, page_address, 1, PageTableEntryFlags::empty()); 187 | 188 | //let cmdline = multiboot.command_line().unwrap(); 189 | BOOT_INFO.cmdline = address as u64; 190 | BOOT_INFO.cmdsize = cmdline.len() as u64; 191 | } 192 | 193 | // Jump to the kernel entry point and provide the Multiboot information to it. 194 | loaderlog!( 195 | "Jumping to HermitCore Application Entry Point at {:#X}", 196 | entry_point 197 | ); 198 | asm!("jmp *$0" :: "r"(entry_point), "{rdi}"(&BOOT_INFO as *const _ as usize) : "memory" : "volatile"); 199 | } 200 | -------------------------------------------------------------------------------- /loader/src/arch/x86_64/processor.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | /// The halt function stops the processor until the next interrupt arrives 9 | pub fn halt() { 10 | unsafe { 11 | asm!("hlt" :::: "volatile"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /loader/src/arch/x86_64/serial.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use core::sync::atomic::spin_loop_hint; 9 | use x86::io::*; 10 | 11 | const UART_TX: u16 = 0; 12 | const UART_IER: u16 = 1; 13 | 14 | const UART_DLL: u16 = 0; 15 | const UART_DLM: u16 = 1; 16 | 17 | const UART_FCR: u16 = 2; 18 | const UART_FCR_ENABLE_FIFO: u8 = 0x01; 19 | const UART_FCR_CLEAR_RECEIVER_FIFO: u8 = 0x02; 20 | const UART_FCR_CLEAR_TRANSMITTER_FIFO: u8 = 0x04; 21 | 22 | const UART_LCR: u16 = 3; 23 | const UART_LCR_WORD_LENGTH_8BITS: u8 = 0x03; 24 | const UART_LCR_DIVISOR_LATCH_ACCESS: u8 = 0x80; 25 | 26 | const UART_LSR: u16 = 5; 27 | const UART_LSR_EMPTY_TRANSMITTER_HOLDING_REGISTER: u8 = 0x20; 28 | 29 | pub struct SerialPort { 30 | port_address: u16, 31 | } 32 | 33 | impl SerialPort { 34 | pub const fn new(port_address: u16) -> Self { 35 | Self { 36 | port_address: port_address, 37 | } 38 | } 39 | 40 | fn read_from_register(&self, register: u16) -> u8 { 41 | unsafe { inb(self.port_address + register) } 42 | } 43 | 44 | fn is_transmitting(&self) -> bool { 45 | (self.read_from_register(UART_LSR) & UART_LSR_EMPTY_TRANSMITTER_HOLDING_REGISTER == 0) 46 | } 47 | 48 | fn write_to_register(&self, register: u16, byte: u8) { 49 | while self.is_transmitting() { 50 | spin_loop_hint(); 51 | } 52 | 53 | unsafe { 54 | outb(self.port_address + register, byte); 55 | } 56 | } 57 | 58 | pub fn write_byte(&self, byte: u8) { 59 | // LF newline characters need to be extended to CRLF over a real serial port. 60 | if byte == b'\n' { 61 | self.write_to_register(UART_TX, b'\r'); 62 | } 63 | 64 | self.write_to_register(UART_TX, byte); 65 | } 66 | 67 | pub fn init(&self, baudrate: u32) { 68 | // Disable port interrupt. 69 | self.write_to_register(UART_IER, 0); 70 | 71 | // Set 8N1 mode (8 bits, 1 stop bit, no parity). 72 | self.write_to_register(UART_LCR, UART_LCR_WORD_LENGTH_8BITS); 73 | 74 | // Set the baudrate. 75 | let divisor = (115200 / baudrate) as u16; 76 | let lcr = self.read_from_register(UART_LCR); 77 | self.write_to_register(UART_LCR, lcr | UART_LCR_DIVISOR_LATCH_ACCESS); 78 | self.write_to_register(UART_DLL, divisor as u8); 79 | self.write_to_register(UART_DLM, (divisor >> 8) as u8); 80 | self.write_to_register(UART_LCR, lcr); 81 | 82 | // Enable and clear FIFOs. 83 | self.write_to_register( 84 | UART_FCR, 85 | UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RECEIVER_FIFO | UART_FCR_CLEAR_TRANSMITTER_FIFO, 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /loader/src/console.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch; 9 | use core::fmt; 10 | 11 | pub struct Console; 12 | 13 | /// A collection of methods that are required to format 14 | /// a message to HermitCore's console. 15 | impl fmt::Write for Console { 16 | /// Print a single character. 17 | fn write_char(&mut self, c: char) -> fmt::Result { 18 | arch::output_message_byte(c as u8); 19 | Ok(()) 20 | } 21 | 22 | /// Print a string of characters. 23 | fn write_str(&mut self, s: &str) -> fmt::Result { 24 | for character in s.chars() { 25 | self.write_char(character).unwrap(); 26 | } 27 | Ok(()) 28 | } 29 | } -------------------------------------------------------------------------------- /loader/src/elf.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | pub const ELF_MAGIC: u32 = 0x464C_457F; 9 | /// 64-bit file 10 | pub const ELF_CLASS_64: u8 = 0x02; 11 | /// Little-Endian encoding 12 | pub const ELF_DATA_2LSB: u8 = 0x01; 13 | /// HermitCore OSABI identification 14 | pub const ELF_PAD_HERMIT: u8 = 0xFF; 15 | 16 | #[repr(C, packed)] 17 | pub struct ElfIdentification { 18 | pub magic: u32, 19 | pub _class: u8, 20 | pub data: u8, 21 | pub version: u8, 22 | pub pad: [u8; 8], 23 | pub nident: u8, 24 | } 25 | 26 | /// Executable 27 | pub const ELF_ET_EXEC: u16 = 0x0002; 28 | 29 | /// x86_64 architecture 30 | #[allow(dead_code)] 31 | pub const ELF_EM_X86_64: u16 = 0x003E; 32 | 33 | /// AArch64 architecture 34 | #[allow(dead_code)] 35 | pub const ELF_EM_AARCH64: u16 = 0x00B7; 36 | 37 | #[repr(C, packed)] 38 | pub struct ElfHeader { 39 | pub ident: ElfIdentification, 40 | pub ty: u16, 41 | pub machine: u16, 42 | pub version: u32, 43 | pub entry: usize, 44 | pub ph_offset: usize, 45 | pub sh_offset: usize, 46 | pub flags: u32, 47 | pub header_size: u16, 48 | pub ph_entry_size: u16, 49 | pub ph_entry_count: u16, 50 | pub sh_entry_size: u16, 51 | pub sh_entry_count: u16, 52 | pub sh_str_table_index: u16, 53 | } 54 | 55 | /// Loadable program segment 56 | pub const ELF_PT_LOAD: u32 = 1; 57 | /// TLS segment 58 | pub const ELF_PT_TLS: u32 = 7; 59 | 60 | #[repr(C, packed)] 61 | pub struct ElfProgramHeader { 62 | pub ty: u32, 63 | pub flags: u32, 64 | pub offset: usize, 65 | pub virt_addr: usize, 66 | pub phys_addr: usize, 67 | pub file_size: usize, 68 | pub mem_size: usize, 69 | pub alignment: usize, 70 | } 71 | -------------------------------------------------------------------------------- /loader/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | #![feature(asm)] 9 | #![feature(const_fn)] 10 | #![feature(lang_items)] 11 | #![feature(panic_info_message)] 12 | #![feature(specialization)] 13 | #![feature(naked_functions)] 14 | #![feature(const_raw_ptr_deref)] 15 | #![feature(core_intrinsics)] 16 | #![no_std] 17 | 18 | // EXTERNAL CRATES 19 | #[macro_use] 20 | extern crate bitflags; 21 | 22 | #[cfg(target_arch = "x86_64")] 23 | extern crate multiboot; 24 | 25 | #[cfg(target_arch = "x86_64")] 26 | extern crate x86; 27 | 28 | // MODULES 29 | #[macro_use] 30 | pub mod macros; 31 | 32 | pub mod arch; 33 | pub mod console; 34 | mod elf; 35 | mod physicalmem; 36 | mod runtime_glue; 37 | 38 | // IMPORTS 39 | use arch::paging::{BasePageSize, LargePageSize, PageSize}; 40 | use arch::BOOT_INFO; 41 | use core::ptr; 42 | use elf::*; 43 | 44 | extern "C" { 45 | static bss_end: u8; 46 | static mut bss_start: u8; 47 | } 48 | 49 | // FUNCTIONS 50 | pub unsafe fn sections_init() { 51 | // Initialize .bss section 52 | ptr::write_bytes( 53 | &mut bss_start as *mut u8, 54 | 0, 55 | &bss_end as *const u8 as usize - &bss_start as *const u8 as usize, 56 | ); 57 | } 58 | 59 | pub unsafe fn check_kernel_elf_file(start_address: usize) -> (usize, usize, usize, usize, usize) { 60 | // Verify that this module is a HermitCore ELF executable. 61 | let header = &*(start_address as *const ElfHeader); 62 | assert!(header.ident.magic == ELF_MAGIC); 63 | assert!(header.ident._class == ELF_CLASS_64); 64 | assert!(header.ident.data == ELF_DATA_2LSB); 65 | assert!(header.ident.pad[0] == ELF_PAD_HERMIT); 66 | assert!(header.ty == ELF_ET_EXEC); 67 | assert!(header.machine == arch::ELF_ARCH); 68 | loaderlog!("This is a supported HermitCore Application"); 69 | 70 | // Get all necessary information about the ELF executable. 71 | let mut physical_address = 0; 72 | let mut virtual_address = 0; 73 | let mut file_size = 0; 74 | let mut mem_size = 0; 75 | 76 | for i in 0..header.ph_entry_count { 77 | let program_header = &*((start_address 78 | + header.ph_offset 79 | + (i * header.ph_entry_size) as usize) as *const ElfProgramHeader); 80 | if program_header.ty == ELF_PT_LOAD { 81 | if physical_address == 0 { 82 | physical_address = start_address + program_header.offset; 83 | } 84 | 85 | if virtual_address == 0 { 86 | virtual_address = program_header.virt_addr; 87 | } 88 | 89 | file_size = program_header.virt_addr + program_header.file_size - virtual_address; 90 | mem_size = program_header.virt_addr + program_header.mem_size - virtual_address; 91 | } else if program_header.ty == ELF_PT_TLS { 92 | BOOT_INFO.tls_start = program_header.virt_addr as u64; 93 | BOOT_INFO.tls_filesz = program_header.file_size as u64; 94 | BOOT_INFO.tls_memsz = program_header.mem_size as u64; 95 | 96 | loaderlog!("Found TLS starts at 0x{:x} (size {} Bytes)", BOOT_INFO.tls_start, BOOT_INFO.tls_memsz); 97 | } 98 | } 99 | 100 | // Verify the information. 101 | assert!(physical_address % BasePageSize::SIZE == 0); 102 | assert!(virtual_address % LargePageSize::SIZE == 0); 103 | assert!(file_size > 0); 104 | assert!(mem_size > 0); 105 | loaderlog!("File Size: {} Bytes", file_size); 106 | loaderlog!("Mem Size: {} Bytes", mem_size); 107 | 108 | ( 109 | physical_address, 110 | virtual_address, 111 | file_size, 112 | mem_size, 113 | header.entry, 114 | ) 115 | } 116 | -------------------------------------------------------------------------------- /loader/src/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | macro_rules! align_down { 10 | ($value:expr, $alignment:expr) => { 11 | $value & !($alignment - 1) 12 | }; 13 | } 14 | 15 | macro_rules! align_up { 16 | ($value:expr, $alignment:expr) => { 17 | align_down!($value + ($alignment - 1), $alignment) 18 | }; 19 | } 20 | 21 | /// Print formatted text to our console. 22 | /// 23 | /// From http://blog.phil-opp.com/rust-os/printing-to-screen.html, but tweaked 24 | /// for HermitCore. 25 | #[macro_export] 26 | macro_rules! print { 27 | ($($arg:tt)+) => ({ 28 | use core::fmt::Write; 29 | 30 | let mut console = crate::console::Console {}; 31 | console.write_fmt(format_args!($($arg)+)).unwrap(); 32 | }); 33 | } 34 | 35 | /// Print formatted text to our console, followed by a newline. 36 | #[macro_export] 37 | macro_rules! println { 38 | ($($arg:tt)+) => (print!("{}\n", format_args!($($arg)+))); 39 | } 40 | 41 | /// Print formatted loader log messages to our console, followed by a newline. 42 | #[macro_export] 43 | macro_rules! loaderlog { 44 | ($($arg:tt)+) => (println!("[LOADER] {}", format_args!($($arg)+))); 45 | } 46 | -------------------------------------------------------------------------------- /loader/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | #![no_std] // don't link the Rust standard library 9 | #![cfg_attr(not(test), no_main)] // disable all Rust-level entry points 10 | #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] 11 | 12 | extern crate hermit_loader; 13 | 14 | use hermit_loader::arch; 15 | use hermit_loader::*; 16 | 17 | /// Entry Point of the HermitCore Loader 18 | /// (called from entry.asm or entry.S) 19 | #[no_mangle] 20 | pub unsafe extern "C" fn loader_main() { 21 | sections_init(); 22 | arch::message_output_init(); 23 | 24 | loaderlog!("Started"); 25 | 26 | let start_address = arch::find_kernel(); 27 | let (physical_address, virtual_address, file_size, mem_size, entry_point) = 28 | check_kernel_elf_file(start_address); 29 | let new_physical_address = 30 | arch::move_kernel(physical_address, virtual_address, mem_size, file_size); 31 | arch::boot_kernel(new_physical_address, virtual_address, mem_size, entry_point); 32 | } 33 | -------------------------------------------------------------------------------- /loader/src/physicalmem.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch::paging::{BasePageSize, PageSize}; 9 | 10 | static mut CURRENT_ADDRESS: usize = 0; 11 | 12 | pub fn init(address: usize) { 13 | unsafe { 14 | CURRENT_ADDRESS = address; 15 | } 16 | } 17 | 18 | pub fn allocate(size: usize) -> usize { 19 | assert!(size > 0); 20 | assert!( 21 | size % BasePageSize::SIZE == 0, 22 | "Size {:#X} is a multiple of {:#X}", 23 | size, 24 | BasePageSize::SIZE 25 | ); 26 | 27 | unsafe { 28 | assert!(CURRENT_ADDRESS > 0, "Trying to allocate physical memory before the Physical Memory Manager has been initialized"); 29 | let address = CURRENT_ADDRESS; 30 | CURRENT_ADDRESS += size; 31 | address 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /loader/src/runtime_glue.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | //! Minor functions that Rust really expects to be defined by the compiler, 9 | //! but which we need to provide manually because we're on bare metal. 10 | 11 | use arch; 12 | use core::panic::PanicInfo; 13 | 14 | #[panic_handler] 15 | fn panic(info: &PanicInfo) -> ! { 16 | loaderlog!("PANIC: "); 17 | 18 | if let Some(location) = info.location() { 19 | loaderlog!("{}:{}: ", location.file(), location.line()); 20 | } 21 | 22 | if let Some(message) = info.message() { 23 | loaderlog!("{}", message); 24 | } 25 | 26 | loaderlog!("\n"); 27 | 28 | loop { 29 | arch::processor::halt(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /loader/x86_64-unknown-hermit-loader.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "linker-flavor": "ld.lld", 4 | "linker": "lld", 5 | "target-endian": "little", 6 | "target-pointer-width": "64", 7 | "target-c-int-width": "32", 8 | "os": "none", 9 | "arch": "x86_64", 10 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 11 | "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float", 12 | "disable-redzone": true, 13 | "eliminate-frame-pointer": true, 14 | "executables": true, 15 | "panic-strategy": "abort" 16 | } 17 | -------------------------------------------------------------------------------- /patches/rust.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/librustc_target/spec/x86_64_unknown_hermit.rs b/src/librustc_target/spec/x86_64_unknown_hermit.rs 2 | index b8be43be097..dd07815b2f4 100644 3 | --- a/src/librustc_target/spec/x86_64_unknown_hermit.rs 4 | +++ b/src/librustc_target/spec/x86_64_unknown_hermit.rs 5 | @@ -4,7 +4,7 @@ pub fn target() -> TargetResult { 6 | let mut base = super::hermit_base::opts(); 7 | base.cpu = "x86-64".to_string(); 8 | base.max_atomic_width = Some(64); 9 | - base.features = "+rdrnd,+rdseed".to_string(); 10 | + base.features = "+rdrnd,+rdseed,-mmx,-sse,+soft-float".to_string(); 11 | base.stack_probes = true; 12 | 13 | Ok(Target { 14 | -------------------------------------------------------------------------------- /patches/uhyve.patch: -------------------------------------------------------------------------------- 1 | commit 8b8846f054bd4e9bdc4f101047d05659b7dad741 2 | Author: mincheol 3 | Date: Wed Oct 2 14:13:54 2019 +0000 4 | 5 | Set USER bits on all levels of the page tables 6 | 7 | diff --git a/src/vm.rs b/src/vm.rs 8 | index 0aa86c9..e7c1b88 100644 9 | --- a/src/vm.rs 10 | +++ b/src/vm.rs 11 | @@ -411,21 +411,22 @@ pub trait Vm { 12 | 13 | pml4.entries[0].set( 14 | BOOT_PDPTE as usize, 15 | - PageTableEntryFlags::PRESENT | PageTableEntryFlags::WRITABLE, 16 | + PageTableEntryFlags::PRESENT | PageTableEntryFlags::WRITABLE | PageTableEntryFlags::USER_ACCESSIBLE, 17 | ); 18 | pml4.entries[511].set( 19 | BOOT_PML4 as usize, 20 | - PageTableEntryFlags::PRESENT | PageTableEntryFlags::WRITABLE, 21 | + PageTableEntryFlags::PRESENT | PageTableEntryFlags::WRITABLE | PageTableEntryFlags::USER_ACCESSIBLE, 22 | ); 23 | pdpte.entries[0].set( 24 | BOOT_PDE as usize, 25 | - PageTableEntryFlags::PRESENT | PageTableEntryFlags::WRITABLE, 26 | + PageTableEntryFlags::PRESENT | PageTableEntryFlags::WRITABLE | PageTableEntryFlags::USER_ACCESSIBLE, 27 | ); 28 | 29 | for i in 0..511 { 30 | pde.entries[i].set( 31 | i * LargePageSize::SIZE, 32 | PageTableEntryFlags::PRESENT 33 | + | PageTableEntryFlags::USER_ACCESSIBLE 34 | | PageTableEntryFlags::WRITABLE 35 | | PageTableEntryFlags::HUGE_PAGE, 36 | ); 37 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly 2 | -------------------------------------------------------------------------------- /src/arch/aarch64/kernel/irq.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | const IRQ_FLAG_F: usize = 1 << 6; 9 | const IRQ_FLAG_I: usize = 1 << 7; 10 | const IRQ_FLAG_A: usize = 1 << 8; 11 | 12 | /// Enable Interrupts 13 | #[inline] 14 | pub fn enable() { 15 | unsafe { 16 | asm!("msr daifclr, 0b111" ::: "memory" : "volatile"); 17 | } 18 | } 19 | 20 | /// Enable Interrupts and wait for the next interrupt (HLT instruction) 21 | /// According to https://lists.freebsd.org/pipermail/freebsd-current/2004-June/029369.html, this exact sequence of assembly 22 | /// instructions is guaranteed to be atomic. 23 | /// This is important, because another CPU could call wakeup_core right when we decide to wait for the next interrupt. 24 | #[inline] 25 | pub fn enable_and_wait() { 26 | // TODO 27 | unsafe { asm!("msr daifclr, 0b111; wfi" :::: "volatile") }; 28 | } 29 | 30 | /// Disable Interrupts 31 | #[inline] 32 | pub fn disable() { 33 | unsafe { 34 | asm!("msr daifset, 0b111" ::: "memory" : "volatile"); 35 | } 36 | } 37 | 38 | /// Disable IRQs (nested) 39 | /// 40 | /// Disable IRQs when unsure if IRQs were enabled at all. 41 | /// This function together with nested_enable can be used 42 | /// in situations when interrupts shouldn't be activated if they 43 | /// were not activated before calling this function. 44 | #[inline] 45 | pub fn nested_disable() -> bool { 46 | let flags: usize; 47 | unsafe { 48 | asm!("mrs $0, daif" : "=r"(flags) :: "memory" : "volatile"); 49 | } 50 | 51 | let mut was_enabled = true; 52 | if flags & (IRQ_FLAG_A | IRQ_FLAG_I | IRQ_FLAG_F) > 0 { 53 | was_enabled = false; 54 | } 55 | 56 | disable(); 57 | was_enabled 58 | } 59 | 60 | /// Enable IRQs (nested) 61 | /// 62 | /// Can be used in conjunction with nested_disable() to only enable 63 | /// interrupts again if they were enabled before. 64 | #[inline] 65 | pub fn nested_enable(was_enabled: bool) { 66 | if was_enabled { 67 | enable(); 68 | } 69 | } 70 | 71 | #[no_mangle] 72 | pub extern "C" fn irq_install_handler(irq_number: u32, handler: usize) { 73 | info!("Install handler for interrupt {}", irq_number); 74 | // TODO 75 | } 76 | -------------------------------------------------------------------------------- /src/arch/aarch64/kernel/percore.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use core::ptr; 9 | use scheduler::PerCoreScheduler; 10 | 11 | #[no_mangle] 12 | pub static mut PERCORE: PerCoreVariables = PerCoreVariables::new(0); 13 | 14 | pub struct PerCoreVariables { 15 | /// APIC ID of this CPU Core. 16 | core_id: PerCoreVariable, 17 | /// Scheduler for this CPU Core. 18 | scheduler: PerCoreVariable<*mut PerCoreScheduler>, 19 | } 20 | 21 | impl PerCoreVariables { 22 | pub const fn new(core_id: usize) -> Self { 23 | Self { 24 | core_id: PerCoreVariable::new(core_id), 25 | scheduler: PerCoreVariable::new(0 as *mut PerCoreScheduler), 26 | } 27 | } 28 | } 29 | 30 | #[repr(C)] 31 | pub struct PerCoreVariable { 32 | data: T, 33 | } 34 | 35 | pub trait PerCoreVariableMethods { 36 | unsafe fn get(&self) -> T; 37 | unsafe fn set(&mut self, value: T); 38 | } 39 | 40 | impl PerCoreVariable { 41 | const fn new(value: T) -> Self { 42 | Self { data: value } 43 | } 44 | } 45 | 46 | // Treat all per-core variables as 64-bit variables by default. This is true for u64, usize, pointers. 47 | // Implement the PerCoreVariableMethods trait functions using 64-bit memory moves. 48 | // The functions are implemented as default functions, which can be overriden in specialized implementations of the trait. 49 | impl PerCoreVariableMethods for PerCoreVariable 50 | where 51 | T: Clone, 52 | { 53 | #[inline] 54 | default unsafe fn get(&self) -> T { 55 | self.data.clone() 56 | } 57 | 58 | #[inline] 59 | default unsafe fn set(&mut self, value: T) { 60 | self.data = value; 61 | } 62 | } 63 | 64 | #[inline] 65 | pub fn core_id() -> usize { 66 | unsafe { PERCORE.core_id.get() } 67 | } 68 | 69 | #[inline] 70 | pub fn core_scheduler() -> &'static mut PerCoreScheduler { 71 | unsafe { &mut *PERCORE.scheduler.get() } 72 | } 73 | 74 | #[inline] 75 | pub fn set_core_scheduler(scheduler: *mut PerCoreScheduler) { 76 | unsafe { 77 | PERCORE.scheduler.set(scheduler); 78 | } 79 | } 80 | 81 | pub fn init() { 82 | // TODO: Implement! 83 | } 84 | -------------------------------------------------------------------------------- /src/arch/aarch64/kernel/processor.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use core::sync::atomic::spin_loop_hint; 9 | 10 | extern "C" { 11 | static mut cpu_freq: u32; 12 | } 13 | 14 | pub struct FPUState { 15 | // TODO 16 | } 17 | 18 | impl FPUState { 19 | pub fn new() -> Self { 20 | Self {} 21 | } 22 | 23 | pub fn restore(&self) { 24 | // TODO 25 | } 26 | 27 | pub fn save(&self) { 28 | // TODO 29 | } 30 | } 31 | 32 | pub fn generate_random_number() -> Option { 33 | None 34 | } 35 | 36 | /// Search the most significant bit 37 | #[inline(always)] 38 | pub fn msb(value: u64) -> Option { 39 | if value > 0 { 40 | let ret: u64; 41 | let u64_bits = 64; 42 | unsafe { 43 | asm!("clz $0, $1; sub $0, $2, $0" : "=r"(ret) : "r"(value), "r"(u64_bits - 1) : "cc" : "volatile"); 44 | } 45 | Some(ret) 46 | } else { 47 | None 48 | } 49 | } 50 | 51 | /// The halt function stops the processor until the next interrupt arrives 52 | pub fn halt() { 53 | unsafe { 54 | asm!("wfi" :::: "volatile"); 55 | } 56 | } 57 | 58 | /// Shutdown the system 59 | pub fn shutdown() -> ! { 60 | info!("Shutting down system"); 61 | 62 | loop { 63 | halt(); 64 | } 65 | } 66 | 67 | pub fn get_timer_ticks() -> u64 { 68 | // We simulate a timer with a 1 microsecond resolution by taking the CPU timestamp 69 | // and dividing it by the CPU frequency in MHz. 70 | 0 71 | } 72 | 73 | pub fn get_frequency() -> u16 { 74 | 0 75 | } 76 | 77 | #[inline] 78 | pub fn get_timestamp() -> u64 { 79 | 0 80 | } 81 | 82 | /// Delay execution by the given number of microseconds using busy-waiting. 83 | #[inline] 84 | pub fn udelay(usecs: u64) { 85 | let end = get_timestamp() + get_frequency() as u64 * usecs; 86 | while get_timestamp() < end { 87 | spin_loop_hint(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/arch/aarch64/kernel/scheduler.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Stefan Lankes, RWTH Aachen University 2 | // 2018 Colin Finck, RWTH Aachen University 3 | // 4 | // MIT License 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | //! Architecture dependent interface to initialize a task 26 | 27 | include!(concat!(env!("CARGO_TARGET_DIR"), "/config.rs")); 28 | 29 | use alloc::rc::Rc; 30 | use arch::aarch64::kernel::percore::*; 31 | use arch::aarch64::kernel::processor; 32 | use core::cell::RefCell; 33 | use core::{mem, ptr}; 34 | use scheduler::task::{Task, TaskFrame, TaskTLS}; 35 | 36 | extern "C" { 37 | static tls_start: u8; 38 | static tls_end: u8; 39 | } 40 | 41 | pub struct TaskStacks { 42 | is_boot_stack: bool, 43 | stack: usize, 44 | } 45 | 46 | impl TaskStacks { 47 | pub fn new() -> Self { 48 | // TODO: Allocate 49 | Self { 50 | is_boot_stack: false, 51 | stack: 0, 52 | } 53 | } 54 | 55 | pub fn from_boot_stacks() -> Self { 56 | // TODO: Get boot stacks 57 | Self { 58 | is_boot_stack: true, 59 | stack: 0, 60 | } 61 | } 62 | } 63 | 64 | impl Drop for TaskStacks { 65 | fn drop(&mut self) { 66 | if !self.is_boot_stack { 67 | // TODO: Deallocate 68 | } 69 | } 70 | } 71 | 72 | extern "C" fn leave_task() -> ! { 73 | core_scheduler().exit(0); 74 | } 75 | 76 | extern "C" fn task_entry(func: extern "C" fn(usize), arg: usize) { 77 | // Check if the task (process or thread) uses Thread-Local-Storage. 78 | let tls_size = unsafe { &tls_end as *const u8 as usize - &tls_start as *const u8 as usize }; 79 | if tls_size > 0 { 80 | // Yes, it does, so we have to allocate TLS memory. 81 | // Allocate enough space for the given size and one more variable of type usize, which holds the tls_pointer. 82 | let tls_allocation_size = tls_size + mem::size_of::(); 83 | let tls = TaskTLS::new(tls_allocation_size); 84 | 85 | // The tls_pointer is the address to the end of the TLS area requested by the task. 86 | let tls_pointer = tls.address() + tls_size; 87 | 88 | // TODO: Implement AArch64 TLS 89 | 90 | // Associate the TLS memory to the current task. 91 | let mut current_task_borrowed = core_scheduler().current_task.borrow_mut(); 92 | debug!( 93 | "Set up TLS for task {} at address {:#X}", 94 | current_task_borrowed.id, 95 | tls.address() 96 | ); 97 | current_task_borrowed.tls = Some(Rc::new(RefCell::new(tls))); 98 | } 99 | 100 | // Call the actual entry point of the task. 101 | func(arg); 102 | } 103 | 104 | impl TaskFrame for Task { 105 | fn create_stack_frame(&mut self, func: extern "C" fn(usize), arg: usize) { 106 | // TODO: Implement AArch64 stack frame 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/arch/aarch64/kernel/serial.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use core::ptr; 9 | 10 | pub struct SerialPort { 11 | port_address: u32, 12 | } 13 | 14 | impl SerialPort { 15 | pub const fn new(port_address: u32) -> Self { 16 | Self { 17 | port_address: port_address, 18 | } 19 | } 20 | 21 | pub fn write_byte(&self, byte: u8) { 22 | let port = self.port_address as *mut u8; 23 | 24 | // LF newline characters need to be extended to CRLF over a real serial port. 25 | if byte == b'\n' { 26 | unsafe { 27 | volatile_store(port, b'\r'); 28 | } 29 | } 30 | 31 | unsafe { 32 | volatile_store(port, byte); 33 | } 34 | } 35 | 36 | pub fn init(&self, baudrate: u32) { 37 | // We don't do anything here (yet). 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/arch/aarch64/kernel/stubs.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | pub fn set_oneshot_timer(wakeup_time: Option) { 9 | // TODO 10 | debug!("set_oneshot_timer stub"); 11 | } 12 | 13 | pub fn wakeup_core(core_to_wakeup: usize) { 14 | // TODO 15 | debug!("wakeup_core stub"); 16 | } 17 | 18 | #[no_mangle] 19 | pub extern "C" fn do_bad_mode() {} 20 | 21 | #[no_mangle] 22 | pub extern "C" fn do_error() {} 23 | 24 | #[no_mangle] 25 | pub extern "C" fn do_fiq() {} 26 | 27 | #[no_mangle] 28 | pub extern "C" fn do_irq() {} 29 | 30 | #[no_mangle] 31 | pub extern "C" fn do_sync() {} 32 | 33 | #[no_mangle] 34 | pub extern "C" fn eoi() {} 35 | 36 | #[no_mangle] 37 | pub extern "C" fn finish_task_switch() {} 38 | 39 | #[no_mangle] 40 | pub extern "C" fn getcontext() {} 41 | 42 | #[no_mangle] 43 | pub extern "C" fn get_current_stack() {} 44 | 45 | #[no_mangle] 46 | pub extern "C" fn makecontext() {} 47 | 48 | #[no_mangle] 49 | pub extern "C" fn setcontext() {} 50 | 51 | #[no_mangle] 52 | pub extern "C" fn switch(_old_stack: *mut usize, _new_stack: usize) {} 53 | -------------------------------------------------------------------------------- /src/arch/aarch64/kernel/systemtime.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use environment; 9 | 10 | extern "C" { 11 | static mut boot_gtod: u64; 12 | } 13 | 14 | pub fn get_boot_time() -> u64 { 15 | unsafe { boot_gtod } 16 | } 17 | -------------------------------------------------------------------------------- /src/arch/aarch64/mm/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | pub mod paging; 9 | pub mod physicalmem; 10 | pub mod virtualmem; 11 | 12 | pub use self::physicalmem::init_page_tables; 13 | 14 | pub fn init() { 15 | physicalmem::init(); 16 | virtualmem::init(); 17 | } 18 | -------------------------------------------------------------------------------- /src/arch/aarch64/mm/physicalmem.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch::aarch64::mm::paging::{BasePageSize, PageSize}; 9 | use collections::Node; 10 | use mm; 11 | use mm::freelist::{FreeList, FreeListEntry}; 12 | use mm::{MM_LOCK, POOL}; 13 | 14 | extern "C" { 15 | static limit: usize; 16 | } 17 | 18 | static mut PHYSICAL_FREE_LIST: FreeList = FreeList::new(); 19 | 20 | fn detect_from_limits() -> Result<(), ()> { 21 | if unsafe { limit } == 0 { 22 | return Err(()); 23 | } 24 | 25 | let entry = Node::new(FreeListEntry { 26 | start: mm::kernel_end_address(), 27 | end: unsafe { limit }, 28 | }); 29 | unsafe { 30 | PHYSICAL_FREE_LIST.list.push(entry); 31 | } 32 | 33 | Ok(()) 34 | } 35 | 36 | pub fn init() { 37 | detect_from_limits().unwrap(); 38 | } 39 | 40 | pub fn init_page_tables() {} 41 | 42 | pub fn allocate(size: usize) -> usize { 43 | assert!(size > 0); 44 | assert!( 45 | size % BasePageSize::SIZE == 0, 46 | "Size {:#X} is not a multiple of {:#X}", 47 | size, 48 | BasePageSize::SIZE 49 | ); 50 | 51 | let _lock = MM_LOCK.lock(); 52 | let result = unsafe { PHYSICAL_FREE_LIST.allocate(size) }; 53 | assert!( 54 | result.is_ok(), 55 | "Could not allocate {:#X} bytes of physical memory", 56 | size 57 | ); 58 | result.unwrap() 59 | } 60 | 61 | pub fn allocate_aligned(size: usize, alignment: usize) -> usize { 62 | assert!(size > 0); 63 | assert!(alignment > 0); 64 | assert!( 65 | size % alignment == 0, 66 | "Size {:#X} is not a multiple of the given alignment {:#X}", 67 | size, 68 | alignment 69 | ); 70 | assert!( 71 | alignment % BasePageSize::SIZE == 0, 72 | "Alignment {:#X} is not a multiple of {:#X}", 73 | alignment, 74 | BasePageSize::SIZE 75 | ); 76 | 77 | let _lock = MM_LOCK.lock(); 78 | let result = unsafe { 79 | POOL.maintain(); 80 | PHYSICAL_FREE_LIST.allocate_aligned(size, alignment) 81 | }; 82 | assert!( 83 | result.is_ok(), 84 | "Could not allocate {:#X} bytes of physical memory aligned to {} bytes", 85 | size, 86 | alignment 87 | ); 88 | result.unwrap() 89 | } 90 | 91 | /// This function must only be called from mm::deallocate! 92 | /// Otherwise, it may fail due to an empty node pool (POOL.maintain() is called in virtualmem::deallocate) 93 | pub fn deallocate(physical_address: usize, size: usize) { 94 | assert!( 95 | physical_address >= mm::kernel_end_address(), 96 | "Physical address {:#X} is not >= KERNEL_END_ADDRESS", 97 | physical_address 98 | ); 99 | assert!(size > 0); 100 | assert!( 101 | size % BasePageSize::SIZE == 0, 102 | "Size {:#X} is not a multiple of {:#X}", 103 | size, 104 | BasePageSize::SIZE 105 | ); 106 | 107 | unsafe { 108 | PHYSICAL_FREE_LIST.deallocate(physical_address, size); 109 | } 110 | } 111 | 112 | pub fn print_information() { 113 | unsafe { 114 | PHYSICAL_FREE_LIST.print_information(" PHYSICAL MEMORY FREE LIST "); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/arch/aarch64/mm/virtualmem.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch::aarch64::mm::paging::{BasePageSize, PageSize}; 9 | use collections::Node; 10 | use mm; 11 | use mm::freelist::{FreeList, FreeListEntry}; 12 | use mm::{MM_LOCK, POOL}; 13 | 14 | static mut KERNEL_FREE_LIST: FreeList = FreeList::new(); 15 | 16 | /// End of the virtual memory address space reserved for kernel memory (4 GiB). 17 | /// This also marks the start of the virtual memory address space reserved for the task heap. 18 | const KERNEL_VIRTUAL_MEMORY_END: usize = 0x1_0000_0000; 19 | 20 | /// End of the virtual memory address space reserved for task memory (128 TiB). 21 | /// This is the maximum contiguous virtual memory area possible with current x86-64 CPUs, which only support 48-bit 22 | /// linear addressing (in two 47-bit areas). 23 | const TASK_VIRTUAL_MEMORY_END: usize = 0x8000_0000_0000; 24 | 25 | pub fn init() { 26 | let entry = Node::new(FreeListEntry { 27 | start: mm::kernel_end_address(), 28 | end: KERNEL_VIRTUAL_MEMORY_END, 29 | }); 30 | unsafe { 31 | KERNEL_FREE_LIST.list.push(entry); 32 | } 33 | } 34 | 35 | pub fn allocate(size: usize) -> usize { 36 | assert!(size > 0); 37 | assert!( 38 | size % BasePageSize::SIZE == 0, 39 | "Size {:#X} is not a multiple of {:#X}", 40 | size, 41 | BasePageSize::SIZE 42 | ); 43 | 44 | let _lock = MM_LOCK.lock(); 45 | let result = unsafe { KERNEL_FREE_LIST.allocate(size) }; 46 | assert!( 47 | result.is_ok(), 48 | "Could not allocate {:#X} bytes of virtual memory", 49 | size 50 | ); 51 | result.unwrap() 52 | } 53 | 54 | pub fn deallocate(virtual_address: usize, size: usize) { 55 | assert!( 56 | virtual_address >= mm::kernel_end_address(), 57 | "Virtual address {:#X} is not >= KERNEL_END_ADDRESS", 58 | virtual_address 59 | ); 60 | assert!( 61 | virtual_address < KERNEL_VIRTUAL_MEMORY_END, 62 | "Virtual address {:#X} is not < KERNEL_VIRTUAL_MEMORY_END", 63 | virtual_address 64 | ); 65 | assert!( 66 | virtual_address % BasePageSize::SIZE == 0, 67 | "Virtual address {:#X} is not a multiple of {:#X}", 68 | virtual_address, 69 | BasePageSize::SIZE 70 | ); 71 | assert!(size > 0); 72 | assert!( 73 | size % BasePageSize::SIZE == 0, 74 | "Size {:#X} is not a multiple of {:#X}", 75 | size, 76 | BasePageSize::SIZE 77 | ); 78 | 79 | let _lock = MM_LOCK.lock(); 80 | unsafe { 81 | POOL.maintain(); 82 | KERNEL_FREE_LIST.deallocate(virtual_address, size); 83 | } 84 | } 85 | 86 | pub fn reserve(virtual_address: usize, size: usize) { 87 | assert!( 88 | virtual_address >= mm::kernel_end_address(), 89 | "Virtual address {:#X} is not >= KERNEL_END_ADDRESS", 90 | virtual_address 91 | ); 92 | assert!( 93 | virtual_address < KERNEL_VIRTUAL_MEMORY_END, 94 | "Virtual address {:#X} is not < KERNEL_VIRTUAL_MEMORY_END", 95 | virtual_address 96 | ); 97 | assert!( 98 | virtual_address % BasePageSize::SIZE == 0, 99 | "Virtual address {:#X} is not a multiple of {:#X}", 100 | virtual_address, 101 | BasePageSize::SIZE 102 | ); 103 | assert!(size > 0); 104 | assert!( 105 | size % BasePageSize::SIZE == 0, 106 | "Size {:#X} is not a multiple of {:#X}", 107 | size, 108 | BasePageSize::SIZE 109 | ); 110 | 111 | let _lock = MM_LOCK.lock(); 112 | let result = unsafe { 113 | POOL.maintain(); 114 | KERNEL_FREE_LIST.reserve(virtual_address, size) 115 | }; 116 | assert!( 117 | result.is_ok(), 118 | "Could not reserve {:#X} bytes of virtual memory at {:#X}", 119 | size, 120 | virtual_address 121 | ); 122 | } 123 | 124 | pub fn print_information() { 125 | unsafe { 126 | KERNEL_FREE_LIST.print_information(" KERNEL VIRTUAL MEMORY FREE LIST "); 127 | } 128 | } 129 | 130 | #[inline] 131 | pub fn task_heap_start() -> usize { 132 | KERNEL_VIRTUAL_MEMORY_END 133 | } 134 | 135 | #[inline] 136 | pub fn task_heap_end() -> usize { 137 | TASK_VIRTUAL_MEMORY_END 138 | } 139 | -------------------------------------------------------------------------------- /src/arch/aarch64/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | pub mod kernel; 9 | pub mod mm; 10 | -------------------------------------------------------------------------------- /src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | // Platform-specific implementations 10 | #[cfg(target_arch = "aarch64")] 11 | pub mod aarch64; 12 | 13 | #[cfg(target_arch = "x86_64")] 14 | pub mod x86_64; 15 | 16 | // Export our platform-specific modules. 17 | #[cfg(target_arch = "aarch64")] 18 | pub use arch::aarch64::*; 19 | 20 | #[cfg(target_arch = "aarch64")] 21 | pub use arch::aarch64::kernel::stubs::{set_oneshot_timer, switch, wakeup_core}; 22 | 23 | #[cfg(target_arch = "aarch64")] 24 | pub use arch::aarch64::kernel::{ 25 | application_processor_init, boot_application_processors, boot_processor_init, 26 | get_processor_count, message_output_init, output_message_byte, 27 | }; 28 | 29 | #[cfg(target_arch = "aarch64")] 30 | use arch::aarch64::kernel::percore::core_scheduler; 31 | 32 | #[cfg(target_arch = "aarch64")] 33 | pub use arch::aarch64::kernel::percore; 34 | 35 | #[cfg(target_arch = "aarch64")] 36 | pub use arch::aarch64::kernel::scheduler; 37 | 38 | #[cfg(target_arch = "aarch64")] 39 | pub use arch::aarch64::kernel::processor; 40 | 41 | #[cfg(target_arch = "aarch64")] 42 | pub use arch::aarch64::kernel::irq; 43 | 44 | #[cfg(target_arch = "aarch64")] 45 | pub use arch::aarch64::kernel::systemtime::get_boot_time; 46 | 47 | #[cfg(target_arch = "x86_64")] 48 | pub use arch::x86_64::*; 49 | 50 | #[cfg(target_arch = "x86_64")] 51 | pub use arch::x86_64::kernel::apic::{set_oneshot_timer, wakeup_core}; 52 | #[cfg(target_arch = "x86_64")] 53 | pub use arch::x86_64::kernel::gdt::set_current_kernel_stack; 54 | #[cfg(target_arch = "x86_64")] 55 | pub use arch::x86_64::kernel::irq; 56 | #[cfg(target_arch = "x86_64")] 57 | pub use arch::x86_64::kernel::percore; 58 | #[cfg(target_arch = "x86_64")] 59 | pub use arch::x86_64::kernel::processor; 60 | #[cfg(target_arch = "x86_64")] 61 | pub use arch::x86_64::kernel::scheduler; 62 | #[cfg(target_arch = "x86_64")] 63 | pub use arch::x86_64::kernel::switch::switch; 64 | #[cfg(target_arch = "x86_64")] 65 | pub use arch::x86_64::kernel::systemtime::get_boot_time; 66 | #[cfg(not(test))] 67 | #[cfg(target_arch = "x86_64")] 68 | pub use arch::x86_64::kernel::{ 69 | application_processor_init, boot_application_processors, boot_processor_init, 70 | }; 71 | #[cfg(target_arch = "x86_64")] 72 | pub use arch::x86_64::kernel::{get_processor_count, message_output_init, output_message_byte}; 73 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # NASM detection will change binary format depending on host system, but 2 | # we only want to generate elf64 for HermitCore 3 | # Note: Has to be set *before* ASM_NASM is enabled 4 | set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64) 5 | 6 | enable_language(ASM_NASM) 7 | 8 | # NASM hack, because it requires include paths to have a trailing /, whereas 9 | # CMake explicitly will remove it when adding includes the usual way 10 | # Note: Has to be set *after* ASM_NASM is enabled 11 | set(CMAKE_ASM_NASM_FLAGS 12 | "${CMAKE_ASM_NASM_FLAGS} -I ${CMAKE_BINARY_DIR}/include/") 13 | 14 | 15 | # Preprocess the PCI IDs into a Rust array. 16 | add_custom_command( 17 | OUTPUT 18 | ${CMAKE_BINARY_DIR}/hermit_rs/pcidata.rs 19 | DEPENDS 20 | ${CMAKE_SOURCE_DIR}/target/pci.ids 21 | COMMAND 22 | pci_ids_parser 23 | ${CMAKE_SOURCE_DIR}/target/pci.ids 24 | ${CMAKE_BINARY_DIR}/hermit_rs/pcidata.rs 25 | VERBATIM) 26 | add_custom_target(pcidata 27 | DEPENDS 28 | ${CMAKE_BINARY_DIR}/hermit_rs/pcidata.rs) 29 | 30 | # Preprocess the SMP Boot Code into a Rust array. 31 | add_custom_command( 32 | OUTPUT 33 | ${CMAKE_BINARY_DIR}/hermit_rs/smp_boot_code.rs 34 | DEPENDS 35 | ${CMAKE_CURRENT_LIST_DIR}/boot.asm 36 | COMMAND 37 | nasm -f bin -o boot.bin ${CMAKE_CURRENT_LIST_DIR}/boot.asm 38 | COMMAND 39 | echo -n "static SMP_BOOT_CODE: [u8; " > smp_boot_code.rs 40 | COMMAND 41 | stat -c %s boot.bin >> smp_boot_code.rs 42 | COMMAND 43 | echo "] = [" >> smp_boot_code.rs 44 | COMMAND 45 | hexdump -v -e "1/1 \"0x%02X,\"" boot.bin >> smp_boot_code.rs 46 | COMMAND 47 | echo "];" >> smp_boot_code.rs 48 | WORKING_DIRECTORY 49 | ${CMAKE_BINARY_DIR}/hermit_rs 50 | VERBATIM) 51 | add_custom_target(smp_boot_code 52 | DEPENDS 53 | ${CMAKE_BINARY_DIR}/hermit_rs/smp_boot_code.rs) 54 | 55 | add_dependencies(hermit_rs pcidata) 56 | add_dependencies(hermit_rs smp_boot_code) 57 | 58 | # Set a source-level dependency from the entry point on the Rust library. 59 | # This causes the entry point to be reassembled when the Rust library changes and subsequently the Hermit library is relinked. 60 | set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/entry.asm" PROPERTIES OBJECT_DEPENDS ${HERMIT_RS}) 61 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/boot.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (c) 2010-2017 Stefan Lankes, RWTH Aachen University 2 | ; 2018 Colin Finck, RWTH Aachen University 3 | ; 4 | ; Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | ; copied, modified, or distributed except according to those terms. 8 | 9 | ; This is the entry point for the application processors. 10 | ; It is loaded at 0x8000 by HermitCore and filled with parameters. 11 | ; It does the switch from Real Mode -> Protected Mode -> Long Mode, 12 | ; sets up CR3 for this CPU, and then calls into entry.asm. 13 | ; 14 | ; In contrast to this self-contained entry point, entry.asm is linked 15 | ; to the rest of HermitCore and thus has access to all exported symbols 16 | ; (like the actual Rust entry point). 17 | 18 | 19 | CR0_PG equ (1 << 31) 20 | CR4_PAE equ (1 << 5) 21 | MSR_EFER equ 0xC0000080 22 | EFER_LME equ (1 << 8) 23 | EFER_NXE equ (1 << 11) 24 | 25 | [BITS 16] 26 | SECTION .text 27 | GLOBAL _start 28 | ORG 0x8000 29 | _start: 30 | jmp _rmstart 31 | 32 | ; PARAMETERS 33 | align 8 34 | entry_point dq 0xDEADC0DE 35 | pml4 dd 0xDEADBEEF 36 | 37 | _rmstart: 38 | cli 39 | lgdt [gdtr] 40 | 41 | ; switch to protected mode by setting PE bit 42 | mov eax, cr0 43 | or al, 0x1 44 | mov cr0, eax 45 | 46 | ; far jump to the 32bit code 47 | jmp dword codesel : _pmstart 48 | 49 | [BITS 32] 50 | ALIGN 4 51 | _pmstart: 52 | xor eax, eax 53 | mov ax, datasel 54 | mov ds, ax 55 | mov es, ax 56 | mov fs, ax 57 | mov gs, ax 58 | mov ss, ax 59 | 60 | jmp short stublet 61 | jmp $ 62 | 63 | ; GDT for the protected mode 64 | ALIGN 4 65 | gdtr: ; descritor table 66 | dw gdt_end-gdt-1 ; limit 67 | dd gdt ; base adresse 68 | gdt: 69 | dd 0,0 ; null descriptor 70 | codesel equ $-gdt 71 | dw 0xFFFF ; segment size 0..15 72 | dw 0x0000 ; segment address 0..15 73 | db 0x00 ; segment address 16..23 74 | db 0x9A ; access permissions und type 75 | db 0xCF ; additional information and segment size 16...19 76 | db 0x00 ; segment address 24..31 77 | datasel equ $-gdt 78 | dw 0xFFFF ; segment size 0..15 79 | dw 0x0000 ; segment address 0..15 80 | db 0x00 ; segment address 16..23 81 | db 0x92 ; access permissions and type 82 | db 0xCF ; additional informationen and degment size 16...19 83 | db 0x00 ; segment address 24..31 84 | gdt_end: 85 | 86 | ALIGN 4 87 | GDTR64: 88 | dw GDT64_end - GDT64 - 1 ; Limit. 89 | dq GDT64 ; Base. 90 | 91 | ; we need a new GDT to switch in the 64bit modus 92 | GDT64: ; Global Descriptor Table (64-bit). 93 | .Null: equ $ - GDT64 ; The null descriptor. 94 | dw 0 ; Limit (low). 95 | dw 0 ; Base (low). 96 | db 0 ; Base (middle) 97 | db 0 ; Access. 98 | db 0 ; Granularity. 99 | db 0 ; Base (high). 100 | .Code: equ $ - GDT64 ; The code descriptor. 101 | dw 0 ; Limit (low). 102 | dw 0 ; Base (low). 103 | db 0 ; Base (middle) 104 | db 10011010b ; Access. 105 | db 00100000b ; Granularity. 106 | db 0 ; Base (high). 107 | .Data: equ $ - GDT64 ; The data descriptor. 108 | dw 0 ; Limit (low). 109 | dw 0 ; Base (low). 110 | db 0 ; Base (middle) 111 | db 10010010b ; Access. 112 | db 00000000b ; Granularity. 113 | db 0 ; Base (high). 114 | GDT64_end: 115 | 116 | ALIGN 4 117 | stublet: 118 | ; Enable PAE mode. 119 | mov eax, cr4 120 | or eax, CR4_PAE 121 | mov cr4, eax 122 | 123 | ; Set the address to PML4 in CR3. 124 | mov eax, dword [pml4] 125 | mov cr3, eax 126 | 127 | ; Enable x86-64 Compatibility Mode by setting EFER_LME. 128 | ; Also enable early access to EXECUTE_DISABLE-protected memory through EFER_NXE. 129 | mov ecx, MSR_EFER 130 | rdmsr 131 | or eax, EFER_LME | EFER_NXE 132 | wrmsr 133 | 134 | ; Enable Paging. 135 | mov eax, cr0 136 | or eax, CR0_PG 137 | mov cr0, eax 138 | 139 | ; Load the 64-bit global descriptor table. 140 | lgdt [GDTR64] 141 | mov ax, GDT64.Data 142 | mov ss, ax 143 | mov ds, ax 144 | mov es, ax 145 | 146 | ; Set the code segment and enter 64-bit long mode. 147 | jmp GDT64.Code:start64 148 | 149 | [BITS 64] 150 | ALIGN 8 151 | start64: 152 | ; Jump to entry.asm 153 | jmp qword [entry_point] 154 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/copy_safe.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use core::ptr::{write_bytes, copy_nonoverlapping}; 3 | use core::mem::size_of; 4 | use x86::msr::*; 5 | use mm; 6 | use arch::x86_64::kernel::processor; 7 | 8 | safe_global_var!(static mut LIST: [usize;100] = [0;100]); 9 | safe_global_var!(static SIZE: usize = 0x1000); 10 | 11 | pub fn unsafe_storage_init() { 12 | let unsafe_storage = mm::unsafe_allocate(SIZE, true); 13 | unsafe { 14 | info!("Init unsafe_storage: {:#X}", unsafe_storage); 15 | wrmsr(IA32_KERNEL_GSBASE, unsafe_storage as u64); 16 | list_add(processor::readgs()); 17 | } 18 | } 19 | 20 | #[inline] 21 | pub fn is_unsafe_storage_init() -> bool { 22 | unsafe { (rdmsr(IA32_KERNEL_GSBASE) != 0) } 23 | } 24 | 25 | #[inline] 26 | pub fn get_unsafe_storage() -> usize { 27 | unsafe { 28 | let address: usize; 29 | 30 | if rdmsr(IA32_KERNEL_GSBASE) == 0 { 31 | return 0; 32 | } 33 | 34 | asm!("swapgs; rdgsbase $0; swapgs" : "=r"(address) ::: "volatile"); 35 | address 36 | } 37 | } 38 | 39 | pub fn list_add(addr: usize) { 40 | safe_global_var!(static mut IDX: usize = 0); 41 | unsafe { 42 | if LIST.iter().any(|v| v == &addr) { 43 | return; 44 | } 45 | if IDX >= 100 { 46 | error!("LIST is full!!"); 47 | error!(" "); 48 | return; 49 | } 50 | LIST[IDX] = addr; 51 | IDX+=1; 52 | }; 53 | } 54 | 55 | #[inline] 56 | fn is_valid(addr: usize) -> bool { 57 | if addr == 0 { 58 | return false; 59 | } 60 | else if unsafe{LIST.iter().any(|v| v == &addr)} { 61 | //info!("addr {:#X} is valid", addr); 62 | return true; 63 | } 64 | else { 65 | //info!("addr {:#X} is invalid", addr); 66 | return false; 67 | } 68 | } 69 | 70 | pub fn copy_from_safe(src: *const T, count: usize) { 71 | if src.is_null() { 72 | error!("copy_from_safe error, null pointer"); 73 | error!(" "); 74 | return; 75 | } 76 | 77 | if count > SIZE { 78 | error!("copy_from_safe error, too large size"); 79 | error!(" "); 80 | return; 81 | } 82 | 83 | if is_valid(src as usize) { 84 | unsafe { 85 | copy_nonoverlapping(src, get_unsafe_storage() as *mut T, count); 86 | } 87 | return; 88 | } 89 | error!("copy_from_safe error"); 90 | error!(" "); 91 | } 92 | 93 | pub fn copy_to_safe(dst: *mut T, count: usize) { 94 | if dst.is_null() { 95 | error!("copy_to_safe error, null pointer"); 96 | error!(" "); 97 | return; 98 | } 99 | 100 | if count > SIZE { 101 | error!("copy_to_safe error, too large size"); 102 | error!(" "); 103 | return; 104 | } 105 | 106 | if is_valid(dst as usize) { 107 | unsafe { 108 | copy_nonoverlapping(get_unsafe_storage() as *const T, dst, count); 109 | } 110 | return; 111 | } 112 | error!("copy_to_safe error"); 113 | error!(" "); 114 | } 115 | 116 | pub fn clear_unsafe_storage() 117 | { 118 | unsafe { write_bytes(get_unsafe_storage() as *mut u8, 0x00, SIZE)}; 119 | } 120 | 121 | pub fn clear_unsafe_storage2(_: *const T) 122 | { 123 | unsafe { write_bytes(get_unsafe_storage() as *mut u8, 0x00, size_of::())}; 124 | } 125 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/gdt.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // 2017 Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | //use alloc::boxed::Box; 10 | use arch::x86_64::kernel::percore::*; 11 | use arch::x86_64::kernel::{BOOT_INFO, BootInfo}; 12 | use arch::x86_64::kernel::copy_safe::*; 13 | use config::*; 14 | use core::{intrinsics, mem}; 15 | use scheduler::task::TaskStatus; 16 | use x86::bits64::segmentation::*; 17 | use x86::bits64::task::*; 18 | use x86::dtables::{DescriptorTablePointer, lgdt}; 19 | use x86::segmentation::*; 20 | use x86::task::*; 21 | use x86::Ring; 22 | use mm; 23 | 24 | pub const GDT_NULL: u16 = 0; 25 | pub const GDT_KERNEL_CODE: u16 = 1; 26 | pub const GDT_KERNEL_DATA: u16 = 2; 27 | pub const GDT_FIRST_TSS: u16 = 3; 28 | 29 | /// We dynamically allocate a GDT large enough to hold the maximum number of entries. 30 | const GDT_ENTRIES: usize = 8192; 31 | 32 | /// We use IST1 through IST4. 33 | /// Each critical exception (NMI, Double Fault, Machine Check) gets a dedicated one while IST1 is shared for all other 34 | /// interrupts. See also irq.rs. 35 | const IST_ENTRIES: usize = 4; 36 | 37 | unsafe_global_var!(static mut GDT: *mut Gdt = 0 as *mut Gdt); 38 | safe_global_var!(static mut GDTR: DescriptorTablePointer = DescriptorTablePointer { 39 | base: 0 as *const Descriptor, 40 | limit: 0, 41 | }); 42 | 43 | struct Gdt { 44 | entries: [Descriptor; GDT_ENTRIES], 45 | } 46 | 47 | pub fn init() { 48 | let gdt_ref; 49 | unsafe { 50 | // Dynamically allocate memory for the GDT. 51 | GDT = ::mm::allocate(mem::size_of::(), true) as *mut Gdt; 52 | 53 | // Get gdt reference 54 | isolation_start!(); 55 | gdt_ref = &mut *GDT; 56 | isolation_end!(); 57 | } 58 | // The NULL descriptor is always the first entry. 59 | (*gdt_ref).entries[GDT_NULL as usize] = Descriptor::NULL; 60 | 61 | // The second entry is a 64-bit Code Segment in kernel-space (Ring 0). 62 | // All other parameters are ignored. 63 | (*gdt_ref).entries[GDT_KERNEL_CODE as usize] = 64 | DescriptorBuilder::code_descriptor(0, 0, CodeSegmentType::ExecuteRead) 65 | .present() 66 | .dpl(Ring::Ring0) 67 | .l() 68 | .finish(); 69 | 70 | // The third entry is a 64-bit Data Segment in kernel-space (Ring 0). 71 | // All other parameters are ignored. 72 | (*gdt_ref).entries[GDT_KERNEL_DATA as usize] = 73 | DescriptorBuilder::data_descriptor(0, 0, DataSegmentType::ReadWrite) 74 | .present() 75 | .dpl(Ring::Ring0) 76 | .finish(); 77 | 78 | // Let GDTR point to our newly crafted GDT. 79 | let temp_gdtr = DescriptorTablePointer::new_from_slice(&((*gdt_ref).entries[0..GDT_ENTRIES])); 80 | unsafe { 81 | GDTR = temp_gdtr; 82 | } 83 | } 84 | 85 | pub fn add_current_core() { 86 | unsafe { 87 | // Load the GDT for the current core. 88 | /* 89 | list_add(&GDTR as *const DescriptorTablePointer as usize); 90 | let unsafe_storage = get_unsafe_storage(); 91 | copy_from_safe(&GDTR, 1); 92 | isolation_start!(); 93 | lgdt(&(*(unsafe_storage as *const DescriptorTablePointer))); 94 | isolation_end!(); 95 | clear_unsafe_storage(); 96 | */ 97 | lgdt(&GDTR); 98 | 99 | // Reload the segment descriptors 100 | load_cs(SegmentSelector::new(GDT_KERNEL_CODE, Ring::Ring0)); 101 | load_ds(SegmentSelector::new(GDT_KERNEL_DATA, Ring::Ring0)); 102 | load_es(SegmentSelector::new(GDT_KERNEL_DATA, Ring::Ring0)); 103 | load_ss(SegmentSelector::new(GDT_KERNEL_DATA, Ring::Ring0)); 104 | } 105 | 106 | // Dynamically allocate memory for a Task-State Segment (TSS) for this core. 107 | let mut boxed_tss = TaskStateSegment::new(); 108 | //let mut boxed_tss = TaskStateSegment::new(); /* FIXME maybe some memory leak? */ 109 | 110 | // Every task later gets its own stack, so this boot stack is only used by the Idle task on each core. 111 | // When switching to another task on this core, this entry is replaced. 112 | 113 | let unsafe_storage = get_unsafe_storage(); 114 | unsafe { 115 | copy_from_safe(BOOT_INFO, 1); 116 | isolation_start!(); 117 | let temp_rsp = intrinsics::volatile_load(&(*(unsafe_storage as *const BootInfo)).current_stack_address) + KERNEL_STACK_SIZE as u64 - 0x10; 118 | isolation_end!(); 119 | boxed_tss.rsp[0] = temp_rsp; 120 | clear_unsafe_storage(); 121 | } 122 | 123 | // Allocate all ISTs for this core. 124 | // Every task later gets its own IST1, so the IST1 allocated here is only used by the Idle task. 125 | for i in 0..IST_ENTRIES { 126 | let ist = ::mm::user_allocate(KERNEL_STACK_SIZE, true); 127 | boxed_tss.ist[i] = (ist + KERNEL_STACK_SIZE - 0x10) as u64; 128 | } 129 | 130 | // Add this TSS to the GDT. 131 | let idx = GDT_FIRST_TSS as usize + (core_id() as usize) * 2; 132 | //let tss = Box::into_raw(boxed_tss); 133 | let tss = &mut boxed_tss as *mut _; 134 | { 135 | let base = tss as u64; 136 | let tss_descriptor: Descriptor64 = 137 | >::tss_descriptor( 138 | base, 139 | base + mem::size_of::() as u64 - 1, 140 | true, 141 | ) 142 | .present() 143 | .dpl(Ring::Ring0) 144 | .finish(); 145 | 146 | list_add(&tss_descriptor as *const _ as usize); 147 | let unsafe_storage = get_unsafe_storage(); 148 | copy_from_safe(&tss_descriptor, 1); 149 | 150 | let gdt_ref; 151 | unsafe { 152 | isolation_start!(); 153 | gdt_ref = &mut (*GDT); 154 | isolation_end!(); 155 | } 156 | let entry = &mut (*gdt_ref).entries[idx..idx + 2]; 157 | 158 | unsafe { 159 | let tss_desc = &mem::transmute::(*(unsafe_storage as *const Descriptor64),); 160 | (*entry).copy_from_slice(tss_desc); 161 | clear_unsafe_storage(); 162 | } 163 | } 164 | 165 | // Load it. 166 | let sel = SegmentSelector::new(idx as u16, Ring::Ring0); 167 | unsafe { 168 | load_tr(sel); 169 | 170 | let alloc_tss = mm::user_allocate(mem::size_of::(), true) as *mut TaskStateSegment; 171 | list_add(alloc_tss as usize); 172 | list_add(tss as usize); 173 | copy_from_safe(tss, 1); 174 | copy_to_safe(alloc_tss, 1); 175 | clear_unsafe_storage(); 176 | // Store it in the PerCoreVariables structure for further manipulation. 177 | PERCORE.tss.safe_set(alloc_tss); 178 | } 179 | } 180 | 181 | #[no_mangle] 182 | pub extern "C" fn set_current_kernel_stack() { 183 | let current_task_borrowed = core_scheduler().current_task.borrow(); 184 | let stack_size = if current_task_borrowed.status == TaskStatus::TaskIdle { 185 | KERNEL_STACK_SIZE 186 | } else { 187 | DEFAULT_STACK_SIZE 188 | }; 189 | 190 | let tss = unsafe { &mut (*PERCORE.tss.safe_get()) }; 191 | 192 | tss.rsp[0] = (current_task_borrowed.stacks.stack + stack_size - 0x10) as u64; 193 | tss.ist[0] = (current_task_borrowed.stacks.ist0 + KERNEL_STACK_SIZE - 0x10) as u64; 194 | } 195 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/idt.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | #![allow(dead_code)] 10 | 11 | use arch::x86_64::kernel::gdt; 12 | use core::sync::atomic::{AtomicBool, Ordering}; 13 | use x86::bits64::paging::VAddr; 14 | use x86::dtables::{DescriptorTablePointer, lidt}; 15 | use x86::segmentation::{SegmentSelector, SystemDescriptorTypes64}; 16 | use x86::Ring; 17 | 18 | /// An interrupt gate descriptor. 19 | /// 20 | /// See Intel manual 3a for details, specifically section "6.14.1 64-Bit Mode 21 | /// IDT" and "Figure 6-7. 64-Bit IDT Gate Descriptors". 22 | #[derive(Debug, Clone, Copy)] 23 | #[repr(C, packed)] 24 | struct IdtEntry { 25 | /// Lower 16 bits of ISR. 26 | pub base_lo: u16, 27 | /// Segment selector. 28 | pub selector: SegmentSelector, 29 | /// This must always be zero. 30 | pub ist_index: u8, 31 | /// Flags. 32 | pub flags: u8, 33 | /// The upper 48 bits of ISR (the last 16 bits must be zero). 34 | pub base_hi: u64, 35 | /// Must be zero. 36 | pub reserved1: u16, 37 | } 38 | 39 | enum Type { 40 | InterruptGate, 41 | TrapGate, 42 | } 43 | 44 | impl Type { 45 | pub fn pack(self) -> u8 { 46 | match self { 47 | Type::InterruptGate => SystemDescriptorTypes64::InterruptGate as u8, 48 | Type::TrapGate => SystemDescriptorTypes64::TrapGate as u8, 49 | } 50 | } 51 | } 52 | 53 | impl IdtEntry { 54 | /// A "missing" IdtEntry. 55 | /// 56 | /// If the CPU tries to invoke a missing interrupt, it will instead 57 | /// send a General Protection fault (13), with the interrupt number and 58 | /// some other data stored in the error code. 59 | pub const MISSING: IdtEntry = IdtEntry { 60 | base_lo: 0, 61 | selector: SegmentSelector::from_raw(0), 62 | ist_index: 0, 63 | flags: 0, 64 | base_hi: 0, 65 | reserved1: 0, 66 | }; 67 | 68 | /// Create a new IdtEntry pointing at `handler`, which must be a function 69 | /// with interrupt calling conventions. (This must be currently defined in 70 | /// assembly language.) The `gdt_code_selector` value must be the offset of 71 | /// code segment entry in the GDT. 72 | /// 73 | /// The "Present" flag set, which is the most common case. If you need 74 | /// something else, you can construct it manually. 75 | pub fn new( 76 | handler: VAddr, 77 | gdt_code_selector: SegmentSelector, 78 | dpl: Ring, 79 | ty: Type, 80 | ist_index: u8, 81 | ) -> IdtEntry { 82 | assert!(ist_index < 0b1000); 83 | IdtEntry { 84 | base_lo: ((handler.as_usize() as u64) & 0xFFFF) as u16, 85 | base_hi: handler.as_usize() as u64 >> 16, 86 | selector: gdt_code_selector, 87 | ist_index: ist_index, 88 | flags: dpl as u8 | ty.pack() | (1 << 7), 89 | reserved1: 0, 90 | } 91 | } 92 | } 93 | 94 | /// Declare an IDT of 256 entries. 95 | /// Although not all entries are used, the rest exists as a bit 96 | /// of a trap. If any undefined IDT entry is hit, it will cause 97 | /// an "Unhandled Interrupt" exception. 98 | pub const IDT_ENTRIES: usize = 256; 99 | 100 | safe_global_var!(static mut IDT: [IdtEntry; IDT_ENTRIES] = [IdtEntry::MISSING; IDT_ENTRIES]); 101 | safe_global_var!(static mut IDTP: DescriptorTablePointer = DescriptorTablePointer { 102 | base: 0 as *const IdtEntry, 103 | limit: 0, 104 | }); 105 | 106 | pub fn install() { 107 | safe_global_var!(static IDT_INIT: AtomicBool = AtomicBool::new(false)); 108 | 109 | unsafe { 110 | let is_init = IDT_INIT.swap(true, Ordering::SeqCst); 111 | 112 | if !is_init { 113 | // TODO: As soon as https://github.com/rust-lang/rust/issues/44580 is implemented, it should be possible to 114 | // implement "new" as "const fn" and do this call already in the initialization of IDTP. 115 | IDTP = DescriptorTablePointer::new_from_slice(&IDT); 116 | }; 117 | 118 | lidt(&IDTP); 119 | } 120 | } 121 | 122 | /// Set an entry in the IDT. 123 | /// 124 | /// # Arguments 125 | /// 126 | /// * `index` - 8-bit index of the interrupt gate to set. 127 | /// * `handler` - Handler function to call for this interrupt/exception. 128 | /// * `ist_index` - Index of the Interrupt Stack Table (IST) to switch to. 129 | /// A zero value means that the stack won't be switched, a value of 1 refers to the first IST entry, etc. 130 | pub fn set_gate(index: u8, handler: usize, ist_index: u8) { 131 | let sel = SegmentSelector::new(gdt::GDT_KERNEL_CODE, Ring::Ring0); 132 | let entry = IdtEntry::new( 133 | VAddr::from_usize(handler), 134 | sel, 135 | Ring::Ring0, 136 | Type::InterruptGate, 137 | ist_index, 138 | ); 139 | 140 | unsafe { 141 | IDT[index as usize] = entry; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/pci.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use alloc::vec::Vec; 9 | use arch::x86_64::kernel::pci_ids::{CLASSES, VENDORS}; 10 | use core::{fmt, u32, u8}; 11 | use synch::spinlock::Spinlock; 12 | use x86::io::*; 13 | 14 | const PCI_MAX_BUS_NUMBER: u8 = 32; 15 | const PCI_MAX_DEVICE_NUMBER: u8 = 32; 16 | 17 | const PCI_CONFIG_ADDRESS_PORT: u16 = 0xCF8; 18 | const PCI_CONFIG_ADDRESS_ENABLE: u32 = 1 << 31; 19 | 20 | const PCI_CONFIG_DATA_PORT: u16 = 0xCFC; 21 | const PCI_COMMAND_BUSMASTER: u32 = 1 << 2; 22 | 23 | const PCI_ID_REGISTER: u32 = 0x00; 24 | const PCI_COMMAND_REGISTER: u32 = 0x04; 25 | const PCI_CLASS_REGISTER: u32 = 0x08; 26 | const PCI_BAR0_REGISTER: u32 = 0x10; 27 | const PCI_INTERRUPT_REGISTER: u32 = 0x3C; 28 | 29 | pub const PCI_BASE_ADDRESS_IO_SPACE: u32 = 1 << 0; 30 | pub const PCI_BASE_ADDRESS_64BIT: u32 = 1 << 2; 31 | pub const PCI_BASE_ADDRESS_MASK: u32 = 0xFFFF_FFF0; 32 | 33 | safe_global_var!(static PCI_ADAPTERS: Spinlock> = Spinlock::new(Vec::new())); 34 | 35 | #[derive(Clone, Copy)] 36 | pub struct PciAdapter { 37 | pub bus: u8, 38 | pub device: u8, 39 | pub vendor_id: u16, 40 | pub device_id: u16, 41 | pub class_id: u8, 42 | pub subclass_id: u8, 43 | pub programming_interface_id: u8, 44 | pub base_addresses: [u32; 6], 45 | pub base_sizes: [u32; 6], 46 | pub irq: u8, 47 | } 48 | 49 | impl PciAdapter { 50 | fn new(bus: u8, device: u8, vendor_id: u16, device_id: u16) -> Self { 51 | let class_ids = read_config(bus, device, PCI_CLASS_REGISTER); 52 | 53 | let mut base_addresses: [u32; 6] = [0; 6]; 54 | let mut base_sizes: [u32; 6] = [0; 6]; 55 | for i in 0..6 { 56 | let register = PCI_BAR0_REGISTER + ((i as u32) << 2); 57 | base_addresses[i] = read_config(bus, device, register) & 0xFFFF_FFFC; 58 | 59 | if base_addresses[i] > 0 { 60 | write_config(bus, device, register, u32::MAX); 61 | base_sizes[i] = !(read_config(bus, device, register) & PCI_BASE_ADDRESS_MASK) + 1; 62 | write_config(bus, device, register, base_addresses[i]); 63 | } 64 | } 65 | 66 | let interrupt_info = read_config(bus, device, PCI_INTERRUPT_REGISTER); 67 | 68 | Self { 69 | bus: bus, 70 | device: device, 71 | vendor_id: vendor_id, 72 | device_id: device_id, 73 | class_id: (class_ids >> 24) as u8, 74 | subclass_id: (class_ids >> 16) as u8, 75 | programming_interface_id: (class_ids >> 8) as u8, 76 | base_addresses: base_addresses, 77 | base_sizes: base_sizes, 78 | irq: interrupt_info as u8, 79 | } 80 | } 81 | 82 | pub fn make_bus_master(&self) { 83 | let mut command = read_config(self.bus, self.device, PCI_COMMAND_REGISTER); 84 | command |= PCI_COMMAND_BUSMASTER; 85 | write_config(self.bus, self.device, PCI_COMMAND_REGISTER, command); 86 | } 87 | } 88 | 89 | impl fmt::Display for PciAdapter { 90 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 91 | // Look for the best matching class name in the PCI Database. 92 | let mut class_name = "Unknown Class"; 93 | for ref c in CLASSES { 94 | if c.id == self.class_id { 95 | class_name = c.name; 96 | for ref sc in c.subclasses { 97 | if sc.id == self.subclass_id { 98 | class_name = sc.name; 99 | break; 100 | } 101 | } 102 | 103 | break; 104 | } 105 | } 106 | 107 | // Look for the vendor and device name in the PCI Database. 108 | let mut vendor_name = "Unknown Vendor"; 109 | let mut device_name = "Unknown Device"; 110 | for ref v in VENDORS { 111 | if v.id == self.vendor_id { 112 | vendor_name = v.name; 113 | for ref d in v.devices { 114 | if d.id == self.device_id { 115 | device_name = d.name; 116 | break; 117 | } 118 | } 119 | 120 | break; 121 | } 122 | } 123 | 124 | // Output detailed readable information about this device. 125 | write!( 126 | f, 127 | "{:02X}:{:02X} {} [{:02X}{:02X}]: {} {} [{:04X}:{:04X}]", 128 | self.bus, 129 | self.device, 130 | class_name, 131 | self.class_id, 132 | self.subclass_id, 133 | vendor_name, 134 | device_name, 135 | self.vendor_id, 136 | self.device_id 137 | )?; 138 | 139 | // If the devices uses an IRQ, output this one as well. 140 | if self.irq != 0 && self.irq != u8::MAX { 141 | write!(f, ", IRQ {}", self.irq)?; 142 | } 143 | 144 | write!(f, ", iobase ")?; 145 | for i in 0..self.base_addresses.len() { 146 | if self.base_addresses[i] > 0 { 147 | write!( 148 | f, 149 | "0x{:x} (size 0x{:x}) ", 150 | self.base_addresses[i], self.base_sizes[i] 151 | )?; 152 | } 153 | } 154 | 155 | Ok(()) 156 | } 157 | } 158 | 159 | fn read_config(bus: u8, device: u8, register: u32) -> u32 { 160 | let address = 161 | PCI_CONFIG_ADDRESS_ENABLE | u32::from(bus) << 16 | u32::from(device) << 11 | register; 162 | unsafe { 163 | outl(PCI_CONFIG_ADDRESS_PORT, address); 164 | inl(PCI_CONFIG_DATA_PORT) 165 | } 166 | } 167 | 168 | fn write_config(bus: u8, device: u8, register: u32, data: u32) { 169 | let address = 170 | PCI_CONFIG_ADDRESS_ENABLE | u32::from(bus) << 16 | u32::from(device) << 11 | register; 171 | unsafe { 172 | outl(PCI_CONFIG_ADDRESS_PORT, address); 173 | outl(PCI_CONFIG_DATA_PORT, data); 174 | } 175 | } 176 | 177 | pub fn get_adapter(vendor_id: u16, device_id: u16) -> Option { 178 | let adapters = PCI_ADAPTERS.lock(); 179 | for adapter in adapters.iter() { 180 | if adapter.vendor_id == vendor_id && adapter.device_id == device_id { 181 | return Some(*adapter); 182 | } 183 | } 184 | 185 | None 186 | } 187 | 188 | pub fn init() { 189 | debug!("Scanning PCI Busses 0 to {}", PCI_MAX_BUS_NUMBER - 1); 190 | let mut adapters = PCI_ADAPTERS.lock(); 191 | 192 | // HermitCore only uses PCI for network devices. 193 | // Therefore, multifunction devices as well as additional bridges are not scanned. 194 | // We also limit scanning to the first 32 buses. 195 | for bus in 0..PCI_MAX_BUS_NUMBER { 196 | for device in 0..PCI_MAX_DEVICE_NUMBER { 197 | let device_vendor_id = read_config(bus, device, PCI_ID_REGISTER); 198 | if device_vendor_id != u32::MAX { 199 | let device_id = (device_vendor_id >> 16) as u16; 200 | let vendor_id = device_vendor_id as u16; 201 | 202 | adapters.push(PciAdapter::new(bus, device, vendor_id, device_id)); 203 | } 204 | } 205 | } 206 | } 207 | 208 | pub fn print_information() { 209 | infoheader!(" PCI BUS INFORMATION "); 210 | 211 | let adapters = PCI_ADAPTERS.lock(); 212 | for adapter in adapters.iter() { 213 | info!("{}", adapter); 214 | } 215 | 216 | infofooter!(); 217 | } 218 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/pic.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch::x86_64::kernel::idt; 9 | use arch::x86_64::kernel::irq::ExceptionStackFrame; 10 | use x86::io::*; 11 | 12 | const PIC1_COMMAND_PORT: u16 = 0x20; 13 | const PIC1_DATA_PORT: u16 = 0x21; 14 | const PIC2_COMMAND_PORT: u16 = 0xA0; 15 | const PIC2_DATA_PORT: u16 = 0xA1; 16 | 17 | pub const PIC1_INTERRUPT_OFFSET: u8 = 32; 18 | const PIC2_INTERRUPT_OFFSET: u8 = 40; 19 | const SPURIOUS_IRQ_NUMBER: u8 = 7; 20 | 21 | /// End-Of-Interrupt Command for an Intel 8259 Programmable Interrupt Controller (PIC). 22 | const PIC_EOI_COMMAND: u8 = 0x20; 23 | 24 | pub fn eoi(int_no: u8) { 25 | unsafe { 26 | // For IRQ 8-15 (mapped to interrupt numbers >= 40), we need to send an EOI to the slave PIC. 27 | if int_no >= 40 { 28 | outb(PIC2_COMMAND_PORT, PIC_EOI_COMMAND); 29 | } 30 | 31 | // In all cases, we need to send an EOI to the master PIC. 32 | outb(PIC1_COMMAND_PORT, PIC_EOI_COMMAND); 33 | } 34 | } 35 | 36 | pub fn init() { 37 | // Even if we mask all interrupts, spurious interrupts may still occur. 38 | // This is especially true for real hardware. So provide a handler for them. 39 | idt::set_gate( 40 | PIC1_INTERRUPT_OFFSET + SPURIOUS_IRQ_NUMBER, 41 | spurious_interrupt_on_master as usize, 42 | 0, 43 | ); 44 | idt::set_gate( 45 | PIC2_INTERRUPT_OFFSET + SPURIOUS_IRQ_NUMBER, 46 | spurious_interrupt_on_slave as usize, 47 | 0, 48 | ); 49 | 50 | unsafe { 51 | // Reinitialize PIC1 and PIC2. 52 | outb(PIC1_COMMAND_PORT, 0x11); 53 | outb(PIC2_COMMAND_PORT, 0x11); 54 | 55 | // Map PIC1 to interrupt numbers >= 32 and PIC2 to interrupt numbers >= 40. 56 | outb(PIC1_DATA_PORT, PIC1_INTERRUPT_OFFSET); 57 | outb(PIC2_DATA_PORT, PIC2_INTERRUPT_OFFSET); 58 | 59 | // Configure PIC1 as master and PIC2 as slave. 60 | outb(PIC1_DATA_PORT, 0x04); 61 | outb(PIC2_DATA_PORT, 0x02); 62 | 63 | // Start them in 8086 mode. 64 | outb(PIC1_DATA_PORT, 0x01); 65 | outb(PIC2_DATA_PORT, 0x01); 66 | 67 | // Mask all interrupts on both PICs. 68 | outb(PIC1_DATA_PORT, 0xFF); 69 | outb(PIC2_DATA_PORT, 0xFF); 70 | } 71 | } 72 | 73 | extern "x86-interrupt" fn spurious_interrupt_on_master(_stack_frame: &mut ExceptionStackFrame) { 74 | debug!("Spurious Interrupt on Master PIC (IRQ7)"); 75 | } 76 | 77 | extern "x86-interrupt" fn spurious_interrupt_on_slave(_stack_frame: &mut ExceptionStackFrame) { 78 | debug!("Spurious Interrupt on Slave PIC (IRQ15)"); 79 | 80 | // As this is an interrupt forwarded by the master, we have to acknowledge it on the master 81 | // (but not on the slave as with all spurious interrupts). 82 | unsafe { 83 | outb(PIC1_COMMAND_PORT, PIC_EOI_COMMAND); 84 | } 85 | } 86 | 87 | fn edit_mask(int_no: u8, insert: bool) { 88 | let port = if int_no >= 40 { 89 | PIC2_DATA_PORT 90 | } else { 91 | PIC1_DATA_PORT 92 | }; 93 | let offset = if int_no >= 40 { 40 } else { 32 }; 94 | 95 | unsafe { 96 | let mask = inb(port); 97 | 98 | if insert { 99 | outb(port, mask | 1 << (int_no - offset)); 100 | } else { 101 | outb(port, mask & !(1 << (int_no - offset))); 102 | } 103 | } 104 | } 105 | 106 | pub fn mask(int_no: u8) { 107 | edit_mask(int_no, true); 108 | } 109 | 110 | pub fn unmask(int_no: u8) { 111 | edit_mask(int_no, false); 112 | } 113 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/pit.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | #![allow(dead_code)] 10 | 11 | use arch::x86_64::kernel::pic; 12 | use x86::io::*; 13 | 14 | const PIT_CLOCK: u64 = 1_193_182; 15 | pub const PIT_INTERRUPT_NUMBER: u8 = pic::PIC1_INTERRUPT_OFFSET + 0; 16 | 17 | const PIT_CHANNEL0_DATA_PORT: u16 = 0x40; 18 | const PIT_CHANNEL1_DATA_PORT: u16 = 0x41; 19 | const PIT_CHANNEL2_DATA_PORT: u16 = 0x42; 20 | const PIT_COMMAND_PORT: u16 = 0x43; 21 | 22 | const PIT_BINARY_OUTPUT: u8 = 0b0000_0000; 23 | const PIT_BCD_OUTPUT: u8 = 0b0000_0001; 24 | 25 | const PIT_COUNTDOWN_MODE: u8 = 0b0000_0000; 26 | const PIT_ONESHOT_MODE: u8 = 0b0000_0010; 27 | const PIT_RATE_GENERATOR_MODE: u8 = 0b0000_0100; 28 | const PIT_SQUARE_WAVE_GENERATOR_MODE: u8 = 0b0000_0110; 29 | const PIT_SW_TRIGGERED_STROBE_MODE: u8 = 0b0000_1000; 30 | const PIT_HW_TRIGGERED_STROBE_MODE: u8 = 0b0000_1010; 31 | 32 | const PIT_LOBYTE_ACCESS: u8 = 0b0001_0000; 33 | const PIT_HIBYTE_ACCESS: u8 = 0b0010_0000; 34 | 35 | const PIT_CHANNEL0: u8 = 0b0000_0000; 36 | const PIT_CHANNEL1: u8 = 0b0100_0000; 37 | const PIT_CHANNEL2: u8 = 0b1000_0000; 38 | 39 | pub fn init(frequency_in_hz: u64) { 40 | pic::unmask(PIT_INTERRUPT_NUMBER); 41 | 42 | unsafe { 43 | // Reset the Programmable Interval Timer (PIT). 44 | outb( 45 | PIT_COMMAND_PORT, 46 | PIT_BINARY_OUTPUT 47 | | PIT_RATE_GENERATOR_MODE 48 | | PIT_LOBYTE_ACCESS 49 | | PIT_HIBYTE_ACCESS 50 | | PIT_CHANNEL0, 51 | ); 52 | 53 | // Calculate the reload value to count down (round it to the closest integer). 54 | // Then transmit it as two individual bytes to the PIT. 55 | let count = (PIT_CLOCK + frequency_in_hz / 2) / frequency_in_hz; 56 | outb(PIT_CHANNEL0_DATA_PORT, count as u8); 57 | outb(PIT_CHANNEL0_DATA_PORT, (count >> 8) as u8); 58 | } 59 | } 60 | 61 | pub fn deinit() { 62 | pic::mask(PIT_INTERRUPT_NUMBER); 63 | } 64 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/serial.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use core::sync::atomic::spin_loop_hint; 9 | use environment; 10 | use x86::io::*; 11 | 12 | const UART_TX: u16 = 0; 13 | const UART_IER: u16 = 1; 14 | 15 | const UART_DLL: u16 = 0; 16 | const UART_DLM: u16 = 1; 17 | 18 | const UART_FCR: u16 = 2; 19 | const UART_FCR_ENABLE_FIFO: u8 = 0x01; 20 | const UART_FCR_CLEAR_RECEIVER_FIFO: u8 = 0x02; 21 | const UART_FCR_CLEAR_TRANSMITTER_FIFO: u8 = 0x04; 22 | 23 | const UART_LCR: u16 = 3; 24 | const UART_LCR_WORD_LENGTH_8BITS: u8 = 0x03; 25 | const UART_LCR_DIVISOR_LATCH_ACCESS: u8 = 0x80; 26 | 27 | const UART_LSR: u16 = 5; 28 | const UART_LSR_EMPTY_TRANSMITTER_HOLDING_REGISTER: u8 = 0x20; 29 | 30 | pub struct SerialPort { 31 | pub port_address: u16, 32 | } 33 | 34 | impl SerialPort { 35 | pub const fn new(port_address: u16) -> Self { 36 | Self { 37 | port_address: port_address, 38 | } 39 | } 40 | 41 | fn read_from_register(&self, register: u16) -> u8 { 42 | unsafe { inb(self.port_address + register) } 43 | } 44 | 45 | fn is_transmitting(&self) -> bool { 46 | // The virtual serial port in uhyve is never blocked. 47 | if environment::is_uhyve() { 48 | return false; 49 | } 50 | 51 | (self.read_from_register(UART_LSR) & UART_LSR_EMPTY_TRANSMITTER_HOLDING_REGISTER == 0) 52 | } 53 | 54 | fn write_to_register(&self, register: u16, byte: u8) { 55 | while self.is_transmitting() { 56 | spin_loop_hint(); 57 | } 58 | 59 | unsafe { 60 | outb(self.port_address + register, byte); 61 | } 62 | } 63 | 64 | pub fn write_byte(&self, byte: u8) { 65 | if self.port_address == 0 { 66 | return; 67 | } 68 | 69 | // LF newline characters need to be extended to CRLF over a real serial port. 70 | if byte == b'\n' { 71 | self.write_to_register(UART_TX, b'\r'); 72 | } 73 | 74 | self.write_to_register(UART_TX, byte); 75 | } 76 | 77 | pub fn init(&self, baudrate: u32) { 78 | // The virtual serial port is always initialized in uhyve. 79 | if !environment::is_uhyve() && self.port_address != 0 { 80 | // Disable port interrupt. 81 | self.write_to_register(UART_IER, 0); 82 | 83 | // Set 8N1 mode (8 bits, 1 stop bit, no parity). 84 | self.write_to_register(UART_LCR, UART_LCR_WORD_LENGTH_8BITS); 85 | 86 | // Set the baudrate. 87 | let divisor = (115_200 / baudrate) as u16; 88 | let lcr = self.read_from_register(UART_LCR); 89 | self.write_to_register(UART_LCR, lcr | UART_LCR_DIVISOR_LATCH_ACCESS); 90 | self.write_to_register(UART_DLL, divisor as u8); 91 | self.write_to_register(UART_DLM, (divisor >> 8) as u8); 92 | self.write_to_register(UART_LCR, lcr); 93 | 94 | // Enable and clear FIFOs. 95 | self.write_to_register( 96 | UART_FCR, 97 | UART_FCR_ENABLE_FIFO 98 | | UART_FCR_CLEAR_RECEIVER_FIFO 99 | | UART_FCR_CLEAR_TRANSMITTER_FIFO, 100 | ); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/smp_boot_code.rs: -------------------------------------------------------------------------------- 1 | safe_global_var!(pub static SMP_BOOT_CODE: [u8; 207] = [ 2 | 0xEB, 0x12, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xDE, 0xC0, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 3 | 0xEF, 0xBE, 0xAD, 0xDE, 0xFA, 0x0F, 0x01, 0x16, 0x40, 0x80, 0x0F, 0x20, 0xC0, 0x0C, 0x01, 0x0F, 4 | 0x22, 0xC0, 0x66, 0xEA, 0x2C, 0x80, 0x00, 0x00, 0x08, 0x00, 0x90, 0x90, 0x31, 0xC0, 0x66, 0xB8, 5 | 0x10, 0x00, 0x8E, 0xD8, 0x8E, 0xC0, 0x8E, 0xE0, 0x8E, 0xE8, 0x8E, 0xD0, 0xEB, 0x46, 0xEB, 0xFE, 6 | 0x17, 0x00, 0x46, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 7 | 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00, 0x90, 0x90, 8 | 0x17, 0x00, 0x6A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 9 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 10 | 0x00, 0x00, 0x90, 0x90, 0x0F, 0x20, 0xE0, 0x83, 0xC8, 0x20, 0x0F, 0x22, 0xE0, 0xA1, 0x10, 0x80, 11 | 0x00, 0x00, 0x0F, 0x22, 0xD8, 0xB9, 0x80, 0x00, 0x00, 0xC0, 0x0F, 0x32, 0x0D, 0x00, 0x09, 0x00, 12 | 0x00, 0x0F, 0x30, 0x0F, 0x20, 0xC0, 0x0D, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x22, 0xC0, 0x0F, 0x01, 13 | 0x15, 0x60, 0x80, 0x00, 0x00, 0x66, 0xB8, 0x10, 0x00, 0x8E, 0xD0, 0x8E, 0xD8, 0x8E, 0xC0, 0xEA, 14 | 0xC8, 0x80, 0x00, 0x00, 0x08, 0x00, 0x90, 0x90, 0xFF, 0x24, 0x25, 0x08, 0x80, 0x00, 0x00, 15 | ]); 16 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/start.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use application_processor_main; 9 | use arch::x86_64::kernel::{BootInfo, BOOT_INFO}; 10 | use boot_processor_main; 11 | use config::KERNEL_STACK_SIZE; 12 | 13 | #[inline(never)] 14 | #[no_mangle] 15 | #[naked] 16 | pub unsafe extern "C" fn _start(boot_info: &'static mut BootInfo) -> ! { 17 | // initialize stack pointer 18 | asm!("mov $0, %rsp; mov %rsp, %rbp" 19 | :: "r"(boot_info.current_stack_address + KERNEL_STACK_SIZE as u64 - 0x10) 20 | :: "volatile"); 21 | 22 | BOOT_INFO = boot_info as *mut BootInfo; 23 | 24 | if boot_info.cpu_online == 0 { 25 | boot_processor_main(); 26 | } else { 27 | application_processor_main(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/switch.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2018 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | #[inline(never)] 9 | #[naked] 10 | pub extern "C" fn switch(_old_stack: *mut usize, _new_stack: usize) { 11 | // rdi = old_stack => the address to store the old rsp 12 | // rsi = new_stack => stack pointer of the new task 13 | 14 | unsafe { 15 | asm!( 16 | // store context 17 | "pushfq\n\t\ 18 | push %rax\n\t\ 19 | push %rcx\n\t\ 20 | push %rdx\n\t\ 21 | push %rbx\n\t\ 22 | push %rbp\n\t\ 23 | push %rsi\n\t\ 24 | push %rdi\n\t\ 25 | push %r8\n\t\ 26 | push %r9\n\t\ 27 | push %r10\n\t\ 28 | push %r11\n\t\ 29 | push %r12\n\t\ 30 | push %r13\n\t\ 31 | push %r14\n\t\ 32 | push %r15\n\t\ 33 | rdfsbaseq %rax\n\t\ 34 | push %rax\n\t\ 35 | xor %rax, %rax\n\t\ 36 | xor %ecx, %ecx\n\t\ 37 | rdpkru\n\t\ 38 | push %rax\n\t\ 39 | // store the old stack pointer in the dereferenced first parameter\n\t\ 40 | // and load the new stack pointer in the second parameter.\n\t\ 41 | mov %rsp, (%rdi)\n\t\ 42 | mov %rsi, %rsp\n\t\ 43 | // Set task switched flag \n\t\ 44 | mov %cr0, %rax\n\t\ 45 | or $$8, %rax\n\t\ 46 | mov %rax, %cr0\n\t\ 47 | // set stack pointer in TSS \n\t\ 48 | call set_current_kernel_stack \n\t\ 49 | // restore context \n\t\ 50 | pop %rax\n\t\ 51 | xor %ecx, %ecx\n\t\ 52 | xor %edx, %edx\n\t\ 53 | wrpkru\n\t\ 54 | pop %rax\n\t\ 55 | wrfsbaseq %rax\n\t\ 56 | pop %r15\n\t\ 57 | pop %r14\n\t\ 58 | pop %r13\n\t\ 59 | pop %r12\n\t\ 60 | pop %r11\n\t\ 61 | pop %r10\n\t\ 62 | pop %r9\n\t\ 63 | pop %r8\n\t\ 64 | pop %rdi\n\t\ 65 | pop %rsi\n\t\ 66 | pop %rbp\n\t\ 67 | pop %rbx\n\t\ 68 | pop %rdx\n\t\ 69 | pop %rcx\n\t\ 70 | pop %rax\n\t\ 71 | popfq" :::: "volatile" 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/arch/x86_64/kernel/vga.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch::x86_64::mm::paging; 9 | use arch::x86_64::mm::paging::{BasePageSize, PageTableEntryFlags}; 10 | use x86::shared::io::*; 11 | 12 | const CRT_CONTROLLER_ADDRESS_PORT: u16 = 0x3D4; 13 | const CRT_CONTROLLER_DATA_PORT: u16 = 0x3D5; 14 | const CURSOR_START_REGISTER: u8 = 0x0A; 15 | const CURSOR_DISABLE: u8 = 0x20; 16 | 17 | const ATTRIBUTE_BLACK: u8 = 0x00; 18 | const ATTRIBUTE_LIGHTGREY: u8 = 0x07; 19 | const COLS: usize = 80; 20 | const ROWS: usize = 25; 21 | const VGA_BUFFER_ADDRESS: usize = 0xB8000; 22 | 23 | safe_global_var!(static mut VGA_SCREEN: VgaScreen = VgaScreen::new()); 24 | 25 | #[derive(Clone, Copy)] 26 | #[repr(C, packed)] 27 | struct VgaCharacter { 28 | character: u8, 29 | attribute: u8, 30 | } 31 | 32 | impl VgaCharacter { 33 | const fn new(character: u8, attribute: u8) -> Self { 34 | Self { 35 | character: character, 36 | attribute: attribute, 37 | } 38 | } 39 | } 40 | 41 | struct VgaScreen { 42 | buffer: *mut [[VgaCharacter; COLS]; ROWS], 43 | current_col: usize, 44 | current_row: usize, 45 | is_initialized: bool, 46 | } 47 | 48 | impl VgaScreen { 49 | const fn new() -> Self { 50 | Self { 51 | buffer: VGA_BUFFER_ADDRESS as *mut _, 52 | current_col: 0, 53 | current_row: 0, 54 | is_initialized: false, 55 | } 56 | } 57 | 58 | fn init(&mut self) { 59 | // Identity map the VGA buffer. We only need the first page. 60 | let mut flags = PageTableEntryFlags::empty(); 61 | flags.device().writable().execute_disable(); 62 | paging::map::(VGA_BUFFER_ADDRESS, VGA_BUFFER_ADDRESS, 1, flags); 63 | 64 | // Disable the cursor. 65 | unsafe { 66 | outb(CRT_CONTROLLER_ADDRESS_PORT, CURSOR_START_REGISTER); 67 | outb(CRT_CONTROLLER_DATA_PORT, CURSOR_DISABLE); 68 | } 69 | 70 | // Clear the screen. 71 | for r in 0..ROWS { 72 | self.clear_row(r); 73 | } 74 | 75 | // Initialization done! 76 | self.is_initialized = true; 77 | } 78 | 79 | #[inline] 80 | fn clear_row(&mut self, row: usize) { 81 | let buffer_ref; 82 | unsafe { 83 | isolation_start!(); 84 | buffer_ref = &mut *(self.buffer); 85 | isolation_end!(); 86 | } 87 | // Overwrite this row by a bogus character in black. 88 | for c in 0..COLS { 89 | (*buffer_ref)[row][c] = VgaCharacter::new(0, ATTRIBUTE_BLACK); 90 | } 91 | } 92 | 93 | fn write_byte(&mut self, byte: u8) { 94 | if !self.is_initialized { 95 | return; 96 | } 97 | 98 | // Move to the next row if we have a newline character or hit the end of a column. 99 | if byte == b'\n' || self.current_col == COLS { 100 | self.current_col = 0; 101 | self.current_row += 1; 102 | } 103 | 104 | // Check if we have hit the end of the screen rows. 105 | if self.current_row == ROWS { 106 | let buffer_ref; 107 | unsafe { 108 | isolation_start!(); 109 | buffer_ref = &mut *(self.buffer); 110 | isolation_end!(); 111 | } 112 | // Shift all rows up by one line, removing the oldest visible screen row. 113 | for r in 1..ROWS { 114 | for c in 0..COLS { 115 | (*buffer_ref)[r - 1][c] = (*self.buffer)[r][c]; 116 | } 117 | } 118 | 119 | // Clear the last screen row and write to it next time. 120 | self.clear_row(ROWS - 1); 121 | self.current_row = ROWS - 1; 122 | } 123 | 124 | if byte != b'\n' { 125 | let buffer_ref; 126 | unsafe { 127 | isolation_start!(); 128 | buffer_ref = &mut *(self.buffer); 129 | isolation_end!(); 130 | } 131 | // Put our character into the VGA screen buffer and advance the column counter. 132 | (*buffer_ref)[self.current_row][self.current_col] = 133 | VgaCharacter::new(byte, ATTRIBUTE_LIGHTGREY); 134 | self.current_col += 1; 135 | } 136 | } 137 | } 138 | 139 | pub fn init() { 140 | unsafe { VGA_SCREEN.init() }; 141 | } 142 | 143 | pub fn write_byte(byte: u8) { 144 | unsafe { 145 | VGA_SCREEN.write_byte(byte); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/arch/x86_64/mm/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | pub mod paging; 9 | pub mod physicalmem; 10 | pub mod virtualmem; 11 | pub mod mpk; 12 | 13 | pub use self::paging::init_page_tables; 14 | use core::mem; 15 | use core::slice; 16 | 17 | fn paddr_to_slice<'a>(p: multiboot::PAddr, sz: usize) -> Option<&'a [u8]> { 18 | unsafe { 19 | let ptr = mem::transmute(p); 20 | Some(slice::from_raw_parts(ptr, sz)) 21 | } 22 | } 23 | 24 | pub fn init() { 25 | paging::init(); 26 | physicalmem::init(); 27 | virtualmem::init(); 28 | } 29 | -------------------------------------------------------------------------------- /src/arch/x86_64/mm/mpk.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use arch::x86_64::mm::paging; 4 | use arch::x86_64::mm::paging::PageSize; 5 | use arch::x86_64::kernel::processor; 6 | 7 | const EINVAL: i32 = 22; 8 | const ENOSYS: i32 = 38; 9 | 10 | pub enum MpkPerm { 11 | MpkRw, 12 | MpkRo, 13 | MpkNone 14 | } 15 | 16 | #[inline] 17 | fn rdpkru() -> u32 { 18 | 19 | let val: u32; 20 | unsafe { 21 | asm!("xor %ecx, %ecx; 22 | rdpkru; 23 | movl %eax, $0" 24 | : "=r"(val) 25 | : 26 | : "eax", "edx", "ecx" 27 | : "volatile"); 28 | } 29 | val 30 | } 31 | 32 | #[inline] 33 | fn wrpkru(val: u32) { 34 | 35 | unsafe { 36 | asm!("mov $0, %eax; 37 | xor %ecx, %ecx; 38 | xor %edx, %edx; 39 | wrpkru; 40 | lfence" 41 | : 42 | : "r"(val) 43 | : "eax", "ecx", "edx" 44 | : "volatile"); 45 | } 46 | } 47 | 48 | pub fn mpk_swap_pkru(new_pkru: u32) -> u32 { 49 | 50 | if processor::supports_ospke() == true { 51 | return 0; 52 | } 53 | 54 | let old_pkru: u32; 55 | old_pkru = rdpkru(); 56 | wrpkru(new_pkru); 57 | return old_pkru; 58 | } 59 | 60 | fn pkru_set_ro(key: u8, val: &mut u32) -> i32 { 61 | 62 | if key > 15 63 | { 64 | return -EINVAL; 65 | } 66 | 67 | *val &= !(1 << (key * 2)); 68 | *val |= 1 << ((key*2) + 1); 69 | 70 | return 0; 71 | } 72 | 73 | fn pkru_set_rw(key: u8, val: &mut u32) -> i32 { 74 | 75 | if key > 15 76 | { 77 | return -EINVAL; 78 | } 79 | 80 | *val &= !(1 << (key*2)); 81 | *val &= !(1 << ((key*2) + 1)); 82 | 83 | return 0; 84 | } 85 | 86 | fn pkru_set_no_access(key: u8, val: &mut u32) -> i32 { 87 | 88 | if key > 15 89 | { 90 | return -EINVAL; 91 | } 92 | 93 | *val |= 1 << (key * 2); 94 | *val |= 1 << ((key * 2) + 1); 95 | 96 | return 0; 97 | } 98 | 99 | pub fn mpk_mem_set_key(mut addr: usize, mut size: usize, key: u8) -> i32 { 100 | 101 | if processor::supports_ospke() == false { 102 | return -ENOSYS; 103 | } 104 | 105 | if key > 15 106 | { 107 | return -EINVAL; 108 | } 109 | 110 | /* If needed floor addr to the nearest page */ 111 | addr = (addr) & !(S::SIZE-1); 112 | /* If needed ceil [addr + size[ to the nearest page */ 113 | if ((S::SIZE-1)&size) > 0 { 114 | size = (size + S::SIZE) & !(S::SIZE-1); 115 | } 116 | 117 | let mut count :usize = size/S::SIZE; 118 | if size%S::SIZE > 0 119 | { 120 | count = count + 1; 121 | } 122 | 123 | paging::set_pkey_on_page_table_entry::(addr, count, key); 124 | return 0; 125 | } 126 | 127 | pub fn mpk_set_perm(key: u8, perm: MpkPerm) -> i32 { 128 | 129 | if processor::supports_ospke() == false { 130 | return -ENOSYS; 131 | } 132 | 133 | let mut pkru: u32; 134 | pkru = rdpkru(); 135 | 136 | match perm { 137 | MpkPerm::MpkRw => { 138 | pkru_set_rw(key, &mut pkru); 139 | } 140 | 141 | MpkPerm::MpkRo => { 142 | pkru_set_ro(key, &mut pkru); 143 | } 144 | 145 | MpkPerm::MpkNone => { 146 | pkru_set_no_access(key, &mut pkru); 147 | } 148 | } 149 | 150 | wrpkru(pkru); 151 | return 0; 152 | } 153 | 154 | pub fn mpk_clear_pkru() { 155 | 156 | if processor::supports_ospke() == false { 157 | return; 158 | } 159 | 160 | wrpkru(0x0); 161 | } 162 | 163 | /* Return the PKRU value */ 164 | pub fn mpk_get_pkru() -> u32 { 165 | 166 | if processor::supports_ospke() == false { 167 | return 0; 168 | } 169 | 170 | return rdpkru(); 171 | } 172 | 173 | /* Set the pkru value to 'val' */ 174 | pub fn mpk_set_pkru(val: u32) { 175 | 176 | if processor::supports_ospke() == true { 177 | wrpkru(val); 178 | } 179 | } -------------------------------------------------------------------------------- /src/arch/x86_64/mm/physicalmem.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch::x86_64::kernel::{get_limit, get_mbinfo}; 9 | use arch::x86_64::mm::paddr_to_slice; 10 | use arch::x86_64::mm::paging::{BasePageSize, PageSize}; 11 | use collections::Node; 12 | use core::sync::atomic::{AtomicUsize, Ordering}; 13 | use mm; 14 | use mm::freelist::{FreeList, FreeListEntry}; 15 | use multiboot::{MemoryType, Multiboot}; 16 | use synch::spinlock::*; 17 | 18 | safe_global_var!(static PHYSICAL_FREE_LIST: SpinlockIrqSave = SpinlockIrqSave::new(FreeList::new())); 19 | safe_global_var!(static TOTAL_MEMORY: AtomicUsize = AtomicUsize::new(0)); 20 | 21 | fn detect_from_multiboot_info() -> Result<(), ()> { 22 | let mb_info = get_mbinfo(); 23 | if mb_info == 0 { 24 | return Err(()); 25 | } 26 | 27 | let mb = unsafe { Multiboot::new(mb_info as u64, paddr_to_slice).unwrap() }; 28 | let all_regions = mb 29 | .memory_regions() 30 | .expect("Could not find a memory map in the Multiboot information"); 31 | let ram_regions = all_regions.filter(|m| { 32 | m.memory_type() == MemoryType::Available 33 | && m.base_address() + m.length() > mm::kernel_end_address() as u64 34 | }); 35 | let mut found_ram = false; 36 | 37 | for m in ram_regions { 38 | found_ram = true; 39 | 40 | let start_address = if m.base_address() <= mm::kernel_start_address() as u64 { 41 | mm::kernel_end_address() 42 | } else { 43 | m.base_address() as usize 44 | }; 45 | 46 | let entry = Node::new(FreeListEntry { 47 | start: start_address, 48 | end: (m.base_address() + m.length()) as usize, 49 | }); 50 | let _ = TOTAL_MEMORY.fetch_add((m.base_address() + m.length()) as usize, Ordering::SeqCst); 51 | PHYSICAL_FREE_LIST.lock().list.push(entry); 52 | } 53 | 54 | assert!( 55 | found_ram, 56 | "Could not find any available RAM in the Multiboot Memory Map" 57 | ); 58 | 59 | Ok(()) 60 | } 61 | 62 | fn detect_from_limits() -> Result<(), ()> { 63 | let limit = get_limit(); 64 | if limit == 0 { 65 | return Err(()); 66 | } 67 | 68 | let entry = Node::new(FreeListEntry { 69 | start: mm::kernel_end_address(), 70 | end: limit, 71 | }); 72 | TOTAL_MEMORY.store(limit, Ordering::SeqCst); 73 | PHYSICAL_FREE_LIST.lock().list.push(entry); 74 | 75 | Ok(()) 76 | } 77 | 78 | pub fn init() { 79 | detect_from_multiboot_info() 80 | .or_else(|_e| detect_from_limits()) 81 | .unwrap(); 82 | } 83 | 84 | pub fn total_memory_size() -> usize { 85 | TOTAL_MEMORY.load(Ordering::SeqCst) 86 | } 87 | 88 | pub fn allocate(size: usize) -> Result { 89 | assert!(size > 0); 90 | assert!( 91 | size % BasePageSize::SIZE == 0, 92 | "Size {:#X} is not a multiple of {:#X}", 93 | size, 94 | BasePageSize::SIZE 95 | ); 96 | 97 | PHYSICAL_FREE_LIST.lock().allocate(size) 98 | } 99 | 100 | pub fn allocate_aligned(size: usize, alignment: usize) -> Result { 101 | assert!(size > 0); 102 | assert!(alignment > 0); 103 | assert!( 104 | size % alignment == 0, 105 | "Size {:#X} is not a multiple of the given alignment {:#X}", 106 | size, 107 | alignment 108 | ); 109 | assert!( 110 | alignment % BasePageSize::SIZE == 0, 111 | "Alignment {:#X} is not a multiple of {:#X}", 112 | alignment, 113 | BasePageSize::SIZE 114 | ); 115 | 116 | PHYSICAL_FREE_LIST.lock().allocate_aligned(size, alignment) 117 | } 118 | 119 | /// This function must only be called from mm::deallocate! 120 | /// Otherwise, it may fail due to an empty node pool (POOL.maintain() is called in virtualmem::deallocate) 121 | pub fn deallocate(physical_address: usize, size: usize) { 122 | assert!( 123 | physical_address >= mm::kernel_end_address(), 124 | "Physical address {:#X} is not >= KERNEL_END_ADDRESS", 125 | physical_address 126 | ); 127 | assert!(size > 0); 128 | assert!( 129 | size % BasePageSize::SIZE == 0, 130 | "Size {:#X} is not a multiple of {:#X}", 131 | size, 132 | BasePageSize::SIZE 133 | ); 134 | 135 | PHYSICAL_FREE_LIST.lock().deallocate(physical_address, size); 136 | } 137 | 138 | pub fn print_information() { 139 | PHYSICAL_FREE_LIST 140 | .lock() 141 | .print_information(" PHYSICAL MEMORY FREE LIST "); 142 | } 143 | -------------------------------------------------------------------------------- /src/arch/x86_64/mm/virtualmem.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch::x86_64::mm::paging::{BasePageSize, PageSize}; 9 | use collections::Node; 10 | use mm; 11 | use mm::freelist::{FreeList, FreeListEntry}; 12 | use synch::spinlock::*; 13 | 14 | safe_global_var!(static KERNEL_FREE_LIST: SpinlockIrqSave = SpinlockIrqSave::new(FreeList::new())); 15 | 16 | pub fn init() { 17 | let entry = Node::new(FreeListEntry { 18 | start: mm::kernel_end_address(), 19 | end: kernel_heap_end(), 20 | }); 21 | KERNEL_FREE_LIST.lock().list.push(entry); 22 | } 23 | 24 | pub fn allocate(size: usize) -> Result { 25 | assert!(size > 0); 26 | assert!( 27 | size % BasePageSize::SIZE == 0, 28 | "Size {:#X} is not a multiple of {:#X}", 29 | size, 30 | BasePageSize::SIZE 31 | ); 32 | 33 | KERNEL_FREE_LIST.lock().allocate(size) 34 | } 35 | 36 | pub fn allocate_aligned(size: usize, alignment: usize) -> Result { 37 | assert!(size > 0); 38 | assert!(alignment > 0); 39 | assert!( 40 | size % alignment == 0, 41 | "Size {:#X} is not a multiple of the given alignment {:#X}", 42 | size, 43 | alignment 44 | ); 45 | assert!( 46 | alignment % BasePageSize::SIZE == 0, 47 | "Alignment {:#X} is not a multiple of {:#X}", 48 | alignment, 49 | BasePageSize::SIZE 50 | ); 51 | 52 | KERNEL_FREE_LIST.lock().allocate_aligned(size, alignment) 53 | } 54 | 55 | pub fn deallocate(virtual_address: usize, size: usize) { 56 | assert!( 57 | virtual_address >= mm::kernel_end_address(), 58 | "Virtual address {:#X} is not >= KERNEL_END_ADDRESS", 59 | virtual_address 60 | ); 61 | assert!( 62 | virtual_address < kernel_heap_end(), 63 | "Virtual address {:#X} is not < kernel_heap_end()", 64 | virtual_address 65 | ); 66 | assert!( 67 | virtual_address % BasePageSize::SIZE == 0, 68 | "Virtual address {:#X} is not a multiple of {:#X}", 69 | virtual_address, 70 | BasePageSize::SIZE 71 | ); 72 | assert!(size > 0); 73 | assert!( 74 | size % BasePageSize::SIZE == 0, 75 | "Size {:#X} is not a multiple of {:#X}", 76 | size, 77 | BasePageSize::SIZE 78 | ); 79 | 80 | KERNEL_FREE_LIST.lock().deallocate(virtual_address, size); 81 | } 82 | 83 | pub fn reserve(virtual_address: usize, size: usize) { 84 | assert!( 85 | virtual_address >= mm::kernel_end_address(), 86 | "Virtual address {:#X} is not >= KERNEL_END_ADDRESS", 87 | virtual_address 88 | ); 89 | assert!( 90 | virtual_address < kernel_heap_end(), 91 | "Virtual address {:#X} is not < kernel_heap_end()", 92 | virtual_address 93 | ); 94 | assert!( 95 | virtual_address % BasePageSize::SIZE == 0, 96 | "Virtual address {:#X} is not a multiple of {:#X}", 97 | virtual_address, 98 | BasePageSize::SIZE 99 | ); 100 | assert!(size > 0); 101 | assert!( 102 | size % BasePageSize::SIZE == 0, 103 | "Size {:#X} is not a multiple of {:#X}", 104 | size, 105 | BasePageSize::SIZE 106 | ); 107 | 108 | let result = KERNEL_FREE_LIST.lock().reserve(virtual_address, size); 109 | assert!( 110 | result.is_ok(), 111 | "Could not reserve {:#X} bytes of virtual memory at {:#X}", 112 | size, 113 | virtual_address 114 | ); 115 | } 116 | 117 | pub fn print_information() { 118 | KERNEL_FREE_LIST 119 | .lock() 120 | .print_information(" KERNEL VIRTUAL MEMORY FREE LIST "); 121 | } 122 | 123 | /// End of the virtual memory address space reserved for kernel memory. 124 | /// This also marks the start of the virtual memory address space reserved for the task heap. 125 | /// In case of pure rust applications, we don't have a task heap. 126 | #[cfg(not(feature = "newlib"))] 127 | #[inline] 128 | pub const fn kernel_heap_end() -> usize { 129 | 0x8000_0000_0000 130 | } 131 | 132 | #[cfg(feature = "newlib")] 133 | #[inline] 134 | pub const fn kernel_heap_end() -> usize { 135 | 0x1_0000_0000 136 | } 137 | -------------------------------------------------------------------------------- /src/arch/x86_64/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | pub mod kernel; 9 | pub mod mm; 10 | -------------------------------------------------------------------------------- /src/collections/doublylinkedlist.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | //! Implementation of a Doubly Linked List in Safe Rust using reference-counting. 9 | //! 10 | //! In contrast to collections provided by Rust, this implementation can insert 11 | //! and remove entries at a specific position in O(1) given an iterator. 12 | 13 | #![allow(dead_code)] 14 | 15 | use alloc::rc::Rc; 16 | use core::cell::RefCell; 17 | 18 | pub struct DoublyLinkedList { 19 | head: Option>>>, 20 | tail: Option>>>, 21 | } 22 | 23 | pub struct Node { 24 | pub value: T, 25 | prev: Option>>>, 26 | next: Option>>>, 27 | } 28 | 29 | impl Node { 30 | pub fn new(value: T) -> Rc> { 31 | Rc::new(RefCell::new(Self { 32 | value: value, 33 | prev: None, 34 | next: None, 35 | })) 36 | } 37 | } 38 | 39 | impl DoublyLinkedList { 40 | pub const fn new() -> Self { 41 | Self { 42 | head: None, 43 | tail: None, 44 | } 45 | } 46 | 47 | pub fn head(&self) -> Option>>> { 48 | self.head.as_ref().cloned() 49 | } 50 | 51 | pub fn tail(&self) -> Option>>> { 52 | self.tail.as_ref().cloned() 53 | } 54 | 55 | pub fn push(&mut self, new_node: Rc>>) { 56 | { 57 | let mut new_node_borrowed = new_node.borrow_mut(); 58 | 59 | // We expect a node that is currently not mounted to any list. 60 | assert!(new_node_borrowed.prev.is_none() && new_node_borrowed.next.is_none()); 61 | 62 | // Check if we already have any nodes in the list. 63 | match self.tail.take() { 64 | Some(tail) => { 65 | // We become the next node of the old list tail and the old list tail becomes our previous node. 66 | tail.borrow_mut().next = Some(new_node.clone()); 67 | new_node_borrowed.prev = Some(tail); 68 | } 69 | None => { 70 | // No nodes yet, so we become the new list head. 71 | self.head = Some(new_node.clone()); 72 | } 73 | } 74 | } 75 | 76 | // In any case, we become the new list tail. 77 | self.tail = Some(new_node); 78 | } 79 | 80 | pub fn insert_before(&mut self, new_node: Rc>>, node: Rc>>) { 81 | let mut node_borrowed = node.borrow_mut(); 82 | 83 | { 84 | let mut new_node_borrowed = new_node.borrow_mut(); 85 | 86 | // We expect a node that is currently not mounted to any list. 87 | assert!(new_node_borrowed.prev.is_none() && new_node_borrowed.next.is_none()); 88 | 89 | // Check if the given node is the first one in the list. 90 | match node_borrowed.prev.take() { 91 | Some(prev_node) => { 92 | // It is not, so its previous node now becomes our previous node. 93 | prev_node.borrow_mut().next = Some(new_node.clone()); 94 | new_node_borrowed.prev = Some(prev_node); 95 | } 96 | None => { 97 | // It is, so we become the new list head. 98 | self.head = Some(new_node.clone()); 99 | } 100 | } 101 | 102 | // The given node becomes our next node. 103 | new_node_borrowed.next = Some(node.clone()); 104 | } 105 | 106 | // We become the previous node of the given node. 107 | node_borrowed.prev = Some(new_node); 108 | } 109 | 110 | pub fn insert_after(&mut self, new_node: Rc>>, node: Rc>>) { 111 | let mut node_borrowed = node.borrow_mut(); 112 | 113 | { 114 | let mut new_node_borrowed = new_node.borrow_mut(); 115 | 116 | // We expect a node that is currently not mounted to any list. 117 | assert!(new_node_borrowed.prev.is_none() && new_node_borrowed.next.is_none()); 118 | 119 | // Check if the given node is the last one in the list. 120 | match node_borrowed.next.take() { 121 | Some(next_node) => { 122 | // It is not, so its next node now becomes our next node. 123 | next_node.borrow_mut().prev = Some(new_node.clone()); 124 | new_node_borrowed.next = Some(next_node); 125 | } 126 | None => { 127 | // It is, so we become the new list tail. 128 | self.tail = Some(new_node.clone()); 129 | } 130 | } 131 | 132 | // The given node becomes our previous node. 133 | new_node_borrowed.prev = Some(node.clone()); 134 | } 135 | 136 | // We become the next node of the given node. 137 | node_borrowed.next = Some(new_node); 138 | } 139 | 140 | pub fn remove(&mut self, node: Rc>>) { 141 | // Unmount the previous and next nodes of the node to remove. 142 | let (prev, next) = { 143 | let mut borrowed = node.borrow_mut(); 144 | (borrowed.prev.take(), borrowed.next.take()) 145 | }; 146 | 147 | // Clone the next node, so we can still check it after remounting. 148 | let next_clone = next.clone(); 149 | 150 | // Check the previous node. 151 | // If we have one, remount the next node to that previous one, skipping our node to remove. 152 | // If not, the next node becomes the new list head. 153 | match prev { 154 | Some(ref prev_node) => prev_node.borrow_mut().next = next, 155 | None => self.head = next, 156 | }; 157 | 158 | // Check the cloned next node. 159 | // If we have one, remount the previous node to that next one, skipping our node to remove. 160 | // If not, the previous node becomes the new list tail. 161 | match next_clone { 162 | Some(ref next_node) => next_node.borrow_mut().prev = prev, 163 | None => self.tail = prev, 164 | }; 165 | } 166 | 167 | pub fn iter(&self) -> Iter { 168 | Iter:: { 169 | current: self.head.as_ref().cloned(), 170 | } 171 | } 172 | } 173 | 174 | impl Default for DoublyLinkedList { 175 | fn default() -> Self { 176 | Self { 177 | head: None, 178 | tail: None, 179 | } 180 | } 181 | } 182 | 183 | pub struct Iter { 184 | current: Option>>>, 185 | } 186 | 187 | impl Iterator for Iter { 188 | type Item = Rc>>; 189 | 190 | fn next(&mut self) -> Option { 191 | // If we have a current node, replace it by a clone of the next node. 192 | // Then we can still return the (previously) current one. 193 | self.current.take().map(|node| { 194 | self.current = node.borrow().next.clone(); 195 | node 196 | }) 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/collections/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | mod doublylinkedlist; 9 | 10 | pub use self::doublylinkedlist::*; 11 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | pub const KERNEL_STACK_SIZE: usize = 32_768; 3 | 4 | #[allow(dead_code)] 5 | pub const DEFAULT_STACK_SIZE: usize = 262_144; -------------------------------------------------------------------------------- /src/console.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | use arch; 10 | use core::fmt; 11 | use synch::spinlock::SpinlockIrqSave; 12 | 13 | pub struct Console; 14 | 15 | /// A collection of methods that are required to format 16 | /// a message to HermitCore's console. 17 | impl fmt::Write for Console { 18 | /// Print a single character. 19 | fn write_char(&mut self, c: char) -> fmt::Result { 20 | arch::output_message_byte(c as u8); 21 | Ok(()) 22 | } 23 | 24 | /// Print a string of characters. 25 | fn write_str(&mut self, s: &str) -> fmt::Result { 26 | for character in s.chars() { 27 | self.write_char(character).unwrap(); 28 | } 29 | Ok(()) 30 | } 31 | } 32 | 33 | safe_global_var!(pub static CONSOLE: SpinlockIrqSave = SpinlockIrqSave::new(Console)); 34 | 35 | #[test] 36 | fn test_console() { 37 | println!("HelloWorld"); 38 | } 39 | -------------------------------------------------------------------------------- /src/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | #[cfg(not(feature = "newlib"))] 9 | pub mod net; 10 | -------------------------------------------------------------------------------- /src/drivers/net/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | //pub mod rtl8139; 9 | pub mod uhyve; 10 | 11 | use alloc::boxed::Box; 12 | use core::ffi::c_void; 13 | use synch::spinlock::SpinlockIrqSave; 14 | 15 | static NIC: SpinlockIrqSave>> = SpinlockIrqSave::new(None); 16 | 17 | pub fn init() -> Result<(), ()> { 18 | let nic = uhyve::init()?; 19 | *NIC.lock() = Some(nic); 20 | 21 | info!("Network initialized!"); 22 | 23 | Ok(()) 24 | } 25 | 26 | pub trait NetworkInterface { 27 | /// check if the driver in polling mode 28 | fn is_polling(&self) -> bool; 29 | /// set driver in polling/non-polling mode 30 | fn set_polling(&mut self, mode: bool); 31 | /// initialize network and returns basic network configuration 32 | fn init( 33 | &mut self, 34 | sem: *const c_void, 35 | ip: &mut [u8; 4], 36 | gateway: &mut [u8; 4], 37 | mac: &mut [u8; 18], 38 | ) -> i32; 39 | /// read packet from network interface 40 | fn read(&mut self, buf: usize, len: usize) -> usize; 41 | /// writr packet to the network interface 42 | fn write(&self, buf: usize, len: usize) -> usize; 43 | } 44 | 45 | #[no_mangle] 46 | pub extern "C" fn sys_network_init( 47 | sem: *const c_void, 48 | ip: &mut [u8; 4], 49 | gateway: &mut [u8; 4], 50 | mac: &mut [u8; 18], 51 | ) -> i32 { 52 | match &mut *NIC.lock() { 53 | Some(nic) => nic.init(sem, ip, gateway, mac), 54 | None => -1, 55 | } 56 | } 57 | 58 | #[no_mangle] 59 | pub extern "C" fn sys_is_polling() -> bool { 60 | match &*NIC.lock() { 61 | Some(nic) => nic.is_polling(), 62 | None => false, 63 | } 64 | } 65 | 66 | #[no_mangle] 67 | pub extern "C" fn sys_set_polling(mode: bool) { 68 | match &mut *NIC.lock() { 69 | Some(nic) => nic.set_polling(mode), 70 | None => {} 71 | } 72 | } 73 | 74 | #[no_mangle] 75 | pub extern "C" fn sys_netread(buf: usize, len: usize) -> usize { 76 | match &mut *NIC.lock() { 77 | Some(nic) => nic.read(buf, len), 78 | None => 0, 79 | } 80 | } 81 | 82 | #[no_mangle] 83 | pub extern "C" fn sys_netwrite(buf: usize, len: usize) -> usize { 84 | match &*NIC.lock() { 85 | Some(nic) => nic.write(buf, len), 86 | None => 0, 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/drivers/net/uhyve.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use alloc::boxed::Box; 9 | use arch::irq; 10 | use core::ffi::c_void; 11 | use core::ptr::read_volatile; 12 | use core::sync::atomic::{AtomicBool, Ordering}; 13 | use core::{ptr, str}; 14 | use drivers::net::NetworkInterface; 15 | use synch; 16 | use syscalls::sys_sem_post; 17 | 18 | #[cfg(target_arch = "x86_64")] 19 | use arch::x86_64::kernel::apic; 20 | #[cfg(target_arch = "x86_64")] 21 | use arch::x86_64::kernel::irq::*; 22 | #[cfg(target_arch = "x86_64")] 23 | use arch::x86_64::kernel::percore::core_scheduler; 24 | #[cfg(target_arch = "x86_64")] 25 | use arch::x86_64::kernel::{get_gateway, get_ip}; 26 | #[cfg(target_arch = "x86_64")] 27 | use arch::x86_64::mm::paging::virt_to_phys; 28 | #[cfg(target_arch = "x86_64")] 29 | use x86::io::*; 30 | 31 | const UHYVE_IRQ_NET: u32 = 11; 32 | const UHYVE_PORT_NETINFO: u16 = 0x600; 33 | const UHYVE_PORT_NETWRITE: u16 = 0x640; 34 | const UHYVE_PORT_NETREAD: u16 = 0x680; 35 | //const UHYVE_PORT_NETSTAT: u16 = 0x700; 36 | 37 | /// Data type to determine the mac address 38 | #[derive(Debug, Default)] 39 | #[repr(C)] 40 | struct UhyveNetinfo { 41 | /// mac address 42 | pub mac: [u8; 18], 43 | } 44 | 45 | pub struct UhyveNetwork { 46 | /// Semaphore to block IP thread 47 | sem: *const c_void, 48 | /// mac address 49 | mac: [u8; 18], 50 | /// is NIC in polling mode? 51 | polling: AtomicBool, 52 | } 53 | 54 | impl UhyveNetwork { 55 | pub const fn new(mac: &[u8; 18]) -> Self { 56 | UhyveNetwork { 57 | sem: ptr::null(), 58 | mac: *mac, 59 | polling: AtomicBool::new(false), 60 | } 61 | } 62 | } 63 | 64 | impl NetworkInterface for UhyveNetwork { 65 | fn is_polling(&self) -> bool { 66 | self.polling.load(Ordering::SeqCst) 67 | } 68 | 69 | fn set_polling(&mut self, mode: bool) { 70 | self.polling.store(mode, Ordering::SeqCst); 71 | if mode && !self.sem.is_null() { 72 | sys_sem_post(self.sem as *const synch::semaphore::Semaphore); 73 | } 74 | } 75 | 76 | fn init( 77 | &mut self, 78 | sem: *const c_void, 79 | ip: &mut [u8; 4], 80 | gateway: &mut [u8; 4], 81 | mac: &mut [u8; 18], 82 | ) -> i32 { 83 | let mac_str = str::from_utf8(&self.mac).unwrap(); 84 | info!("MAC address: {}", mac_str); 85 | 86 | let myip = get_ip(); 87 | let mygw = get_gateway(); 88 | 89 | self.sem = sem; 90 | mac[0..].copy_from_slice(mac_str.as_bytes()); 91 | ip.copy_from_slice(&myip); 92 | gateway.copy_from_slice(&mygw); 93 | 94 | 0 95 | } 96 | 97 | fn write(&self, buf: usize, len: usize) -> usize { 98 | let uhyve_write = UhyveWrite::new(virt_to_phys(buf), len); 99 | 100 | let irq = irq::nested_disable(); 101 | unsafe { 102 | outl( 103 | UHYVE_PORT_NETWRITE, 104 | virt_to_phys(&uhyve_write as *const _ as usize) as u32, 105 | ); 106 | } 107 | irq::nested_enable(irq); 108 | 109 | let ret = uhyve_write.ret(); 110 | if ret != 0 { 111 | error!("Unable to send message: {}", ret); 112 | } 113 | 114 | uhyve_write.len() 115 | } 116 | 117 | fn read(&mut self, buf: usize, len: usize) -> usize { 118 | let data = UhyveRead::new(virt_to_phys(buf), len); 119 | let irq = irq::nested_disable(); 120 | unsafe { 121 | outl( 122 | UHYVE_PORT_NETREAD, 123 | virt_to_phys(&data as *const _ as usize) as u32, 124 | ); 125 | } 126 | 127 | if data.ret() == 0 { 128 | trace!("resize message to {} bytes", data.len()); 129 | 130 | irq::nested_enable(irq); 131 | data.len() 132 | } else { 133 | self.set_polling(false); 134 | irq::nested_enable(irq); 135 | 0 136 | } 137 | } 138 | } 139 | 140 | /// Datatype to receive packets from uhyve 141 | #[derive(Debug)] 142 | #[repr(C)] 143 | struct UhyveRead { 144 | /// address to the received data 145 | pub data: usize, 146 | /// length of the buffer 147 | pub len: usize, 148 | /// amount of received data (in bytes) 149 | pub ret: i32, 150 | } 151 | 152 | impl UhyveRead { 153 | pub fn new(data: usize, len: usize) -> Self { 154 | UhyveRead { 155 | data: data, 156 | len: len, 157 | ret: 0, 158 | } 159 | } 160 | 161 | pub fn len(&self) -> usize { 162 | unsafe { read_volatile(&self.len) } 163 | } 164 | 165 | pub fn ret(&self) -> i32 { 166 | unsafe { read_volatile(&self.ret) } 167 | } 168 | } 169 | 170 | /// Datatype to forward packets to uhyve 171 | #[derive(Debug)] 172 | #[repr(C)] 173 | struct UhyveWrite { 174 | /// address to the data 175 | pub data: usize, 176 | /// length of the data 177 | pub len: usize, 178 | /// return value, transfered bytes 179 | pub ret: i32, 180 | } 181 | 182 | impl UhyveWrite { 183 | pub fn new(data: usize, len: usize) -> Self { 184 | UhyveWrite { 185 | data: data, 186 | len: len, 187 | ret: 0, 188 | } 189 | } 190 | 191 | pub fn ret(&self) -> i32 { 192 | unsafe { read_volatile(&self.ret) } 193 | } 194 | 195 | pub fn len(&self) -> usize { 196 | unsafe { read_volatile(&self.len) } 197 | } 198 | } 199 | 200 | pub fn init() -> Result, ()> { 201 | // does uhyve configure the network interface? 202 | let ip = get_ip(); 203 | if ip[0] == 0xff && ip[1] == 0xff && ip[2] == 0xff && ip[3] == 0xff { 204 | return Err(()); 205 | } 206 | 207 | debug!("Initialize uhyve network interface!"); 208 | 209 | irq::disable(); 210 | 211 | let nic = { 212 | let info: UhyveNetinfo = UhyveNetinfo::default(); 213 | 214 | unsafe { 215 | outl( 216 | UHYVE_PORT_NETINFO, 217 | virt_to_phys(&info as *const _ as usize) as u32, 218 | ); 219 | } 220 | 221 | Box::new(UhyveNetwork::new(&info.mac)) 222 | }; 223 | 224 | // Install interrupt handler 225 | irq_install_handler(UHYVE_IRQ_NET, uhyve_irqhandler as usize); 226 | 227 | irq::enable(); 228 | 229 | Ok(nic) 230 | } 231 | 232 | #[cfg(target_arch = "x86_64")] 233 | extern "x86-interrupt" fn uhyve_irqhandler(_stack_frame: &mut ExceptionStackFrame) { 234 | debug!("Receive network interrupt from uhyve"); 235 | crate::drivers::net::sys_set_polling(true); 236 | apic::eoi(); 237 | core_scheduler().scheduler(); 238 | } 239 | -------------------------------------------------------------------------------- /src/environment.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | //! Determining and providing information about the environment (unikernel 9 | //! vs. multi-kernel, hypervisor, etc.) as well as central parsing of the 10 | //! command-line parameters. 11 | 12 | #[cfg(target_arch = "x86_64")] 13 | pub use arch::x86_64::kernel::{ 14 | get_base_address, get_cmdline, get_cmdsize, get_image_size, get_tls_filesz, get_tls_memsz, 15 | get_tls_start, is_single_kernel, is_uhyve, 16 | }; 17 | 18 | #[cfg(target_arch = "aarch64")] 19 | pub use arch::aarch64::kernel::{ 20 | get_base_address, get_cmdline, get_cmdsize, get_image_size, is_single_kernel, is_uhyve, 21 | }; 22 | 23 | use core::slice::from_raw_parts; 24 | use core::str::from_utf8_unchecked; 25 | use mm; 26 | 27 | safe_global_var!(static mut COMMAND_LINE_CPU_FREQUENCY: u16 = 0); 28 | safe_global_var!(static mut IS_PROXY: bool = false); 29 | 30 | fn parse_command_line() { 31 | let cmdsize = get_cmdsize(); 32 | if cmdsize == 0 { 33 | return; 34 | } 35 | 36 | // Convert the command-line into a Rust string slice. 37 | let cmdline = get_cmdline() as *const u8; 38 | let slice; 39 | let cmdline_str; 40 | unsafe { 41 | slice = isolate_function_strong!(from_raw_parts(cmdline, cmdsize)); 42 | cmdline_str = isolate_function_strong!(from_utf8_unchecked(slice)); 43 | } 44 | 45 | // Check for the -freq option. 46 | if let Some(freq_index) = cmdline_str.find("-freq") { 47 | let cmdline_freq_str = cmdline_str.split_at(freq_index + "-freq".len()).1; 48 | let mhz_str = cmdline_freq_str 49 | .split(' ') 50 | .next() 51 | .expect("Invalid -freq command line"); 52 | unsafe { 53 | COMMAND_LINE_CPU_FREQUENCY = mhz_str 54 | .parse() 55 | .expect("Could not parse -freq command line as number"); 56 | } 57 | } 58 | 59 | // Check for the -proxy option. 60 | unsafe { IS_PROXY = cmdline_str.find("-proxy").is_some(); } 61 | } 62 | 63 | pub fn init() { 64 | parse_command_line(); 65 | 66 | if is_uhyve() || is_single_kernel() { 67 | // We are running under uhyve or baremetal, which implies unikernel mode and no communication with "proxy". 68 | unsafe { IS_PROXY = false; } 69 | } else { 70 | // We are running side-by-side to Linux, which implies communication with "proxy". 71 | unsafe { IS_PROXY = true; } 72 | } 73 | } 74 | 75 | /// CPU Frequency in MHz if given through the -freq command-line parameter, otherwise zero. 76 | pub fn get_command_line_cpu_frequency() -> u16 { 77 | unsafe { COMMAND_LINE_CPU_FREQUENCY } 78 | } 79 | 80 | /// Whether HermitCore shall communicate with the "proxy" application over a network interface. 81 | /// Only valid after calling init()! 82 | pub fn is_proxy() -> bool { 83 | unsafe { IS_PROXY } 84 | } 85 | -------------------------------------------------------------------------------- /src/kernel_message_buffer.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | //! Kernel Message Buffer for Multi-Kernel mode. 9 | //! Can be read from the Linux side as no serial port is available. 10 | 11 | use core::intrinsics::volatile_store; 12 | use core::sync::atomic::{AtomicUsize, Ordering}; 13 | use mm; 14 | 15 | const KMSG_SIZE: usize = 0x1000; 16 | 17 | #[repr(align(64))] 18 | #[repr(C)] 19 | struct KmsgSection { 20 | buffer: [u8; KMSG_SIZE + 1], 21 | } 22 | 23 | unsafe_global_var!( static mut KMSG: KmsgSection = KmsgSection { 24 | buffer: [0; KMSG_SIZE + 1], 25 | }); 26 | 27 | safe_global_var!(static BUFFER_INDEX: AtomicUsize = AtomicUsize::new(0)); 28 | 29 | unsafe fn write_byte(buffer: *mut T, byte: T) { 30 | volatile_store(buffer, byte); 31 | } 32 | 33 | pub fn kmsg_write_byte(byte: u8) { 34 | let index = BUFFER_INDEX.fetch_add(1, Ordering::SeqCst); 35 | unsafe { 36 | isolate_function_weak!(write_byte(&mut KMSG.buffer[index % KMSG_SIZE], byte)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/logging.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2019 Stefan Lankes, RWTH Aachen University 2 | // 2017 Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | use log::{set_logger, set_max_level, LevelFilter, Metadata, Record}; 10 | 11 | /// Data structure to filter kernel messages 12 | struct KernelLogger; 13 | 14 | impl log::Log for KernelLogger { 15 | fn enabled(&self, _: &Metadata) -> bool { 16 | true 17 | } 18 | 19 | fn flush(&self) { 20 | // nothing to do 21 | } 22 | 23 | fn log(&self, record: &Record) { 24 | if self.enabled(record.metadata()) { 25 | println!( 26 | "[{}][{}] {}", 27 | crate::arch::percore::core_id(), 28 | record.level(), 29 | record.args() 30 | ); 31 | } 32 | } 33 | } 34 | 35 | pub fn init() { 36 | set_logger(&KernelLogger).expect("Can't initialize logger"); 37 | set_max_level(LevelFilter::Info); 38 | } 39 | 40 | macro_rules! infoheader { 41 | // This should work on paper, but it's currently not supported :( 42 | // Refer to https://github.com/rust-lang/rust/issues/46569 43 | /*($($arg:tt)+) => ({ 44 | info!(""); 45 | info!("{:=^70}", format_args!($($arg)+)); 46 | });*/ 47 | ($str:expr) => {{ 48 | info!(""); 49 | info!("{:=^70}", $str); 50 | }}; 51 | } 52 | 53 | macro_rules! infoentry { 54 | ($str:expr, $rhs:expr) => (infoentry!($str, "{}", $rhs)); 55 | ($str:expr, $($arg:tt)+) => (info!("{:25}{}", concat!($str, ":"), format_args!($($arg)+))); 56 | } 57 | 58 | macro_rules! infofooter { 59 | () => {{ 60 | info!("{:=^70}", '='); 61 | info!(""); 62 | }}; 63 | } 64 | -------------------------------------------------------------------------------- /src/runtime_glue.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // 2018 Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | //! Minor functions that Rust really expects to be defined by the compiler, 10 | //! but which we need to provide manually because we're on bare metal. 11 | 12 | use alloc::alloc::Layout; 13 | use arch; 14 | use core::panic::PanicInfo; 15 | 16 | // see https://users.rust-lang.org/t/psa-breaking-change-panic-fmt-language-item-removed-in-favor-of-panic-implementation/17875 17 | #[linkage = "weak"] 18 | #[panic_handler] 19 | fn panic(info: &PanicInfo) -> ! { 20 | print!("[{}][!!!PANIC!!!] ", arch::percore::core_id()); 21 | 22 | if let Some(location) = info.location() { 23 | print!("{}:{}: ", location.file(), location.line()); 24 | } 25 | 26 | if let Some(message) = info.message() { 27 | print!("{}", message); 28 | } 29 | 30 | println!(""); 31 | 32 | loop { 33 | arch::processor::halt(); 34 | } 35 | } 36 | 37 | #[linkage = "weak"] 38 | #[alloc_error_handler] 39 | fn rust_oom(layout: Layout) -> ! { 40 | println!( 41 | "[{}][!!!OOM!!!] Memory allocation of {} bytes failed", 42 | arch::percore::core_id(), 43 | layout.size() 44 | ); 45 | loop { 46 | arch::processor::halt(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/synch/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // 2018 Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | //! Synchronization primitives 10 | 11 | pub mod recmutex; 12 | pub mod semaphore; 13 | pub mod spinlock; 14 | -------------------------------------------------------------------------------- /src/synch/recmutex.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch::percore::*; 9 | use scheduler; 10 | use scheduler::task::{PriorityTaskQueue, TaskId}; 11 | use synch::spinlock::Spinlock; 12 | 13 | struct RecursiveMutexState { 14 | current_tid: Option, 15 | count: usize, 16 | queue: PriorityTaskQueue, 17 | } 18 | 19 | pub struct RecursiveMutex { 20 | state: Spinlock, 21 | } 22 | 23 | impl RecursiveMutex { 24 | pub const fn new() -> Self { 25 | Self { 26 | state: Spinlock::new(RecursiveMutexState { 27 | current_tid: None, 28 | count: 0, 29 | queue: PriorityTaskQueue::new(), 30 | }), 31 | } 32 | } 33 | 34 | pub fn acquire(&self) { 35 | // Get information about the current task. 36 | let core_scheduler = core_scheduler(); 37 | let tid = core_scheduler.current_task.borrow().id; 38 | 39 | loop { 40 | { 41 | let mut locked_state = self.state.lock(); 42 | 43 | // Is the mutex currently acquired? 44 | if let Some(current_tid) = locked_state.current_tid { 45 | // Has it been acquired by the same task? 46 | if current_tid == tid { 47 | // Yes, so just increment the counter (recursive mutex behavior). 48 | locked_state.count += 1; 49 | return; 50 | } 51 | } else { 52 | // The mutex is currently not acquired, so we become its new owner. 53 | locked_state.current_tid = Some(tid); 54 | locked_state.count = 1; 55 | return; 56 | } 57 | 58 | // The mutex is currently acquired by another task. 59 | // Block the current task and add it to the wakeup queue. 60 | core_scheduler 61 | .blocked_tasks 62 | .lock() 63 | .add(core_scheduler.current_task.clone(), None); 64 | locked_state.queue.push(core_scheduler.current_task.clone()); 65 | } 66 | 67 | // Switch to the next task. 68 | core_scheduler.reschedule(); 69 | } 70 | } 71 | 72 | pub fn release(&self) { 73 | let mut locked_state = self.state.lock(); 74 | 75 | // We could do a sanity check here whether the RecursiveMutex is actually held by the current task. 76 | // But let's just trust our code using this function for the sake of simplicity and performance. 77 | 78 | // Decrement the counter (recursive mutex behavior). 79 | locked_state.count -= 1; 80 | if locked_state.count == 0 { 81 | // Release the entire recursive mutex. 82 | locked_state.current_tid = None; 83 | 84 | // Wake up any task that has been waiting for this mutex. 85 | if let Some(task) = locked_state.queue.pop() { 86 | let core_scheduler = scheduler::get_scheduler(task.borrow().core_id); 87 | core_scheduler.blocked_tasks.lock().custom_wakeup(task); 88 | } 89 | } 90 | } 91 | } 92 | 93 | // Same unsafe impls as `RecursiveMutex` 94 | unsafe impl Sync for RecursiveMutex {} 95 | unsafe impl Send for RecursiveMutex {} 96 | -------------------------------------------------------------------------------- /src/synch/semaphore.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Stefan Lankes, RWTH Aachen University 2 | // 2018 Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | use arch::percore::*; 10 | use scheduler; 11 | use scheduler::task::{PriorityTaskQueue, WakeupReason}; 12 | use synch::spinlock::SpinlockIrqSave; 13 | 14 | struct SemaphoreState { 15 | /// Resource available count 16 | count: isize, 17 | /// Priority queue of waiting tasks 18 | queue: PriorityTaskQueue, 19 | } 20 | 21 | /// A counting, blocking, semaphore. 22 | /// 23 | /// Semaphores are a form of atomic counter where access is only granted if the 24 | /// counter is a positive value. Each acquisition will block the calling thread 25 | /// until the counter is positive, and each release will increment the counter 26 | /// and unblock any threads if necessary. 27 | /// 28 | /// # Examples 29 | /// 30 | /// ``` 31 | /// 32 | /// // Create a semaphore that represents 5 resources 33 | /// let sem = Semaphore::new(5); 34 | /// 35 | /// // Acquire one of the resources 36 | /// sem.acquire(); 37 | /// 38 | /// // Acquire one of the resources for a limited period of time 39 | /// { 40 | /// let _guard = sem.access(); 41 | /// // ... 42 | /// } // resources is released here 43 | /// 44 | /// // Release our initially acquired resource 45 | /// sem.release(); 46 | /// 47 | /// Interface is derived from https://doc.rust-lang.org/1.7.0/src/std/sync/semaphore.rs.html 48 | /// ``` 49 | pub struct Semaphore { 50 | state: SpinlockIrqSave, 51 | } 52 | 53 | // Same unsafe impls as `Semaphore` 54 | unsafe impl Sync for Semaphore {} 55 | unsafe impl Send for Semaphore {} 56 | 57 | impl Semaphore { 58 | /// Creates a new semaphore with the initial count specified. 59 | /// 60 | /// The count specified can be thought of as a number of resources, and a 61 | /// call to `acquire` or `access` will block until at least one resource is 62 | /// available. It is valid to initialize a semaphore with a negative count. 63 | pub const fn new(count: isize) -> Self { 64 | Self { 65 | state: SpinlockIrqSave::new(SemaphoreState { 66 | count: count, 67 | queue: PriorityTaskQueue::new(), 68 | }), 69 | } 70 | } 71 | 72 | /// Acquires a resource of this semaphore, blocking the current thread until 73 | /// it can do so or until the wakeup time has elapsed. 74 | /// 75 | /// This method will block until the internal count of the semaphore is at 76 | /// least 1. 77 | pub fn acquire(&self, wakeup_time: Option) -> bool { 78 | // Reset last_wakeup_reason. 79 | let core_scheduler = core_scheduler(); 80 | core_scheduler.current_task.borrow_mut().last_wakeup_reason = WakeupReason::Custom; 81 | 82 | // Loop until we have acquired the semaphore. 83 | loop { 84 | { 85 | let mut locked_state = self.state.lock(); 86 | 87 | if locked_state.count > 0 { 88 | // Successfully acquired the semaphore. 89 | locked_state.count -= 1; 90 | return true; 91 | } else if core_scheduler.current_task.borrow().last_wakeup_reason 92 | == WakeupReason::Timer 93 | { 94 | // We could not acquire the semaphore and we were woken up because the wakeup time has elapsed. 95 | // Don't try again and return the failure status. 96 | locked_state 97 | .queue 98 | .remove(core_scheduler.current_task.clone()); 99 | return false; 100 | } 101 | 102 | // We couldn't acquire the semaphore. 103 | // Block the current task and add it to the wakeup queue. 104 | core_scheduler 105 | .blocked_tasks 106 | .lock() 107 | .add(core_scheduler.current_task.clone(), wakeup_time); 108 | locked_state.queue.push(core_scheduler.current_task.clone()); 109 | } 110 | 111 | // Switch to the next task. 112 | core_scheduler.reschedule(); 113 | } 114 | } 115 | 116 | pub fn try_acquire(&self) -> bool { 117 | let mut locked_state = self.state.lock(); 118 | 119 | if locked_state.count > 0 { 120 | locked_state.count -= 1; 121 | true 122 | } else { 123 | false 124 | } 125 | } 126 | 127 | /// Release a resource from this semaphore. 128 | /// 129 | /// This will increment the number of resources in this semaphore by 1 and 130 | /// will notify any pending waiters in `acquire` or `access` if necessary. 131 | pub fn release(&self) { 132 | let mut locked_state = self.state.lock(); 133 | locked_state.count += 1; 134 | 135 | // Wake up any task that has been waiting for this semaphore. 136 | if let Some(task) = locked_state.queue.pop() { 137 | let core_scheduler = scheduler::get_scheduler(task.borrow().core_id); 138 | core_scheduler.blocked_tasks.lock().custom_wakeup(task); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/syscalls/condvar.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Stefan Lankes, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use alloc::boxed::Box; 9 | use arch::percore::*; 10 | use core::mem; 11 | use scheduler; 12 | use scheduler::task::PriorityTaskQueue; 13 | use mm; 14 | 15 | struct CondQueue { 16 | queue: PriorityTaskQueue, 17 | id: usize, 18 | } 19 | 20 | impl CondQueue { 21 | pub fn new(id: usize) -> Self { 22 | CondQueue { 23 | queue: PriorityTaskQueue::new(), 24 | id: id, 25 | } 26 | } 27 | } 28 | 29 | impl Drop for CondQueue { 30 | fn drop(&mut self) { 31 | debug!("Drop queue for condition variable with id 0x{:x}", self.id); 32 | } 33 | } 34 | 35 | #[no_mangle] 36 | fn __sys_destroy_queue(ptr: usize) -> i32 { 37 | let id = ptr as *mut usize; 38 | if id.is_null() { 39 | debug!("sys_wait: ivalid address to condition variable"); 40 | return -1; 41 | } 42 | 43 | let temp_id; 44 | unsafe { 45 | isolation_start!(); 46 | temp_id = *id; 47 | isolation_end!(); 48 | } 49 | if temp_id != 0 { 50 | let cond = unsafe { isolate_function_strong!(Box::from_raw(temp_id as *mut CondQueue)) }; 51 | mem::drop(cond); 52 | 53 | // reset id 54 | unsafe { 55 | isolation_start!(); 56 | *id = 0; 57 | isolation_end!(); 58 | } 59 | } 60 | 0 61 | } 62 | 63 | #[no_mangle] 64 | pub unsafe fn sys_destroy_queue(ptr: usize) -> i32 { 65 | let ret = kernel_function!(__sys_destroy_queue(ptr)); 66 | return ret; 67 | } 68 | 69 | #[no_mangle] 70 | fn __sys_notify(ptr: usize, count: i32) -> i32 { 71 | let id = ptr as *const usize; 72 | let temp_id; 73 | unsafe { 74 | isolation_start!(); 75 | temp_id = *id; 76 | isolation_end!(); 77 | } 78 | if id.is_null() || temp_id == 0 { 79 | // invalid argument 80 | debug!("sys_notify: invalid address to condition variable"); 81 | return -1; 82 | } 83 | 84 | let cond; 85 | unsafe { 86 | isolation_start!(); 87 | cond = &mut *((*id) as *mut CondQueue); 88 | isolation_end!(); 89 | } 90 | 91 | if count < 0 { 92 | // Wake up all task that has been waiting for this condition variable 93 | while let Some(task) = cond.queue.pop() { 94 | let core_scheduler = scheduler::get_scheduler(task.borrow().core_id); 95 | core_scheduler.blocked_tasks.lock().custom_wakeup(task); 96 | } 97 | } else { 98 | for _ in 0..count { 99 | // Wake up any task that has been waiting for this condition variable 100 | if let Some(task) = cond.queue.pop() { 101 | let core_scheduler = scheduler::get_scheduler(task.borrow().core_id); 102 | core_scheduler.blocked_tasks.lock().custom_wakeup(task); 103 | } else { 104 | debug!("Unable to wakeup task"); 105 | } 106 | } 107 | } 108 | 0 109 | } 110 | 111 | #[no_mangle] 112 | pub unsafe fn sys_notify(ptr: usize, count: i32) -> i32 { 113 | let ret = kernel_function!(__sys_notify(ptr, count)); 114 | return ret; 115 | } 116 | 117 | #[no_mangle] 118 | fn __sys_add_queue(ptr: usize, timeout_ns: i64) -> i32 { 119 | let id = ptr as *mut usize; 120 | if id.is_null() { 121 | debug!("sys_wait: ivalid address to condition variable"); 122 | kernel_exit!("sys_add_queue"); 123 | return -1; 124 | } 125 | 126 | let temp_id; 127 | unsafe { 128 | isolation_start!(); 129 | temp_id = *id; 130 | isolation_end!(); 131 | } 132 | if temp_id == 0 { 133 | debug!("Create condition variable queue"); 134 | let queue = Box::new(CondQueue::new(ptr)); 135 | let temp = Box::into_raw(queue) as usize; 136 | unsafe { 137 | isolation_start!(); 138 | *id = temp; 139 | isolation_end!(); 140 | } 141 | } 142 | 143 | let wakeup_time = if timeout_ns <= 0 { 144 | None 145 | } else { 146 | Some(timeout_ns as u64 / 1000) 147 | }; 148 | 149 | // Block the current task and add it to the wakeup queue. 150 | let core_scheduler = core_scheduler(); 151 | core_scheduler 152 | .blocked_tasks 153 | .lock() 154 | .add(core_scheduler.current_task.clone(), wakeup_time); 155 | 156 | unsafe { 157 | isolation_start!(); 158 | let cond = &mut *((*id) as *mut CondQueue); 159 | isolation_end!(); 160 | cond.queue.push(core_scheduler.current_task.clone()); 161 | } 162 | 0 163 | } 164 | 165 | #[no_mangle] 166 | pub unsafe fn sys_add_queue(ptr: usize, timeout_ns: i64) -> i32 { 167 | let ret = kernel_function!(__sys_add_queue(ptr, timeout_ns)); 168 | return ret; 169 | } 170 | 171 | #[no_mangle] 172 | fn __sys_wait(_ptr: usize) -> i32 { 173 | // Switch to the next task. 174 | core_scheduler().reschedule(); 175 | 0 176 | } 177 | 178 | #[no_mangle] 179 | pub fn sys_wait(_ptr: usize) -> i32 { 180 | let ret = kernel_function!(__sys_wait(_ptr)); 181 | return ret; 182 | } 183 | -------------------------------------------------------------------------------- /src/syscalls/interfaces/generic.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Stefan Lankes, RWTH Aachen University 2 | // Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | use syscalls::interfaces::SyscallInterface; 10 | 11 | // The generic interface simply uses all default implementations of the 12 | // SyscallInterface trait. 13 | pub struct Generic; 14 | impl SyscallInterface for Generic {} 15 | -------------------------------------------------------------------------------- /src/syscalls/interfaces/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Stefan Lankes, RWTH Aachen University 2 | // Colin Finck, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | mod generic; 10 | mod uhyve; 11 | 12 | pub use self::generic::*; 13 | pub use self::uhyve::*; 14 | use alloc::boxed::Box; 15 | use arch; 16 | use console; 17 | use core::fmt::Write; 18 | use core::{isize, ptr, str}; 19 | use core::slice::from_raw_parts; 20 | use errno::*; 21 | use mm; 22 | 23 | pub trait SyscallInterface: Send + Sync { 24 | fn init(&self) { 25 | // Interface-specific initialization steps. 26 | } 27 | 28 | fn get_application_parameters(&self) -> (i32, *const *const u8, *const *const u8) { 29 | let argc = 1; 30 | let dummy = Box::new("name\0".as_ptr()); 31 | let argv = Box::leak(dummy) as *const *const u8; 32 | let environ = ptr::null() as *const *const u8; 33 | 34 | (argc, argv, environ) 35 | } 36 | 37 | fn shutdown(&self, _arg: i32) -> ! { 38 | arch::processor::shutdown(); 39 | } 40 | 41 | fn unlink(&self, _name: *const u8) -> i32 { 42 | debug!("unlink is unimplemented, returning -ENOSYS"); 43 | -ENOSYS 44 | } 45 | 46 | fn open(&self, _name: *const u8, _flags: i32, _mode: i32) -> i32 { 47 | debug!("open is unimplemented, returning -ENOSYS"); 48 | -ENOSYS 49 | } 50 | 51 | fn close(&self, fd: i32) -> i32 { 52 | // we don't have to close standard descriptors 53 | if fd < 3 { 54 | return 0; 55 | } 56 | 57 | debug!("close is only implemented for stdout & stderr, returning -EINVAL"); 58 | -EINVAL 59 | } 60 | 61 | fn read(&self, _fd: i32, _buf: *mut u8, _len: usize) -> isize { 62 | debug!("read is unimplemented, returning -ENOSYS"); 63 | -ENOSYS as isize 64 | } 65 | 66 | fn __write(&self, fd: i32, buf: *const u8, len: usize) -> isize { 67 | 68 | if fd > 2 { 69 | debug!("write is only implemented for stdout & stderr"); 70 | return -EINVAL as isize; 71 | } 72 | 73 | assert!(len <= isize::MAX as usize); 74 | 75 | unsafe { 76 | let slice = isolate_function_weak!(from_raw_parts(buf, len)); 77 | console::CONSOLE 78 | .lock() 79 | .write_str(str::from_utf8_unchecked(slice)) 80 | .unwrap(); 81 | } 82 | 83 | len as isize 84 | } 85 | 86 | fn write(&self, fd: i32, buf: *const u8, len: usize) -> isize { 87 | let ret = kernel_function!(self.__write(fd, buf, len)); 88 | return ret; 89 | } 90 | 91 | fn lseek(&self, _fd: i32, _offset: isize, _whence: i32) -> isize { 92 | debug!("lseek is unimplemented"); 93 | -ENOSYS as isize 94 | } 95 | 96 | fn stat(&self, _file: *const u8, _st: usize) -> i32 { 97 | debug!("stat is unimplemented"); 98 | -ENOSYS 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/syscalls/lwip.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch; 9 | use arch::percore::*; 10 | use console; 11 | use core::u32; 12 | use synch::spinlock::SpinlockIrqSaveGuard; 13 | use syscalls::tasks::Tid; 14 | //use mm; 15 | 16 | /// Enables lwIP's printf to print a whole string without being interrupted by 17 | /// a message from the kernel. 18 | safe_global_var!(static mut CONSOLE_GUARD: Option> = None); 19 | 20 | /// Task ID of the single TCP/IP Task spawned by lwIP. 21 | /// Initialized to u32::MAX by default, which is a very unlikely task ID. 22 | safe_global_var!(static mut LWIP_TCPIP_TASK_ID: Tid = u32::MAX); 23 | 24 | pub fn get_lwip_tcpip_task_id() -> Tid { 25 | unsafe { LWIP_TCPIP_TASK_ID } 26 | } 27 | 28 | #[no_mangle] 29 | fn __sys_lwip_register_tcpip_task(id: Tid) { 30 | unsafe { 31 | LWIP_TCPIP_TASK_ID = id; 32 | } 33 | } 34 | 35 | #[no_mangle] 36 | pub extern "C" fn sys_lwip_register_tcpip_task(id: Tid) { 37 | kernel_function!(__sys_lwip_register_tcpip_task(id)); 38 | } 39 | 40 | #[no_mangle] 41 | fn __sys_lwip_get_errno() -> i32 { 42 | core_scheduler().current_task.borrow().lwip_errno 43 | } 44 | 45 | #[no_mangle] 46 | pub extern "C" fn sys_lwip_get_errno() -> i32 { 47 | let lwip_errno = kernel_function!(__sys_lwip_get_errno()); 48 | return lwip_errno; 49 | } 50 | 51 | #[no_mangle] 52 | fn __sys_lwip_set_errno(errno: i32) { 53 | core_scheduler().current_task.borrow_mut().lwip_errno = errno; 54 | } 55 | 56 | #[no_mangle] 57 | pub extern "C" fn sys_lwip_set_errno(errno: i32) { 58 | kernel_function!(__sys_lwip_set_errno(errno)); 59 | } 60 | 61 | #[no_mangle] 62 | fn __sys_acquire_putchar_lock() { 63 | unsafe { 64 | assert!(CONSOLE_GUARD.is_none()); 65 | CONSOLE_GUARD = Some(console::CONSOLE.lock()); 66 | } 67 | } 68 | 69 | #[no_mangle] 70 | pub extern "C" fn sys_acquire_putchar_lock() { 71 | kernel_function!(__sys_acquire_putchar_lock()); 72 | } 73 | 74 | #[no_mangle] 75 | fn __sys_putchar(character: u8) { 76 | arch::output_message_byte(character); 77 | } 78 | 79 | #[no_mangle] 80 | pub extern "C" fn sys_putchar(character: u8) { 81 | kernel_function!(__sys_putchar(character)); 82 | } 83 | 84 | #[no_mangle] 85 | fn __sys_release_putchar_lock() { 86 | unsafe { 87 | assert!(CONSOLE_GUARD.is_some()); 88 | drop(CONSOLE_GUARD.take()); 89 | } 90 | } 91 | 92 | #[no_mangle] 93 | pub extern "C" fn sys_release_putchar_lock() { 94 | kernel_function!(__sys_release_putchar_lock()); 95 | } 96 | -------------------------------------------------------------------------------- /src/syscalls/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // Stefan Lankes, RWTH Aachen University 3 | // 4 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 7 | // copied, modified, or distributed except according to those terms. 8 | 9 | mod condvar; 10 | mod interfaces; 11 | #[cfg(feature = "newlib")] 12 | mod lwip; 13 | mod processor; 14 | mod random; 15 | mod recmutex; 16 | mod semaphore; 17 | mod spinlock; 18 | mod system; 19 | mod tasks; 20 | mod timer; 21 | 22 | pub use self::condvar::*; 23 | pub use self::processor::*; 24 | pub use self::random::*; 25 | pub use self::recmutex::*; 26 | pub use self::semaphore::*; 27 | pub use self::spinlock::*; 28 | pub use self::system::*; 29 | pub use self::tasks::*; 30 | pub use self::timer::*; 31 | use environment; 32 | #[cfg(feature = "newlib")] 33 | use synch::spinlock::SpinlockIrqSave; 34 | use syscalls::interfaces::SyscallInterface; 35 | 36 | #[cfg(feature = "newlib")] 37 | const LWIP_FD_BIT: i32 = (1 << 30); 38 | 39 | #[cfg(feature = "newlib")] 40 | safe_global_var!(pub static LWIP_LOCK: SpinlockIrqSave<()> = SpinlockIrqSave::new(())); 41 | 42 | safe_global_var!(static mut SYS: &'static dyn SyscallInterface = &interfaces::Generic); 43 | 44 | pub fn init() { 45 | 46 | // We know that HermitCore has successfully initialized a network interface. 47 | // Now check if we can load a more specific SyscallInterface to make use of networking. 48 | if environment::is_proxy() { 49 | panic!("Currently, we don't support the proxy mode!"); 50 | } else if environment::is_uhyve() { 51 | unsafe {SYS = &interfaces::Uhyve}; 52 | } 53 | 54 | // Perform interface-specific initialization steps. 55 | unsafe {SYS.init()}; 56 | 57 | random_init(); 58 | #[cfg(feature = "newlib")] 59 | sbrk_init(); 60 | } 61 | 62 | pub fn get_application_parameters() -> (i32, *const *const u8, *const *const u8) { 63 | unsafe { SYS.get_application_parameters() } 64 | } 65 | 66 | #[no_mangle] 67 | pub extern "C" fn sys_shutdown(arg: i32) -> ! { 68 | unsafe { kernel_function!(SYS.shutdown(arg)) } 69 | } 70 | 71 | #[no_mangle] 72 | pub extern "C" fn sys_unlink(name: *const u8) -> i32 { 73 | unsafe { kernel_function!(SYS.unlink(name)) } 74 | } 75 | 76 | #[no_mangle] 77 | pub extern "C" fn sys_open(name: *const u8, flags: i32, mode: i32) -> i32 { 78 | unsafe { kernel_function!(SYS.open(name, flags, mode)) } 79 | } 80 | 81 | #[no_mangle] 82 | pub extern "C" fn sys_close(fd: i32) -> i32 { 83 | unsafe { kernel_function!(SYS.close(fd)) } 84 | } 85 | 86 | #[no_mangle] 87 | pub extern "C" fn sys_read(fd: i32, buf: *mut u8, len: usize) -> isize { 88 | unsafe { kernel_function!(SYS.read(fd, buf, len)) } 89 | } 90 | 91 | #[no_mangle] 92 | pub extern "C" fn sys_write(fd: i32, buf: *const u8, len: usize) -> isize { 93 | unsafe { kernel_function!(SYS.write(fd, buf, len)) } 94 | } 95 | 96 | #[no_mangle] 97 | pub extern "C" fn sys_lseek(fd: i32, offset: isize, whence: i32) -> isize { 98 | unsafe { kernel_function!(SYS.lseek(fd, offset, whence)) } 99 | } 100 | 101 | #[no_mangle] 102 | pub extern "C" fn sys_stan(file: *const u8, st: usize) -> i32 { 103 | unsafe { kernel_function!(SYS.stat(file, st)) } 104 | } 105 | -------------------------------------------------------------------------------- /src/syscalls/processor.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch; 9 | //use mm; 10 | 11 | /** Returns the number of processors currently online. */ 12 | #[no_mangle] 13 | fn __sys_get_processor_count() -> usize { 14 | arch::get_processor_count() 15 | } 16 | 17 | #[no_mangle] 18 | pub extern "C" fn sys_get_processor_count() -> usize { 19 | let ret = kernel_function!(__sys_get_processor_count()); 20 | return ret; 21 | } 22 | 23 | /** Returns the processor frequency in MHz. */ 24 | #[no_mangle] 25 | fn __sys_get_processor_frequency() -> u16 { 26 | arch::processor::get_frequency() 27 | } 28 | 29 | #[no_mangle] 30 | pub extern "C" fn sys_get_processor_frequency() -> u16 { 31 | let ret = kernel_function!(__sys_get_processor_frequency()); 32 | return ret; 33 | } 34 | -------------------------------------------------------------------------------- /src/syscalls/random.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch; 9 | use synch::spinlock::Spinlock; 10 | //use mm; 11 | 12 | safe_global_var!(static PARK_MILLER_LEHMER_SEED: Spinlock = Spinlock::new(0)); 13 | 14 | #[no_mangle] 15 | fn generate_park_miller_lehmer_random_number() -> u32 { 16 | let mut seed = PARK_MILLER_LEHMER_SEED.lock(); 17 | let random = ((u64::from(*seed) * 48271) % 2_147_483_647) as u32; 18 | *seed = random; 19 | random 20 | } 21 | 22 | #[no_mangle] 23 | fn __sys_rand() -> u32 { 24 | if let Some(value) = arch::processor::generate_random_number() { 25 | value 26 | } else { 27 | generate_park_miller_lehmer_random_number() 28 | } 29 | } 30 | 31 | #[no_mangle] 32 | pub extern "C" fn sys_rand() -> u32 { 33 | let ret = kernel_function!(__sys_rand()); 34 | return ret; 35 | } 36 | 37 | #[no_mangle] 38 | pub fn random_init() { 39 | *PARK_MILLER_LEHMER_SEED.lock() = arch::processor::get_timestamp() as u32; 40 | } 41 | -------------------------------------------------------------------------------- /src/syscalls/recmutex.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use alloc::boxed::Box; 9 | use errno::*; 10 | use synch::recmutex::RecursiveMutex; 11 | use mm; 12 | 13 | #[no_mangle] 14 | fn __sys_recmutex_init(recmutex: *mut *mut RecursiveMutex) -> i32 { 15 | if recmutex.is_null() { 16 | return -EINVAL; 17 | } 18 | 19 | // Create a new boxed recursive mutex and return a pointer to the raw memory. 20 | let boxed_mutex = Box::new(RecursiveMutex::new()); 21 | let temp = Box::into_raw(boxed_mutex); 22 | unsafe { 23 | isolation_start!(); 24 | *recmutex = temp; 25 | isolation_end!(); 26 | } 27 | 0 28 | } 29 | 30 | #[no_mangle] 31 | pub extern "C" fn sys_recmutex_init(recmutex: *mut *mut RecursiveMutex) -> i32 { 32 | let ret = kernel_function!(__sys_recmutex_init(recmutex)); 33 | return ret; 34 | } 35 | 36 | #[no_mangle] 37 | fn __sys_recmutex_destroy(recmutex: *mut RecursiveMutex) -> i32 { 38 | if recmutex.is_null() { 39 | return -EINVAL; 40 | } 41 | 42 | // Consume the pointer to the raw memory into a Box again 43 | // and drop the Box to free the associated memory. 44 | /*unsafe { 45 | isolate_function_strong!(Box::from_raw(recmutex)); 46 | }*/ 47 | 0 48 | } 49 | 50 | #[no_mangle] 51 | pub extern "C" fn sys_recmutex_destroy(recmutex: *mut RecursiveMutex) -> i32 { 52 | let ret = kernel_function!(__sys_recmutex_destroy(recmutex)); 53 | return ret; 54 | } 55 | 56 | #[no_mangle] 57 | fn __sys_recmutex_lock(recmutex: *mut RecursiveMutex) -> i32 { 58 | if recmutex.is_null() { 59 | return -EINVAL; 60 | } 61 | 62 | let mutex = unsafe { 63 | isolation_start!(); 64 | let temp = &*recmutex; 65 | isolation_end!(); 66 | temp 67 | }; 68 | mutex.acquire(); 69 | 0 70 | } 71 | 72 | #[no_mangle] 73 | pub extern "C" fn sys_recmutex_lock(recmutex: *mut RecursiveMutex) -> i32 { 74 | let ret = kernel_function!(__sys_recmutex_lock(recmutex)); 75 | return ret; 76 | } 77 | 78 | #[no_mangle] 79 | fn __sys_recmutex_unlock(recmutex: *mut RecursiveMutex) -> i32 { 80 | if recmutex.is_null() { 81 | return -EINVAL; 82 | } 83 | 84 | let mutex = unsafe { 85 | isolation_start!(); 86 | let temp = &*recmutex; 87 | isolation_end!(); 88 | temp 89 | }; 90 | mutex.release(); 91 | 0 92 | } 93 | 94 | #[no_mangle] 95 | pub extern "C" fn sys_recmutex_unlock(recmutex: *mut RecursiveMutex) -> i32 { 96 | let ret = kernel_function!(__sys_recmutex_unlock(recmutex)); 97 | return ret; 98 | } 99 | -------------------------------------------------------------------------------- /src/syscalls/semaphore.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use alloc::boxed::Box; 9 | use arch; 10 | use errno::*; 11 | use synch::semaphore::Semaphore; 12 | use mm; 13 | 14 | #[no_mangle] 15 | fn __sys_sem_init(sem: *mut *mut Semaphore, value: u32) -> i32 { 16 | //println!("sys_sem_init, sem: {:#X}", sem as usize); 17 | if sem.is_null() { 18 | return -EINVAL; 19 | } 20 | 21 | // Create a new boxed semaphore and return a pointer to the raw memory. 22 | let boxed_semaphore = Box::new(Semaphore::new(value as isize)); 23 | let temp = Box::into_raw(boxed_semaphore); 24 | unsafe { 25 | isolation_start!(); 26 | *sem = temp; 27 | isolation_end!(); 28 | } 29 | 0 30 | } 31 | 32 | #[no_mangle] 33 | pub extern "C" fn sys_sem_init(sem: *mut *mut Semaphore, value: u32) -> i32 { 34 | let ret = kernel_function!(__sys_sem_init(sem, value)); 35 | return ret; 36 | } 37 | 38 | #[no_mangle] 39 | fn __sys_sem_destroy(sem: *mut Semaphore) -> i32 { 40 | if sem.is_null() { 41 | return -EINVAL; 42 | } 43 | 44 | // Consume the pointer to the raw memory into a Box again 45 | // and drop the Box to free the associated memory. 46 | /*unsafe { 47 | isolate_function_strong!(Box::from_raw(sem)); 48 | }*/ 49 | 0 50 | } 51 | 52 | #[no_mangle] 53 | pub extern "C" fn sys_sem_destroy(sem: *mut Semaphore) -> i32 { 54 | let ret = kernel_function!(__sys_sem_destroy(sem: *mut Semaphore)); 55 | return ret; 56 | } 57 | 58 | #[no_mangle] 59 | fn __sys_sem_post(sem: *const Semaphore) -> i32 { 60 | if sem.is_null() { 61 | return -EINVAL; 62 | } 63 | 64 | // Get a reference to the given semaphore and release it. 65 | let semaphore = unsafe { 66 | isolation_start!(); 67 | let temp = &*sem; 68 | isolation_end!(); 69 | temp 70 | }; 71 | semaphore.release(); 72 | 0 73 | } 74 | 75 | #[no_mangle] 76 | pub extern "C" fn sys_sem_post(sem: *const Semaphore) -> i32 { 77 | let ret = kernel_function!(__sys_sem_post(sem: *const Semaphore)); 78 | return ret; 79 | } 80 | 81 | #[no_mangle] 82 | fn __sys_sem_trywait(sem: *const Semaphore) -> i32 { 83 | if sem.is_null() { 84 | return -EINVAL; 85 | } 86 | 87 | // Get a reference to the given semaphore and acquire it in a non-blocking fashion. 88 | let semaphore = unsafe { 89 | isolation_start!(); 90 | let temp = &*sem; 91 | isolation_end!(); 92 | temp 93 | }; 94 | if semaphore.try_acquire() { 95 | 0 96 | } else { 97 | -ECANCELED 98 | } 99 | } 100 | 101 | #[no_mangle] 102 | pub extern "C" fn sys_sem_trywait(sem: *const Semaphore) -> i32 { 103 | let ret = kernel_function!(__sys_sem_trywait(sem)); 104 | return ret; 105 | } 106 | 107 | #[no_mangle] 108 | fn __sys_sem_timedwait(sem: *const Semaphore, ms: u32) -> i32 { 109 | //println!("sys_sem_timedwait, sem: {:#X}", sem as usize); 110 | if sem.is_null() { 111 | return -EINVAL; 112 | } 113 | 114 | // Calculate the absolute wakeup time in processor timer ticks out of the relative timeout in milliseconds. 115 | let wakeup_time = if ms > 0 { 116 | Some(arch::processor::get_timer_ticks() + u64::from(ms) * 1000) 117 | } else { 118 | None 119 | }; 120 | 121 | // Get a reference to the given semaphore and wait until we have acquired it or the wakeup time has elapsed. 122 | let semaphore = unsafe { 123 | isolation_start!(); 124 | let temp = &*sem; 125 | isolation_end!(); 126 | temp 127 | }; 128 | if semaphore.acquire(wakeup_time) { 129 | 0 130 | } else { 131 | -ETIME 132 | } 133 | } 134 | 135 | #[no_mangle] 136 | pub extern "C" fn sys_sem_timedwait(sem: *const Semaphore, ms: u32) -> i32 { 137 | return kernel_function!(__sys_sem_timedwait(sem, ms)); 138 | } 139 | 140 | #[no_mangle] 141 | pub extern "C" fn sys_sem_cancelablewait(sem: *const Semaphore, ms: u32) -> i32 { 142 | sys_sem_timedwait(sem, ms) 143 | } 144 | -------------------------------------------------------------------------------- /src/syscalls/spinlock.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use alloc::boxed::Box; 9 | use errno::*; 10 | use synch::spinlock::*; 11 | use mm; 12 | 13 | pub struct SpinlockContainer<'a> { 14 | lock: Spinlock<()>, 15 | guard: Option>, 16 | } 17 | 18 | pub struct SpinlockIrqSaveContainer<'a> { 19 | lock: SpinlockIrqSave<()>, 20 | guard: Option>, 21 | } 22 | 23 | #[no_mangle] 24 | fn __sys_spinlock_init(lock: *mut *mut SpinlockContainer) -> i32 { 25 | if lock.is_null() { 26 | return -EINVAL; 27 | } 28 | 29 | let boxed_container = Box::new(SpinlockContainer { 30 | lock: Spinlock::new(()), 31 | guard: None, 32 | }); 33 | let ret = Box::into_raw(boxed_container); 34 | unsafe { 35 | isolation_start!(); 36 | *lock = ret; 37 | isolation_end!(); 38 | } 39 | 0 40 | } 41 | 42 | #[no_mangle] 43 | pub extern "C" fn sys_spinlock_init(lock: *mut *mut SpinlockContainer) -> i32 { 44 | let ret = kernel_function!(__sys_spinlock_init(lock)); 45 | return ret; 46 | } 47 | 48 | #[no_mangle] 49 | fn __sys_spinlock_destroy(lock: *mut SpinlockContainer) -> i32 { 50 | if lock.is_null() { 51 | return -EINVAL; 52 | } 53 | 54 | // Consume the lock into a box, which is then dropped. 55 | unsafe { 56 | isolate_function_strong!(Box::from_raw(lock)); 57 | } 58 | 0 59 | } 60 | 61 | #[no_mangle] 62 | pub extern "C" fn sys_spinlock_destroy(lock: *mut SpinlockContainer) -> i32 { 63 | let ret = kernel_function!(__sys_spinlock_destroy(lock)); 64 | return ret; 65 | } 66 | 67 | #[no_mangle] 68 | fn __sys_spinlock_lock(lock: *mut SpinlockContainer) -> i32 { 69 | if lock.is_null() { 70 | return -EINVAL; 71 | } 72 | 73 | let container = unsafe { 74 | isolation_start!(); 75 | let ret = &mut *lock; 76 | isolation_end!(); 77 | ret 78 | }; 79 | assert!( 80 | container.guard.is_none(), 81 | "Called sys_spinlock_lock when a lock is already held!" 82 | ); 83 | container.guard = Some(container.lock.lock()); 84 | 0 85 | } 86 | 87 | #[no_mangle] 88 | pub extern "C" fn sys_spinlock_lock(lock: *mut SpinlockContainer) -> i32 { 89 | let ret = kernel_function!(__sys_spinlock_lock(lock)); 90 | return ret; 91 | } 92 | 93 | #[no_mangle] 94 | fn __sys_spinlock_unlock(lock: *mut SpinlockContainer) -> i32 { 95 | if lock.is_null() { 96 | return -EINVAL; 97 | } 98 | 99 | let container = unsafe { 100 | isolation_start!(); 101 | let ret = &mut *lock; 102 | isolation_end!(); 103 | ret 104 | }; 105 | assert!( 106 | container.guard.is_some(), 107 | "Called sys_spinlock_unlock when no lock is currently held!" 108 | ); 109 | container.guard = None; 110 | 0 111 | } 112 | 113 | #[no_mangle] 114 | pub extern "C" fn sys_spinlock_unlock(lock: *mut SpinlockContainer) -> i32 { 115 | let ret = kernel_function!(__sys_spinlock_unlock(lock)); 116 | return ret; 117 | } 118 | 119 | #[no_mangle] 120 | fn __sys_spinlock_irqsave_init(lock: *mut *mut SpinlockIrqSaveContainer) -> i32 { 121 | if lock.is_null() { 122 | return -EINVAL; 123 | } 124 | 125 | let boxed_container = Box::new(SpinlockIrqSaveContainer { 126 | lock: SpinlockIrqSave::new(()), 127 | guard: None, 128 | }); 129 | let ret = Box::into_raw(boxed_container); 130 | unsafe { 131 | isolation_start!(); 132 | *lock = ret; 133 | isolation_end!(); 134 | }; 135 | 0 136 | } 137 | 138 | #[no_mangle] 139 | pub extern "C" fn sys_spinlock_irqsave_init(lock: *mut *mut SpinlockIrqSaveContainer) -> i32 { 140 | let ret = kernel_function!(__sys_spinlock_irqsave_init(lock)); 141 | return ret; 142 | } 143 | 144 | #[no_mangle] 145 | fn __sys_spinlock_irqsave_destroy(lock: *mut SpinlockIrqSaveContainer) -> i32 { 146 | if lock.is_null() { 147 | return -EINVAL; 148 | } 149 | 150 | // Consume the lock into a box, which is then dropped. 151 | unsafe { 152 | isolate_function_strong!(Box::from_raw(lock)); 153 | } 154 | 0 155 | } 156 | 157 | #[no_mangle] 158 | pub extern "C" fn sys_spinlock_irqsave_destroy(lock: *mut SpinlockIrqSaveContainer) -> i32 { 159 | let ret = kernel_function!(__sys_spinlock_irqsave_destroy(lock)); 160 | return ret; 161 | } 162 | 163 | #[no_mangle] 164 | fn __sys_spinlock_irqsave_lock(lock: *mut SpinlockIrqSaveContainer) -> i32 { 165 | if lock.is_null() { 166 | return -EINVAL; 167 | } 168 | 169 | let container = unsafe { 170 | isolation_start!(); 171 | let ret = &mut *lock; 172 | isolation_end!(); 173 | ret 174 | }; 175 | assert!( 176 | container.guard.is_none(), 177 | "Called sys_spinlock_irqsave_lock when a lock is already held!" 178 | ); 179 | container.guard = Some(container.lock.lock()); 180 | 0 181 | } 182 | 183 | #[no_mangle] 184 | pub extern "C" fn sys_spinlock_irqsave_lock(lock: *mut SpinlockIrqSaveContainer) -> i32 { 185 | let ret = kernel_function!(__sys_spinlock_irqsave_lock(lock)); 186 | return ret; 187 | } 188 | 189 | #[no_mangle] 190 | fn __sys_spinlock_irqsave_unlock(lock: *mut SpinlockIrqSaveContainer) -> i32 { 191 | if lock.is_null() { 192 | return -EINVAL; 193 | } 194 | 195 | let container = unsafe { 196 | isolation_start!(); 197 | let ret = &mut *lock; 198 | isolation_end!(); 199 | ret 200 | }; 201 | assert!( 202 | container.guard.is_some(), 203 | "Called sys_spinlock_irqsave_unlock when no lock is currently held!" 204 | ); 205 | container.guard = None; 206 | 0 207 | } 208 | 209 | #[no_mangle] 210 | pub extern "C" fn sys_spinlock_irqsave_unlock(lock: *mut SpinlockIrqSaveContainer) -> i32 { 211 | let ret = kernel_function!(__sys_spinlock_irqsave_unlock(lock)); 212 | return ret; 213 | } 214 | -------------------------------------------------------------------------------- /src/syscalls/system.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // MIT License 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | use arch; 25 | //use mm; 26 | 27 | #[no_mangle] 28 | fn __sys_getpagesize() -> i32 { 29 | arch::mm::paging::get_application_page_size() as i32 30 | } 31 | 32 | #[no_mangle] 33 | pub extern "C" fn sys_getpagesize() -> i32 { 34 | let ret = kernel_function!(__sys_getpagesize()); 35 | return ret; 36 | } 37 | -------------------------------------------------------------------------------- /src/syscalls/timer.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Colin Finck, RWTH Aachen University 2 | // 3 | // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | // copied, modified, or distributed except according to those terms. 7 | 8 | use arch; 9 | use errno::*; 10 | use syscalls::sys_usleep; 11 | use mm; 12 | 13 | #[derive(Copy, Clone, Debug)] 14 | #[repr(C)] 15 | pub struct itimerval { 16 | pub it_interval: timeval, 17 | pub it_value: timeval, 18 | } 19 | 20 | #[derive(Copy, Clone, Debug)] 21 | #[repr(C)] 22 | pub struct timespec { 23 | pub tv_sec: i64, 24 | pub tv_nsec: i64, 25 | } 26 | 27 | #[derive(Copy, Clone, Debug)] 28 | #[repr(C)] 29 | pub struct timeval { 30 | pub tv_sec: i64, 31 | pub tv_usec: i64, 32 | } 33 | 34 | pub const CLOCK_REALTIME: u64 = 1; 35 | pub const CLOCK_PROCESS_CPUTIME_ID: u64 = 2; 36 | pub const CLOCK_THREAD_CPUTIME_ID: u64 = 3; 37 | pub const CLOCK_MONOTONIC: u64 = 4; 38 | pub const TIMER_ABSTIME: i32 = 4; 39 | 40 | fn microseconds_to_timespec(microseconds: u64, result: &mut timespec) { 41 | result.tv_sec = (microseconds / 1_000_000) as i64; 42 | result.tv_nsec = ((microseconds % 1_000_000) * 1000) as i64; 43 | } 44 | 45 | fn microseconds_to_timeval(microseconds: u64, result: &mut timeval) { 46 | result.tv_sec = (microseconds / 1_000_000) as i64; 47 | result.tv_usec = (microseconds % 1_000_000) as i64; 48 | } 49 | 50 | #[no_mangle] 51 | fn __sys_clock_getres(clock_id: u64, res: *mut timespec) -> i32 { 52 | assert!( 53 | !res.is_null(), 54 | "sys_clock_getres called with a zero res parameter" 55 | ); 56 | let result = unsafe { 57 | isolation_start!(); 58 | let temp = &mut *res; 59 | isolation_end!(); 60 | temp 61 | }; 62 | 63 | match clock_id { 64 | CLOCK_REALTIME | CLOCK_PROCESS_CPUTIME_ID | CLOCK_THREAD_CPUTIME_ID | CLOCK_MONOTONIC => { 65 | // All clocks in HermitCore have 1 microsecond resolution. 66 | microseconds_to_timespec(1, result); 67 | 0 68 | } 69 | _ => { 70 | debug!("Called sys_clock_getres for unsupported clock {}", clock_id); 71 | -EINVAL 72 | } 73 | } 74 | } 75 | 76 | #[no_mangle] 77 | pub extern "C" fn sys_clock_getres(clock_id: u64, res: *mut timespec) -> i32 { 78 | let ret = kernel_function!(__sys_clock_getres(clock_id, res)); 79 | return ret; 80 | } 81 | 82 | #[no_mangle] 83 | fn __sys_clock_gettime(clock_id: u64, tp: *mut timespec) -> i32 { 84 | assert!( 85 | !tp.is_null(), 86 | "sys_clock_gettime called with a zero tp parameter" 87 | ); 88 | let result = unsafe { 89 | isolation_start!(); 90 | let temp = &mut *tp; 91 | isolation_end!(); 92 | temp 93 | }; 94 | 95 | match clock_id { 96 | CLOCK_REALTIME | CLOCK_MONOTONIC => { 97 | let mut microseconds = arch::processor::get_timer_ticks(); 98 | 99 | if clock_id == CLOCK_REALTIME { 100 | microseconds += arch::get_boot_time(); 101 | } 102 | 103 | microseconds_to_timespec(microseconds, result); 104 | 0 105 | } 106 | _ => { 107 | debug!( 108 | "Called sys_clock_gettime for unsupported clock {}", 109 | clock_id 110 | ); 111 | -EINVAL 112 | } 113 | } 114 | } 115 | 116 | #[no_mangle] 117 | pub extern "C" fn sys_clock_gettime(clock_id: u64, tp: *mut timespec) -> i32 { 118 | kernel_function!(__sys_clock_gettime(clock_id, tp)) 119 | } 120 | 121 | #[no_mangle] 122 | fn __sys_clock_nanosleep( 123 | clock_id: u64, 124 | flags: i32, 125 | rqtp: *const timespec, 126 | _rmtp: *mut timespec, 127 | ) -> i32 { 128 | assert!( 129 | !rqtp.is_null(), 130 | "sys_clock_nanosleep called with a zero rqtp parameter" 131 | ); 132 | let requested_time = unsafe { 133 | isolation_start!(); 134 | let temp = &*rqtp; 135 | isolation_end!(); 136 | temp 137 | }; 138 | if requested_time.tv_sec < 0 139 | || requested_time.tv_nsec < 0 140 | || requested_time.tv_nsec > 999_999_999 141 | { 142 | debug!("sys_clock_nanosleep called with an invalid requested time, returning -EINVAL"); 143 | return -EINVAL; 144 | } 145 | 146 | match clock_id { 147 | CLOCK_REALTIME | CLOCK_MONOTONIC => { 148 | let mut microseconds = (requested_time.tv_sec as u64) * 1_000_000 149 | + (requested_time.tv_nsec as u64) / 1_000; 150 | 151 | if flags & TIMER_ABSTIME > 0 { 152 | microseconds -= arch::processor::get_timer_ticks(); 153 | 154 | if clock_id == CLOCK_REALTIME { 155 | microseconds -= arch::get_boot_time(); 156 | } 157 | } 158 | use x86_64::kernel::percore::core_scheduler; 159 | kernel_exit!("__sys_clock_nanosleep"); 160 | sys_usleep(microseconds); 161 | 0 162 | } 163 | _ => { 164 | -EINVAL 165 | } 166 | } 167 | } 168 | 169 | #[no_mangle] 170 | pub extern "C" fn sys_clock_nanosleep( 171 | clock_id: u64, 172 | flags: i32, 173 | rqtp: *const timespec, 174 | rmtp: *mut timespec, 175 | ) -> i32 { 176 | let ret = kernel_function!(__sys_clock_nanosleep(clock_id, flags, rqtp, rmtp)); 177 | return ret; 178 | } 179 | 180 | #[no_mangle] 181 | pub extern "C" fn sys_clock_settime(_clock_id: u64, _tp: *const timespec) -> i32 { 182 | // We don't support setting any clocks yet. 183 | debug!("sys_clock_settime is unimplemented, returning -EINVAL"); 184 | -EINVAL 185 | } 186 | 187 | #[no_mangle] 188 | fn __sys_gettimeofday(tp: *mut timeval, tz: usize) -> i32 { 189 | if let Some(result) = unsafe { isolate_function_strong!(tp.as_mut()) } { 190 | // Return the current time based on the wallclock time when we were booted up 191 | // plus the current timer ticks. 192 | let microseconds = arch::get_boot_time() + arch::processor::get_timer_ticks(); 193 | microseconds_to_timeval(microseconds, result); 194 | } 195 | 196 | if tz > 0 { 197 | debug!("The tz parameter in sys_gettimeofday is unimplemented, returning -EINVAL"); 198 | return -EINVAL; 199 | } 200 | 201 | 0 202 | } 203 | 204 | #[no_mangle] 205 | pub extern "C" fn sys_gettimeofday(tp: *mut timeval, tz: usize) -> i32 { 206 | let ret = kernel_function!(__sys_gettimeofday(tp, tz)); 207 | return ret; 208 | } 209 | 210 | #[no_mangle] 211 | pub extern "C" fn sys_setitimer( 212 | _which: i32, 213 | _value: *const itimerval, 214 | _ovalue: *mut itimerval, 215 | ) -> i32 { 216 | debug!("Called sys_setitimer, which is unimplemented and always returns 0"); 217 | 0 218 | } 219 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rusty_tests" 3 | version = "0.1.0" 4 | authors = ["Stefan Lankes "] 5 | edition = "2018" 6 | publish = false 7 | 8 | [dependencies] 9 | rayon = "1.2.0" 10 | http = "0.1.18" 11 | 12 | # The development profile, used for `cargo build`. 13 | [profile.dev] 14 | opt-level = 1 # controls the `--opt-level` the compiler builds with 15 | debug = true # controls whether the compiler passes `-C debuginfo` 16 | # a value of `true` is equivalent to `2` 17 | rpath = false # controls whether the compiler passes `-C rpath` 18 | lto = false # controls `-C lto` for binaries and staticlibs 19 | debug-assertions = true # controls whether debug assertions are enabled 20 | 21 | [profile.release] 22 | opt-level = 3 23 | debug = false 24 | rpath = false 25 | lto = true 26 | debug-assertions = false 27 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | arch ?= x86_64 2 | target ?= $(arch)-unknown-hermit 3 | release ?= 0 4 | 5 | opt := 6 | rdir := debug 7 | 8 | ifeq ($(release), 1) 9 | opt := --release 10 | rdir := release 11 | endif 12 | 13 | RN := 14 | ifdef COMSPEC 15 | RM := del 16 | else 17 | RM := rm -rf 18 | endif 19 | 20 | .PHONY: default clean 21 | 22 | default: 23 | RUSTFLAGS="-L ../target/$(target)-kernel/$(rdir) -C link-arg=-Tsrc/linker.ld -Ccodegen-units=1 -Cforce-frame-pointers=yes" cargo build $(opt) --target $(target) 24 | @objcopy --only-keep-debug target/$(arch)-unknown-hermit/$(rdir)/rusty_tests target/$(arch)-unknown-hermit/$(rdir)/rusty_tests.sym 25 | @objcopy --strip-debug target/$(arch)-unknown-hermit/$(rdir)/rusty_tests 26 | 27 | clean: 28 | @$(RM) target/x86_64-unknown-hermit 29 | -------------------------------------------------------------------------------- /tests/src/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf64-x86-64") 2 | OUTPUT_ARCH("i386:x86-64") 3 | ENTRY(_start) 4 | 5 | SECTIONS { 6 | . = 2M; 7 | 8 | .text : AT(ADDR(.text)) 9 | { 10 | *(.text) 11 | *(.text.*) 12 | } 13 | 14 | .rodata : AT(ADDR(.rodata)) 15 | { 16 | *(.rodata) 17 | *(.rodata.*) 18 | } 19 | 20 | .data ALIGN(4096) : AT(ADDR(.data)) 21 | { 22 | *(.data) 23 | *(.data.*) 24 | } 25 | 26 | .bss ALIGN(4096) : AT(ADDR(.bss)) 27 | { 28 | __bss_start = .; 29 | *(.bss) 30 | *(.bss.*) 31 | __bss_end = .; 32 | } 33 | 34 | .safe_data 0x400000: 35 | { 36 | __safe_data_start = .; 37 | *(.safe_data) 38 | *(.safe_data.*) 39 | . = 0x600000; 40 | } 41 | 42 | .unsafe_data 0x600000: 43 | { 44 | __unsafe_data_start = .; 45 | *(.unsafe_data) 46 | *(.unsafe_data.*) 47 | . = 0x800000; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(asm)] 2 | 3 | extern crate http; 4 | extern crate rayon; 5 | 6 | mod tests; 7 | 8 | use std::io::Write; 9 | use std::sync::{Arc,Mutex}; 10 | use std::sync::atomic::{AtomicU32, Ordering}; 11 | use tests::*; 12 | 13 | fn test_result(result: Result<(), T>) -> &'static str { 14 | match result { 15 | Ok(_) => "ok", 16 | Err(_) => "failed!", 17 | } 18 | } 19 | 20 | fn test_syscall_cost() { 21 | use std::time::Instant; 22 | use std::process::id; 23 | let now = Instant::now(); 24 | for _ in 0..100000000 { 25 | let _ = id(); 26 | } 27 | let elapsed = now.elapsed().as_secs_f64(); 28 | println!("getpid {} s", elapsed); 29 | } 30 | 31 | fn test_syscall_cost2() { 32 | extern "C" { 33 | fn sys_getpid() -> u32; 34 | //fn sys_getprio(id: *const u32) -> i32; 35 | } 36 | 37 | use std::time::Instant; 38 | let now = Instant::now(); 39 | for _ in 0..100000000 { 40 | unsafe { 41 | let _ = sys_getpid(); 42 | } 43 | } 44 | let elapsed = now.elapsed().as_secs_f64(); 45 | println!("sys_getpid {} s", elapsed); 46 | } 47 | 48 | fn vulnerable_function(string: String, address: *mut String) { 49 | unsafe { 50 | println!("bafore writing"); 51 | *address = string; 52 | println!("after writing"); 53 | } 54 | } 55 | 56 | fn security_evaluation_user_isolation() { 57 | let s = "hello".to_string(); 58 | vulnerable_function(s, 0x400000usize as *mut _); 59 | } 60 | 61 | //static COUNTER: AtomicU32 = AtomicU32::new(8); 62 | //static results: Mutex> = Mutex::new(Vec::::new()); 63 | /* 64 | fn test_threading() -> Result<(), ()> { 65 | use std::thread; 66 | use std::time::Instant; 67 | use std::process::id; 68 | 69 | extern "C" { 70 | fn sys_getpid() -> u32; 71 | fn sys_getprio(id: *const u32) -> i32; 72 | } 73 | 74 | // Make a vector to hold the children which are spawned. 75 | let mut children = vec![]; 76 | let results = Arc::new(Mutex::new(vec![])); 77 | //let now = Instant::now(); 78 | 79 | for i in 0..8 { 80 | let results = results.clone(); 81 | let now = Instant::now(); 82 | 83 | // Spin up another thread 84 | children.push(thread::spawn(move || { 85 | unsafe { 86 | for _ in 0..(10000000/8) { 87 | let _ = sys_getpid(); 88 | } 89 | } 90 | let elapsed = now.elapsed().as_secs_f64(); 91 | results.lock().unwrap().push(elapsed); 92 | 93 | //println!("id:{} COUNTER: {} {}", id, COUNTER.load(Ordering::SeqCst), now.elapsed().as_secs_f64()); 94 | COUNTER.fetch_sub(1, Ordering::SeqCst); 95 | })); 96 | } 97 | 98 | println!("before join"); 99 | while COUNTER.load(Ordering::Relaxed) > 0 { 100 | unsafe { asm!("pause" :::: "volatile"); } 101 | } 102 | while let Some(i) = results.lock().unwrap().pop() { 103 | println!("{}", i); 104 | } 105 | 106 | /*for child in children { 107 | // Wait for the thread to finish. Returns a result. 108 | let _ = child.join(); 109 | }*/ 110 | 111 | Ok(()) 112 | } 113 | */ 114 | 115 | fn main() { 116 | println!("Test {} ... {}", stringify!(hello), test_result(hello())); 117 | 118 | /* 119 | test_syscall_cost(); 120 | test_syscall_cost2(); 121 | test_threading(); 122 | security_evaluation_user_isolation(); 123 | 124 | println!( 125 | "Test {} ... {}", 126 | stringify!(test_pkru_context_switch), 127 | test_result(test_pkru_context_switch()) 128 | ); 129 | 130 | println!( 131 | "Test {} ... {}", 132 | stringify!(print_argv), 133 | test_result(print_argv()) 134 | ); 135 | println!( 136 | "Test {} ... {}", 137 | stringify!(print_env), 138 | test_result(print_env()) 139 | ); 140 | 141 | println!( 142 | "Test {} ... {}", 143 | stringify!(read_file), 144 | test_result(read_file()) 145 | ); 146 | println!( 147 | "Test {} ... {}", 148 | stringify!(create_file), 149 | test_result(create_file()) 150 | ); 151 | 152 | println!("before alloc"); 153 | unsafe { 154 | let layout: std::alloc::Layout = std::alloc::Layout::from_size_align(8, 8).unwrap(); 155 | let a = std::alloc::alloc(layout); 156 | } 157 | println!("after alloc"); 158 | println!( 159 | "Test {} ... {}", 160 | stringify!(threading), 161 | test_result(threading()) 162 | ); 163 | 164 | println!( 165 | "Test {} ... {}", 166 | stringify!(pi_sequential), 167 | test_result(pi_sequential(1000000)) 168 | ); 169 | 170 | println!( 171 | "Test {} ... {}", 172 | stringify!(pi_parallel), 173 | test_result(pi_parallel(2, 5000000)) 174 | ); 175 | println!( 176 | "Test {} ... {}", 177 | stringify!(laplace), 178 | test_result(laplace(128, 128)) 179 | ); 180 | 181 | println!( 182 | "Test {} ... {}", 183 | stringify!(test_matmul_strassen), 184 | test_result(test_matmul_strassen()) 185 | ); 186 | println!( 187 | "Test {} ... {}", 188 | stringify!(thread_creation), 189 | test_result(thread_creation()) 190 | ); 191 | 192 | println!( 193 | "Test {} ... {}", 194 | stringify!(bench_sched_one_thread), 195 | test_result(bench_sched_one_thread()) 196 | ); 197 | 198 | println!( 199 | "Test {} ... {}", 200 | stringify!(bench_sched_two_threads), 201 | test_result(bench_sched_two_threads()) 202 | ); 203 | println!( 204 | "Test {} ... {}", 205 | stringify!(test_http_request), 206 | test_result(test_http_request()) 207 | ); 208 | */ 209 | } 210 | -------------------------------------------------------------------------------- /tests/src/tests/laplace.rs: -------------------------------------------------------------------------------- 1 | use rayon::prelude::*; 2 | use std::vec; 3 | 4 | fn get_residual(matrix: &[f64], size_x: usize, size_y: usize) -> f64 { 5 | let sum = (1..size_y - 1) 6 | .into_par_iter() 7 | .map(|y| { 8 | let mut local_sum = 0.0; 9 | 10 | for x in 1..(size_x - 1) { 11 | let new = (matrix[y * size_x + x - 1] 12 | + matrix[y * size_x + x + 1] 13 | + matrix[(y + 1) * size_x + x] 14 | + matrix[(y - 1) * size_x + x]) 15 | * 0.25; 16 | 17 | let diff = new - matrix[y * size_x + x]; 18 | local_sum += diff * diff; 19 | } 20 | 21 | local_sum 22 | }) 23 | .sum(); 24 | 25 | sum 26 | } 27 | 28 | fn iteration(cur: &[f64], next: &mut [f64], size_x: usize, size_y: usize) { 29 | next.par_chunks_mut(size_y) 30 | .enumerate() // to figure out where this chunk came from 31 | .for_each(|(chunk_index, slice)| { 32 | if chunk_index > 0 && chunk_index < size_y - 1 { 33 | let offset_base = chunk_index * size_x; 34 | 35 | for x in 1..size_x - 1 { 36 | slice[x] = (cur[offset_base + x - 1] 37 | + cur[offset_base + x + 1] 38 | + cur[offset_base + size_x + x] 39 | + cur[offset_base - size_x + x]) 40 | * 0.25; 41 | } 42 | } 43 | }); 44 | } 45 | 46 | #[inline(never)] 47 | pub fn compute(mut matrix: vec::Vec>, size_x: usize, size_y: usize) -> (usize, f64) { 48 | let mut counter = 0; 49 | 50 | while counter < 1000 { 51 | { 52 | // allow a borrow and a reference to the same vector 53 | let (current, next) = matrix.split_at_mut(1); 54 | 55 | iteration(¤t[0], &mut next[0], size_x, size_y); 56 | } 57 | matrix.swap(0, 1); 58 | 59 | counter += 1; 60 | } 61 | 62 | (counter, get_residual(&matrix[0], size_x, size_y)) 63 | } 64 | -------------------------------------------------------------------------------- /x86_64-unknown-hermit-kernel.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "linker-flavor": "gcc", 4 | "target-endian": "little", 5 | "target-pointer-width": "64", 6 | "target-c-int-width" : "32", 7 | "os": "none", 8 | "arch": "x86_64", 9 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 10 | "pre-link-args": { 11 | "gcc": ["-m64"] 12 | }, 13 | "cpu": "x86-64", 14 | "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float", 15 | "disable-redzone": true, 16 | "eliminate-frame-pointer": true, 17 | "linker-is-gnu": true, 18 | "no-compiler-rt": true, 19 | "archive-format": "gnu", 20 | "code-model": "kernel", 21 | "relocation-model": "static", 22 | "panic-strategy": "abort" 23 | } 24 | --------------------------------------------------------------------------------