diff --git a/.gitignore b/.gitignore index b55f6b82c..378e4538d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ build_i686/ build_x86_64/ build_conan/ +# nix +/result + # ignore log files in test tree test/**/*.log diff --git a/deps/musl/default.nix b/deps/musl/default.nix index 3921abdcd..239cb8943 100644 --- a/deps/musl/default.nix +++ b/deps/musl/default.nix @@ -2,6 +2,7 @@ stdenv , pkgs , linuxHeaders ? null +, debug ? false }: stdenv.mkDerivation rec { pname = "musl-includeos"; @@ -40,7 +41,8 @@ stdenv.mkDerivation rec { ./configure --prefix=$out --disable-shared --enable-debug --with-malloc=oldmalloc CROSS_COMPILE=${stdenv.targetPlatform.config}- ''; - CFLAGS = "-Wno-error=int-conversion -nostdinc"; + dontStrip = debug; + CFLAGS = "-Wno-error=int-conversion -nostdinc${pkgs.lib.optionalString debug " -g"}"; meta = { description = "musl - Linux based libc, built with IncludeOS linux-like syscalls"; diff --git a/example/vm.json b/example/vm.json new file mode 100644 index 000000000..2c63c0851 --- /dev/null +++ b/example/vm.json @@ -0,0 +1,2 @@ +{ +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..d804df560 --- /dev/null +++ b/flake.lock @@ -0,0 +1,63 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1748026580, + "narHash": "sha256-rWtXrcIzU5wm/C8F9LWvUfBGu5U5E7cFzPYT1pHIJaQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "11cb3517b3af6af300dd6c055aeda73c9bf52c48", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "25.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1748026580, + "narHash": "sha256-rWtXrcIzU5wm/C8F9LWvUfBGu5U5E7cFzPYT1pHIJaQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "11cb3517b3af6af300dd6c055aeda73c9bf52c48", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "25.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "vmrunner": "vmrunner" + } + }, + "vmrunner": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1776892605, + "narHash": "sha256-r64A8ec7eGMReJw4gUKIUM25jNs20HcapzDZLno1Ic8=", + "owner": "mazunki", + "repo": "vmrunner", + "rev": "d07f22b5c1503b58d4fb67d8331efa67564d8db8", + "type": "github" + }, + "original": { + "owner": "mazunki", + "ref": "dev", + "repo": "vmrunner", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..a95d826a8 --- /dev/null +++ b/flake.nix @@ -0,0 +1,113 @@ +{ + description = "IncludeOS"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/25.05"; + # vmrunner.url = "github:includeos/vmrunner"; + vmrunner.url = "github:mazunki/vmrunner"; + }; + + outputs = { self, nixpkgs, vmrunner }: + let + system = "x86_64-linux"; + + mkIncludeos = { withCcache ? false, smp ? false, debug ? false }: + let + overlays = [ + (import ./overlay.nix { + inherit withCcache smp debug; + disableTargetWarning = true; + }) + ]; + pkgs = import nixpkgs { config = {}; inherit overlays system; }; + pkgsIncludeOS = pkgs.pkgsIncludeOS; + stdenvIncludeOS = pkgs.stdenvIncludeOS; + includeos = pkgsIncludeOS.includeos; + + chainloader = + let + chainloaderPkgs = import nixpkgs { + inherit system; + config = {}; + overlays = [ + (import ./overlay.nix { inherit withCcache; smp = false; disableTargetWarning = true; }) + ]; + crossSystem = { config = "i686-unknown-linux-musl"; }; + }; + in import ./chainloader.nix { + inherit nixpkgs withCcache; + pkgs = chainloaderPkgs; + }; + in { + inherit stdenvIncludeOS; # modified stdenv scope used by includeos (llvm, musl, libcxx) + inherit pkgsIncludeOS; # the musl/clang package scope used by IncludeOS + inherit pkgs; # nixpkgs with the IncludeOS overlay applied + inherit includeos; # the IncludeOS derivation to add to buildInputs + inherit chainloader; # 32-bit boot stub that hotswaps into the 64-bit unikernel + }; + + default = mkIncludeos {}; + defaultDebug = mkIncludeos { debug = true; }; + mkUnikernel = import ./mkUnikernel.nix { inherit system default defaultDebug vmrunner; }; + in { + packages.${system} = { + default = default.includeos // { debug = defaultDebug.includeos; }; + chainloader = default.chainloader; + example = mkUnikernel { unikernel = ./example; }; + }; + + devShells.${system}.default = import ./develop.nix { includeos = default.includeos; }; + + lib.${system} = { + inherit mkIncludeos mkUnikernel; + + mkChainloader = { mem }: + vmrunner.lib.${system}.mkBoot default.chainloader { inherit mem; }; + + boot = { kernel ? "service", mem, kvm ? false, debug ? false }: + let + chainloaders = self.lib.${system}.mkChainloader { inherit mem; }; + chainloader = if kvm then chainloaders.kvm + else if debug then chainloaders.debug + else chainloaders.default; + in { + type = "app"; + program = "${default.pkgs.writeShellScript "boot-unikernel" '' + set -e + dir="''${1:-./result}" + ${default.pkgs.lib.optionalString debug ''echo "boot: $dir/${kernel}" >&2''} + exec ${chainloader}/bin/boot "$dir/${kernel}" "$@" + ''}"; + }; + + mkBoot1 = { src, kernel ? "service", kvm ? false, mem ? "16G", debug ? false }: + let + bootApp = self.lib.${system}.boot { inherit kernel kvm debug mem; }; + in { + type = "app"; + program = "${default.pkgs.writeShellScript "run-flake-package" '' + set -e + exec ${bootApp.program} ${src} "$@" + ''}"; + }; + + mkBoot = args: + let + debugSrc = args.debugSrc or args.src; + base = builtins.removeAttrs args [ "debugSrc" ]; + tcg = self.lib.${system}.mkBoot1 (base // { kvm = false; debug = false; }); + kvm = self.lib.${system}.mkBoot1 (base // { kvm = true; debug = false; }); + debug = self.lib.${system}.mkBoot1 (base // { src = debugSrc; kvm = false; debug = true; }); + in + tcg // { inherit tcg kvm debug; }; + }; + + apps.${system} = { + default = self.apps.${system}.boot-unikernel; + + boot-unikernel = self.lib.${system}.boot { mem = "128m"; }; + + example = self.lib.${system}.mkBoot { src = self.packages.${system}.example; kernel = "hello_includeos.elf.bin"; }; + }; + }; +} diff --git a/mkUnikernel.nix b/mkUnikernel.nix new file mode 100644 index 000000000..2958babbb --- /dev/null +++ b/mkUnikernel.nix @@ -0,0 +1,86 @@ +# mkUnikernel.nix +{ system, default, defaultDebug, vmrunner }: +args: +let + debug = args.debug or false; + ios = if args ? includeos then args.includeos + else if debug then defaultDebug + else default; + vmrunnerPkg = if args ? vmrunner then args.vmrunner + else vmrunner.packages.${system}.default; + + unikernel = args.unikernel or ./example; + test = args.test or "test.py"; + doCheck = args.doCheck or false; + arch = args.arch or "x86_64"; + forProduction = args.forProduction or false; + + src = unikernel; +in +ios.includeos.stdenv.mkDerivation { + pname = "includeos_unikernel"; + version = "dev"; + dontStrip = true; + inherit doCheck; + inherit src; + + nativeBuildInputs = [ + ios.pkgs.buildPackages.nasm + ios.pkgs.buildPackages.cmake + ios.pkgsIncludeOS.suppressTargetWarningHook + ]; + + buildInputs = [ + ios.includeos + ios.chainloader + ]; + + cmakeFlags = [ + "-DARCH=${arch}" + "-DINCLUDEOS_PACKAGE=${ios.includeos}" + "-DCMAKE_MODULE_PATH=${ios.includeos}/cmake" + "-DFOR_PRODUCTION=${if forProduction then "ON" else "OFF"}" + "-DCMAKE_BUILD_TYPE=${if debug then "Debug" else "Release"}" + ] ++ ios.pkgs.lib.optionals debug [ + "-DCMAKE_C_FLAGS=-ffile-prefix-map=/build/${builtins.baseNameOf src}=/build/unikernel" + "-DCMAKE_CXX_FLAGS=-ffile-prefix-map=/build/${builtins.baseNameOf src}=/build/unikernel" + ]; + + installPhase = '' + runHook preInstall + # we want to place any files required by the test into the output + find -mindepth 1 -maxdepth 1 -type f -exec install -v -D -t "$out/" {} \; + + # especially the unikernel image, in case it wasn't at the rootdir already + find -mindepth 2 -name '*.elf.bin' -exec install -v -t "$out/" {} \; + runHook postInstall + ''; + + nativeCheckInputs = [ + vmrunnerPkg + ios.pkgs.grub2 + ios.pkgs.python3 + ios.pkgs.qemu + ios.pkgs.iputils + ios.pkgs.xorriso + ]; + + checkInputs = [ + ios.includeos.lest + ]; + + checkPhase = '' + runHook preCheck + set -e + if [ -e "${src}/${test}" ]; then + echo "Running IncludeOS test: ${src}/${test}" + python3 "${src}/${test}" + else + echo "Default test script '${test}', but no test was found 😟" + echo "For a custom path, consider specifying the path to the test script:" + echo " --argstr test 'path/to/test.py'" + exit 1 + fi + runHook postCheck + ''; +} diff --git a/overlay.nix b/overlay.nix index de76d9084..bb6e9f987 100644 --- a/overlay.nix +++ b/overlay.nix @@ -2,6 +2,7 @@ withCcache, # Enable ccache. Requires correct permissions, see below. disableTargetWarning ? true, # TODO: see https://github.com/NixOS/nixpkgs/issues/395191 smp, # Enable multicore support (SMP) + debug ? false, } : final: prev: { @@ -13,7 +14,7 @@ final: prev: { musl-unpatched = self.callPackage ./deps/musl-unpatched/default.nix { linuxHeaders = prev.linuxHeaders; }; # Import IncludeOS musl which will be built and linked with IncludeOS services - musl-includeos = self.callPackage ./deps/musl/default.nix { }; + musl-includeos = self.callPackage ./deps/musl/default.nix { inherit debug; }; # Clang with unpatched musl for building libcxx clang_musl_unpatched_nolibcxx = self.llvmPkgs.clangNoLibcxx.override (old: { @@ -191,7 +192,10 @@ final: prev: { smpFlags = if smp then [ "-DSMP=ON" ] else []; - cmakeFlags = archFlags ++ smpFlags; + debugFlags = if debug then [ "-DCMAKE_BUILD_TYPE=Debug" ] else []; + dontStrip = debug; + + cmakeFlags = archFlags ++ smpFlags ++ debugFlags; # Add some pasthroughs, for easily building the dependencies (for debugging): # $ nix-build -A NAME