292 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			292 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
# Xen hypervisor (Dom0) support.
 | 
						|
 | 
						|
{ config, lib, pkgs, ... }:
 | 
						|
 | 
						|
with lib;
 | 
						|
 | 
						|
let
 | 
						|
  cfg = config.virtualisation.xen;
 | 
						|
in
 | 
						|
 | 
						|
{
 | 
						|
  ###### interface
 | 
						|
 | 
						|
  options = {
 | 
						|
 | 
						|
    virtualisation.xen.enable =
 | 
						|
      mkOption {
 | 
						|
        default = false;
 | 
						|
        description =
 | 
						|
          ''
 | 
						|
            Setting this option enables the Xen hypervisor, a
 | 
						|
            virtualisation technology that allows multiple virtual
 | 
						|
            machines, known as <emphasis>domains</emphasis>, to run
 | 
						|
            concurrently on the physical machine.  NixOS runs as the
 | 
						|
            privileged <emphasis>Domain 0</emphasis>.  This option
 | 
						|
            requires a reboot to take effect.
 | 
						|
          '';
 | 
						|
      };
 | 
						|
 | 
						|
    virtualisation.xen.bootParams =
 | 
						|
      mkOption {
 | 
						|
        default = "";
 | 
						|
        description =
 | 
						|
          ''
 | 
						|
            Parameters passed to the Xen hypervisor at boot time.
 | 
						|
          '';
 | 
						|
      };
 | 
						|
 | 
						|
    virtualisation.xen.domain0MemorySize =
 | 
						|
      mkOption {
 | 
						|
        default = 0;
 | 
						|
        example = 512;
 | 
						|
        description =
 | 
						|
          ''
 | 
						|
            Amount of memory (in MiB) allocated to Domain 0 on boot.
 | 
						|
            If set to 0, all memory is assigned to Domain 0.
 | 
						|
          '';
 | 
						|
      };
 | 
						|
 | 
						|
    virtualisation.xen.bridge =
 | 
						|
      mkOption {
 | 
						|
        default = "xenbr0";
 | 
						|
        description =
 | 
						|
          ''
 | 
						|
            Create a bridge for the Xen domUs to connect to.
 | 
						|
          '';
 | 
						|
      };
 | 
						|
 | 
						|
    virtualisation.xen.stored =
 | 
						|
      mkOption {
 | 
						|
        type = types.path;
 | 
						|
        description =
 | 
						|
          ''
 | 
						|
            Xen Store daemon to use. Defaults to oxenstored of the xen package.
 | 
						|
          '';
 | 
						|
      };
 | 
						|
 | 
						|
    virtualisation.xen.trace =
 | 
						|
      mkOption {
 | 
						|
        default = false;
 | 
						|
        description =
 | 
						|
          ''
 | 
						|
            Enable Xen tracing.
 | 
						|
          '';
 | 
						|
      };
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  ###### implementation
 | 
						|
 | 
						|
  config = mkIf cfg.enable {
 | 
						|
    assertions = [ {
 | 
						|
      assertion = pkgs.stdenv.isx86_64;
 | 
						|
      message = "Xen currently not supported on ${pkgs.stdenv.system}";
 | 
						|
    } {
 | 
						|
      assertion = config.boot.loader.grub.enable && (config.boot.loader.grub.efiSupport == false);
 | 
						|
      message = "Xen currently does not support EFI boot";
 | 
						|
    } ];
 | 
						|
 | 
						|
    virtualisation.xen.stored = mkDefault "${pkgs.xen}/bin/oxenstored";
 | 
						|
 | 
						|
    environment.systemPackages = [ pkgs.xen ];
 | 
						|
 | 
						|
    # Make sure Domain 0 gets the required configuration
 | 
						|
    #boot.kernelPackages = pkgs.boot.kernelPackages.override { features={xen_dom0=true;}; };
 | 
						|
 | 
						|
    boot.kernelModules =
 | 
						|
      [ "xen-evtchn" "xen-gntdev" "xen-gntalloc" "xen-blkback" "xen-netback"
 | 
						|
        "xen-pciback" "evtchn" "gntdev" "netbk" "blkbk" "xen-scsibk"
 | 
						|
        "usbbk" "pciback" "xen-acpi-processor" "blktap2" "tun" "netxen_nic"
 | 
						|
        "xen_wdt" "xen-acpi-processor" "xen-privcmd" "xen-scsiback"
 | 
						|
        "xenfs"
 | 
						|
      ];
 | 
						|
 | 
						|
 | 
						|
    # The radeonfb kernel module causes the screen to go black as soon
 | 
						|
    # as it's loaded, so don't load it.
 | 
						|
    boot.blacklistedKernelModules = [ "radeonfb" ];
 | 
						|
 | 
						|
    # Increase the number of loopback devices from the default (8),
 | 
						|
    # which is way too small because every VM virtual disk requires a
 | 
						|
    # loopback device.
 | 
						|
    boot.extraModprobeConfig =
 | 
						|
      ''
 | 
						|
        options loop max_loop=64
 | 
						|
      '';
 | 
						|
 | 
						|
    virtualisation.xen.bootParams = [] ++
 | 
						|
      optionals cfg.trace [ "loglvl=all" "guest_loglvl=all" ] ++
 | 
						|
      optional (cfg.domain0MemorySize != 0) "dom0_mem=${toString cfg.domain0MemorySize}M";
 | 
						|
 | 
						|
    system.extraSystemBuilderCmds =
 | 
						|
      ''
 | 
						|
        ln -s ${pkgs.xen}/boot/xen.gz $out/xen.gz
 | 
						|
        echo "${toString cfg.bootParams}" > $out/xen-params
 | 
						|
      '';
 | 
						|
 | 
						|
    # Mount the /proc/xen pseudo-filesystem.
 | 
						|
    system.activationScripts.xen =
 | 
						|
      ''
 | 
						|
        if [ -d /proc/xen ]; then
 | 
						|
            ${pkgs.kmod}/bin/modprobe xenfs 2> /dev/null
 | 
						|
            ${pkgs.utillinux}/bin/mountpoint -q /proc/xen || \
 | 
						|
                ${pkgs.utillinux}/bin/mount -t xenfs none /proc/xen
 | 
						|
        fi
 | 
						|
      '';
 | 
						|
 | 
						|
    # Domain 0 requires a pvops-enabled kernel.
 | 
						|
    system.requiredKernelConfig = with config.lib.kernelConfig;
 | 
						|
      [ (isYes "XEN")
 | 
						|
        (isYes "X86_IO_APIC")
 | 
						|
        (isYes "ACPI")
 | 
						|
        (isYes "XEN_DOM0")
 | 
						|
        (isYes "PCI_XEN")
 | 
						|
        (isYes "XEN_DEV_EVTCHN")
 | 
						|
        (isYes "XENFS")
 | 
						|
        (isYes "XEN_COMPAT_XENFS")
 | 
						|
        (isYes "XEN_SYS_HYPERVISOR")
 | 
						|
        (isYes "XEN_GNTDEV")
 | 
						|
        (isYes "XEN_BACKEND")
 | 
						|
        (isModule "XEN_NETDEV_BACKEND")
 | 
						|
        (isModule "XEN_BLKDEV_BACKEND")
 | 
						|
        (isModule "XEN_PCIDEV_BACKEND")
 | 
						|
        (isYes "XEN_BALLOON")
 | 
						|
        (isYes "XEN_SCRUB_PAGES")
 | 
						|
      ];
 | 
						|
 | 
						|
 | 
						|
    environment.etc =
 | 
						|
      [ { source = "${pkgs.xen}/etc/xen/xl.conf";
 | 
						|
          target = "xen/xl.conf";
 | 
						|
        }
 | 
						|
        { source = "${pkgs.xen}/etc/xen/scripts";
 | 
						|
          target = "xen/scripts";
 | 
						|
        }
 | 
						|
      ];
 | 
						|
 | 
						|
    # Xen provides udev rules.
 | 
						|
    services.udev.packages = [ pkgs.xen ];
 | 
						|
 | 
						|
    services.udev.path = [ pkgs.bridge-utils pkgs.iproute ];
 | 
						|
 | 
						|
    systemd.services.xen-store = {
 | 
						|
      description = "Xen Store Daemon";
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
      after = [ "network.target" "xen-store.socket" ];
 | 
						|
      requires = [ "xen-store.socket" ];
 | 
						|
      preStart = ''
 | 
						|
        export XENSTORED_ROOTDIR="/var/lib/xenstored"
 | 
						|
        rm -f "$XENSTORED_ROOTDIR"/tdb* &>/dev/null
 | 
						|
 | 
						|
        mkdir -p /var/run
 | 
						|
        ${optionalString cfg.trace "mkdir -p /var/log/xen"}
 | 
						|
        grep -q control_d /proc/xen/capabilities
 | 
						|
        '';
 | 
						|
      serviceConfig.ExecStart = ''
 | 
						|
        ${cfg.stored}${optionalString cfg.trace " -T /var/log/xen/xenstored-trace.log"} --no-fork
 | 
						|
        '';
 | 
						|
      postStart = ''
 | 
						|
        time=0
 | 
						|
        timeout=30
 | 
						|
        # Wait for xenstored to actually come up, timing out after 30 seconds
 | 
						|
        while [ $time -lt $timeout ] && ! `${pkgs.xen}/bin/xenstore-read -s / >/dev/null 2>&1` ; do
 | 
						|
            time=$(($time+1))
 | 
						|
            sleep 1
 | 
						|
        done
 | 
						|
 | 
						|
        # Exit if we timed out
 | 
						|
        if ! [ $time -lt $timeout ] ; then
 | 
						|
            echo "Could not start Xenstore Daemon"
 | 
						|
            exit 1
 | 
						|
        fi
 | 
						|
 | 
						|
        ${pkgs.xen}/bin/xenstore-write "/local/domain/0/name" "Domain-0"
 | 
						|
        ${pkgs.xen}/bin/xenstore-write "/local/domain/0/domid" 0
 | 
						|
        '';
 | 
						|
    };
 | 
						|
 | 
						|
    systemd.sockets.xen-store = {
 | 
						|
      description = "XenStore Socket for userspace API";
 | 
						|
      wantedBy = [ "sockets.target" ];
 | 
						|
      socketConfig = {
 | 
						|
        ListenStream = [ "/var/run/xenstored/socket" "/var/run/xenstored/socket_ro" ];
 | 
						|
        SocketMode = "0660";
 | 
						|
        SocketUser = "root";
 | 
						|
        SocketGroup = "root";
 | 
						|
      };
 | 
						|
    };
 | 
						|
 | 
						|
 | 
						|
    systemd.services.xen-console = {
 | 
						|
      description = "Xen Console Daemon";
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
      after = [ "xen-store.service" ];
 | 
						|
      preStart = ''
 | 
						|
        mkdir -p /var/run/xen
 | 
						|
        ${optionalString cfg.trace "mkdir -p /var/log/xen"}
 | 
						|
        grep -q control_d /proc/xen/capabilities
 | 
						|
        '';
 | 
						|
      serviceConfig = {
 | 
						|
        ExecStart = ''
 | 
						|
          ${pkgs.xen}/bin/xenconsoled${optionalString cfg.trace " --log=all --log-dir=/var/log/xen"}
 | 
						|
          '';
 | 
						|
      };
 | 
						|
    };
 | 
						|
 | 
						|
 | 
						|
    systemd.services.xen-qemu = {
 | 
						|
      description = "Xen Qemu Daemon";
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
      after = [ "xen-console.service" ];
 | 
						|
      serviceConfig.ExecStart = ''
 | 
						|
        ${pkgs.xen}/lib/xen/bin/qemu-system-i386 -xen-domid 0 -xen-attach -name dom0 -nographic -M xenpv \
 | 
						|
           -monitor /dev/null -serial /dev/null -parallel /dev/null
 | 
						|
        '';
 | 
						|
    };
 | 
						|
 | 
						|
 | 
						|
    systemd.services.xen-watchdog = {
 | 
						|
      description = "Xen Watchdog Daemon";
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
      after = [ "xen-qemu.service" ];
 | 
						|
      serviceConfig.ExecStart = "${pkgs.xen}/bin/xenwatchdogd 30 15";
 | 
						|
      serviceConfig.Type = "forking";
 | 
						|
      serviceConfig.RestartSec = "1";
 | 
						|
      serviceConfig.Restart = "on-failure";
 | 
						|
    };
 | 
						|
 | 
						|
 | 
						|
    systemd.services.xen-bridge = {
 | 
						|
      description = "Xen bridge";
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
      before = [ "xen-domains.service" ];
 | 
						|
      serviceConfig.RemainAfterExit = "yes";
 | 
						|
      serviceConfig.ExecStart = "${pkgs.bridge-utils}/bin/brctl addbr ${cfg.bridge}";
 | 
						|
      postStart = "${pkgs.inetutils}/bin/ifconfig ${cfg.bridge} up";
 | 
						|
      serviceConfig.ExecStop = "${pkgs.inetutils}/bin/ifconfig ${cfg.bridge} down";
 | 
						|
      postStop = "${pkgs.bridge-utils}/bin/brctl delbr ${cfg.bridge}";
 | 
						|
    };
 | 
						|
 | 
						|
 | 
						|
    systemd.services.xen-domains = {
 | 
						|
      description = "Xen domains - automatically starts, saves and restores Xen domains";
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
      after = [ "xen-bridge.service" "xen-qemu.service" ];
 | 
						|
      ## To prevent a race between dhcpcd and xend's bridge setup script
 | 
						|
      ## (which renames eth* to peth* and recreates eth* as a virtual
 | 
						|
      ## device), start dhcpcd after xend.
 | 
						|
      before = [ "dhcpd.service" ];
 | 
						|
      restartIfChanged = false;
 | 
						|
      serviceConfig.RemainAfterExit = "yes";
 | 
						|
      path = [ pkgs.xen ];
 | 
						|
      environment.XENDOM_CONFIG = "${pkgs.xen}/etc/sysconfig/xendomains";
 | 
						|
      preStart = "mkdir -p /var/lock/subsys -m 755";
 | 
						|
      serviceConfig.ExecStart = "${pkgs.xen}/etc/init.d/xendomains start";
 | 
						|
      serviceConfig.ExecStop = "${pkgs.xen}/etc/init.d/xendomains stop";
 | 
						|
    };
 | 
						|
 | 
						|
  };
 | 
						|
 | 
						|
}
 |