# Xen hypervisor (Dom0) support. { config, pkgs, ... }: with pkgs.lib; let cfg = config.virtualisation.xen; xen = pkgs.xen; xendConfig = pkgs.writeText "xend-config.sxp" '' (loglevel DEBUG) (network-script network-bridge) (vif-script vif-bridge) ''; 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 domains, to run concurrently on the physical machine. NixOS runs as the privileged Domain 0. 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. ''; }; }; ###### implementation config = mkIf cfg.enable { environment.systemPackages = [ xen ]; # Domain 0 requires a pvops-enabled kernel. boot.kernelPackages = pkgs.linuxPackages_2_6_32_xen; boot.kernelModules = [ "xen_evtchn" "xen_gntdev" "xen_blkback" "xen_netback" "xen_pciback" "blktap" "tun" ]; # 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 = [ "loglvl=all" "guest_loglvl=all" ] ++ optional (cfg.domain0MemorySize != 0) "dom0_mem=${toString cfg.domain0MemorySize}M"; system.extraSystemBuilderCmds = '' ln -s ${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.sysvtools}/bin/mountpoint -q /proc/xen || \ ${pkgs.utillinux}/bin/mount -t xenfs none /proc/xen fi ''; jobs.xend = { description = "Xen control daemon"; startOn = "stopped udevtrigger"; path = [ pkgs.bridge_utils pkgs.gawk pkgs.iproute pkgs.nettools pkgs.utillinux pkgs.bash xen pkgs.pciutils pkgs.procps ]; preStart = '' ${xen}/sbin/xend start # Wait until Xend is running. for ((i = 0; i < 60; i++)); do echo "waiting for xend..."; ${xen}/sbin/xend status && break; done ${xen}/sbin/xend status || exit 1 ''; postStop = "${xen}/sbin/xend stop"; }; jobs.xendomains = { description = "Automatically starts, saves and restores Xen domains on startup/shutdown"; startOn = "started xend"; stopOn = "starting shutdown and stopping xend"; path = [ pkgs.xen ]; environment.XENDOM_CONFIG = "${xen}/etc/sysconfig/xendomains"; preStart = '' mkdir -p /var/lock/subsys -m 755 ${xen}/etc/init.d/xendomains start ''; postStop = "${xen}/etc/init.d/xendomains stop"; }; # To prevent a race between dhclient and xend's bridge setup # script (which renames eth* to peth* and recreates eth* as a # virtual device), start dhclient after xend. jobs.dhclient.startOn = mkOverride 50 "started xend"; environment.etc = [ { source = xendConfig; target = "xen/xend-config.sxp"; } { source = "${xen}/etc/xen/scripts"; target = "xen/scripts"; } ]; # Xen provides udev rules. services.udev.packages = [ xen ]; services.udev.path = [ pkgs.bridge_utils pkgs.iproute ]; }; }