Commit d715abab1154

Vincent Demeester <vincent@sbr.pm>
2019-03-04 16:32:45
modules: add qemu binfmt emulation ๐Ÿ’ƒ
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 5958e9e
machine/shikoku.nix
@@ -47,6 +47,7 @@ with import ../assets/machines.nix; {
     gaming.enable = true;
     #ipfs.enable = true;
     nix-config.buildCores = 4;
+    qemu-user = { arm = true; aarch64 = true; };
     ssh.enable = true;
     virtualization = {
       enable = true;
machine/wakasu.nix
@@ -18,6 +18,7 @@ with import ../assets/machines.nix; {
     laptop.enable = true;
     desktop.autoLogin = true;
     nix-config.buildCores = 4;
+    qemu-user = { arm = true; aarch64 = true; };
     ssh.enable = true;
     virtualization = {
       enable = true;
modules/profiles/qemu.nix
@@ -0,0 +1,49 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.profiles.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 = {
+    profiles.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/default.nix) ];
+    };
+    boot.binfmtMiscRegistrations =
+      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}");
+  };
+}
modules/module-list.nix
@@ -21,6 +21,7 @@
     ./profiles/nix-auto-update.nix
     ./profiles/printing.nix
     ./profiles/pulseaudio.nix
+    ./profiles/qemu.nix
     ./profiles/scanning.nix
     ./profiles/ssh.nix
     ./profiles/syncthing.nix
overlays/qemu/qemu/default.nix
@@ -0,0 +1,43 @@
+{ 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 []) ++ [ "--default-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
+  '';
+}
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)
overlays/qemu/qemu/qemu-wrap.c
@@ -0,0 +1,58 @@
+#include <alloca.h>
+#include <malloc.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#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;
+};
overlays/qemu/default.nix
@@ -0,0 +1,11 @@
+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"; };
+}