diff --git a/overlays/qemu/default.nix b/overlays/qemu/default.nix new file mode 100644 index 0000000..3b655bb --- /dev/null +++ b/overlays/qemu/default.nix @@ -0,0 +1,14 @@ +# Based up original waokr by cleverca22 +# https://github.com/cleverca22/nixos-configs/blob/master/overlays/qemu/default.nix + +self: super: + +{ + qemu-user-arm = if self.stdenv.system == "x86_64-linux" + then self.pkgsi686Linux.callPackage ./qemu { user_arch = "arm"; } + else self.callPackage ./qemu { user_arch = "arm"; }; + qemu-user-x86 = self.callPackage ./qemu { user_arch = "x86_64"; }; + qemu-user-arm64 = self.callPackage ./qemu { user_arch = "aarch64"; }; + qemu-user-riscv32 = self.callPackage ./qemu { user_arch = "riscv32"; }; + qemu-user-riscv64 = self.callPackage ./qemu { user_arch = "riscv64"; }; +} diff --git a/overlays/qemu/qemu/default.nix b/overlays/qemu/qemu/default.nix new file mode 100644 index 0000000..34a987d --- /dev/null +++ b/overlays/qemu/qemu/default.nix @@ -0,0 +1,46 @@ +# Based up original waokr by cleverca22 +# https://raw.githubusercontent.com/cleverca22/nixos-configs/master/overlays/qemu/qemu/default.nix + +{ stdenv, fetchurl, python, pkgconfig, zlib, glib, user_arch, flex, bison, +makeStaticLibraries, glibc, qemu, fetchFromGitHub }: + +let + env2 = makeStaticLibraries stdenv; + myglib = (glib.override { stdenv = env2; }).overrideAttrs (drv: { + mesonFlags = (drv.mesonFlags or []) ++ [ "-Ddefault_library=both" ]; + }); + riscv_src = fetchFromGitHub { + owner = "riscv"; + repo = "riscv-qemu"; + rev = "7d2d2add16aff0304ab0c279152548dbd04a2138"; # riscv-all + sha256 = "16an7ifi2ifzqnlz0218rmbxq9vid434j98g14141qvlcl7gzsy2"; + }; + is_riscv = (user_arch == "riscv32") || (user_arch == "riscv64"); + arch_map = { + arm = "i386"; + aarch64 = "x86_64"; + riscv64 = "x86_64"; + x86_64 = "x86_64"; + }; +in +stdenv.mkDerivation rec { + name = "qemu-user-${user_arch}-${version}"; + version = "3.1.0"; + src = if is_riscv then riscv_src else qemu.src; + buildInputs = [ python pkgconfig zlib.static myglib flex bison glibc.static ]; + patches = [ ./qemu-stack.patch ]; + configureFlags = [ + "--enable-linux-user" "--target-list=${user_arch}-linux-user" + "--disable-bsd-user" "--disable-system" "--disable-vnc" + "--disable-curses" "--disable-sdl" "--disable-vde" + "--disable-bluez" "--disable-kvm" + "--static" + "--disable-tools" + "--cpu=${arch_map.${user_arch}}" + ]; + NIX_LDFLAGS = [ "-lglib-2.0" ]; + enableParallelBuilding = true; + postInstall = '' + cc -static ${./qemu-wrap.c} -D QEMU_ARM_BIN="\"qemu-${user_arch}"\" -o $out/bin/qemu-wrap + ''; +} diff --git a/overlays/qemu/qemu/qemu-stack.patch b/overlays/qemu/qemu/qemu-stack.patch new file mode 100644 index 0000000..ce97c9e --- /dev/null +++ b/overlays/qemu/qemu/qemu-stack.patch @@ -0,0 +1,11 @@ +--- a/linux-user/elfload.c 2016-09-02 12:34:22.000000000 -0300 ++++ b/linux-user/elfload.c 2017-07-09 18:44:22.420244038 -0300 +@@ -1419,7 +1419,7 @@ + * dependent on stack size, but guarantee at least 32 pages for + * backwards compatibility. + */ +-#define STACK_LOWER_LIMIT (32 * TARGET_PAGE_SIZE) ++#define STACK_LOWER_LIMIT (128 * TARGET_PAGE_SIZE) + + static abi_ulong setup_arg_pages(struct linux_binprm *bprm, + struct image_info *info) diff --git a/overlays/qemu/qemu/qemu-wrap.c b/overlays/qemu/qemu/qemu-wrap.c new file mode 100644 index 0000000..7c6ac69 --- /dev/null +++ b/overlays/qemu/qemu/qemu-wrap.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include +#include + +#if !defined(QEMU_ARM_BIN) + #define QEMU_ARM_BIN "qemu-arm" +#endif + +const char * qemu_arm_bin = QEMU_ARM_BIN; + +// This program takes arguments according to the behavior of binfmt_misc with +// the preserve-argv[0] flag set. +// +// The first value in argv is the name of this executable, uninteresting. +// The second value is the full path of the executable to run with the +// alternate interpreter. +// The third value is the name that executable was called with. +// +// This program passes the third value in to qemu-arm after the -0 flag. +int main(int argc, char const* argv[]) { + // Abort if we don't have sufficient arguments + if(argc < 3){ + fprintf( stderr, "qemu-arm wrapper called with too few arguments.\nEnsure that the 'P' flag is set in binfmt_misc.\n"); + return -1; + } + + char *qemu; + asprintf(&qemu, "%s/%s", dirname(argv[0]), qemu_arm_bin); + + // Allocate the new argc array to pass to qemu-arm + const int new_argc = argc + 1; + char** const new_argv = alloca((new_argc + 1) * sizeof(void *)); + + // Fill this new array + new_argv[0] = qemu; + new_argv[1] = strdup("-0"); + new_argv[2] = strdup(argv[2]); + new_argv[3] = strdup(argv[1]); + for(int i = 4; i < new_argc; ++i){ + new_argv[i] = strdup(argv[i-1]); + } + new_argv[new_argc] = NULL; + + // Run qemu with the new arguments + execvp(new_argv[0], new_argv); + const int ret = errno; + + // Clean up, haha C + for(int i = 0; i < new_argc; ++i){ + free(new_argv[i]); + } + + return ret; +}; diff --git a/roles/qemu.nix b/roles/qemu.nix new file mode 100644 index 0000000..89319cb --- /dev/null +++ b/roles/qemu.nix @@ -0,0 +1,53 @@ +# Based up original work by cleverca22 +# https://github.com/cleverca22/nixos-configs/blob/master/qemu.nix + + +{ config, pkgs, lib, ... }: + +with lib; +let + cfg = config.qemu-user; + arm = { + interpreter = "${pkgs.qemu-user-arm}/bin/qemu-arm"; + magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00''; + mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff''; + }; + aarch64 = { + interpreter = "${pkgs.qemu-user-arm64}/bin/qemu-aarch64"; + magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00''; + mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff''; + }; + riscv64 = { + interpreter = "${pkgs.qemu-riscv64}/bin/qemu-riscv64"; + magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00''; + mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff''; + }; +in { + options = { + qemu-user = { + arm = mkEnableOption "enable 32bit arm emulation"; + aarch64 = mkEnableOption "enable 64bit arm emulation"; + riscv64 = mkEnableOption "enable 64bit riscv emulation"; + }; + nix.supportedPlatforms = mkOption { + type = types.listOf types.str; + description = "extra platforms that nix will run binaries for"; + default = []; + }; + }; + config = mkIf (cfg.arm || cfg.aarch64) { + nixpkgs = { + overlays = [ (import ../overlays/qemu) ]; + }; + boot.binfmt.registrations = + optionalAttrs cfg.arm { inherit arm; } // + optionalAttrs cfg.aarch64 { inherit aarch64; } // + optionalAttrs cfg.riscv64 { inherit riscv64; }; + nix.supportedPlatforms = (optionals cfg.arm [ "armv6l-linux" "armv7l-linux" ]) + ++ (optional cfg.aarch64 "aarch64-linux"); + nix.extraOptions = '' + extra-platforms = ${toString config.nix.supportedPlatforms} i686-linux + ''; + nix.sandboxPaths = [ "/run/binfmt" ] ++ (optional cfg.arm "${pkgs.qemu-user-arm}") ++ (optional cfg.aarch64 "${pkgs.qemu-user-arm64}"); + }; +}