summary refs log tree commit diff
path: root/nixos/modules/system
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2013-10-10 13:28:20 +0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2013-10-10 13:28:20 +0200
commit5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010 (patch)
treea6c0f605be6de3f372ae69905b331f9f75452da7 /nixos/modules/system
parent6070bc016bd2fd945b04347e25cfd3738622d2ac (diff)
downloadnixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.gz
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.bz2
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.lz
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.xz
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.zst
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.zip
Move all of NixOS to nixos/ in preparation of the repository merge
Diffstat (limited to 'nixos/modules/system')
-rw-r--r--nixos/modules/system/activation/activation-script.nix155
-rw-r--r--nixos/modules/system/activation/no-clone.nix11
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl362
-rw-r--r--nixos/modules/system/activation/top-level.nix194
-rw-r--r--nixos/modules/system/boot/kernel.nix304
-rw-r--r--nixos/modules/system/boot/kexec.nix21
-rw-r--r--nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub-builder.sh131
-rw-r--r--nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub.nix98
-rw-r--r--nixos/modules/system/boot/loader/efi.nix49
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh106
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir.nix63
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix261
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl265
-rw-r--r--nixos/modules/system/boot/loader/grub/memtest.nix39
-rw-r--r--nixos/modules/system/boot/loader/grub/winkler-gnu-blue-640x480.pngbin0 -> 74487 bytes
-rw-r--r--nixos/modules/system/boot/loader/grub/winkler-gnu-blue.README6
-rw-r--r--nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py114
-rw-r--r--nixos/modules/system/boot/loader/gummiboot/gummiboot.nix67
-rw-r--r--nixos/modules/system/boot/loader/init-script/init-script-builder.sh88
-rw-r--r--nixos/modules/system/boot/loader/init-script/init-script.nix50
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/builder.sh109
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix38
-rw-r--r--nixos/modules/system/boot/luksroot.nix176
-rw-r--r--nixos/modules/system/boot/modprobe.nix112
-rw-r--r--nixos/modules/system/boot/readonly-mountpoint.c20
-rw-r--r--nixos/modules/system/boot/shutdown.nix27
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh374
-rw-r--r--nixos/modules/system/boot/stage-1.nix343
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh173
-rw-r--r--nixos/modules/system/boot/stage-2.nix100
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix364
-rw-r--r--nixos/modules/system/boot/systemd.nix678
-rw-r--r--nixos/modules/system/etc/etc.nix117
-rw-r--r--nixos/modules/system/etc/make-etc.sh42
-rw-r--r--nixos/modules/system/etc/setup-etc.pl68
-rw-r--r--nixos/modules/system/upstart/upstart.nix286
36 files changed, 5411 insertions, 0 deletions
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
new file mode 100644
index 00000000000..dc017563217
--- /dev/null
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -0,0 +1,155 @@
+# generate the script used to activate the configuration.
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  addAttributeName = mapAttrs (a: v: v // {
+    text = ''
+      #### Activation script snippet ${a}:
+      ${v.text}
+    '';
+  });
+
+  path =
+    [ pkgs.coreutils pkgs.gnugrep pkgs.findutils
+      pkgs.glibc # needed for getent
+      pkgs.shadow
+      pkgs.nettools # needed for hostname
+    ];
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    system.activationScripts = mkOption {
+      default = {};
+
+      example = {
+        stdio = {
+          text = ''
+            # Needed by some programs.
+            ln -sfn /proc/self/fd /dev/fd
+            ln -sfn /proc/self/fd/0 /dev/stdin
+            ln -sfn /proc/self/fd/1 /dev/stdout
+            ln -sfn /proc/self/fd/2 /dev/stderr
+          '';
+          deps = [];
+        };
+      };
+
+      description = ''
+        Activate the new configuration (i.e., update /etc, make accounts,
+        and so on).
+      '';
+
+      merge = mergeTypedOption "script" builtins.isAttrs (fold mergeAttrs {});
+
+      apply = set: {
+        script =
+          ''
+            #! ${pkgs.stdenv.shell}
+
+            systemConfig=@out@
+
+            export PATH=/empty
+            for i in ${toString path}; do
+                PATH=$PATH:$i/bin:$i/sbin
+            done
+
+            # Ensure a consistent umask.
+            umask 0022
+
+            ${
+              let
+                set' = mapAttrs (n: v: if builtins.isString v then noDepEntry v else v) set;
+                withHeadlines = addAttributeName set';
+              in textClosureMap id (withHeadlines) (attrNames withHeadlines)
+            }
+
+            # Make this configuration the current configuration.
+            # The readlink is there to ensure that when $systemConfig = /system
+            # (which is a symlink to the store), /run/current-system is still
+            # used as a garbage collection root.
+            ln -sfn "$(readlink -f "$systemConfig")" /run/current-system
+
+            # Prevent the current configuration from being garbage-collected.
+            ln -sfn /run/current-system /nix/var/nix/gcroots/current-system
+          '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    system.activationScripts.stdio =
+      ''
+        # Needed by some programs.
+        ln -sfn /proc/self/fd /dev/fd
+        ln -sfn /proc/self/fd/0 /dev/stdin
+        ln -sfn /proc/self/fd/1 /dev/stdout
+        ln -sfn /proc/self/fd/2 /dev/stderr
+      '';
+
+    system.activationScripts.var =
+      ''
+        # Various log/runtime directories.
+
+        touch /var/run/utmp # must exist
+        chgrp ${toString config.ids.gids.utmp} /var/run/utmp
+        chmod 664 /var/run/utmp
+
+        mkdir -m 0755 -p /var/run/nix/current-load # for distributed builds
+        mkdir -m 0700 -p /var/run/nix/remote-stores
+
+        # Directory holding symlinks to currently running Upstart
+        # jobs.  Used to determine which jobs need to be restarted
+        # when switching to a new configuration.
+        mkdir -m 0700 -p /var/run/upstart-jobs
+
+        mkdir -m 0755 -p /var/log
+
+        touch /var/log/wtmp # must exist
+        chmod 644 /var/log/wtmp
+
+        touch /var/log/lastlog
+        chmod 644 /var/log/lastlog
+
+        mkdir -m 1777 -p /var/tmp
+
+        # Empty, read-only home directory of many system accounts.
+        mkdir -m 0555 -p /var/empty
+      '';
+
+    system.activationScripts.media =
+      ''
+        mkdir -m 0755 -p /media
+      '';
+
+    system.activationScripts.usrbinenv =
+      ''
+        mkdir -m 0755 -p /usr/bin
+        ln -sfn ${pkgs.coreutils}/bin/env /usr/bin/.env.tmp
+        mv /usr/bin/.env.tmp /usr/bin/env # atomically replace /usr/bin/env
+      '';
+
+    system.activationScripts.tmpfs =
+      ''
+        ${pkgs.utillinux}/bin/mount -o "remount,size=${config.boot.devSize}" none /dev
+        ${pkgs.utillinux}/bin/mount -o "remount,size=${config.boot.devShmSize}" none /dev/shm
+        ${pkgs.utillinux}/bin/mount -o "remount,size=${config.boot.runSize}" none /run
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/system/activation/no-clone.nix b/nixos/modules/system/activation/no-clone.nix
new file mode 100644
index 00000000000..f15809e4d8b
--- /dev/null
+++ b/nixos/modules/system/activation/no-clone.nix
@@ -0,0 +1,11 @@
+# This configuration is not made to figure inside the module-list.nix to
+# allow clone of the first level.
+{pkgs, ...}:
+
+with pkgs.lib;
+
+{
+  boot.loader.grub.device = mkOverrideTemplate 0 {} "nodev";
+  nesting.children = mkOverrideTemplate 0 {} [];
+  nesting.clone = mkOverrideTemplate 0 {} [];
+}
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
new file mode 100644
index 00000000000..33ae3aef9fc
--- /dev/null
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -0,0 +1,362 @@
+#! @perl@
+
+use strict;
+use warnings;
+use File::Basename;
+use File::Slurp;
+use Sys::Syslog qw(:standard :macros);
+use Cwd 'abs_path';
+
+my $out = "@out@";
+
+my $startListFile = "/run/systemd/start-list";
+my $restartListFile = "/run/systemd/restart-list";
+my $reloadListFile = "/run/systemd/reload-list";
+
+my $action = shift @ARGV;
+
+if (!defined $action || ($action ne "switch" && $action ne "boot" && $action ne "test")) {
+    print STDERR <<EOF;
+Usage: $0 [switch|boot|test]
+
+switch: make the configuration the boot default and activate now
+boot:   make the configuration the boot default
+test:   activate the configuration, but don\'t make it the boot default
+EOF
+    exit 1;
+}
+
+die "This is not a NixOS installation (/etc/NIXOS is missing)!\n" unless -f "/etc/NIXOS";
+
+openlog("nixos", "", LOG_USER);
+
+# Install or update the bootloader.
+if ($action eq "switch" || $action eq "boot") {
+    system("@installBootLoader@ $out") == 0 or exit 1;
+}
+
+# Just in case the new configuration hangs the system, do a sync now.
+system("@coreutils@/bin/sync") unless ($ENV{"NIXOS_NO_SYNC"} // "") eq "1";
+
+exit 0 if $action eq "boot";
+
+# Check if we can activate the new configuration.
+my $oldVersion = read_file("/run/current-system/init-interface-version", err_mode => 'quiet') // "";
+my $newVersion = read_file("$out/init-interface-version");
+
+if ($newVersion ne $oldVersion) {
+    print STDERR <<EOF;
+Warning: the new NixOS configuration has an ‘init’ that is
+incompatible with the current configuration.  The new configuration
+won\'t take effect until you reboot the system.
+EOF
+    exit 100;
+}
+
+syslog(LOG_NOTICE, "switching to system configuration $out");
+
+# Ignore SIGHUP so that we're not killed if we're running on (say)
+# virtual console 1 and we restart the "tty1" unit.
+$SIG{PIPE} = "IGNORE";
+
+sub getActiveUnits {
+    # FIXME: use D-Bus or whatever to query this, since parsing the
+    # output of list-units is likely to break.
+    my $lines = `@systemd@/bin/systemctl list-units --full`;
+    my $res = {};
+    foreach my $line (split '\n', $lines) {
+        chomp $line;
+        last if $line eq "";
+        $line =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s/ or next;
+        next if $1 eq "UNIT";
+        $res->{$1} = { load => $2, state => $3, substate => $4 };
+    }
+    return $res;
+}
+
+sub parseFstab {
+    my ($filename) = @_;
+    my ($fss, $swaps);
+    foreach my $line (read_file($filename, err_mode => 'quiet')) {
+        chomp $line;
+        $line =~ s/^\s*#.*//;
+        next if $line =~ /^\s*$/;
+        my @xs = split / /, $line;
+        if ($xs[2] eq "swap") {
+            $swaps->{$xs[0]} = { options => $xs[3] // "" };
+        } else {
+            $fss->{$xs[1]} = { device => $xs[0], fsType => $xs[2], options => $xs[3] // "" };
+        }
+    }
+    return ($fss, $swaps);
+}
+
+sub parseUnit {
+    my ($filename) = @_;
+    my $info = {};
+    foreach my $line (read_file($filename)) {
+        # FIXME: not quite correct.
+        $line =~ /^([^=]+)=(.*)$/ or next;
+        $info->{$1} = $2;
+    }
+    return $info;
+}
+
+sub boolIsTrue {
+    my ($s) = @_;
+    return $s eq "yes" || $s eq "true";
+}
+
+# Stop all services that no longer exist or have changed in the new
+# configuration.
+my (@unitsToStop, @unitsToSkip);
+my $activePrev = getActiveUnits;
+while (my ($unit, $state) = each %{$activePrev}) {
+    my $baseUnit = $unit;
+
+    # Recognise template instances.
+    $baseUnit = "$1\@.$2" if $unit =~ /^(.*)@[^\.]*\.(.*)$/;
+    my $prevUnitFile = "/etc/systemd/system/$baseUnit";
+    my $newUnitFile = "$out/etc/systemd/system/$baseUnit";
+
+    my $baseName = $baseUnit;
+    $baseName =~ s/\.[a-z]*$//;
+
+    if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
+        if (! -e $newUnitFile) {
+            push @unitsToStop, $unit;
+        }
+
+        elsif ($unit =~ /\.target$/) {
+            my $unitInfo = parseUnit($newUnitFile);
+
+            # Cause all active target units to be restarted below.
+            # This should start most changed units we stop here as
+            # well as any new dependencies (including new mounts and
+            # swap devices).  FIXME: the suspend target is sometimes
+            # active after the system has resumed, which probably
+            # should not be the case.  Just ignore it.
+            if ($unit ne "suspend.target" && $unit ne "hibernate.target" && $unit ne "hybrid-sleep.target") {
+                unless (boolIsTrue($unitInfo->{'RefuseManualStart'} // "no")) {
+                    write_file($startListFile, { append => 1 }, "$unit\n");
+                }
+            }
+
+            # Stop targets that have X-StopOnReconfiguration set.
+            # This is necessary to respect dependency orderings
+            # involving targets: if unit X starts after target Y and
+            # target Y starts after unit Z, then if X and Z have both
+            # changed, then X should be restarted after Z.  However,
+            # if target Y is in the "active" state, X and Z will be
+            # restarted at the same time because X's dependency on Y
+            # is already satisfied.  Thus, we need to stop Y first.
+            # Stopping a target generally has no effect on other units
+            # (unless there is a PartOf dependency), so this is just a
+            # bookkeeping thing to get systemd to do the right thing.
+            if (boolIsTrue($unitInfo->{'X-StopOnReconfiguration'} // "no")) {
+                push @unitsToStop, $unit;
+            }
+        }
+
+        elsif (abs_path($prevUnitFile) ne abs_path($newUnitFile)) {
+            if ($unit eq "sysinit.target" || $unit eq "basic.target" || $unit eq "multi-user.target" || $unit eq "graphical.target") {
+                # Do nothing.  These cannot be restarted directly.
+            } elsif ($unit =~ /\.mount$/) {
+                # Reload the changed mount unit to force a remount.
+                write_file($reloadListFile, { append => 1 }, "$unit\n");
+            } elsif ($unit =~ /\.socket$/ || $unit =~ /\.path$/) {
+                # FIXME: do something?
+            } else {
+                my $unitInfo = parseUnit($newUnitFile);
+                if (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes")) {
+                    push @unitsToSkip, $unit;
+                } else {
+                    # If this unit is socket-activated, then stop the
+                    # socket unit(s) as well, and restart the
+                    # socket(s) instead of the service.
+                    my $socketActivated = 0;
+                    if ($unit =~ /\.service$/) {
+                        my @sockets = split / /, ($unitInfo->{Sockets} // "");
+                        if (scalar @sockets == 0) {
+                            @sockets = ("$baseName.socket");
+                        }
+                        foreach my $socket (@sockets) {
+                            if (defined $activePrev->{$socket}) {
+                                push @unitsToStop, $socket;
+                                write_file($startListFile, { append => 1 }, "$socket\n");
+                                $socketActivated = 1;
+                            }
+                        }
+                    }
+
+                    if (!boolIsTrue($unitInfo->{'X-StopIfChanged'} // "yes")) {
+
+                        # This unit should be restarted instead of
+                        # stopped and started.
+                        write_file($restartListFile, { append => 1 }, "$unit\n");
+
+                    } else {
+
+                        # If the unit is not socket-activated, record
+                        # that this unit needs to be started below.
+                        # We write this to a file to ensure that the
+                        # service gets restarted if we're interrupted.
+                        if (!$socketActivated) {
+                            write_file($startListFile, { append => 1 }, "$unit\n");
+                        }
+
+                        push @unitsToStop, $unit;
+
+                    }
+                }
+            }
+        }
+    }
+}
+
+sub pathToUnitName {
+    my ($path) = @_;
+    die unless substr($path, 0, 1) eq "/";
+    return "-" if $path eq "/";
+    $path = substr($path, 1);
+    $path =~ s/\//-/g;
+    # FIXME: handle - and unprintable characters.
+    return $path;
+}
+
+sub unique {
+    my %seen;
+    my @res;
+    foreach my $name (@_) {
+        next if $seen{$name};
+        $seen{$name} = 1;
+        push @res, $name;
+    }
+    return @res;
+}
+
+# Compare the previous and new fstab to figure out which filesystems
+# need a remount or need to be unmounted.  New filesystems are mounted
+# automatically by starting local-fs.target.  FIXME: might be nicer if
+# we generated units for all mounts; then we could unify this with the
+# unit checking code above.
+my ($prevFss, $prevSwaps) = parseFstab "/etc/fstab";
+my ($newFss, $newSwaps) = parseFstab "$out/etc/fstab";
+foreach my $mountPoint (keys %$prevFss) {
+    my $prev = $prevFss->{$mountPoint};
+    my $new = $newFss->{$mountPoint};
+    my $unit = pathToUnitName($mountPoint) . ".mount";
+    if (!defined $new) {
+        # Filesystem entry disappeared, so unmount it.
+        push @unitsToStop, $unit;
+    } elsif ($prev->{fsType} ne $new->{fsType} || $prev->{device} ne $new->{device}) {
+        # Filesystem type or device changed, so unmount and mount it.
+        write_file($startListFile, { append => 1 }, "$unit\n");
+        push @unitsToStop, $unit;
+    } elsif ($prev->{options} ne $new->{options}) {
+        # Mount options changes, so remount it.
+        write_file($reloadListFile, { append => 1 }, "$unit\n");
+    }
+}
+
+# Also handles swap devices.
+foreach my $device (keys %$prevSwaps) {
+    my $prev = $prevSwaps->{$device};
+    my $new = $newSwaps->{$device};
+    if (!defined $new) {
+        # Swap entry disappeared, so turn it off.  Can't use
+        # "systemctl stop" here because systemd has lots of alias
+        # units that prevent a stop from actually calling
+        # "swapoff".
+        print STDERR "stopping swap device: $device\n";
+        system("@utillinux@/sbin/swapoff", $device);
+    }
+    # FIXME: update swap options (i.e. its priority).
+}
+
+if (scalar @unitsToStop > 0) {
+    @unitsToStop = unique(@unitsToStop);
+    print STDERR "stopping the following units: ", join(", ", sort(@unitsToStop)), "\n";
+    system("@systemd@/bin/systemctl", "stop", "--", @unitsToStop); # FIXME: ignore errors?
+}
+
+print STDERR "NOT restarting the following units: ", join(", ", sort(@unitsToSkip)), "\n"
+    if scalar @unitsToSkip > 0;
+
+# Activate the new configuration (i.e., update /etc, make accounts,
+# and so on).
+my $res = 0;
+print STDERR "activating the configuration...\n";
+system("$out/activate", "$out") == 0 or $res = 2;
+
+# Restart systemd if necessary.
+if (abs_path("/proc/1/exe") ne abs_path("@systemd@/lib/systemd/systemd")) {
+    print STDERR "restarting systemd...\n";
+    system("@systemd@/bin/systemctl", "daemon-reexec") == 0 or $res = 2;
+}
+
+# Forget about previously failed services.
+system("@systemd@/bin/systemctl", "reset-failed");
+
+# Make systemd reload its units.
+system("@systemd@/bin/systemctl", "daemon-reload") == 0 or $res = 3;
+
+# Restart changed services (those that have to be restarted rather
+# than stopped and started).
+my @restart = unique(split('\n', read_file($restartListFile, err_mode => 'quiet') // ""));
+if (scalar @restart > 0) {
+    print STDERR "restarting the following units: ", join(", ", sort(@restart)), "\n";
+    system("@systemd@/bin/systemctl", "restart", "--", @restart) == 0 or $res = 4;
+    unlink($restartListFile);
+}
+
+# Start all active targets, as well as changed units we stopped above.
+# The latter is necessary because some may not be dependencies of the
+# targets (i.e., they were manually started).  FIXME: detect units
+# that are symlinks to other units.  We shouldn't start both at the
+# same time because we'll get a "Failed to add path to set" error from
+# systemd.
+my @start = unique("default.target", "timers.target", split('\n', read_file($startListFile, err_mode => 'quiet') // ""));
+print STDERR "starting the following units: ", join(", ", sort(@start)), "\n";
+system("@systemd@/bin/systemctl", "start", "--", @start) == 0 or $res = 4;
+unlink($startListFile);
+
+# Reload units that need it.  This includes remounting changed mount
+# units.
+my @reload = unique(split '\n', read_file($reloadListFile, err_mode => 'quiet') // "");
+if (scalar @reload > 0) {
+    print STDERR "reloading the following units: ", join(", ", sort(@reload)), "\n";
+    system("@systemd@/bin/systemctl", "reload", "--", @reload) == 0 or $res = 4;
+    unlink($reloadListFile);
+}
+
+# Signal dbus to reload its configuration.
+system("@systemd@/bin/systemctl", "reload", "dbus.service");
+
+# Print failed and new units.
+my (@failed, @new, @restarting);
+my $activeNew = getActiveUnits;
+while (my ($unit, $state) = each %{$activeNew}) {
+    push @failed, $unit if $state->{state} eq "failed" || $state->{substate} eq "auto-restart";
+    push @new, $unit if $state->{state} ne "failed" && !defined $activePrev->{$unit};
+}
+
+print STDERR "the following new units were started: ", join(", ", sort(@new)), "\n"
+    if scalar @new > 0;
+
+if (scalar @failed > 0) {
+    print STDERR "warning: the following units failed: ", join(", ", sort(@failed)), "\n";
+    foreach my $unit (@failed) {
+        print STDERR "\n";
+        system("COLUMNS=1000 @systemd@/bin/systemctl status --no-pager '$unit' >&2");
+    }
+    $res = 4;
+}
+
+if ($res == 0) {
+    syslog(LOG_NOTICE, "finished switching to system configuration $out");
+} else {
+    syslog(LOG_ERR, "switching to system configuration $out failed (status $res)");
+}
+
+exit $res;
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
new file mode 100644
index 00000000000..32157e41985
--- /dev/null
+++ b/nixos/modules/system/activation/top-level.nix
@@ -0,0 +1,194 @@
+{ config, pkgs, modules, baseModules, ... }:
+
+with pkgs.lib;
+
+let
+
+
+  # This attribute is responsible for creating boot entries for
+  # child configuration. They are only (directly) accessible
+  # when the parent configuration is boot default. For example,
+  # you can provide an easy way to boot the same configuration
+  # as you use, but with another kernel
+  # !!! fix this
+  cloner = inheritParent: list: with pkgs.lib;
+    map (childConfig:
+      (import ../../../lib/eval-config.nix {
+        inherit baseModules;
+        modules =
+           (optionals inheritParent modules)
+        ++ [ ./no-clone.nix ]
+        ++ [ childConfig ];
+      }).config.system.build.toplevel
+    ) list;
+
+  children =
+     cloner false config.nesting.children
+  ++ cloner true config.nesting.clone;
+
+
+  systemBuilder =
+    let
+      kernelPath = "${config.boot.kernelPackages.kernel}/" +
+        "${config.system.boot.loader.kernelFile}";
+    in ''
+      mkdir $out
+
+      if [ ! -f ${kernelPath} ]; then
+        echo "The bootloader cannot find the proper kernel image."
+        echo "(Expecting ${kernelPath})"
+        false
+      fi
+
+      ln -s ${kernelPath} $out/kernel
+      ln -s ${config.system.modulesTree} $out/kernel-modules
+
+      ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd
+
+      echo "$activationScript" > $out/activate
+      substituteInPlace $out/activate --subst-var out
+      chmod u+x $out/activate
+      unset activationScript
+
+      cp ${config.system.build.bootStage2} $out/init
+      substituteInPlace $out/init --subst-var-by systemConfig $out
+
+      ln -s ${config.system.build.etc}/etc $out/etc
+      ln -s ${config.system.path} $out/sw
+      ln -s "$systemd" $out/systemd
+      ln -s ${config.hardware.firmware} $out/firmware
+
+      echo -n "$kernelParams" > $out/kernel-params
+      echo -n "$configurationName" > $out/configuration-name
+      echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
+      echo -n "$nixosVersion" > $out/nixos-version
+
+      mkdir $out/fine-tune
+      childCount=0
+      for i in $children; do
+        childCount=$(( childCount + 1 ))
+        ln -s $i $out/fine-tune/child-$childCount
+      done
+
+      mkdir $out/bin
+      substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
+      chmod +x $out/bin/switch-to-configuration
+
+      ${config.system.extraSystemBuilderCmds}
+    '';
+
+
+  # Putting it all together.  This builds a store path containing
+  # symlinks to the various parts of the built configuration (the
+  # kernel, the Upstart services, the init scripts, etc.) as well as a
+  # script `switch-to-configuration' that activates the configuration
+  # and makes it bootable.
+  system = pkgs.stdenv.mkDerivation {
+    name = "nixos-${config.system.nixosVersion}";
+    preferLocalBuild = true;
+    buildCommand = systemBuilder;
+
+    inherit (pkgs) utillinux coreutils;
+    systemd = config.systemd.package;
+
+    inherit children;
+    kernelParams =
+      config.boot.kernelParams ++ config.boot.extraKernelParams;
+    installBootLoader =
+      config.system.build.installBootLoader
+      or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
+    activationScript = config.system.activationScripts.script;
+    nixosVersion = config.system.nixosVersion;
+
+    jobs = map (j: j.name) (attrValues config.jobs);
+
+    # Pass the names of all Upstart tasks to the activation script.
+    tasks = attrValues (mapAttrs (n: v: if v.task then ["[${v.name}]=1"] else []) config.jobs);
+
+    # Pass the names of all Upstart jobs that shouldn't be restarted
+    # to the activation script.
+    noRestartIfChanged = attrValues (mapAttrs (n: v: if v.restartIfChanged then [] else ["[${v.name}]=1"]) config.jobs);
+
+    configurationName = config.boot.loader.grub.configurationName;
+
+    # Needed by switch-to-configuration.
+    perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
+  };
+
+
+in
+
+{
+  options = {
+
+    system.build = mkOption {
+      default = {};
+      description = ''
+        Attribute set of derivations used to setup the system.
+      '';
+    };
+
+    nesting.children = mkOption {
+      default = [];
+      description = ''
+        Additional configurations to build.
+      '';
+    };
+
+    nesting.clone = mkOption {
+      default = [];
+      description = ''
+        Additional configurations to build based on the current
+        configuration which is has a lower priority.
+      '';
+    };
+
+    system.boot.loader.id = mkOption {
+      default = "";
+      description = ''
+        Id string of the used bootloader.
+      '';
+    };
+
+    system.boot.loader.kernelFile = mkOption {
+      default = pkgs.stdenv.platform.kernelTarget;
+      type = types.uniq types.string;
+      description = ''
+        Name of the kernel file to be passed to the bootloader.
+      '';
+    };
+
+    system.copySystemConfiguration = mkOption {
+      default = false;
+      description = ''
+        If enabled, copies the NixOS configuration file
+        <literal>$NIXOS_CONFIG</literal> (usually
+        <filename>/etc/nixos/configuration.nix</filename>)
+        to the system store path.
+      '';
+    };
+
+    system.extraSystemBuilderCmds = mkOption {
+      default = "";
+      internal = true;
+      merge = concatStringsSep "\n";
+      description = ''
+        This code will be added to the builder creating the system store path.
+      '';
+    };
+
+  };
+
+
+  config = {
+
+    system.extraSystemBuilderCmds =
+      optionalString
+        config.system.copySystemConfiguration
+        "cp ${maybeEnv "NIXOS_CONFIG" "/etc/nixos/configuration.nix"} $out";
+
+    system.build.toplevel = system;
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
new file mode 100644
index 00000000000..4ceabb20df5
--- /dev/null
+++ b/nixos/modules/system/boot/kernel.nix
@@ -0,0 +1,304 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  kernel = config.boot.kernelPackages.kernel;
+
+  kernelModulesConf = pkgs.writeText "nixos.conf"
+    ''
+      ${concatStringsSep "\n" config.boot.kernelModules}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    boot.kernelPackages = mkOption {
+      default = pkgs.linuxPackages;
+      # We don't want to evaluate all of linuxPackages for the manual
+      # - some of it might not even evaluate correctly.
+      defaultText = "pkgs.linuxPackages";
+      example = "pkgs.linuxPackages_2_6_25";
+      description = ''
+        This option allows you to override the Linux kernel used by
+        NixOS.  Since things like external kernel module packages are
+        tied to the kernel you're using, it also overrides those.
+        This option is a function that takes Nixpkgs as an argument
+        (as a convenience), and returns an attribute set containing at
+        the very least an attribute <varname>kernel</varname>.
+        Additional attributes may be needed depending on your
+        configuration.  For instance, if you use the NVIDIA X driver,
+        then it also needs to contain an attribute
+        <varname>nvidia_x11</varname>.
+      '';
+    };
+
+    boot.kernelParams = mkOption {
+      default = [ ];
+      description = ''
+        The kernel parameters.  If you want to add additional
+        parameters, it's best to set
+        <option>boot.extraKernelParams</option>.
+      '';
+    };
+
+    boot.extraKernelParams = mkOption {
+      default = [ ];
+      example = [ "boot.trace" ];
+      description = "Additional user-defined kernel parameters.";
+    };
+
+    boot.consoleLogLevel = mkOption {
+      type = types.int;
+      default = 4;
+      description = ''
+        The kernel console log level.  Only log messages with a
+        priority numerically less than this will appear on the
+        console.
+      '';
+    };
+
+    boot.vesa = mkOption {
+      default = false;
+      description = ''
+        Whether to activate VESA video mode on boot.
+      '';
+    };
+
+    boot.extraModulePackages = mkOption {
+      default = [];
+      # !!! example = [pkgs.nvidia_x11];
+      description = "A list of additional packages supplying kernel modules.";
+    };
+
+    boot.kernelModules = mkOption {
+      default = [];
+      description = ''
+        The set of kernel modules to be loaded in the second stage of
+        the boot process.  Note that modules that are needed to
+        mount the root file system should be added to
+        <option>boot.initrd.availableKernelModules</option> or
+        <option>boot.initrd.kernelModules</option>.
+      '';
+    };
+
+    boot.initrd.availableKernelModules = mkOption {
+      default = [];
+      example = [ "sata_nv" "ext3" ];
+      description = ''
+        The set of kernel modules in the initial ramdisk used during the
+        boot process.  This set must include all modules necessary for
+        mounting the root device.  That is, it should include modules
+        for the physical device (e.g., SCSI drivers) and for the file
+        system (e.g., ext3).  The set specified here is automatically
+        closed under the module dependency relation, i.e., all
+        dependencies of the modules list here are included
+        automatically.  The modules listed here are available in the
+        initrd, but are only loaded on demand (e.g., the ext3 module is
+        loaded automatically when an ext3 filesystem is mounted, and
+        modules for PCI devices are loaded when they match the PCI ID
+        of a device in your system).  To force a module to be loaded,
+        include it in <option>boot.initrd.kernelModules</option>.
+      '';
+    };
+
+    boot.initrd.kernelModules = mkOption {
+      default = [];
+      description = "List of modules that are always loaded by the initrd.";
+    };
+
+    system.modulesTree = mkOption {
+      internal = true;
+      default = [];
+      description = ''
+        Tree of kernel modules.  This includes the kernel, plus modules
+        built outside of the kernel.  Combine these into a single tree of
+        symlinks because modprobe only supports one directory.
+      '';
+      merge = mergeListOption;
+      # Convert the list of path to only one path.
+      apply = pkgs.aggregateModules;
+    };
+
+    system.requiredKernelConfig = mkOption {
+      default = [];
+      example = literalExample ''
+        with config.lib.kernelConfig; [
+          (isYes "MODULES")
+          (isEnabled "FB_CON_DECOR")
+          (isEnabled "BLK_DEV_INITRD")
+        ]
+      '';
+      internal = true;
+      type = types.listOf types.attrs;
+      description = ''
+        This option allows modules to specify the kernel config options that
+        must be set (or unset) for the module to work. Please use the
+        lib.kernelConfig functions to build list elements.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    system.build = { inherit kernel; };
+
+    system.modulesTree = [ kernel ] ++ config.boot.extraModulePackages;
+
+    # Implement consoleLogLevel both in early boot and using sysctl
+    # (so you don't need to reboot to have changes take effect).
+    boot.kernelParams =
+      [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++
+      optionals config.boot.vesa [ "vga=0x317" ];
+
+    boot.kernel.sysctl."kernel.printk" = config.boot.consoleLogLevel;
+
+    boot.kernelModules = [ "loop" ];
+
+    boot.initrd.availableKernelModules =
+      [ # Note: most of these (especially the SATA/PATA modules)
+        # shouldn't be included by default since nixos-hardware-scan
+        # detects them, but I'm keeping them for now for backwards
+        # compatibility.
+
+        # Some SATA/PATA stuff.
+        "ahci"
+        "sata_nv"
+        "sata_via"
+        "sata_sis"
+        "sata_uli"
+        "ata_piix"
+        "pata_marvell"
+
+        # Standard SCSI stuff.
+        "sd_mod"
+        "sr_mod"
+
+        # Standard IDE stuff.
+        "ide_cd"
+        "ide_disk"
+        "ide_generic"
+
+        # Support USB keyboards, in case the boot fails and we only have
+        # a USB keyboard.
+        "uhci_hcd"
+        "ehci_hcd"
+        "ehci_pci"
+        "ohci_hcd"
+        "xhci_hcd"
+        "usbhid"
+        "hid_generic"
+
+        # Unix domain sockets (needed by udev).
+        "unix"
+
+        # Misc. stuff.
+        "pcips2" "xtkbd"
+
+        # To wait for SCSI devices to appear.
+        "scsi_wait_scan"
+      ];
+
+    boot.initrd.kernelModules =
+      [ # For LVM.
+        "dm_mod"
+      ];
+
+    # The Linux kernel >= 2.6.27 provides firmware.
+    hardware.firmware = [ "${kernel}/lib/firmware" ];
+
+    # Create /etc/modules-load.d/nixos.conf, which is read by
+    # systemd-modules-load.service to load required kernel modules.
+    # FIXME: ensure that systemd-modules-load.service is restarted if
+    # this file changes.
+    environment.etc = singleton
+      { target = "modules-load.d/nixos.conf";
+        source = kernelModulesConf;
+      };
+
+    # Sigh.  This overrides systemd's systemd-modules-load.service
+    # just so we can set a restart trigger.  Also make
+    # multi-user.target pull it in so that it gets started if it
+    # failed earlier.
+    systemd.services."systemd-modules-load" =
+      { description = "Load Kernel Modules";
+        wantedBy = [ "sysinit.target" "multi-user.target" ];
+        before = [ "sysinit.target" "shutdown.target" ];
+        unitConfig =
+          { DefaultDependencies = "no";
+            Conflicts = "shutdown.target";
+          };
+        serviceConfig =
+          { Type = "oneshot";
+            RemainAfterExit = true;
+            ExecStart = "${config.systemd.package}/lib/systemd/systemd-modules-load";
+            # Ignore failed module loads.  Typically some of the
+            # modules in ‘boot.kernelModules’ are "nice to have but
+            # not required" (e.g. acpi-cpufreq), so we don't want to
+            # barf on those.
+            SuccessExitStatus = "0 1";
+          };
+        restartTriggers = [ kernelModulesConf ];
+      };
+
+    lib.kernelConfig = {
+      isYes = option: {
+        assertion = config: config.isYes option;
+        message = "CONFIG_${option} is not yes!";
+        configLine = "CONFIG_${option}=y";
+      };
+
+      isNo = option: {
+        assertion = config: config.isNo option;
+        message = "CONFIG_${option} is not no!";
+        configLine = "CONFIG_${option}=n";
+      };
+
+      isModule = option: {
+        assertion = config: config.isModule option;
+        message = "CONFIG_${option} is not built as a module!";
+        configLine = "CONFIG_${option}=m";
+      };
+
+      ### Usually you will just want to use these two
+      # True if yes or module
+      isEnabled = option: {
+        assertion = config: config.isEnabled option;
+        message = "CONFIG_${option} is not enabled!";
+        configLine = "CONFIG_${option}=y";
+      };
+
+      # True if no or omitted
+      isDisabled = option: {
+        assertion = config: config.isDisabled option;
+        message = "CONFIG_${option} is not disabled!";
+        configLine = "CONFIG_${option}=n";
+      };
+    };
+
+    # The config options that all modules can depend upon
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      # !!! Should this really be needed?
+      (isYes "MODULES")
+      (isYes "BINFMT_ELF")
+    ];
+
+    # nixpkgs kernels are assumed to have all required features
+    assertions = if config.boot.kernelPackages.kernel ? features then [] else
+      let cfg = config.boot.kernelPackages.kernel.config; in map (attrs:
+        { assertion = attrs.assertion cfg; inherit (attrs) message; }
+      ) config.system.requiredKernelConfig;
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/kexec.nix b/nixos/modules/system/boot/kexec.nix
new file mode 100644
index 00000000000..b7821f9509f
--- /dev/null
+++ b/nixos/modules/system/boot/kexec.nix
@@ -0,0 +1,21 @@
+{ config, pkgs, ... }:
+
+{
+  environment.systemPackages = [ pkgs.kexectools ];
+
+  systemd.services."prepare-kexec" =
+    { description = "Preparation for kexec";
+      wantedBy = [ "kexec.target" ];
+      before = [ "systemd-kexec.service" ];
+      unitConfig.DefaultDependencies = false;
+      serviceConfig.Type = "oneshot";
+      path = [ pkgs.kexectools ];
+      script =
+        ''
+          p=$(readlink -f /nix/var/nix/profiles/system)
+          if ! [ -d $p ]; then exit 1; fi
+          exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
+        '';
+    };
+
+}
\ No newline at end of file
diff --git a/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub-builder.sh b/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub-builder.sh
new file mode 100644
index 00000000000..2f550c98428
--- /dev/null
+++ b/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub-builder.sh
@@ -0,0 +1,131 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin:$i/sbin; done
+
+default=$1
+if test -z "$1"; then
+    echo "Syntax: efi-boot-stub-builder.sh <DEFAULT-CONFIG>"
+    exit 1
+fi
+
+echo "updating the efi system partition..."
+
+# Convert a path to a file in the Nix store such as
+# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
+# Also, efi executables need the .efi extension
+cleanName() {
+    local path="$1"
+    echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g' | sed 's|@kernelFile@$|@kernelFile@.efi|'
+}
+
+# Copy a file from the Nix store to the EFI system partition
+declare -A filesCopied
+
+copyToKernelsDir() {
+    local src="$1"
+    local dst="@efiSysMountPoint@/efi/nixos/$(cleanName $src)"
+    # Don't copy the file if $dst already exists.  This means that we
+    # have to create $dst atomically to prevent partially copied
+    # kernels or initrd if this script is ever interrupted.
+    if ! test -e $dst; then
+        local dstTmp=$dst.tmp.$$
+        cp $src $dstTmp
+        mv $dstTmp $dst
+    fi
+    filesCopied[$dst]=1
+    result=$dst
+}
+
+# Copy its kernel, initrd, and startup script to the efi system partition
+# Add the efibootmgr entry if requested
+addEntry() {
+    local path="$1"
+    local generation="$2"
+
+    if ! test -e $path/kernel -a -e $path/initrd; then
+        return
+    fi
+
+    local kernel=$(readlink -f $path/kernel)
+    local initrd=$(readlink -f $path/initrd)
+    copyToKernelsDir $kernel; kernel=$result
+    copyToKernelsDir $initrd; initrd=$result
+
+    local startup="@efiSysMountPoint@/efi/nixos/generation-$generation-startup.nsh"
+    if ! test -e $startup; then
+        local dstTmp=$startup.tmp.$$
+	echo "$(echo $kernel | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') systemConfig=$(readlink -f $path) init=$(readlink -f $path/init) initrd=$(echo $initrd | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') $(cat $path/kernel-params)" > $dstTmp
+        mv $dstTmp $startup
+    fi
+    filesCopied[$startup]=1
+
+    if test -n "@runEfibootmgr@"; then
+      set +e
+      efibootmgr -c -d "@efiDisk@" -g -l $(echo $kernel | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') -L "NixOS $generation Generation" -p "@efiPartition@" \
+        -u systemConfig=$(readlink -f $path) init=$(readlink -f $path/init) initrd=$(echo $initrd | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') $(cat $path/kernel-params) > /dev/null 2>&1
+      set -e
+    fi
+
+    if test $(readlink -f "$path") = "$default"; then
+      if test -n "@runEfibootmgr@"; then
+        set +e
+        defaultbootnum=$(efibootmgr | grep "NixOS $generation Generation" | sed 's/Boot//' | sed 's/\*.*//')
+	set -e
+      fi
+
+      if test -n "@installStartupNsh@"; then
+        sed 's|.*@kernelFile@.efi|@kernelFile@.efi|' < $startup > "@efiSysMountPoint@/startup.nsh"
+        cp $kernel "@efiSysMountPoint@/@kernelFile@.efi"
+      fi
+    fi
+}
+
+mkdir -p "@efiSysMountPoint@/efi/nixos/"
+
+# Remove all old boot manager entries
+if test -n "@runEfibootmgr@"; then
+  set +e
+  modprobe efivars > /dev/null 2>&1
+  for bootnum in $(efibootmgr | grep "NixOS" | grep "Generation" | sed 's/Boot//' | sed 's/\*.*//'); do
+    efibootmgr -B -b "$bootnum" > /dev/null 2>&1
+  done
+  set -e
+fi
+
+# Add all generations of the system profile to the system partition, in reverse
+# (most recent to least recent) order.
+for generation in $(
+    (cd /nix/var/nix/profiles && ls -d system-*-link) \
+    | sed 's/system-\([0-9]\+\)-link/\1/' \
+    | sort -n -r); do
+    link=/nix/var/nix/profiles/system-$generation-link
+    addEntry $link $generation
+done
+
+if test -n "@runEfibootmgr@"; then
+  set +e
+  efibootmgr -o $defaultbootnum > /dev/null 2>&1
+  set -e
+fi
+
+if test -n "@efiShell@"; then
+  mkdir -pv "@efiSysMountPoint@"/efi/boot
+  cp "@efiShell@" "@efiSysMountPoint@"/efi/boot/boot"@targetArch@".efi
+fi
+
+# Remove obsolete files from the EFI system partition
+for fn in "@efiSysMountPoint@/efi/nixos/"*; do
+    if ! test "${filesCopied[$fn]}" = 1; then
+        rm -vf -- "$fn"
+    fi
+done
+
+# Run any extra commands users may need
+if test -n "@runEfibootmgr@"; then
+  set +e
+  @postEfiBootMgrCommands@
+  set -e
+fi
diff --git a/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub.nix b/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub.nix
new file mode 100644
index 00000000000..735784327bc
--- /dev/null
+++ b/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub.nix
@@ -0,0 +1,98 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+  efiBootStubBuilder = pkgs.substituteAll {
+    src = ./efi-boot-stub-builder.sh;
+    isExecutable = true;
+    inherit (pkgs) bash;
+    path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.glibc] ++ (pkgs.stdenv.lib.optionals config.boot.loader.efi.canTouchEfiVariables [pkgs.efibootmgr pkgs.module_init_tools]);
+    inherit (config.boot.loader.efiBootStub) installStartupNsh;
+
+    inherit (config.boot.loader.efi) efiSysMountPoint;
+
+    inherit (config.boot.loader.efi.efibootmgr) efiDisk efiPartition postEfiBootMgrCommands;
+
+    runEfibootmgr = config.boot.loader.efi.canTouchEfiVariables;
+
+    efiShell = if config.boot.loader.efiBootStub.installShell then
+      if pkgs.stdenv.isi686 then
+        pkgs.fetchurl {
+          url = "https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2/EdkShellBinPkg/FullShell/Ia32/Shell_Full.efi";
+          sha256 = "1gv6kyaspczdp7x8qnx5x76ilriaygkfs99ay7ihhdi6riclkhfl";
+        }
+      else
+        pkgs.fetchurl {
+          url = "https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2/EdkShellBinPkg/FullShell/X64/Shell_Full.efi";
+          sha256 = "1g18z84rlavxr5gsrh2g942rfr6znv9fs3fqww5m7dhmnysgyv8p";
+        }
+    else
+      null;
+
+    kernelFile = platform.kernelTarget;
+    targetArch = if pkgs.stdenv.isi686 then
+      "IA32"
+    else if pkgs.stdenv.isx86_64 then
+      "X64"
+    else
+      throw "Unsupported architecture";
+  };
+
+  # Temporary check, for nixos to cope both with nixpkgs stdenv-updates and trunk
+  platform = pkgs.stdenv.platform;
+in
+{
+  options = {
+    boot = {
+      loader = {
+        efiBootStub = {
+
+          enable = mkOption {
+            default = false;
+            description = ''
+              Whether to use the linux kernel as an EFI bootloader.
+              When enabled, the kernel, initrd, and an EFI shell script
+              to boot the system are copied to the EFI system partition.
+            '';
+          };
+
+          installStartupNsh = mkOption {
+            default = false;
+            description = ''
+              Whether to install a startup.nsh in the root of the EFI system partition.
+              For now, it will just boot the latest version when run, the eventual goal
+              is to have a basic menu-type interface.
+            '';
+          };
+
+          installShell = mkOption {
+            default = false;
+            description = ''
+              Whether to install an EFI shell in \EFI\BOOT.
+              This _should_ only be needed for removable devices
+              (CDs, usb sticks, etc.), but it may be an option for broken
+              systems where efibootmgr doesn't work. Particularly useful in
+              conjunction with installStartupNsh
+            '';
+          };
+
+        };
+      };
+    };
+  };
+
+  config = mkIf config.boot.loader.efiBootStub.enable {
+    assertions = [ { assertion = ! config.boot.kernelPackages.kernel ? features || config.boot.kernelPackages.kernel.features ? efiBootStub; message = "This kernel does not support the EFI boot stub"; } ];
+  
+    system = {
+      build.installBootLoader = efiBootStubBuilder;
+      boot.loader.id = "efiBootStub";
+      boot.loader.kernelFile = platform.kernelTarget;
+      requiredKernelConfig = with config.lib.kernelConfig; [
+        (isYes "EFI_STUB")
+      ];
+    };
+  };
+
+}
diff --git a/nixos/modules/system/boot/loader/efi.nix b/nixos/modules/system/boot/loader/efi.nix
new file mode 100644
index 00000000000..827b3e39122
--- /dev/null
+++ b/nixos/modules/system/boot/loader/efi.nix
@@ -0,0 +1,49 @@
+{ pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options.boot.loader.efi = {
+    canTouchEfiVariables = mkOption {
+      default = false;
+
+      type = types.bool;
+
+      description = "Whether or not the installation process should modify efi boot variables.";
+    };
+
+    efibootmgr = {
+      efiDisk = mkOption {
+        default = "/dev/sda";
+
+        type = types.string;
+
+        description = "The disk that contains the EFI system partition.";
+      };
+
+      efiPartition = mkOption {
+        default = "1";
+        description = "The partition number of the EFI system partition.";
+      };
+
+      postEfiBootMgrCommands = mkOption {
+        default = "";
+        type = types.string;
+        description = ''
+          Shell commands to be executed immediately after efibootmgr has setup the system EFI.
+          Some systems do not follow the EFI specifications properly and insert extra entries.
+          Others will brick (fix by removing battery) on boot when it finds more than X entries.
+          This hook allows for running a few extra efibootmgr commands to combat these issues.
+        '';
+      };
+    };
+
+    efiSysMountPoint = mkOption {
+      default = "/boot";
+
+      type = types.string;
+
+      description = "Where the EFI System Partition is mounted.";
+    };
+  };
+}
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh b/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh
new file mode 100644
index 00000000000..e723b9eb7cb
--- /dev/null
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh
@@ -0,0 +1,106 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+default=$1
+if test -z "$1"; then
+    echo "Syntax: generations-dir-builder.sh <DEFAULT-CONFIG>"
+    exit 1
+fi
+
+echo "updating the boot generations directory..."
+
+mkdir -p /boot
+
+rm -Rf /boot/system* || true
+
+target=/boot/grub/menu.lst
+tmp=$target.tmp
+
+# Convert a path to a file in the Nix store such as
+# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
+cleanName() {
+    local path="$1"
+    echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
+}
+
+# Copy a file from the Nix store to /boot/kernels.
+declare -A filesCopied
+
+copyToKernelsDir() {
+    local src="$1"
+    local dst="/boot/kernels/$(cleanName $src)"
+    # Don't copy the file if $dst already exists.  This means that we
+    # have to create $dst atomically to prevent partially copied
+    # kernels or initrd if this script is ever interrupted.
+    if ! test -e $dst; then
+        local dstTmp=$dst.tmp.$$
+        cp $src $dstTmp
+        mv $dstTmp $dst
+    fi
+    filesCopied[$dst]=1
+    result=$dst
+}
+
+
+# Copy its kernel and initrd to /boot/kernels.
+addEntry() {
+    local path="$1"
+    local generation="$2"
+    local outdir=/boot/system-$generation
+
+    if ! test -e $path/kernel -a -e $path/initrd; then
+        return
+    fi
+
+    local kernel=$(readlink -f $path/kernel)
+    local initrd=$(readlink -f $path/initrd)
+
+    if test -n "@copyKernels@"; then
+        copyToKernelsDir $kernel; kernel=$result
+        copyToKernelsDir $initrd; initrd=$result
+    fi
+    
+    mkdir -p $outdir
+    ln -sf $(readlink -f $path) $outdir/system
+    ln -sf $(readlink -f $path/init) $outdir/init
+    ln -sf $initrd $outdir/initrd
+    ln -sf $kernel $outdir/kernel
+
+    if test $(readlink -f "$path") = "$default"; then
+      cp "$kernel" /boot/nixos-kernel
+      cp "$initrd" /boot/nixos-initrd
+      cp "$(readlink -f "$path/init")" /boot/nixos-init
+
+      mkdir -p /boot/default
+      # ln -sfT: overrides target even if it exists.
+      ln -sfT $(readlink -f $path) /boot/default/system
+      ln -sfT $(readlink -f $path/init) /boot/default/init
+      ln -sfT $initrd /boot/default/initrd
+      ln -sfT $kernel /boot/default/kernel
+    fi
+}
+
+if test -n "@copyKernels@"; then
+    mkdir -p /boot/kernels
+fi
+
+# Add all generations of the system profile to the menu, in reverse
+# (most recent to least recent) order.
+for generation in $(
+    (cd /nix/var/nix/profiles && ls -d system-*-link) \
+    | sed 's/system-\([0-9]\+\)-link/\1/' \
+    | sort -n -r); do
+    link=/nix/var/nix/profiles/system-$generation-link
+    addEntry $link $generation
+done
+
+# Remove obsolete files from /boot/kernels.
+for fn in /boot/kernels/*; do
+    if ! test "${filesCopied[$fn]}" = 1; then
+        rm -vf -- "$fn"
+    fi
+done
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
new file mode 100644
index 00000000000..9855c8c19dd
--- /dev/null
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
@@ -0,0 +1,63 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  generationsDirBuilder = pkgs.substituteAll {
+    src = ./generations-dir-builder.sh;
+    isExecutable = true;
+    inherit (pkgs) bash;
+    path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+    inherit (config.boot.loader.generationsDir) copyKernels;
+  };
+
+  # Temporary check, for nixos to cope both with nixpkgs stdenv-updates and trunk
+  platform = pkgs.stdenv.platform;
+
+in
+
+{
+  options = {
+
+    boot.loader.generationsDir = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to create symlinks to the system generations under
+          <literal>/boot</literal>.  When enabled,
+          <literal>/boot/default/kernel</literal>,
+          <literal>/boot/default/initrd</literal>, etc., are updated to
+          point to the current generation's kernel image, initial RAM
+          disk, and other bootstrap files.
+
+          This optional is not necessary with boot loaders such as GNU GRUB
+          for which the menu is updated to point to the latest bootstrap
+          files.  However, it is needed for U-Boot on platforms where the
+          boot command line is stored in flash memory rather than in a
+          menu file.
+        '';
+      };
+
+      copyKernels = mkOption {
+        default = false;
+        description = "
+          Whether copy the necessary boot files into /boot, so
+          /nix/store is not needed by the boot loader.
+        ";
+      };
+
+    };
+
+  };
+
+
+  config = mkIf config.boot.loader.generationsDir.enable {
+
+    system.build.installBootLoader = generationsDirBuilder;
+    system.boot.loader.id = "generationsDir";
+    system.boot.loader.kernelFile = platform.kernelTarget;
+
+  };
+}
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
new file mode 100644
index 00000000000..8e9f3253f87
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -0,0 +1,261 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.boot.loader.grub;
+
+  realGrub = if cfg.version == 1 then pkgs.grub else pkgs.grub2;
+
+  grub =
+    # Don't include GRUB if we're only generating a GRUB menu (e.g.,
+    # in EC2 instances).
+    if cfg.devices == ["nodev"]
+    then null
+    else realGrub;
+
+  f = x: if x == null then "" else "" + x;
+
+  grubConfig = pkgs.writeText "grub-config.xml" (builtins.toXML
+    { splashImage = f config.boot.loader.grub.splashImage;
+      grub = f grub;
+      shell = "${pkgs.stdenv.shell}";
+      fullVersion = (builtins.parseDrvName realGrub.name).version;
+      inherit (cfg)
+        version extraConfig extraPerEntryConfig extraEntries
+        extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels timeout
+        default devices;
+      path = (makeSearchPath "bin" [
+        pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils
+      ]) + ":" + (makeSearchPath "sbin" [
+        pkgs.mdadm
+      ]);
+    });
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    boot.loader.grub = {
+
+      enable = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Whether to enable the GNU GRUB boot loader.
+        '';
+      };
+
+      version = mkOption {
+        default = 2;
+        example = 1;
+        type = types.int;
+        description = ''
+          The version of GRUB to use: <literal>1</literal> for GRUB
+          Legacy (versions 0.9x), or <literal>2</literal> (the
+          default) for GRUB 2.
+        '';
+      };
+
+      device = mkOption {
+        default = "";
+        example = "/dev/hda";
+        type = types.uniq types.string;
+        description = ''
+          The device on which the GRUB boot loader will be installed.
+          The special value <literal>nodev</literal> means that a GRUB
+          boot menu will be generated, but GRUB itself will not
+          actually be installed.  To install GRUB on multiple devices,
+          use <literal>boot.loader.grub.devices</literal>.
+        '';
+      };
+
+      devices = mkOption {
+        default = [];
+        example = [ "/dev/hda" ];
+        type = types.listOf types.string;
+        description = ''
+          The devices on which the boot loader, GRUB, will be
+          installed. Can be used instead of <literal>device</literal> to
+          install grub into multiple devices (e.g., if as softraid arrays holding /boot).
+        '';
+      };
+
+      # !!! How can we mark options as obsolete?
+      bootDevice = mkOption {
+        default = "";
+        description = "Obsolete.";
+      };
+
+      configurationName = mkOption {
+        default = "";
+        example = "Stable 2.6.21";
+        type = types.uniq types.string;
+        description = ''
+          GRUB entry name instead of default.
+        '';
+      };
+
+      extraPrepareConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Additional bash commands to be run at the script that
+          prepares the grub menu entries.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        example = "serial; terminal_output.serial";
+        type = types.lines;
+        description = ''
+          Additional GRUB commands inserted in the configuration file
+          just before the menu entries.
+        '';
+      };
+
+      extraPerEntryConfig = mkOption {
+        default = "";
+        example = "root (hd0)";
+        type = types.lines;
+        description = ''
+          Additional GRUB commands inserted in the configuration file
+          at the start of each NixOS menu entry.
+        '';
+      };
+
+      extraEntries = mkOption {
+        default = "";
+        type = types.lines;
+        example = ''
+          # GRUB 1 example (not GRUB 2 compatible)
+          title Windows
+            chainloader (hd0,1)+1
+
+          # GRUB 2 example
+          menuentry "Windows7" {
+            title Windows7
+            insmod ntfs
+            set root='(hd1,1)'
+            chainloader +1
+          }
+        '';
+        description = ''
+          Any additional entries you want added to the GRUB boot menu.
+        '';
+      };
+
+      extraEntriesBeforeNixOS = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether extraEntries are included before the default option.
+        '';
+      };
+
+      extraFiles = mkOption {
+        default = {};
+        example = literalExample ''
+          { "memtest.bin" = "${pkgs.memtest86plus}/memtest.bin"; }
+        '';
+        description = ''
+          A set of files to be copied to <filename>/boot</filename>.
+          Each attribute name denotes the destination file name in
+          <filename>/boot</filename>, while the corresponding
+          attribute value specifies the source file.
+        '';
+      };
+
+      splashImage = mkOption {
+        default =
+          if cfg.version == 1
+          then pkgs.fetchurl {
+            url = http://www.gnome-look.org/CONTENT/content-files/36909-soft-tux.xpm.gz;
+            sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59";
+          }
+          # GRUB 1.97 doesn't support gzipped XPMs.
+          else ./winkler-gnu-blue-640x480.png;
+        example = null;
+        description = ''
+          Background image used for GRUB.  It must be a 640x480,
+          14-colour image in XPM format, optionally compressed with
+          <command>gzip</command> or <command>bzip2</command>.  Set to
+          <literal>null</literal> to run GRUB in text mode.
+        '';
+      };
+
+      configurationLimit = mkOption {
+        default = 100;
+        example = 120;
+        type = types.int;
+        description = ''
+          Maximum of configurations in boot menu. GRUB has problems when
+          there are too many entries.
+        '';
+      };
+
+      copyKernels = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether the GRUB menu builder should copy kernels and initial
+          ramdisks to /boot.  This is done automatically if /boot is
+          on a different partition than /.
+        '';
+      };
+
+      timeout = mkOption {
+        default = 5;
+        type = types.int;
+        description = ''
+          Timeout (in seconds) until GRUB boots the default menu item.
+        '';
+      };
+
+      default = mkOption {
+        default = 0;
+        type = types.int;
+        description = ''
+          Index of the default menu item to be booted.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    boot.loader.grub.devices = optional (cfg.device != "") cfg.device;
+
+    system.build = mkAssert (cfg.devices != [])
+      "You must set the ‘boot.loader.grub.device’ option to make the system bootable."
+      { installBootLoader =
+          "PERL5LIB=${makePerlPath [ pkgs.perlPackages.XMLLibXML pkgs.perlPackages.XMLSAX ]} " +
+          "${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig}";
+        inherit grub;
+      };
+
+    # Common attribute for boot loaders so only one of them can be
+    # set at once.
+    system.boot.loader.id = "grub";
+
+    environment.systemPackages = [ grub ];
+
+    boot.loader.grub.extraPrepareConfig =
+      concatStrings (mapAttrsToList (n: v: ''
+        ${pkgs.coreutils}/bin/cp -pf "${v}" "/boot/${n}"
+      '') config.boot.loader.grub.extraFiles);
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
new file mode 100644
index 00000000000..a83733db63b
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -0,0 +1,265 @@
+use strict;
+use warnings;
+use XML::LibXML;
+use File::Basename;
+use File::Path;
+use File::stat;
+use File::Copy;
+use POSIX;
+use Cwd;
+
+my $defaultConfig = $ARGV[1] or die;
+
+my $dom = XML::LibXML->load_xml(location => $ARGV[0]);
+
+sub get { my ($name) = @_; return $dom->findvalue("/expr/attrs/attr[\@name = '$name']/*/\@value"); }
+
+sub readFile {
+    my ($fn) = @_; local $/ = undef;
+    open FILE, "<$fn" or return undef; my $s = <FILE>; close FILE;
+    local $/ = "\n"; chomp $s; return $s;
+}
+
+sub writeFile {
+    my ($fn, $s) = @_;
+    open FILE, ">$fn" or die "cannot create $fn: $!\n";
+    print FILE $s or die;
+    close FILE or die;
+}
+
+my $grub = get("grub");
+my $grubVersion = int(get("version"));
+my $extraConfig = get("extraConfig");
+my $extraPrepareConfig = get("extraPrepareConfig");
+my $extraPerEntryConfig = get("extraPerEntryConfig");
+my $extraEntries = get("extraEntries");
+my $extraEntriesBeforeNixOS = get("extraEntriesBeforeNixOS") eq "true";
+my $splashImage = get("splashImage");
+my $configurationLimit = int(get("configurationLimit"));
+my $copyKernels = get("copyKernels") eq "true";
+my $timeout = int(get("timeout"));
+my $defaultEntry = int(get("default"));
+$ENV{'PATH'} = get("path");
+
+die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
+
+print STDERR "updating GRUB $grubVersion menu...\n";
+
+mkpath("/boot/grub", 0, 0700);
+
+
+# Discover whether /boot is on the same filesystem as / and
+# /nix/store.  If not, then all kernels and initrds must be copied to
+# /boot, and all paths in the GRUB config file must be relative to the
+# root of the /boot filesystem.  `$bootRoot' is the path to be
+# prepended to paths under /boot.
+my $bootRoot = "/boot";
+if (stat("/")->dev != stat("/boot")->dev) {
+    $bootRoot = "";
+    $copyKernels = 1;
+} elsif (stat("/boot")->dev != stat("/nix/store")->dev) {
+    $copyKernels = 1;
+}
+
+
+# Generate the header.
+my $conf .= "# Automatically generated.  DO NOT EDIT THIS FILE!\n";
+
+if ($grubVersion == 1) {
+    $conf .= "
+        default $defaultEntry
+        timeout $timeout
+    ";
+    if ($splashImage) {
+        copy $splashImage, "/boot/background.xpm.gz" or die "cannot copy $splashImage to /boot\n";
+        $conf .= "splashimage $bootRoot/background.xpm.gz\n";
+    }
+}
+
+else {
+    $conf .= "
+        if [ -s \$prefix/grubenv ]; then
+          load_env
+        fi
+
+        # ‘grub-reboot’ sets a one-time saved entry, which we process here and
+        # then delete.
+        if [ \"\${saved_entry}\" ]; then
+          # The next line *has* to look exactly like this, otherwise KDM's
+          # reboot feature won't work properly with GRUB 2.
+          set default=\"\${saved_entry}\"
+          set saved_entry=
+          set prev_saved_entry=
+          save_env saved_entry
+          save_env prev_saved_entry
+          set timeout=1
+        else
+          set default=$defaultEntry
+          set timeout=$timeout
+        fi
+
+        if loadfont $bootRoot/grub/fonts/unicode.pf2; then
+          set gfxmode=640x480
+          insmod gfxterm
+          insmod vbe
+          terminal_output gfxterm
+        fi
+    ";
+
+    if ($splashImage) {
+        # FIXME: GRUB 1.97 doesn't resize the background image if it
+        # doesn't match the video resolution.
+        copy $splashImage, "/boot/background.png" or die "cannot copy $splashImage to /boot\n";
+        $conf .= "
+            insmod png
+            if background_image $bootRoot/background.png; then
+              set color_normal=white/black
+              set color_highlight=black/white
+            else
+              set menu_color_normal=cyan/blue
+              set menu_color_highlight=white/blue
+            fi
+        ";
+    }
+}
+
+$conf .= "$extraConfig\n";
+
+
+# Generate the menu entries.
+$conf .= "\n";
+
+my %copied;
+mkpath("/boot/kernels", 0, 0755) if $copyKernels;
+
+sub copyToKernelsDir {
+    my ($path) = @_;
+    return $path unless $copyKernels;
+    $path =~ /\/nix\/store\/(.*)/ or die;
+    my $name = $1; $name =~ s/\//-/g;
+    my $dst = "/boot/kernels/$name";
+    # Don't copy the file if $dst already exists.  This means that we
+    # have to create $dst atomically to prevent partially copied
+    # kernels or initrd if this script is ever interrupted.
+    if (! -e $dst) {
+        my $tmp = "$dst.tmp";
+        copy $path, $tmp or die "cannot copy $path to $tmp\n";
+        rename $tmp, $dst or die "cannot rename $tmp to $dst\n";
+    }
+    $copied{$dst} = 1;
+    return "$bootRoot/kernels/$name";
+}
+
+sub addEntry {
+    my ($name, $path) = @_;
+    return unless -e "$path/kernel" && -e "$path/initrd";
+
+    my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
+    my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));
+    my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef;
+
+    # FIXME: $confName
+
+    my $kernelParams =
+        "systemConfig=" . Cwd::abs_path($path) . " " .
+        "init=" . Cwd::abs_path("$path/init") . " " .
+        readFile("$path/kernel-params");
+    my $xenParams = $xen && -e "$path/xen-params" ? readFile("$path/xen-params") : "";
+
+    if ($grubVersion == 1) {
+        $conf .= "title $name\n";
+        $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
+        $conf .= "  kernel $xen $xenParams\n" if $xen;
+        $conf .= "  " . ($xen ? "module" : "kernel") . " $kernel $kernelParams\n";
+        $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n\n";
+    } else {
+        $conf .= "menuentry \"$name\" {\n";
+        $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
+        $conf .= "  multiboot $xen $xenParams\n" if $xen;
+        $conf .= "  " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
+        $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n";
+        $conf .= "}\n\n";
+    }
+}
+
+
+# Add default entries.
+$conf .= "$extraEntries\n" if $extraEntriesBeforeNixOS;
+
+addEntry("NixOS - Default", $defaultConfig);
+
+$conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
+
+# extraEntries could refer to @bootRoot@, which we have to substitute
+$conf =~ s/\@bootRoot\@/$bootRoot/g;
+
+# Emit submenus for all system profiles.
+sub addProfile {
+    my ($profile, $description) = @_;
+
+    # Add entries for all generations of this profile.
+    $conf .= "submenu \"$description\" {\n" if $grubVersion == 2;
+
+    sub nrFromGen { my ($x) = @_; $x =~ /\/\w+-(\d+)-link/; return $1; }
+
+    my @links = sort
+        { nrFromGen($b) <=> nrFromGen($a) }
+        (glob "$profile-*-link");
+
+    my $curEntry = 0;
+    foreach my $link (@links) {
+        last if $curEntry++ >= $configurationLimit;
+        my $date = strftime("%F", localtime(lstat($link)->mtime));
+        my $version =
+            -e "$link/nixos-version"
+            ? readFile("$link/nixos-version")
+            : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]);
+        addEntry("NixOS - Configuration " . nrFromGen($link) . " ($date - $version)", $link);
+    }
+
+    $conf .= "}\n" if $grubVersion == 2;
+}
+
+addProfile "/nix/var/nix/profiles/system", "NixOS - All configurations";
+
+if ($grubVersion == 2) {
+    for my $profile (glob "/nix/var/nix/profiles/system-profiles/*") {
+        my $name = basename($profile);
+        next unless $name =~ /^\w+$/;
+        addProfile $profile, "NixOS - Profile '$name'";
+    }
+}
+
+# Run extraPrepareConfig in sh
+if ($extraPrepareConfig ne "") {
+  system((get("shell"), "-c", $extraPrepareConfig));
+}
+
+# Atomically update the GRUB config.
+my $confFile = $grubVersion == 1 ? "/boot/grub/menu.lst" : "/boot/grub/grub.cfg";
+my $tmpFile = $confFile . ".tmp";
+writeFile($tmpFile, $conf);
+rename $tmpFile, $confFile or die "cannot rename $tmpFile to $confFile\n";
+
+
+# Remove obsolete files from /boot/kernels.
+foreach my $fn (glob "/boot/kernels/*") {
+    next if defined $copied{$fn};
+    print STDERR "removing obsolete file $fn\n";
+    unlink $fn;
+}
+
+
+# Install GRUB if the version changed from the last time we installed
+# it.  FIXME: shouldn't we reinstall if ‘devices’ changed?
+my $prevVersion = readFile("/boot/grub/version") // "";
+if (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1" || get("fullVersion") ne $prevVersion) {
+    foreach my $dev ($dom->findnodes('/expr/attrs/attr[@name = "devices"]/list/string/@value')) {
+        $dev = $dev->findvalue(".") or die;
+        next if $dev eq "nodev";
+        print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n";
+        system("$grub/sbin/grub-install", "--recheck", Cwd::abs_path($dev)) == 0
+            or die "$0: installation of GRUB on $dev failed\n";
+    }
+    writeFile("/boot/grub/version", get("fullVersion"));
+}
diff --git a/nixos/modules/system/boot/loader/grub/memtest.nix b/nixos/modules/system/boot/loader/grub/memtest.nix
new file mode 100644
index 00000000000..a0726c01e20
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/memtest.nix
@@ -0,0 +1,39 @@
+# This module adds Memtest86+ to the GRUB boot menu.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  memtest86 = pkgs.memtest86plus;
+in
+
+{
+  options = {
+
+    boot.loader.grub.memtest86 = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Make Memtest86+, a memory testing program, available from the
+        GRUB boot menu.
+      '';
+    };
+  };
+
+  config = mkIf config.boot.loader.grub.memtest86 {
+
+    boot.loader.grub.extraEntries = mkFixStrictness (
+      if config.boot.loader.grub.version == 2 then
+        ''
+          menuentry "Memtest86+" {
+            linux16 @bootRoot@/memtest.bin
+          }
+        ''
+      else
+        throw "Memtest86+ is not supported with GRUB 1.");
+
+    boot.loader.grub.extraFiles."memtest.bin" = "${memtest86}/memtest.bin";
+
+  };
+}
diff --git a/nixos/modules/system/boot/loader/grub/winkler-gnu-blue-640x480.png b/nixos/modules/system/boot/loader/grub/winkler-gnu-blue-640x480.png
new file mode 100644
index 00000000000..35bbb57b51e
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/winkler-gnu-blue-640x480.png
Binary files differdiff --git a/nixos/modules/system/boot/loader/grub/winkler-gnu-blue.README b/nixos/modules/system/boot/loader/grub/winkler-gnu-blue.README
new file mode 100644
index 00000000000..9616362dce2
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/winkler-gnu-blue.README
@@ -0,0 +1,6 @@
+This is a resized version of
+
+  http://www.gnu.org/graphics/winkler-gnu-blue.png
+
+by Kyle Winkler and released under the Free Art License
+(http://artlibre.org/licence.php/lalgb.html).
diff --git a/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py b/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
new file mode 100644
index 00000000000..9ea224b51f6
--- /dev/null
+++ b/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
@@ -0,0 +1,114 @@
+#! @python@/bin/python
+import argparse
+import shutil
+import os
+import errno
+import subprocess
+import glob
+import tempfile
+import errno
+
+def copy_if_not_exists(source, dest):
+    known_paths.append(dest)
+    if not os.path.exists(dest):
+        shutil.copyfile(source, dest)
+
+system_dir = lambda generation: "/nix/var/nix/profiles/system-%d-link" % (generation)
+
+def write_entry(generation, kernel, initrd):
+    entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation)
+    generation_dir = os.readlink(system_dir(generation))
+    tmp_path = "%s.tmp" % (entry_file)
+    kernel_params = "systemConfig=%s init=%s/init " % (generation_dir, generation_dir)
+    with open("%s/kernel-params" % (generation_dir)) as params_file:
+        kernel_params = kernel_params + params_file.read()
+    with open(tmp_path, 'w') as f:
+        print >> f, "title NixOS"
+        print >> f, "version Generation %d" % (generation)
+        if machine_id is not None: print >> f, "machine-id %s" % (machine_id)
+        print >> f, "linux %s" % (kernel)
+        print >> f, "initrd %s" % (initrd)
+        print >> f, "options %s" % (kernel_params)
+    os.rename(tmp_path, entry_file)
+
+def write_loader_conf(generation):
+    with open("@efiSysMountPoint@/loader/loader.conf.tmp", 'w') as f:
+        if "@timeout@" != "":
+            print >> f, "timeout @timeout@"
+        print >> f, "default nixos-generation-%d" % (generation)
+    os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
+
+def copy_from_profile(generation, name):
+    store_file_path = os.readlink("%s/%s" % (system_dir(generation), name))
+    suffix = os.path.basename(store_file_path)
+    store_dir = os.path.basename(os.path.dirname(store_file_path))
+    efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
+    copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
+    return efi_file_path
+
+def add_entry(generation):
+    efi_kernel_path = copy_from_profile(generation, "kernel")
+    efi_initrd_path = copy_from_profile(generation, "initrd")
+    write_entry(generation, efi_kernel_path, efi_initrd_path)
+
+def mkdir_p(path):
+    try:
+        os.makedirs(path)
+    except OSError as e:
+        if e.errno != errno.EEXIST or not os.path.isdir(path):
+            raise
+
+def get_generations(profile):
+    gen_list = subprocess.check_output([
+        "@nix@/bin/nix-env",
+        "--list-generations",
+        "-p",
+        "/nix/var/nix/profiles/%s" % (profile)
+        ])
+    gen_lines = gen_list.split('\n')
+    gen_lines.pop()
+    return [ int(line.split()[0]) for line in gen_lines ]
+
+def remove_old_entries(gens):
+    slice_start = len("@efiSysMountPoint@/loader/entries/nixos-generation-")
+    slice_end = -1 * len(".conf")
+    for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos-generation-[1-9]*.conf"):
+        try:
+            gen = int(path[slice_start:slice_end])
+            if not gen in gens:
+                os.unlink(path)
+        except ValueError:
+            pass
+    for path in glob.iglob("@efiSysMountPoint@/efi/nixos/*"):
+        if not path in known_paths:
+            os.unlink(path)
+
+parser = argparse.ArgumentParser(description='Update NixOS-related gummiboot files')
+parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot')
+args = parser.parse_args()
+
+# We deserve our own env var!
+if os.getenv("NIXOS_INSTALL_GRUB") == "1":
+    if "@canTouchEfiVariables@" == "1":
+        subprocess.check_call(["@gummiboot@/bin/gummiboot", "--path=@efiSysMountPoint@", "install"])
+    else:
+        subprocess.check_call(["@gummiboot@/bin/gummiboot", "--path=@efiSysMountPoint@", "--no-variables", "install"])
+
+known_paths = []
+mkdir_p("@efiSysMountPoint@/efi/nixos")
+mkdir_p("@efiSysMountPoint@/loader/entries")
+try:
+    with open("/etc/machine-id") as machine_file:
+        machine_id = machine_file.readlines()[0]
+except IOError as e:
+    if e.errno != errno.ENOENT:
+        raise
+    machine_id = None
+
+gens = get_generations("system")
+for gen in gens:
+    add_entry(gen)
+    if os.readlink(system_dir(gen)) == args.default_config:
+        write_loader_conf(gen)
+
+remove_old_entries(gens)
diff --git a/nixos/modules/system/boot/loader/gummiboot/gummiboot.nix b/nixos/modules/system/boot/loader/gummiboot/gummiboot.nix
new file mode 100644
index 00000000000..9193cd3bc53
--- /dev/null
+++ b/nixos/modules/system/boot/loader/gummiboot/gummiboot.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.boot.loader.gummiboot;
+
+  efi = config.boot.loader.efi;
+
+  gummibootBuilder = pkgs.substituteAll {
+    src = ./gummiboot-builder.py;
+
+    isExecutable = true;
+
+    inherit (pkgs) python gummiboot;
+
+    inherit (config.environment) nix;
+
+    inherit (cfg) timeout;
+
+    inherit (efi) efiSysMountPoint canTouchEfiVariables;
+  };
+in {
+  options.boot.loader.gummiboot = {
+    enable = mkOption {
+      default = false;
+
+      type = types.bool;
+
+      description = "Whether to enable the gummiboot UEFI boot manager";
+    };
+
+    timeout = mkOption {
+      default = null;
+
+      example = 4;
+
+      type = types.nullOr types.int;
+
+      description = ''
+        Timeout (in seconds) for how long to show the menu (null if none).
+        Note that even with no timeout the menu can be forced if the space
+        key is pressed during bootup
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub;
+
+        message = "This kernel does not support the EFI boot stub";
+      }
+    ];
+
+    system = {
+      build.installBootLoader = gummibootBuilder;
+
+      boot.loader.id = "gummiboot";
+
+      requiredKernelConfig = with config.lib.kernelConfig; [
+        (isYes "EFI_STUB")
+      ];
+    };
+  };
+}
diff --git a/nixos/modules/system/boot/loader/init-script/init-script-builder.sh b/nixos/modules/system/boot/loader/init-script/init-script-builder.sh
new file mode 100644
index 00000000000..502b3b63af2
--- /dev/null
+++ b/nixos/modules/system/boot/loader/init-script/init-script-builder.sh
@@ -0,0 +1,88 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+if test $# -ne 1; then
+    echo "Usage: init-script-builder.sh DEFAULT-CONFIG"
+    exit 1
+fi
+
+defaultConfig="$1"
+
+
+[ "$(stat -f -c '%i' /)" = "$(stat -f -c '%i' /boot)" ] || {
+  # see grub-menu-builder.sh
+  echo "WARNING: /boot being on a different filesystem not supported by init-script-builder.sh"
+}
+
+
+
+target="/sbin/init"
+targetOther="/boot/init-other-configurations-contents.txt"
+
+tmp="$target.tmp"
+tmpOther="$targetOther.tmp"
+
+
+configurationCounter=0
+numAlienEntries=`cat <<EOF | egrep '^[[:space:]]*title' | wc -l
+@extraEntries@
+EOF`
+
+
+
+
+# Add an entry to $targetOther
+addEntry() {
+    local name="$1"
+    local path="$2"
+    local shortSuffix="$3"
+
+    configurationCounter=$((configurationCounter + 1))
+
+    local stage2=$path/init
+
+    content="$(
+      echo "#!/bin/sh"
+      echo "# $name"
+      echo "# created by init-script-builder.sh"
+      echo "export systemConfig=$(readlink -f $path)"
+      echo "exec $stage2"
+    )"
+
+    [ "$path" != "$defaultConfig" ] || { 
+      echo "$content" > $tmp
+      echo "# older configurations: $targetOther" >> $tmp
+      chmod +x $tmp
+    }
+
+    echo -e "$content\n\n" >> $tmpOther
+}
+
+
+mkdir -p /boot /sbin
+
+addEntry "NixOS - Default" $defaultConfig ""
+
+# Add all generations of the system profile to the menu, in reverse
+# (most recent to least recent) order.
+for link in $((ls -d $defaultConfig/fine-tune/* ) | sort -n); do
+    date=$(stat --printf="%y\n" $link | sed 's/\..*//')
+    addEntry "NixOS - variation" $link ""
+done
+
+for generation in $(
+    (cd /nix/var/nix/profiles && ls -d system-*-link) \
+    | sed 's/system-\([0-9]\+\)-link/\1/' \
+    | sort -n -r); do
+    link=/nix/var/nix/profiles/system-$generation-link
+    date=$(stat --printf="%y\n" $link | sed 's/\..*//')
+    kernelVersion=$(cd $(dirname $(readlink -f $link/kernel))/lib/modules && echo *)
+    addEntry "NixOS - Configuration $generation ($date - $kernelVersion)" $link "$generation ($date)"
+done
+
+mv $tmpOther $targetOther
+mv $tmp $target
diff --git a/nixos/modules/system/boot/loader/init-script/init-script.nix b/nixos/modules/system/boot/loader/init-script/init-script.nix
new file mode 100644
index 00000000000..4b0fcd85b4b
--- /dev/null
+++ b/nixos/modules/system/boot/loader/init-script/init-script.nix
@@ -0,0 +1,50 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  initScriptBuilder = pkgs.substituteAll {
+    src = ./init-script-builder.sh;
+    isExecutable = true;
+    inherit (pkgs) bash;
+    path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    boot.loader.initScript = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Some systems require a /sbin/init script which is started.
+          Or having it makes starting NixOS easier.
+          This applies to some kind of hosting services and user mode linux.
+
+          Additionally this script will create
+          /boot/init-other-configurations-contents.txt containing
+          contents of remaining configurations. You can copy paste them into
+          /sbin/init manually running a rescue system or such.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.boot.loader.initScript.enable {
+
+    system.build.installBootLoader = initScriptBuilder;
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/loader/raspberrypi/builder.sh b/nixos/modules/system/boot/loader/raspberrypi/builder.sh
new file mode 100644
index 00000000000..f6ccfe493d8
--- /dev/null
+++ b/nixos/modules/system/boot/loader/raspberrypi/builder.sh
@@ -0,0 +1,109 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+default=$1
+if test -z "$1"; then
+    echo "Syntax: builder.sh <DEFAULT-CONFIG>"
+    exit 1
+fi
+
+echo "updating the boot generations directory..."
+
+mkdir -p /boot/old
+
+# Convert a path to a file in the Nix store such as
+# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
+cleanName() {
+    local path="$1"
+    echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
+}
+
+# Copy a file from the Nix store to /boot/kernels.
+declare -A filesCopied
+
+copyToKernelsDir() {
+    local src="$1"
+    local dst="/boot/old/$(cleanName $src)"
+    # Don't copy the file if $dst already exists.  This means that we
+    # have to create $dst atomically to prevent partially copied
+    # kernels or initrd if this script is ever interrupted.
+    if ! test -e $dst; then
+        local dstTmp=$dst.tmp.$$
+        cp $src $dstTmp
+        mv $dstTmp $dst
+    fi
+    filesCopied[$dst]=1
+    result=$dst
+}
+
+copyForced() {
+    local src="$1"
+    local dst="$2"
+    cp $src $dst.tmp
+    mv $dst.tmp $dst
+}
+
+outdir=/boot/old
+mkdir -p $outdir || true
+
+# Copy its kernel and initrd to /boot/kernels.
+addEntry() {
+    local path="$1"
+    local generation="$2"
+
+    if ! test -e $path/kernel -a -e $path/initrd; then
+        return
+    fi
+
+    local kernel=$(readlink -f $path/kernel)
+    # local initrd=$(readlink -f $path/initrd)
+
+    if test -n "@copyKernels@"; then
+        copyToKernelsDir $kernel; kernel=$result
+        # copyToKernelsDir $initrd; initrd=$result
+    fi
+    
+    echo $(readlink -f $path) > $outdir/$generation-system
+    echo $(readlink -f $path/init) > $outdir/$generation-init
+    cp $path/kernel-params $outdir/$generation-cmdline.txt
+    # echo $initrd > $outdir/$generation-initrd
+    echo $kernel > $outdir/$generation-kernel
+
+    if test $(readlink -f "$path") = "$default"; then
+      copyForced $kernel /boot/kernel.img
+      # copyForced $initrd /boot/initrd
+      cp "$(readlink -f "$path/init")" /boot/nixos-init
+      echo "`cat $path/kernel-params` init=$path/init" >/boot/cmdline.txt
+
+      echo "$2" > /boot/defaultgeneration
+    fi
+}
+
+# Add all generations of the system profile to the menu, in reverse
+# (most recent to least recent) order.
+for generation in $(
+    (cd /nix/var/nix/profiles && ls -d system-*-link) \
+    | sed 's/system-\([0-9]\+\)-link/\1/' \
+    | sort -n -r); do
+    link=/nix/var/nix/profiles/system-$generation-link
+    addEntry $link $generation
+done
+
+# Add the firmware files
+fwdir=@firmware@/share/raspberrypi/boot/
+copyForced $fwdir/bootcode.bin  /boot/bootcode.bin
+copyForced $fwdir/fixup.dat     /boot/fixup.dat
+copyForced $fwdir/fixup_cd.dat  /boot/fixup_cd.dat
+copyForced $fwdir/start.elf     /boot/start.elf
+copyForced $fwdir/start_cd.elf  /boot/start_cd.elf
+
+# Remove obsolete files from /boot/old.
+for fn in /boot/old/*linux* /boot/old/*initrd*; do
+    if ! test "${filesCopied[$fn]}" = 1; then
+        rm -vf -- "$fn"
+    fi
+done
diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
new file mode 100644
index 00000000000..5bc856c3df0
--- /dev/null
+++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
@@ -0,0 +1,38 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  builder = pkgs.substituteAll {
+    src = ./builder.sh;
+    isExecutable = true;
+    inherit (pkgs) bash;
+    path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+    firmware = pkgs.raspberrypifw;
+  };
+
+  platform = pkgs.stdenv.platform;
+
+in
+
+{
+  options = {
+
+    boot.loader.raspberryPi.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to create files with the system generations in
+        <literal>/boot</literal>.
+        <literal>/boot/old</literal> will hold files from old generations.
+      '';
+    };
+
+  };
+
+  config = mkIf config.boot.loader.raspberryPi.enable {
+    system.build.installBootLoader = builder;
+    system.boot.loader.id = "raspberrypi";
+    system.boot.loader.kernelFile = platform.kernelTarget;
+  };
+}
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
new file mode 100644
index 00000000000..29f5eb4fd77
--- /dev/null
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -0,0 +1,176 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  luks = config.boot.initrd.luks;
+
+  openCommand = { name, device, keyFile, keyFileSize, allowDiscards, ... }: ''
+    # Wait for luksRoot to appear, e.g. if on a usb drive.
+    # XXX: copied and adapted from stage-1-init.sh - should be
+    # available as a function.
+    if ! test -e ${device}; then
+        echo -n "waiting 10 seconds for device ${device} to appear..."
+        for try in $(seq 10); do
+            sleep 1
+            if test -e ${device}; then break; fi
+            echo -n .
+        done
+        echo "ok"
+    fi
+
+    ${optionalString (keyFile != null) ''
+    if ! test -e ${keyFile}; then
+        echo -n "waiting 10 seconds for key file ${keyFile} to appear..."
+        for try in $(seq 10); do
+            sleep 1
+            if test -e ${keyFile}; then break; fi
+            echo -n .
+        done
+        echo "ok"
+    fi
+    ''}
+
+    # open luksRoot and scan for logical volumes
+    cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
+      ${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
+  '';
+
+  isPreLVM = f: f.preLVM;
+  preLVM = filter isPreLVM luks.devices;
+  postLVM = filter (f: !(isPreLVM f)) luks.devices;
+
+in
+{
+
+  options = {
+    boot.initrd.luks.enable = mkOption {
+      default = false;
+      description = "Obsolete.";
+    };
+
+    boot.initrd.luks.mitigateDMAAttacks = mkOption {
+      default = true;
+      description = ''
+        Unless enabled, encryption keys can be easily recovered by an attacker with physical
+        access to any machine with PCMCIA, ExpressCard, ThunderBolt or FireWire port.
+        More information: http://en.wikipedia.org/wiki/DMA_attack
+
+        This option blacklists FireWire drivers, but doesn't remove them. You can manually
+        load the drivers if you need to use a FireWire device, but don't forget to unload them!
+      '';
+    };
+
+    boot.initrd.luks.cryptoModules = mkOption {
+      default =
+        [ "aes" "aes_generic" "blowfish" "twofish"
+          "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512"
+          (if pkgs.stdenv.system == "x86_64-linux" then "aes_x86_64" else "aes_i586")
+        ];
+      description = ''
+        A list of cryptographic kernel modules needed to decrypt the root device(s).
+        The default includes all common modules.
+      '';
+    };
+
+    boot.initrd.luks.devices = mkOption {
+      default = [ ];
+      example = [ { name = "luksroot"; device = "/dev/sda3"; preLVM = true; } ];
+      description = ''
+        The list of devices that should be decrypted using LUKS before trying to mount the
+        root partition. This works for both LVM-over-LUKS and LUKS-over-LVM setups.
+
+        The devices are decrypted to the device mapper names defined.
+
+        Make sure that initrd has the crypto modules needed for decryption.
+      '';
+
+      type = types.listOf types.optionSet;
+
+      options = {
+
+        name = mkOption {
+          example = "luksroot";
+          type = types.string;
+          description = "Named to be used for the generated device in /dev/mapper.";
+        };
+
+        device = mkOption {
+          example = "/dev/sda2";
+          type = types.string;
+          description = "Path of the underlying block device.";
+        };
+
+        keyFile = mkOption {
+          default = null;
+          example = "/dev/sdb1";
+          type = types.nullOr types.string;
+          description = ''
+            The name of the file (can be a raw device or a partition) that
+            should be used as the decryption key for the encrypted device. If
+            not specified, you will be prompted for a passphrase instead.
+          '';
+        };
+
+        keyFileSize = mkOption {
+          default = null;
+          example = 4096;
+          type = types.nullOr types.int;
+          description = ''
+            The size of the key file. Use this if only the beginning of the
+            key file should be used as a key (often the case if a raw device
+            or partition is used as key file). If not specified, the whole
+            <literal>keyFile</literal> will be used decryption, instead of just
+            the first <literal>keyFileSize</literal> bytes.
+          '';
+        };
+
+        preLVM = mkOption {
+          default = true;
+          type = types.bool;
+          description = "Whether the luksOpen will be attempted before LVM scan or after it.";
+        };
+
+        allowDiscards = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            Whether to allow TRIM requests to the underlying device. This option
+            has security implications, please read the LUKS documentation before
+            activating in.
+          '';
+        };
+
+      };
+    };
+  };
+
+  config = mkIf (luks.devices != []) {
+
+    # actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested
+    boot.blacklistedKernelModules = optionals luks.mitigateDMAAttacks
+      ["firewire_ohci" "firewire_core" "firewire_sbp2"];
+
+    # Some modules that may be needed for mounting anything ciphered
+    boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" ] ++ luks.cryptoModules;
+
+    # copy the cryptsetup binary and it's dependencies
+    boot.initrd.extraUtilsCommands = ''
+      cp -pdv ${pkgs.cryptsetup}/sbin/cryptsetup $out/bin
+      # XXX: do we have a function that does this?
+      for lib in $(ldd $out/bin/cryptsetup |grep '=>' |grep /nix/store/ |cut -d' ' -f3); do
+        cp -pdvn $lib $out/lib
+        cp -pvn $(readlink -f $lib) $out/lib
+      done
+    '';
+
+    boot.initrd.extraUtilsCommandsTest = ''
+      $out/bin/cryptsetup --version
+    '';
+
+    boot.initrd.preLVMCommands = concatMapStrings openCommand preLVM;
+    boot.initrd.postDeviceCommands = concatMapStrings openCommand postLVM;
+
+    environment.systemPackages = [ pkgs.cryptsetup ];
+  };
+}
diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix
new file mode 100644
index 00000000000..8b2762e2526
--- /dev/null
+++ b/nixos/modules/system/boot/modprobe.nix
@@ -0,0 +1,112 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    system.sbin.modprobe = mkOption {
+      internal = true;
+      default = pkgs.writeTextFile {
+        name = "modprobe";
+        destination = "/sbin/modprobe";
+        executable = true;
+        text =
+          ''
+            #! ${pkgs.stdenv.shell}
+            export MODULE_DIR=/run/current-system/kernel-modules/lib/modules
+
+            # Fall back to the kernel modules used at boot time if the
+            # modules in the current configuration don't match the
+            # running kernel.
+            if [ ! -d "$MODULE_DIR/$(${pkgs.coreutils}/bin/uname -r)" ]; then
+                MODULE_DIR=/run/booted-system/kernel-modules/lib/modules/
+            fi
+
+            exec ${pkgs.kmod}/sbin/modprobe "$@"
+          '';
+      };
+      description = ''
+        Wrapper around modprobe that sets the path to the modules
+        tree.
+      '';
+    };
+
+    boot.blacklistedKernelModules = mkOption {
+      default = [];
+      example = [ "cirrusfb" "i2c_piix4" ];
+      description = ''
+        List of names of kernel modules that should not be loaded
+        automatically by the hardware probing code.
+      '';
+    };
+
+    boot.extraModprobeConfig = mkOption {
+      default = "";
+      example =
+        ''
+          options parport_pc io=0x378 irq=7 dma=1
+        '';
+      description = ''
+        Any additional configuration to be appended to the generated
+        <filename>modprobe.conf</filename>.  This is typically used to
+        specify module options.  See
+        <citerefentry><refentrytitle>modprobe.conf</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+      type = types.lines;
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.etc = singleton
+      { source = pkgs.writeText "modprobe.conf"
+          ''
+            ${flip concatMapStrings config.boot.blacklistedKernelModules (name: ''
+              blacklist ${name}
+            '')}
+            ${config.boot.extraModprobeConfig}
+          '';
+        target = "modprobe.d/nixos.conf";
+      };
+
+    environment.systemPackages = [ config.system.sbin.modprobe pkgs.kmod ];
+
+    boot.blacklistedKernelModules =
+      [ # This module is for debugging and generates gigantic amounts
+        # of log output, so it should never be loaded automatically.
+        "evbug"
+
+        # This module causes ALSA to occassionally select the wrong
+        # default sound device, and is little more than an annoyance
+        # on modern machines.
+        "snd_pcsp"
+
+        # The cirrusfb module prevents X11 from starting.  FIXME:
+        # Ubuntu blacklists all framebuffer devices because they're
+        # "buggy" and cause suspend problems.  Maybe we should too?
+        "cirrusfb"
+      ];
+
+    system.activationScripts.modprobe =
+      ''
+        # Allow the kernel to find our wrapped modprobe (which searches
+        # in the right location in the Nix store for kernel modules).
+        # We need this when the kernel (or some module) auto-loads a
+        # module.
+        echo ${config.system.sbin.modprobe}/sbin/modprobe > /proc/sys/kernel/modprobe
+      '';
+
+    environment.variables.MODULE_DIR = "/run/current-system/kernel-modules/lib/modules";
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/readonly-mountpoint.c b/nixos/modules/system/boot/readonly-mountpoint.c
new file mode 100644
index 00000000000..27b66687382
--- /dev/null
+++ b/nixos/modules/system/boot/readonly-mountpoint.c
@@ -0,0 +1,20 @@
+#include <sys/statvfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char ** argv) {
+	struct statvfs stat;
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s PATH", argv[0]);
+		exit(2);
+	}
+	if (statvfs(argv[1], &stat) != 0) {
+		perror("statvfs");
+		exit(3);
+	}
+	if (stat.f_flag & ST_RDONLY)
+		exit(0);
+	else
+		exit(1);
+}
+
diff --git a/nixos/modules/system/boot/shutdown.nix b/nixos/modules/system/boot/shutdown.nix
new file mode 100644
index 00000000000..ad71a2e816e
--- /dev/null
+++ b/nixos/modules/system/boot/shutdown.nix
@@ -0,0 +1,27 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  # This unit saves the value of the system clock to the hardware
+  # clock on shutdown.
+  systemd.units."save-hwclock.service" =
+    { wantedBy = [ "shutdown.target" ];
+
+      text =
+        ''
+          [Unit]
+          Description=Save Hardware Clock
+          DefaultDependencies=no
+          Before=shutdown.target
+
+          [Service]
+          Type=oneshot
+          ExecStart=${pkgs.utillinux}/sbin/hwclock --systohc ${if config.time.hardwareClockInLocalTime then "--localtime" else "--utc"}
+        '';
+    };
+
+  boot.kernel.sysctl."kernel.poweroff_cmd" = "${config.systemd.package}/sbin/poweroff";
+
+}
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
new file mode 100644
index 00000000000..e3e07c08580
--- /dev/null
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -0,0 +1,374 @@
+#! @shell@
+
+targetRoot=/mnt-root
+console=tty1
+
+export LD_LIBRARY_PATH=@extraUtils@/lib
+export PATH=@extraUtils@/bin:@extraUtils@/sbin
+
+
+fail() {
+    if [ -n "$panicOnFail" ]; then exit 1; fi
+
+    # If starting stage 2 failed, allow the user to repair the problem
+    # in an interactive shell.
+    cat <<EOF
+
+An error occured in stage 1 of the boot process, which must mount the
+root filesystem on \`$targetRoot' and then start stage 2.  Press one
+of the following keys:
+
+EOF
+    if [ -n "$allowShell" ]; then cat <<EOF
+  i) to launch an interactive shell
+  f) to start an interactive shell having pid 1 (needed if you want to
+     start stage 2's init manually)
+EOF
+    fi
+    cat <<EOF
+  r) to reboot immediately
+  *) to ignore the error and continue
+EOF
+
+    read reply
+
+    if [ -n "$allowShell" -a "$reply" = f ]; then
+        exec setsid @shell@ -c "@shell@ < /dev/$console >/dev/$console 2>/dev/$console"
+    elif [ -n "$allowShell" -a "$reply" = i ]; then
+        echo "Starting interactive shell..."
+        setsid @shell@ -c "@shell@ < /dev/$console >/dev/$console 2>/dev/$console" || fail
+    elif [ "$reply" = r ]; then
+        echo "Rebooting..."
+        reboot -f
+    else
+        echo "Continuing..."
+    fi
+}
+
+trap 'fail' 0
+
+
+# Print a greeting.
+echo
+echo "<<< NixOS Stage 1 >>>"
+echo
+
+
+# Mount special file systems.
+mkdir -p /etc
+touch /etc/fstab # to shut up mount
+touch /etc/mtab # to shut up mke2fs
+mkdir -p /proc
+mount -t proc none /proc
+mkdir -p /sys
+mount -t sysfs none /sys
+mount -t devtmpfs -o "size=@devSize@" none /dev
+mkdir -p /run
+mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
+
+
+# Process the kernel command line.
+export stage2Init=/init
+for o in $(cat /proc/cmdline); do
+    case $o in
+        console=*)
+            set -- $(IFS==; echo $o)
+            params=$2
+            set -- $(IFS=,; echo $params)
+            console=$1
+            ;;
+        init=*)
+            set -- $(IFS==; echo $o)
+            stage2Init=$2
+            ;;
+        boot.trace|debugtrace)
+            # Show each command.
+            set -x
+            ;;
+        boot.shell_on_fail)
+            allowShell=1
+            ;;
+        boot.debug1|debug1) # stop right away
+            allowShell=1
+            fail
+            ;;
+        boot.debug1devices) # stop after loading modules and creating device nodes
+            allowShell=1
+            debug1devices=1
+            ;;
+        boot.debug1mounts) # stop after mounting file systems
+            allowShell=1
+            debug1mounts=1
+            ;;
+        boot.panic_on_fail|stage1panic=1)
+            panicOnFail=1
+            ;;
+        root=*)
+            # If a root device is specified on the kernel command
+            # line, make it available through the symlink /dev/root.
+            # Recognise LABEL= and UUID= to support UNetbootin.
+            set -- $(IFS==; echo $o)
+            if [ $2 = "LABEL" ]; then
+                root="/dev/disk/by-label/$3"
+            elif [ $2 = "UUID" ]; then
+                root="/dev/disk/by-uuid/$3"
+            else
+                root=$2
+            fi
+            ln -s "$root" /dev/root
+            ;;
+    esac
+done
+
+
+# Load the required kernel modules.
+mkdir -p /lib
+ln -s @modulesClosure@/lib/modules /lib/modules
+echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
+for i in @kernelModules@; do
+    echo "loading module $(basename $i)..."
+    modprobe $i || true
+done
+
+
+# Create device nodes in /dev.
+echo "running udev..."
+mkdir -p /etc/udev
+ln -sfn @udevRules@ /etc/udev/rules.d
+mkdir -p /dev/.mdadm
+systemd-udevd --daemon
+udevadm trigger --action=add
+udevadm settle || true
+modprobe scsi_wait_scan || true
+udevadm settle || true
+
+
+# Load boot-time keymap before any LVM/LUKS initialization
+@extraUtils@/bin/busybox loadkmap < "@busyboxKeymap@"
+
+
+# XXX: Use case usb->lvm will still fail, usb->luks->lvm is covered
+@preLVMCommands@
+
+
+echo "starting device mapper and LVM..."
+lvm vgchange -ay
+
+if test -n "$debug1devices"; then fail; fi
+
+
+@postDeviceCommands@
+
+
+# Try to resume - all modules are loaded now, and devices exist
+if test -e /sys/power/tuxonice/resume; then
+    if test -n "$(cat /sys/power/tuxonice/resume)"; then
+        echo 0 > /sys/power/tuxonice/user_interface/enabled
+        echo 1 > /sys/power/tuxonice/do_resume || echo "failed to resume..."
+    fi
+fi
+
+if test -e /sys/power/resume -a -e /sys/power/disk; then
+    echo "@resumeDevice@" > /sys/power/resume 2> /dev/null || echo "failed to resume..."
+    echo shutdown > /sys/power/disk
+fi
+
+
+# Return true if the machine is on AC power, or if we can't determine
+# whether it's on AC power.
+onACPower() {
+    ! test -d "/proc/acpi/battery" ||
+    ! ls /proc/acpi/battery/BAT[0-9]* > /dev/null 2>&1 ||
+    ! cat /proc/acpi/battery/BAT*/state | grep "^charging state" | grep -q "discharg"
+}
+
+
+# Check the specified file system, if appropriate.
+checkFS() {
+    local device="$1"
+    local fsType="$2"
+
+    # Only check block devices.
+    if [ ! -b "$device" ]; then return 0; fi
+
+    # Don't check ROM filesystems.
+    if [ "$fsType" = iso9660 -o "$fsType" = udf ]; then return 0; fi
+
+    # If we couldn't figure out the FS type, then skip fsck.
+    if [ "$fsType" = auto ]; then
+        echo 'cannot check filesystem with type "auto"!'
+        return 0
+    fi
+
+    # Optionally, skip fsck on journaling filesystems.  This option is
+    # a hack - it's mostly because e2fsck on ext3 takes much longer to
+    # recover the journal than the ext3 implementation in the kernel
+    # does (minutes versus seconds).
+    if test -z "@checkJournalingFS@" -a \
+        \( "$fsType" = ext3 -o "$fsType" = ext4 -o "$fsType" = reiserfs \
+        -o "$fsType" = xfs -o "$fsType" = jfs \)
+    then
+        return 0
+    fi
+
+    # Don't run `fsck' if the machine is on battery power.  !!! Is
+    # this a good idea?
+    if ! onACPower; then
+        echo "on battery power, so no \`fsck' will be performed on \`$device'"
+        return 0
+    fi
+
+    echo "checking $device..."
+
+    fsckFlags=
+    if test "$fsType" != "btrfs"; then
+        fsckFlags="-V -a"
+    fi
+    fsck $fsckFlags "$device"
+    fsckResult=$?
+
+    if test $(($fsckResult | 2)) = $fsckResult; then
+        echo "fsck finished, rebooting..."
+        sleep 3
+        reboot -f
+    fi
+
+    if test $(($fsckResult | 4)) = $fsckResult; then
+        echo "$device has unrepaired errors, please fix them manually."
+        fail
+    fi
+
+    if test $fsckResult -ge 8; then
+        echo "fsck on $device failed."
+        fail
+    fi
+
+    return 0
+}
+
+
+# Function for mounting a file system.
+mountFS() {
+    local device="$1"
+    local mountPoint="$2"
+    local options="$3"
+    local fsType="$4"
+
+    if [ "$fsType" = auto ]; then
+        fsType=$(blkid -o value -s TYPE "$device")
+        if [ -z "$fsType" ]; then fsType=auto; fi
+    fi
+
+    echo "$device /mnt-root$mountPoint $fsType $options" >> /etc/fstab
+
+    checkFS "$device" "$fsType"
+
+    echo "mounting $device on $mountPoint..."
+
+    mkdir -p "/mnt-root$mountPoint" || true
+
+    # For CIFS mounts, retry a few times before giving up.
+    local n=0
+    while true; do
+        mount "/mnt-root$mountPoint" && break
+        if [ "$fsType" != cifs -o "$n" -ge 10 ]; then fail; break; fi
+        echo "retrying..."
+        n=$((n + 1))
+    done
+}
+
+
+# Try to find and mount the root device.
+mkdir /mnt-root
+
+exec 3< @fsInfo@
+
+while read -u 3 mountPoint; do
+    read -u 3 device
+    read -u 3 fsType
+    read -u 3 options
+
+    # !!! Really quick hack to support bind mounts, i.e., where the
+    # "device" should be taken relative to /mnt-root, not /.  Assume
+    # that every device that starts with / but doesn't start with /dev
+    # is a bind mount.
+    pseudoDevice=
+    case $device in
+        /dev/*)
+            ;;
+        //*)
+            # Don't touch SMB/CIFS paths.
+            pseudoDevice=1
+            ;;
+        /*)
+            device=/mnt-root$device
+            ;;
+        *)
+            # Not an absolute path; assume that it's a pseudo-device
+            # like an NFS path (e.g. "server:/path").
+            pseudoDevice=1
+            ;;
+    esac
+
+    # USB storage devices tend to appear with some delay.  It would be
+    # great if we had a way to synchronously wait for them, but
+    # alas...  So just wait for a few seconds for the device to
+    # appear.  If it doesn't appear, try to mount it anyway (and
+    # probably fail).  This is a fallback for non-device "devices"
+    # that we don't properly recognise.
+    if test -z "$pseudoDevice" -a ! -e $device; then
+        echo -n "waiting for device $device to appear..."
+        for try in $(seq 1 20); do
+            sleep 1
+            if test -e $device; then break; fi
+            echo -n "."
+        done
+        echo
+    fi
+
+    # Wait once more for the udev queue to empty, just in case it's
+    # doing something with $device right now.
+    udevadm settle || true
+
+    mountFS "$device" "$mountPoint" "$options" "$fsType"
+done
+
+exec 3>&-
+
+
+@postMountCommands@
+
+
+# Stop udevd.
+udevadm control --exit || true
+
+# Kill any remaining processes, just to be sure we're not taking any
+# with us into stage 2. unionfs-fuse mounts require the unionfs process.
+pkill -9 -v '(1|unionfs)'
+
+
+if test -n "$debug1mounts"; then fail; fi
+
+
+# Restore /proc/sys/kernel/modprobe to its original value.
+echo /sbin/modprobe > /proc/sys/kernel/modprobe
+
+
+# Start stage 2.  `switch_root' deletes all files in the ramfs on the
+# current root.  Note that $stage2Init might be an absolute symlink,
+# in which case "-e" won't work because we're not in the chroot yet.
+if ! test -e "$targetRoot/$stage2Init" -o -L "$targetRoot/$stage2Init"; then
+    echo "stage 2 init script ($targetRoot/$stage2Init) not found"
+    fail
+fi
+
+mkdir -m 0755 -p $targetRoot/proc $targetRoot/sys $targetRoot/dev $targetRoot/run
+
+mount --move /proc $targetRoot/proc
+mount --move /sys $targetRoot/sys
+mount --move /dev $targetRoot/dev
+mount --move /run $targetRoot/run
+
+exec env -i $(type -P switch_root) "$targetRoot" "$stage2Init"
+
+fail # should never be reached
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
new file mode 100644
index 00000000000..3836d639513
--- /dev/null
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -0,0 +1,343 @@
+# This module builds the initial ramdisk, which contains an init
+# script that performs the first stage of booting the system: it loads
+# the modules necessary to mount the root file system, then calls the
+# init in the root file system to start the second boot stage.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  udev = config.systemd.package;
+
+  kernelPackages = config.boot.kernelPackages;
+  modulesTree = config.system.modulesTree;
+
+
+  # Determine the set of modules that we need to mount the root FS.
+  modulesClosure = pkgs.makeModulesClosure {
+    rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
+    kernel = modulesTree;
+    allowMissing = true;
+  };
+
+
+  needsCifsUtils = kernelPackages.kernel ? features
+                && kernelPackages.kernel.features ? needsCifsUtils
+                && kernelPackages.kernel.features.needsCifsUtils
+                && any (fs: fs.fsType == "cifs") fileSystems;
+
+  busybox =
+    if needsCifsUtils
+    then pkgs.busybox.override {
+           extraConfig = ''
+             CONFIG_FEATURE_MOUNT_CIFS n
+             CONFIG_FEATURE_MOUNT_HELPERS y
+           '';
+         }
+    else pkgs.busybox;
+
+
+  # Some additional utilities needed in stage 1, like mount, lvm, fsck
+  # etc.  We don't want to bring in all of those packages, so we just
+  # copy what we need.  Instead of using statically linked binaries,
+  # we just copy what we need from Glibc and use patchelf to make it
+  # work.
+  extraUtils = pkgs.runCommand "extra-utils"
+    { buildInputs = [pkgs.nukeReferences];
+      allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
+      doublePatchelf = pkgs.stdenv.isArm;
+    }
+    ''
+      mkdir -p $out/bin $out/lib
+
+      # Copy what we need from Glibc.
+      cp -pv ${pkgs.glibc}/lib/ld*.so.? $out/lib
+      cp -pv ${pkgs.glibc}/lib/libc.so.* $out/lib
+      cp -pv ${pkgs.glibc}/lib/libm.so.* $out/lib
+      cp -pv ${pkgs.glibc}/lib/libpthread.so.* $out/lib
+      cp -pv ${pkgs.glibc}/lib/librt.so.* $out/lib
+      cp -pv ${pkgs.glibc}/lib/libdl.so.* $out/lib
+      cp -pv ${pkgs.gcc.gcc}/lib*/libgcc_s.so.* $out/lib
+
+      # Copy BusyBox.
+      cp -rvd ${busybox}/{bin,sbin} $out/
+      chmod -R u+w $out
+
+      # Copy some utillinux stuff.
+      cp -v ${pkgs.utillinux}/sbin/blkid $out/bin
+      cp -pdv ${pkgs.utillinux}/lib/libblkid*.so.* $out/lib
+      cp -pdv ${pkgs.utillinux}/lib/libuuid*.so.* $out/lib
+
+      # Copy dmsetup and lvm.
+      cp -v ${pkgs.lvm2}/sbin/dmsetup $out/bin/dmsetup
+      cp -v ${pkgs.lvm2}/sbin/lvm $out/bin/lvm
+      cp -v ${pkgs.lvm2}/lib/libdevmapper.so.*.* $out/lib
+      cp -v ${pkgs.systemd}/lib/libsystemd-daemon.so.* $out/lib
+
+      # Add RAID mdadm tool.
+      cp -v ${pkgs.mdadm}/sbin/mdadm $out/bin/mdadm
+
+      # Copy udev.
+      cp -v ${udev}/lib/systemd/systemd-udevd ${udev}/bin/udevadm $out/bin
+      cp -v ${udev}/lib/udev/*_id $out/bin
+      cp -pdv ${udev}/lib/libudev.so.* $out/lib
+      cp -v ${pkgs.kmod}/lib/libkmod.so.* $out/lib
+      cp -v ${pkgs.acl}/lib/libacl.so.* $out/lib
+      cp -v ${pkgs.attr}/lib/libattr.so.* $out/lib
+
+      # Copy modprobe.
+      cp -v ${pkgs.kmod}/bin/kmod $out/bin/
+      ln -s kmod $out/bin/modprobe
+
+      # Maybe copy cifs utils
+      ${optionalString needsCifsUtils ''
+        cp -v ${pkgs.cifs_utils}/sbin/mount.cifs $out/bin
+      ''}
+
+      ${config.boot.initrd.extraUtilsCommands}
+
+      # Strip binaries further than normal.
+      chmod -R u+w $out
+      stripDirs "lib bin" "-s"
+
+      # Run patchelf to make the programs refer to the copied libraries.
+      for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs $i; fi; done
+
+      for i in $out/bin/*; do
+          if ! test -L $i; then
+              echo "patching $i..."
+              patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
+              if [ -n "$doublePatchelf" ]; then
+                  patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
+              fi
+          fi
+      done
+
+      # Make sure that the patchelf'ed binaries still work.
+      echo "testing patched programs..."
+      $out/bin/ash -c 'echo hello world' | grep "hello world"
+      export LD_LIBRARY_PATH=$out/lib
+      $out/bin/mount --help 2>&1 | grep "BusyBox"
+      $out/bin/udevadm --version
+      $out/bin/dmsetup --version 2>&1 | tee -a log | grep "version:"
+      LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a log | grep "LVM"
+      $out/bin/mdadm --version
+
+      ${config.boot.initrd.extraUtilsCommandsTest}
+    ''; # */
+
+
+  # The initrd only has to mount / or any FS marked as necessary for
+  # booting (such as the FS containing /nix/store, or an FS needed for
+  # mounting /, like / on a loopback).
+  fileSystems = filter
+    (fs: fs.neededForBoot || elem fs.mountPoint [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ])
+    (attrValues config.fileSystems);
+
+
+  udevRules = pkgs.stdenv.mkDerivation {
+    name = "udev-rules";
+    buildCommand = ''
+      ensureDir $out
+
+      echo 'ENV{LD_LIBRARY_PATH}="${extraUtils}/lib"' > $out/00-env.rules
+
+      cp -v ${udev}/lib/udev/rules.d/60-cdrom_id.rules $out/
+      cp -v ${udev}/lib/udev/rules.d/60-persistent-storage.rules $out/
+      cp -v ${udev}/lib/udev/rules.d/80-drivers.rules $out/
+      cp -v ${pkgs.lvm2}/lib/udev/rules.d/*.rules $out/
+      cp -v ${pkgs.mdadm}/lib/udev/rules.d/*.rules $out/
+
+      for i in $out/*.rules; do
+          substituteInPlace $i \
+            --replace ata_id ${extraUtils}/bin/ata_id \
+            --replace scsi_id ${extraUtils}/bin/scsi_id \
+            --replace cdrom_id ${extraUtils}/bin/cdrom_id \
+            --replace ${pkgs.utillinux}/sbin/blkid ${extraUtils}/bin/blkid \
+            --replace /sbin/blkid ${extraUtils}/bin/blkid \
+            --replace ${pkgs.lvm2}/sbin ${extraUtils}/bin \
+            --replace /sbin/mdadm ${extraUtils}/bin/mdadm
+      done
+
+      # Work around a bug in QEMU, which doesn't implement the "READ
+      # DISC INFORMATION" SCSI command:
+      #   https://bugzilla.redhat.com/show_bug.cgi?id=609049
+      # As a result, `cdrom_id' doesn't print
+      # ID_CDROM_MEDIA_TRACK_COUNT_DATA, which in turn prevents the
+      # /dev/disk/by-label symlinks from being created.  We need these
+      # in the NixOS installation CD, so use ID_CDROM_MEDIA in the
+      # corresponding udev rules for now.  This was the behaviour in
+      # udev <= 154.  See also
+      #   http://www.spinics.net/lists/hotplug/msg03935.html
+      substituteInPlace $out/60-persistent-storage.rules \
+        --replace ID_CDROM_MEDIA_TRACK_COUNT_DATA ID_CDROM_MEDIA
+    ''; # */
+  };
+
+
+  # The binary keymap for busybox to load at boot.
+  busyboxKeymap = pkgs.runCommand "boottime-keymap"
+    { preferLocalBuild = true; }
+    ''
+      ${pkgs.kbd}/bin/loadkeys -qb "${config.i18n.consoleKeyMap}" > $out ||
+        ${pkgs.kbd}/bin/loadkeys -qbu "${config.i18n.consoleKeyMap}" > $out
+    '';
+
+
+  # The init script of boot stage 1 (loading kernel modules for
+  # mounting the root FS).
+  bootStage1 = pkgs.substituteAll {
+    src = ./stage-1-init.sh;
+
+    shell = "${extraUtils}/bin/ash";
+
+    isExecutable = true;
+
+    inherit udevRules extraUtils modulesClosure busyboxKeymap;
+
+    inherit (config.boot) resumeDevice devSize runSize;
+
+    inherit (config.boot.initrd) checkJournalingFS
+      preLVMCommands postDeviceCommands postMountCommands kernelModules;
+
+    fsInfo =
+      let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType fs.options ];
+      in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f fileSystems));
+  };
+
+
+  # The closure of the init script of boot stage 1 is what we put in
+  # the initial RAM disk.
+  initialRamdisk = pkgs.makeInitrd {
+    inherit (config.boot.initrd) compressor;
+
+    contents =
+      [ { object = bootStage1;
+          symlink = "/init";
+        }
+        { object = pkgs.writeText "mdadm.conf" config.boot.initrd.mdadmConf;
+          symlink = "/etc/mdadm.conf";
+        }
+      ];
+  };
+
+in
+
+{
+  options = {
+
+    boot.resumeDevice = mkOption {
+      default = "";
+      example = "0:0";
+      description = "
+        Device for manual resume attempt during boot. Looks like
+        major:minor. ls -l /dev/SWAP_PARTION shows them.
+      ";
+    };
+
+    boot.initrd.checkJournalingFS = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether to run fsck on journaling filesystems such as ext3.
+      '';
+    };
+
+    boot.initrd.mdadmConf = mkOption {
+      default = "";
+      type = with types; string;
+      description = ''
+        Contents of /etc/mdadm.conf at initrd.
+      '';
+    };
+
+    boot.initrd.preLVMCommands = mkOption {
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed immediately before lvm discovery.
+      '';
+    };
+
+    boot.initrd.postDeviceCommands = mkOption {
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed immediately after stage 1 of the
+        boot has loaded kernel modules and created device nodes in
+        /dev.
+      '';
+    };
+
+    boot.initrd.postMountCommands = mkOption {
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed immediately after the stage 1
+        filesystems have been mounted.
+      '';
+    };
+
+    boot.initrd.extraUtilsCommands = mkOption {
+      internal = true;
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed in the builder of the
+        extra-utils derivation.  This can be used to provide
+        additional utilities in the initial ramdisk.
+      '';
+    };
+
+    boot.initrd.extraUtilsCommandsTest = mkOption {
+      internal = true;
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed in the builder of the
+        extra-utils derivation after patchelf has done its
+        job.  This can be used to test additional utilities
+        copied in extraUtilsCommands.
+      '';
+    };
+
+    boot.initrd.compressor = mkOption {
+      default = "gzip -9";
+
+      type = types.string;
+
+      description = "The compressor to use on the initrd";
+
+      example = "xz";
+    };
+
+    fileSystems = mkOption {
+      options.neededForBoot = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If set, this file system will be mounted in the initial
+          ramdisk.  By default, this applies to the root file system
+          and to the file system containing
+          <filename>/nix/store</filename>.
+        '';
+      };
+    };
+
+  };
+
+  config = {
+
+    system.build.bootStage1 = bootStage1;
+    system.build.initialRamdisk = initialRamdisk;
+    system.build.extraUtils = extraUtils;
+
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "TMPFS")
+      (isYes "BLK_DEV_INITRD")
+    ];
+
+  };
+}
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
new file mode 100644
index 00000000000..2fadd3de1f0
--- /dev/null
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -0,0 +1,173 @@
+#! @shell@
+
+systemConfig=@systemConfig@
+
+export HOME=/root
+
+
+# Print a greeting.
+echo
+echo -e "\e[1;32m<<< NixOS Stage 2 >>>\e[0m"
+echo
+
+
+# Set the PATH.
+setPath() {
+    local dirs="$1"
+    export PATH=/empty
+    for i in $dirs; do
+        PATH=$PATH:$i/bin
+        if test -e $i/sbin; then
+            PATH=$PATH:$i/sbin
+        fi
+    done
+}
+
+setPath "@path@"
+
+
+# Normally, stage 1 mounts the root filesystem read/writable.
+# However, in some environments, stage 2 is executed directly, and the
+# root is read-only.  So make it writable here.
+mount -n -o remount,rw /
+
+
+# Likewise, stage 1 mounts /proc, /dev and /sys, so if we don't have a
+# stage 1, we need to do that here.
+if [ ! -e /proc/1 ]; then
+    mkdir -m 0755 -p /proc
+    mount -n -t proc none /proc
+    mkdir -m 0755 -p /dev
+    mount -t devtmpfs none /dev
+fi
+
+
+echo "booting system configuration $systemConfig" > /dev/kmsg
+
+
+# Make /nix/store a read-only bind mount to enforce immutability of
+# the Nix store.  Note that we can't use "chown root:nixbld" here
+# because users/groups might not exist yet.
+chown 0:30000 /nix/store
+chmod 1775 /nix/store
+if [ -n "@readOnlyStore@" ]; then
+    if ! readonly-mountpoint /nix/store; then
+        mount --bind /nix/store /nix/store
+        mount -o remount,ro,bind /nix/store
+    fi
+fi
+
+
+# Provide a /etc/mtab.
+mkdir -m 0755 -p /etc
+test -e /etc/fstab || touch /etc/fstab # to shut up mount
+rm -f /etc/mtab* # not that we care about stale locks
+ln -s /proc/mounts /etc/mtab
+
+
+# Process the kernel command line.
+for o in $(cat /proc/cmdline); do
+    case $o in
+        boot.debugtrace)
+            # Show each command.
+            set -x
+            ;;
+        resume=*)
+            set -- $(IFS==; echo $o)
+            resumeDevice=$2
+            ;;
+    esac
+done
+
+
+# More special file systems, initialise required directories.
+mkdir -m 0755 /dev/shm
+mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" tmpfs /dev/shm
+mkdir -m 0755 -p /dev/pts
+[ -e /proc/bus/usb ] && mount -t usbfs none /proc/bus/usb # UML doesn't have USB by default
+mkdir -m 01777 -p /tmp
+mkdir -m 0755 -p /var /var/log /var/lib /var/db
+mkdir -m 0755 -p /nix/var
+mkdir -m 0700 -p /root
+mkdir -m 0755 -p /bin # for the /bin/sh symlink
+mkdir -m 0755 -p /home
+mkdir -m 0755 -p /etc/nixos
+
+
+# Miscellaneous boot time cleanup.
+rm -rf /var/run /var/lock
+rm -f /etc/resolv.conf
+touch /etc/resolv.conf
+rm -f /etc/{group,passwd,shadow}.lock
+
+if test -n "@cleanTmpDir@"; then
+    echo -n "cleaning \`/tmp'..."
+    find /tmp -maxdepth 1 -mindepth 1 -print0 | xargs -0r rm -rf --one-file-system
+    echo " done"
+else
+    # Get rid of ICE locks...
+    rm -rf /tmp/.ICE-unix
+fi
+
+# ... and ensure that it's owned by root.
+mkdir -m 1777 /tmp/.ICE-unix
+
+# This is a good time to clean up /nix/var/nix/chroots.  Doing an `rm
+# -rf' on it isn't safe in general because it can contain bind mounts
+# to /nix/store and other places.  But after rebooting these are all
+# gone, of course.
+rm -rf /nix/var/nix/chroots # recreated in activate-configuration.sh
+
+
+# Also get rid of temporary GC roots.
+rm -rf /nix/var/nix/gcroots/tmp /nix/var/nix/temproots
+
+
+# Create a tmpfs on /run to hold runtime state for programs such as
+# udev (if stage 1 hasn't already done so).
+if ! mountpoint -q /run; then
+    rm -rf /run
+    mkdir -m 0755 -p /run
+    mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
+fi
+
+mkdir -m 0755 -p /run/lock
+
+
+# For backwards compatibility, symlink /var/run to /run, and /var/lock
+# to /run/lock.
+ln -s /run /var/run
+ln -s /run/lock /var/lock
+
+
+# Clear the resume device.
+if test -n "$resumeDevice"; then
+    mkswap "$resumeDevice" || echo 'Failed to clear saved image.'
+fi
+
+
+# Run the script that performs all configuration activation that does
+# not have to be done at boot time.
+echo "running activation script..."
+$systemConfig/activate
+
+
+# Record the boot configuration.
+ln -sfn "$systemConfig" /run/booted-system
+
+# Prevent the booted system form being garbage-collected If it weren't
+# a gcroot, if we were running a different kernel, switched system,
+# and garbage collected all, we could not load kernel modules anymore.
+ln -sfn /run/booted-system /nix/var/nix/gcroots/booted-system
+
+
+# Run any user-specified commands.
+@shell@ @postBootCommands@
+
+
+# Start systemd.
+echo "starting systemd..."
+PATH=/run/current-system/systemd/lib/systemd \
+    MODULE_DIR=/run/booted-system/kernel-modules/lib/modules \
+    LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive \
+    exec systemd --log-target=journal # --log-level=debug --log-target=console --crash-shell
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
new file mode 100644
index 00000000000..ff17535e418
--- /dev/null
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -0,0 +1,100 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  kernel = config.boot.kernelPackages.kernel;
+  activateConfiguration = config.system.activationScripts.script;
+
+  readonlyMountpoint = pkgs.runCommand "readonly-mountpoint" {} ''
+    mkdir -p $out/bin
+    cc -O3 ${./readonly-mountpoint.c} -o $out/bin/readonly-mountpoint
+    strip -s $out/bin/readonly-mountpoint
+  '';
+
+  bootStage2 = pkgs.substituteAll {
+    src = ./stage-2-init.sh;
+    shellDebug = "${pkgs.bashInteractive}/bin/bash";
+    isExecutable = true;
+    inherit (config.boot) devShmSize runSize cleanTmpDir;
+    inherit (config.nix) readOnlyStore;
+    ttyGid = config.ids.gids.tty;
+    path =
+      [ pkgs.coreutils
+        pkgs.utillinux
+        pkgs.sysvtools
+      ] ++ (optional config.boot.cleanTmpDir pkgs.findutils)
+      ++ optional config.nix.readOnlyStore readonlyMountpoint;
+    postBootCommands = pkgs.writeText "local-cmds"
+      ''
+        ${config.boot.postBootCommands}
+        ${config.powerManagement.powerUpCommands}
+      '';
+  };
+
+in
+
+{
+  options = {
+
+    boot = {
+
+      postBootCommands = mkOption {
+        default = "";
+        example = "rm -f /var/log/messages";
+        type = types.string;
+        description = ''
+          Shell commands to be executed just before systemd is started.
+        '';
+      };
+
+      devSize = mkOption {
+        default = "5%";
+        example = "32m";
+        type = types.uniq types.string;
+        description = ''
+          Size limit for the /dev tmpfs. Look at mount(8), tmpfs size option,
+          for the accepted syntax.
+        '';
+      };
+
+      devShmSize = mkOption {
+        default = "50%";
+        example = "256m";
+        type = types.uniq types.string;
+        description = ''
+          Size limit for the /dev/shm tmpfs. Look at mount(8), tmpfs size option,
+          for the accepted syntax.
+        '';
+      };
+
+      runSize = mkOption {
+        default = "25%";
+        example = "256m";
+        type = types.uniq types.string;
+        description = ''
+          Size limit for the /run tmpfs. Look at mount(8), tmpfs size option,
+          for the accepted syntax.
+        '';
+      };
+
+      cleanTmpDir = mkOption {
+        default = false;
+        example = true;
+        description = ''
+          Delete all files in /tmp/ during boot.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = {
+
+    system.build.bootStage2 = bootStage2;
+
+  };
+}
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
new file mode 100644
index 00000000000..dfb9036ab4d
--- /dev/null
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -0,0 +1,364 @@
+{ config, pkgs }:
+
+with pkgs.lib;
+
+rec {
+
+  unitOptions = {
+
+    enable = mkOption {
+      default = true;
+      types = types.bool;
+      description = ''
+        If set to false, this unit will be a symlink to
+        /dev/null. This is primarily useful to prevent specific
+        template instances (e.g. <literal>serial-getty@ttyS0</literal>)
+        from being started.
+      '';
+    };
+
+    description = mkOption {
+      default = "";
+      types = types.uniq types.string;
+      description = "Description of this unit used in systemd messages and progress indicators.";
+    };
+
+    requires = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        Start the specified units when this unit is started, and stop
+        this unit when the specified units are stopped or fail.
+      '';
+    };
+
+    wants = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        Start the specified units when this unit is started.
+      '';
+    };
+
+    after = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are started at the same time as
+        this unit, delay this unit until they have started.
+      '';
+    };
+
+    before = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are started at the same time as
+        this unit, delay them until this unit has started.
+      '';
+    };
+
+    bindsTo = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        Like ‘requires’, but in addition, if the specified units
+        unexpectedly disappear, this unit will be stopped as well.
+      '';
+    };
+
+    partOf = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are stopped or restarted, then this
+        unit is stopped or restarted as well.
+      '';
+    };
+
+    conflicts = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are started, then this unit is stopped
+        and vice versa.
+      '';
+    };
+
+    requiredBy = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = "Units that require (i.e. depend on and need to go down with) this unit.";
+    };
+
+    wantedBy = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = "Units that want (i.e. depend on) this unit.";
+    };
+
+    unitConfig = mkOption {
+      default = {};
+      example = { RequiresMountsFor = "/data"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Unit]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+    restartTriggers = mkOption {
+      default = [];
+      description = ''
+        An arbitrary list of items such as derivations.  If any item
+        in the list changes between reconfigurations, the service will
+        be restarted.
+      '';
+    };
+
+  };
+
+
+  serviceOptions = unitOptions // {
+
+    environment = mkOption {
+      default = {};
+      type = types.attrs;
+      example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
+      description = "Environment variables passed to the service's processes.";
+    };
+
+    path = mkOption {
+      default = [];
+      apply = ps: "${makeSearchPath "bin" ps}:${makeSearchPath "sbin" ps}";
+      description = ''
+        Packages added to the service's <envar>PATH</envar>
+        environment variable.  Both the <filename>bin</filename>
+        and <filename>sbin</filename> subdirectories of each
+        package are added.
+      '';
+    };
+
+    serviceConfig = mkOption {
+      default = {};
+      example =
+        { StartLimitInterval = 10;
+          RestartSec = 5;
+        };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Service]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.service</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+
+      check = v:
+        let assertValueOneOf = name: values: attr:
+              let val = getAttr name attr;
+              in optional ( hasAttr name attr && !elem val values) "${name} ${val} not known to systemd";
+            checkType = assertValueOneOf "Type" ["simple" "forking" "oneshot" "dbus" "notify" "idle"];
+            checkRestart = assertValueOneOf "Restart" ["no" "on-success" "on-failure" "on-abort" "always"];
+            errors = concatMap (c: c v) [checkType checkRestart];
+        in if errors == [] then true
+           else builtins.trace (concatStringsSep "\n" errors) false;
+    };
+
+    script = mkOption {
+      type = types.uniq types.string;
+      default = "";
+      description = "Shell commands executed as the service's main process.";
+    };
+
+    scriptArgs = mkOption {
+      type = types.uniq types.string;
+      default = "";
+      description = "Arguments passed to the main process script.";
+    };
+
+    preStart = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed before the service's main process
+        is started.
+      '';
+    };
+
+    postStart = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed after the service's main process
+        is started.
+      '';
+    };
+
+    postStop = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed after the service's main process
+        has exited.
+      '';
+    };
+
+    restartIfChanged = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether the service should be restarted during a NixOS
+        configuration switch if its definition has changed.
+      '';
+    };
+
+    stopIfChanged = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        If set, a changed unit is restarted by calling
+        <command>systemctl stop</command> in the old configuration,
+        then <command>systemctl start</command> in the new one.
+        Otherwise, it is restarted in a single step using
+        <command>systemctl restart</command> in the new configuration.
+        The latter is less correct because it runs the
+        <literal>ExecStop</literal> commands from the new
+        configuration.
+      '';
+    };
+
+    startAt = mkOption {
+      type = types.uniq types.string;
+      default = "";
+      example = "Sun 14:00:00";
+      description = ''
+        Automatically start this unit at the given date/time, which
+        must be in the format described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry>.  This is equivalent
+        to adding a corresponding timer unit with
+        <option>OnCalendar</option> set to the value given here.
+      '';
+    };
+
+  };
+
+
+  socketOptions = unitOptions // {
+
+    listenStreams = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      example = [ "0.0.0.0:993" "/run/my-socket" ];
+      description = ''
+        For each item in this list, a <literal>ListenStream</literal>
+        option in the <literal>[Socket]</literal> section will be created.
+      '';
+    };
+
+    socketConfig = mkOption {
+      default = {};
+      example = { ListenStream = "/run/my-socket"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Socket]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.socket</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+  };
+
+
+  timerOptions = unitOptions // {
+
+    timerConfig = mkOption {
+      default = {};
+      example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Timer]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.timer</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> and
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+  };
+
+
+  mountOptions = unitOptions // {
+
+    what = mkOption {
+      example = "/dev/sda1";
+      type = types.uniq types.string;
+      description = "Absolute path of device node, file or other resource. (Mandatory)";
+    };
+
+    where = mkOption {
+      example = "/mnt";
+      type = types.uniq types.string;
+      description = ''
+        Absolute path of a directory of the mount point.
+        Will be created if it doesn't exist. (Mandatory)
+      '';
+    };
+
+    type = mkOption {
+      default = "";
+      example = "ext4";
+      type = types.uniq types.string;
+      description = "File system type.";
+    };
+
+    options = mkOption {
+      default = "";
+      example = "noatime";
+      type = types.string;
+      merge = concatStringsSep ",";
+      description = "Options used to mount the file system.";
+    };
+
+    mountConfig = mkOption {
+      default = {};
+      example = { DirectoryMode = "0775"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Mount]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.mount</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+  };
+
+  automountOptions = unitOptions // {
+
+    where = mkOption {
+      example = "/mnt";
+      type = types.uniq types.string;
+      description = ''
+        Absolute path of a directory of the mount point.
+        Will be created if it doesn't exist. (Mandatory)
+      '';
+    };
+
+    automountConfig = mkOption {
+      default = {};
+      example = { DirectoryMode = "0775"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Automount]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.automount</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+  };
+
+}
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
new file mode 100644
index 00000000000..a5e1165574c
--- /dev/null
+++ b/nixos/modules/system/boot/systemd.nix
@@ -0,0 +1,678 @@
+{ config, pkgs, utils, ... }:
+
+with pkgs.lib;
+with utils;
+with import ./systemd-unit-options.nix { inherit config pkgs; };
+
+let
+
+  cfg = config.systemd;
+
+  systemd = cfg.package;
+
+  makeUnit = name: unit:
+    pkgs.runCommand "unit" { inherit (unit) text; preferLocalBuild = true; }
+      (if unit.enable then  ''
+        mkdir -p $out
+        echo -n "$text" > $out/${name}
+      '' else ''
+        mkdir -p $out
+        ln -s /dev/null $out/${name}
+      '');
+
+  upstreamUnits =
+    [ # Targets.
+      "basic.target"
+      "sysinit.target"
+      "sockets.target"
+      "graphical.target"
+      "multi-user.target"
+      "getty.target"
+      "network.target"
+      "network-online.target"
+      "nss-lookup.target"
+      "nss-user-lookup.target"
+      "time-sync.target"
+      #"cryptsetup.target"
+      "sigpwr.target"
+      "timers.target"
+      "paths.target"
+
+      # Rescue mode.
+      "rescue.target"
+      "rescue.service"
+
+      # Udev.
+      "systemd-udevd-control.socket"
+      "systemd-udevd-kernel.socket"
+      "systemd-udevd.service"
+      "systemd-udev-settle.service"
+      "systemd-udev-trigger.service"
+
+      # Hardware (started by udev when a relevant device is plugged in).
+      "sound.target"
+      "bluetooth.target"
+      "printer.target"
+      "smartcard.target"
+
+      # Login stuff.
+      "systemd-logind.service"
+      "autovt@.service"
+      #"systemd-vconsole-setup.service"
+      "systemd-user-sessions.service"
+      "dbus-org.freedesktop.login1.service"
+      "user@.service"
+
+      # Journal.
+      "systemd-journald.socket"
+      "systemd-journald.service"
+      "systemd-journal-flush.service"
+      "syslog.socket"
+
+      # SysV init compatibility.
+      "systemd-initctl.socket"
+      "systemd-initctl.service"
+
+      # Kernel module loading.
+      #"systemd-modules-load.service"
+
+      # Filesystems.
+      "systemd-fsck@.service"
+      "systemd-fsck-root.service"
+      "systemd-remount-fs.service"
+      "local-fs.target"
+      "local-fs-pre.target"
+      "remote-fs.target"
+      "remote-fs-pre.target"
+      "swap.target"
+      "dev-hugepages.mount"
+      "dev-mqueue.mount"
+      "sys-fs-fuse-connections.mount"
+      "sys-kernel-config.mount"
+      "sys-kernel-debug.mount"
+
+      # Hibernate / suspend.
+      "hibernate.target"
+      "suspend.target"
+      "sleep.target"
+      "hybrid-sleep.target"
+      "systemd-hibernate.service"
+      "systemd-suspend.service"
+      "systemd-hybrid-sleep.service"
+      "systemd-shutdownd.socket"
+      "systemd-shutdownd.service"
+
+      # Reboot stuff.
+      "reboot.target"
+      "systemd-reboot.service"
+      "poweroff.target"
+      "systemd-poweroff.service"
+      "halt.target"
+      "systemd-halt.service"
+      "ctrl-alt-del.target"
+      "shutdown.target"
+      "umount.target"
+      "final.target"
+      "kexec.target"
+      "systemd-kexec.service"
+
+      # Password entry.
+      "systemd-ask-password-console.path"
+      "systemd-ask-password-console.service"
+      "systemd-ask-password-wall.path"
+      "systemd-ask-password-wall.service"
+    ]
+
+    ++ optionals cfg.enableEmergencyMode [
+      "emergency.target"
+      "emergency.service"
+    ];
+
+  upstreamWants =
+    [ #"basic.target.wants"
+      "sysinit.target.wants"
+      "sockets.target.wants"
+      "local-fs.target.wants"
+      "multi-user.target.wants"
+      "shutdown.target.wants"
+      "timers.target.wants"
+    ];
+
+  makeJobScript = name: text:
+    let x = pkgs.writeTextFile { name = "unit-script"; executable = true; destination = "/bin/${name}"; inherit text; };
+    in "${x}/bin/${name}";
+
+  unitConfig = { name, config, ... }: {
+    config = {
+      unitConfig =
+        { Requires = concatStringsSep " " config.requires;
+          Wants = concatStringsSep " " config.wants;
+          After = concatStringsSep " " config.after;
+          Before = concatStringsSep " " config.before;
+          BindsTo = concatStringsSep " " config.bindsTo;
+          PartOf = concatStringsSep " " config.partOf;
+          Conflicts = concatStringsSep " " config.conflicts;
+          "X-Restart-Triggers" = toString config.restartTriggers;
+        } // optionalAttrs (config.description != "") {
+          Description = config.description;
+        };
+    };
+  };
+
+  serviceConfig = { name, config, ... }: {
+    config = {
+      # Default path for systemd services.  Should be quite minimal.
+      path =
+        [ pkgs.coreutils
+          pkgs.findutils
+          pkgs.gnugrep
+          pkgs.gnused
+          systemd
+        ];
+    };
+  };
+
+  mountConfig = { name, config, ... }: {
+    config = {
+      mountConfig =
+        { What = config.what;
+          Where = config.where;
+        } // optionalAttrs (config.type != "") {
+          Type = config.type;
+        } // optionalAttrs (config.options != "") {
+          Options = config.options;
+        };
+    };
+  };
+
+  automountConfig = { name, config, ... }: {
+    config = {
+      automountConfig =
+        { Where = config.where;
+        };
+    };
+  };
+
+  toOption = x:
+    if x == true then "true"
+    else if x == false then "false"
+    else toString x;
+
+  attrsToSection = as:
+    concatStrings (concatLists (mapAttrsToList (name: value:
+      map (x: ''
+          ${name}=${toOption x}
+        '')
+        (if isList value then value else [value]))
+        as));
+
+  targetToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+        '';
+    };
+
+  serviceToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Service]
+          Environment=PATH=${def.path}
+          Environment=LD_LIBRARY_PATH=
+          ${let env = cfg.globalEnvironment // def.environment;
+            in concatMapStrings (n: "Environment=\"${n}=${getAttr n env}\"\n") (attrNames env)}
+          ${optionalString (!def.restartIfChanged) "X-RestartIfChanged=false"}
+          ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
+
+          ${optionalString (def.preStart != "") ''
+            ExecStartPre=${makeJobScript "${name}-pre-start" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.preStart}
+            ''}
+          ''}
+
+          ${optionalString (def.script != "") ''
+            ExecStart=${makeJobScript "${name}-start" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.script}
+            ''} ${def.scriptArgs}
+          ''}
+
+          ${optionalString (def.postStart != "") ''
+            ExecStartPost=${makeJobScript "${name}-post-start" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.postStart}
+            ''}
+          ''}
+
+          ${optionalString (def.postStop != "") ''
+            ExecStopPost=${makeJobScript "${name}-post-stop" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.postStop}
+            ''}
+          ''}
+
+          ${attrsToSection def.serviceConfig}
+        '';
+    };
+
+  socketToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Socket]
+          ${attrsToSection def.socketConfig}
+          ${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
+        '';
+    };
+
+  timerToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Timer]
+          ${attrsToSection def.timerConfig}
+        '';
+    };
+
+  mountToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Mount]
+          ${attrsToSection def.mountConfig}
+        '';
+    };
+
+  automountToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Automount]
+          ${attrsToSection def.automountConfig}
+        '';
+    };
+
+  nixosUnits = mapAttrsToList makeUnit cfg.units;
+
+  units = pkgs.runCommand "units" { preferLocalBuild = true; }
+    ''
+      mkdir -p $out
+      for i in ${toString upstreamUnits}; do
+        fn=${systemd}/example/systemd/system/$i
+        if ! [ -e $fn ]; then echo "missing $fn"; false; fi
+        if [ -L $fn ]; then
+          cp -pd $fn $out/
+        else
+          ln -s $fn $out/
+        fi
+      done
+
+      for i in ${toString upstreamWants}; do
+        fn=${systemd}/example/systemd/system/$i
+        if ! [ -e $fn ]; then echo "missing $fn"; false; fi
+        x=$out/$(basename $fn)
+        mkdir $x
+        for i in $fn/*; do
+          y=$x/$(basename $i)
+          cp -pd $i $y
+          if ! [ -e $y ]; then rm -v $y; fi
+        done
+      done
+
+      for i in ${toString nixosUnits}; do
+        ln -s $i/* $out/
+      done
+
+      for i in ${toString cfg.packages}; do
+        ln -s $i/etc/systemd/system/* $out/
+      done
+
+      ${concatStrings (mapAttrsToList (name: unit:
+          concatMapStrings (name2: ''
+            mkdir -p $out/${name2}.wants
+            ln -sfn ../${name} $out/${name2}.wants/
+          '') unit.wantedBy) cfg.units)}
+
+      ${concatStrings (mapAttrsToList (name: unit:
+          concatMapStrings (name2: ''
+            mkdir -p $out/${name2}.requires
+            ln -sfn ../${name} $out/${name2}.requires/
+          '') unit.requiredBy) cfg.units)}
+
+      ln -s ${cfg.defaultUnit} $out/default.target
+
+      ln -s rescue.target $out/kbrequest.target
+
+      mkdir -p $out/getty.target.wants/
+      ln -s ../getty@tty1.service $out/getty.target.wants/
+
+      ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \
+            ../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/
+    ''; # */
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    systemd.package = mkOption {
+      default = pkgs.systemd;
+      type = types.package;
+      description = "The systemd package.";
+    };
+
+    systemd.units = mkOption {
+      description = "Definition of systemd units.";
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = {
+        text = mkOption {
+          types = types.uniq types.string;
+          description = "Text of this systemd unit.";
+        };
+        enable = mkOption {
+          default = true;
+          types = types.bool;
+          description = ''
+            If set to false, this unit will be a symlink to
+            /dev/null. This is primarily useful to prevent specific
+            template instances (e.g. <literal>serial-getty@ttyS0</literal>)
+            from being started.
+          '';
+        };
+        requiredBy = mkOption {
+          default = [];
+          types = types.listOf types.string;
+          description = "Units that require (i.e. depend on and need to go down with) this unit.";
+        };
+        wantedBy = mkOption {
+          default = [];
+          types = types.listOf types.string;
+          description = "Units that want (i.e. depend on) this unit.";
+        };
+      };
+    };
+
+    systemd.packages = mkOption {
+      default = [];
+      type = types.listOf types.package;
+      description = "Packages providing systemd units.";
+    };
+
+    systemd.targets = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ unitOptions unitConfig ];
+      description = "Definition of systemd target units.";
+    };
+
+    systemd.services = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ serviceOptions unitConfig serviceConfig ];
+      description = "Definition of systemd service units.";
+    };
+
+    systemd.sockets = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ socketOptions unitConfig ];
+      description = "Definition of systemd socket units.";
+    };
+
+    systemd.timers = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ timerOptions unitConfig ];
+      description = "Definition of systemd timer units.";
+    };
+
+    systemd.mounts = mkOption {
+      default = [];
+      type = types.listOf types.optionSet;
+      options = [ mountOptions unitConfig mountConfig ];
+      description = ''
+        Definition of systemd mount units.
+        This is a list instead of an attrSet, because systemd mandates the names to be derived from
+        the 'where' attribute.
+      '';
+    };
+
+    systemd.automounts = mkOption {
+      default = [];
+      type = types.listOf types.optionSet;
+      options = [ automountOptions unitConfig automountConfig ];
+      description = ''
+        Definition of systemd automount units.
+        This is a list instead of an attrSet, because systemd mandates the names to be derived from
+        the 'where' attribute.
+      '';
+    };
+
+    systemd.defaultUnit = mkOption {
+      default = "multi-user.target";
+      type = types.uniq types.string;
+      description = "Default unit started when the system boots.";
+    };
+
+    systemd.globalEnvironment = mkOption {
+      type = types.attrs;
+      default = {};
+      example = { TZ = "CET"; };
+      description = ''
+        Environment variables passed to <emphasis>all</emphasis> systemd units.
+      '';
+    };
+
+    services.journald.console = mkOption {
+      default = "";
+      type = types.uniq types.string;
+      description = "If non-empty, write log messages to the specified TTY device.";
+    };
+
+    services.journald.rateLimitInterval = mkOption {
+      default = "10s";
+      type = types.uniq types.string;
+      description = ''
+        Configures the rate limiting interval that is applied to all
+        messages generated on the system. This rate limiting is applied
+        per-service, so that two services which log do not interfere with
+        each other's limit. The value may be specified in the following
+        units: s, min, h, ms, us. To turn off any kind of rate limiting,
+        set either value to 0.
+      '';
+    };
+
+    services.journald.rateLimitBurst = mkOption {
+      default = 100;
+      type = types.uniq types.int;
+      description = ''
+        Configures the rate limiting burst limit (number of messages per
+        interval) that is applied to all messages generated on the system.
+        This rate limiting is applied per-service, so that two services
+        which log do not interfere with each other's limit.
+      '';
+    };
+
+    services.logind.extraConfig = mkOption {
+      default = "";
+      type = types.uniq types.string;
+      example = "HandleLidSwitch=ignore";
+      description = ''
+        Extra config options for systemd-logind. See man logind.conf for
+        available options.
+      '';
+    };
+
+    systemd.enableEmergencyMode = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether to enable emergency mode, which is an
+        <command>sulogin</command> shell started on the console if
+        mounting a filesystem fails.  Since some machines (like EC2
+        instances) have no console of any kind, emergency mode doesn't
+        make sense, and it's better to continue with the boot insofar
+        as possible.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    system.build.units = units;
+
+    environment.systemPackages = [ systemd ];
+
+    environment.etc."systemd/system".source = units;
+
+    environment.etc."systemd/system.conf".text =
+      ''
+        [Manager]
+      '';
+
+    environment.etc."systemd/journald.conf".text =
+      ''
+        [Journal]
+        RateLimitInterval=${config.services.journald.rateLimitInterval}
+        RateLimitBurst=${toString config.services.journald.rateLimitBurst}
+        ${optionalString (config.services.journald.console != "") ''
+          ForwardToConsole=yes
+          TTYPath=${config.services.journald.console}
+        ''}
+      '';
+
+    environment.etc."systemd/logind.conf".text =
+      ''
+        [Login]
+        ${config.services.logind.extraConfig}
+      '';
+
+    environment.etc."systemd/sleep.conf".text =
+      ''
+        [Sleep]
+      '';
+
+    system.activationScripts.systemd = stringAfter [ "groups" ]
+      ''
+        mkdir -m 0755 -p /var/lib/udev
+        mkdir -p /var/log/journal
+        chmod 0755 /var/log/journal
+
+        # Regenerate the hardware database /var/lib/udev/hwdb.bin
+        # whenever systemd changes.
+        if [ ! -e /var/lib/udev/prev-systemd -o "$(readlink /var/lib/udev/prev-systemd)" != ${systemd} ]; then
+          echo "regenerating udev hardware database..."
+          ${systemd}/bin/udevadm hwdb --update && ln -sfn ${systemd} /var/lib/udev/prev-systemd
+        fi
+
+        # Make all journals readable to users in the wheel and adm
+        # groups, in addition to those in the systemd-journal group.
+        # Users can always read their own journals.
+        ${pkgs.acl}/bin/setfacl -nm g:wheel:rx,d:g:wheel:rx,g:adm:rx,d:g:adm:rx /var/log/journal
+      '';
+
+    # Target for ‘charon send-keys’ to hook into.
+    systemd.targets.keys =
+      { description = "Security Keys";
+      };
+
+    systemd.units =
+      mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets
+      // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
+      // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
+      // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers
+      // listToAttrs (map
+                   (v: let n = escapeSystemdPath v.where;
+                       in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
+      // listToAttrs (map
+                   (v: let n = escapeSystemdPath v.where;
+                       in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
+
+    system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled [
+      "CGROUPS" "AUTOFS4_FS" "DEVTMPFS"
+    ];
+
+    environment.shellAliases =
+      { start = "systemctl start";
+        stop = "systemctl stop";
+        restart = "systemctl restart";
+        status = "systemctl status";
+      };
+
+    users.extraGroups.systemd-journal.gid = config.ids.gids.systemd-journal;
+
+    # Generate timer units for all services that have a ‘startAt’ value.
+    systemd.timers =
+      mapAttrs (name: service:
+        { wantedBy = [ "timers.target" ];
+          timerConfig.OnCalendar = service.startAt;
+        })
+        (filterAttrs (name: service: service.startAt != "") cfg.services);
+
+    # FIXME: These are borrowed from upstream systemd.
+    systemd.services."systemd-update-utmp" =
+      { description = "Update UTMP about System Reboot/Shutdown";
+        wantedBy = [ "sysinit.target" ];
+        after = [ "systemd-remount-fs.service" ];
+        before = [ "sysinit.target" "shutdown.target" ];
+        conflicts = [ "shutdown.target" ];
+        unitConfig = {
+          DefaultDependencies = false;
+          RequiresMountsFor = "/var/log";
+        };
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${systemd}/lib/systemd/systemd-update-utmp reboot";
+          ExecStop = "${systemd}/lib/systemd/systemd-update-utmp shutdown";
+        };
+        restartIfChanged = false;
+      };
+
+    systemd.services."systemd-random-seed" =
+      { description = "Load/Save Random Seed";
+        wantedBy = [ "sysinit.target" "multi-user.target" ];
+        after = [ "systemd-remount-fs.service" ];
+        before = [ "sysinit.target" "shutdown.target" ];
+        conflicts = [ "shutdown.target" ];
+        unitConfig = {
+          DefaultDependencies = false;
+          RequiresMountsFor = "/var/lib";
+        };
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${systemd}/lib/systemd/systemd-random-seed load";
+          ExecStop = "${systemd}/lib/systemd/systemd-random-seed save";
+        };
+      };
+
+  };
+}
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
new file mode 100644
index 00000000000..91fcdcf2435
--- /dev/null
+++ b/nixos/modules/system/etc/etc.nix
@@ -0,0 +1,117 @@
+# Management of static files in /etc.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  etc' = filter (f: f.enable) (attrValues config.environment.etc);
+
+  etc = pkgs.stdenv.mkDerivation {
+    name = "etc";
+
+    builder = ./make-etc.sh;
+
+    preferLocalBuild = true;
+
+    /* !!! Use toXML. */
+    sources = map (x: x.source) etc';
+    targets = map (x: x.target) etc';
+    modes = map (x: x.mode) etc';
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    environment.etc = mkOption {
+      type = types.loaOf types.optionSet;
+      default = {};
+      example =
+        { hosts =
+            { source = "/nix/store/.../etc/dir/file.conf.example";
+              mode = "0440";
+            };
+          "default/useradd".text = "GROUP=100 ...";
+        };
+      description = ''
+        Set of files that have to be linked in <filename>/etc</filename>.
+      '';
+
+      options = singleton ({ name, config, ... }:
+        { options = {
+
+            enable = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether this /etc file should be generated.  This
+                option allows specific /etc files to be disabled.
+              '';
+            };
+
+            target = mkOption {
+              description = ''
+                Name of symlink (relative to
+                <filename>/etc</filename>).  Defaults to the attribute
+                name.
+              '';
+            };
+
+            text = mkOption {
+              default = null;
+              type = types.nullOr types.string;
+              description = "Text of the file.";
+            };
+
+            source = mkOption {
+              types = types.path;
+              description = "Path of the source file.";
+            };
+
+            mode = mkOption {
+              default = "symlink";
+              example = "0600";
+              description = ''
+                If set to something else than <literal>symlink</literal>,
+                the file is copied instead of symlinked, with the given
+                file mode.
+              '';
+            };
+
+          };
+
+          config = {
+            target = mkDefault name;
+            source = mkIf (config.text != null)
+              (mkDefault (pkgs.writeText "etc-file" config.text));
+          };
+
+        });
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    system.build.etc = etc;
+
+    system.activationScripts.etc = stringAfter [ "stdio" ]
+      ''
+        # Set up the statically computed bits of /etc.
+        echo "setting up /etc..."
+        ${pkgs.perl}/bin/perl ${./setup-etc.pl} ${etc}/etc
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/system/etc/make-etc.sh b/nixos/modules/system/etc/make-etc.sh
new file mode 100644
index 00000000000..7cf68db9ddc
--- /dev/null
+++ b/nixos/modules/system/etc/make-etc.sh
@@ -0,0 +1,42 @@
+source $stdenv/setup
+
+mkdir -p $out/etc
+
+set -f
+sources_=($sources)
+targets_=($targets)
+modes_=($modes)
+set +f
+
+for ((i = 0; i < ${#targets_[@]}; i++)); do
+    source="${sources_[$i]}"
+    target="${targets_[$i]}"
+
+    if [[ "$source" =~ '*' ]]; then
+
+        # If the source name contains '*', perform globbing.
+        mkdir -p $out/etc/$target
+        for fn in $source; do
+            ln -s "$fn" $out/etc/$target/
+        done
+
+    else
+        
+        mkdir -p $out/etc/$(dirname $target)
+        if ! [ -e $out/etc/$target ]; then
+            ln -s $source $out/etc/$target
+        else
+            echo "duplicate entry $target -> $source"
+            if test "$(readlink $out/etc/$target)" != "$source"; then
+                echo "mismatched duplicate entry $(readlink $out/etc/$target) <-> $source"
+                exit 1
+            fi
+        fi
+        
+        if test "${modes_[$i]}" != symlink; then
+            echo "${modes_[$i]}" > $out/etc/$target.mode
+        fi
+        
+    fi
+done
+
diff --git a/nixos/modules/system/etc/setup-etc.pl b/nixos/modules/system/etc/setup-etc.pl
new file mode 100644
index 00000000000..7cb6d2a6a45
--- /dev/null
+++ b/nixos/modules/system/etc/setup-etc.pl
@@ -0,0 +1,68 @@
+use strict;
+use File::Find;
+use File::Copy;
+use File::Path;
+use File::Basename;
+
+my $etc = $ARGV[0] or die;
+my $static = "/etc/static";
+
+sub atomicSymlink {
+    my ($source, $target) = @_;
+    my $tmp = "$target.tmp";
+    unlink $tmp;
+    symlink $source, $tmp or return 1;
+    rename $tmp, $target or return 1;
+    return 1;
+}
+
+
+# Atomically update /etc/static to point at the etc files of the
+# current configuration.
+atomicSymlink $etc, $static or die;
+
+
+# Remove dangling symlinks that point to /etc/static.  These are
+# configuration files that existed in a previous configuration but not
+# in the current one.  For efficiency, don't look under /etc/nixos
+# (where all the NixOS sources live).
+sub cleanup {
+    if ($File::Find::name eq "/etc/nixos") {
+        $File::Find::prune = 1;
+        return;
+    }
+    if (-l $_) {
+        my $target = readlink $_;
+        if (substr($target, 0, length $static) eq $static) {
+            my $x = "/etc/static/" . substr($File::Find::name, length "/etc/");
+            unless (-l $x) {
+                print STDERR "removing obsolete symlink ‘$File::Find::name’...\n";
+                unlink "$_";
+            }
+        }
+    }
+}
+
+find(\&cleanup, "/etc");
+
+
+# For every file in the etc tree, create a corresponding symlink in
+# /etc to /etc/static.  The indirection through /etc/static is to make
+# switching to a new configuration somewhat more atomic.
+sub link {
+    my $fn = substr $File::Find::name, length($etc) + 1 or next;
+    my $target = "/etc/$fn";
+    File::Path::make_path(dirname $target);
+    if (-e "$_.mode") {
+        open MODE, "<$_.mode";
+        my $mode = <MODE>; chomp $mode;
+        close MODE;
+        copy "$static/$fn", "$target.tmp" or warn;
+        chmod oct($mode), "$target.tmp" or warn;
+        rename "$target.tmp", $target or warn;
+    } elsif (-l "$_") {
+        atomicSymlink "$static/$fn", $target or warn;
+    }
+}
+
+find(\&link, $etc);
diff --git a/nixos/modules/system/upstart/upstart.nix b/nixos/modules/system/upstart/upstart.nix
new file mode 100644
index 00000000000..5d5139b7a57
--- /dev/null
+++ b/nixos/modules/system/upstart/upstart.nix
@@ -0,0 +1,286 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+with import ../boot/systemd-unit-options.nix { inherit config pkgs; };
+
+let
+
+  userExists = u:
+    (u == "") || any (uu: uu.name == u) (attrValues config.users.extraUsers);
+
+  groupExists = g:
+    (g == "") || any (gg: gg.name == g) (attrValues config.users.extraGroups);
+
+  makeJobScript = name: content: "${pkgs.writeScriptBin name content}/bin/${name}";
+
+  # From a job description, generate an systemd unit file.
+  makeUnit = job:
+
+    let
+      hasMain = job.script != "" || job.exec != "";
+
+      env = job.environment;
+
+      preStartScript = makeJobScript "${job.name}-pre-start"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.preStart}
+        '';
+
+      startScript = makeJobScript "${job.name}-start"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${if job.script != "" then job.script else ''
+            exec ${job.exec}
+          ''}
+        '';
+
+      postStartScript = makeJobScript "${job.name}-post-start"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.postStart}
+        '';
+
+      preStopScript = makeJobScript "${job.name}-pre-stop"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.preStop}
+        '';
+
+      postStopScript = makeJobScript "${job.name}-post-stop"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.postStop}
+        '';
+    in {
+
+      inherit (job) description requires before partOf environment path restartIfChanged unitConfig;
+
+      after =
+        (if job.startOn == "stopped udevtrigger" then [ "systemd-udev-settle.service" ] else
+         if job.startOn == "started udev" then [ "systemd-udev.service" ] else
+         if job.startOn == "started network-interfaces" then [ "network-interfaces.target" ] else
+         if job.startOn == "started networking" then [ "network.target" ] else
+         if job.startOn == "ip-up" then [] else
+         if job.startOn == "" || job.startOn == "startup" then [] else
+         builtins.trace "Warning: job ‘${job.name}’ has unknown startOn value ‘${job.startOn}’." []
+        ) ++ job.after;
+
+      wants = 
+        (if job.startOn == "stopped udevtrigger" then [ "systemd-udev-settle.service" ] else []
+        ) ++ job.wants;
+
+      wantedBy =
+        (if job.startOn == "" then [] else
+         if job.startOn == "ip-up" then [ "ip-up.target" ] else
+         [ "multi-user.target" ]) ++ job.wantedBy;
+
+      serviceConfig =
+        job.serviceConfig
+        // optionalAttrs (job.preStart != "" && (job.script != "" || job.exec != ""))
+          { ExecStartPre = preStartScript; }
+        // optionalAttrs (job.preStart != "" && job.script == "" && job.exec == "")
+          { ExecStart = preStartScript; }
+        // optionalAttrs (job.script != "" || job.exec != "")
+          { ExecStart = startScript; }
+        // optionalAttrs (job.postStart != "")
+          { ExecStartPost = postStartScript; }
+        // optionalAttrs (job.preStop != "")
+          { ExecStop = preStopScript; }
+        // optionalAttrs (job.postStop != "")
+          { ExecStopPost = postStopScript; }
+        // (if job.script == "" && job.exec == "" then { Type = "oneshot"; RemainAfterExit = true; } else
+            if job.daemonType == "fork" || job.daemonType == "daemon" then { Type = "forking"; GuessMainPID = true; } else
+            if job.daemonType == "none" then { } else
+            throw "invalid daemon type `${job.daemonType}'")
+        // optionalAttrs (!job.task && job.respawn)
+          { Restart = "always"; }
+        // optionalAttrs job.task
+          { Type = "oneshot"; RemainAfterExit = false; };
+    };
+
+
+  jobOptions = serviceOptions // {
+
+    name = mkOption {
+      # !!! The type should ensure that this could be a filename.
+      type = types.string;
+      example = "sshd";
+      description = ''
+        Name of the Upstart job.
+      '';
+    };
+
+    startOn = mkOption {
+      # !!! Re-enable this once we're on Upstart >= 0.6.
+      #type = types.string;
+      default = "";
+      description = ''
+        The Upstart event that triggers this job to be started.
+        If empty, the job will not start automatically.
+      '';
+    };
+
+    stopOn = mkOption {
+      type = types.string;
+      default = "starting shutdown";
+      description = ''
+        The Upstart event that triggers this job to be stopped.
+      '';
+    };
+
+    postStart = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed after the job is started (i.e. after
+        the job's main process is started), but before the job is
+        considered “running”.
+      '';
+    };
+
+    preStop = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed before the job is stopped
+        (i.e. before Upstart kills the job's main process).  This can
+        be used to cleanly shut down a daemon.
+      '';
+    };
+
+    postStop = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed after the job has stopped
+        (i.e. after the job's main process has terminated).
+      '';
+    };
+
+    exec = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Command to start the job's main process.  If empty, the
+        job has no main process, but can still have pre/post-start
+        and pre/post-stop scripts, and is considered “running”
+        until it is stopped.
+      '';
+    };
+
+    respawn = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to restart the job automatically if its process
+        ends unexpectedly.
+      '';
+    };
+
+    task = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether this job is a task rather than a service.  Tasks
+        are executed only once, while services are restarted when
+        they exit.
+      '';
+    };
+
+    daemonType = mkOption {
+      type = types.string;
+      default = "none";
+      description = ''
+        Determines how Upstart detects when a daemon should be
+        considered “running”.  The value <literal>none</literal> means
+        that the daemon is considered ready immediately.  The value
+        <literal>fork</literal> means that the daemon will fork once.
+        The value <literal>daemon</literal> means that the daemon will
+        fork twice.  The value <literal>stop</literal> means that the
+        daemon will raise the SIGSTOP signal to indicate readiness.
+      '';
+    };
+
+    setuid = mkOption {
+      type = types.string;
+      check = userExists;
+      default = "";
+      description = ''
+        Run the daemon as a different user.
+      '';
+    };
+
+    setgid = mkOption {
+      type = types.string;
+      check = groupExists;
+      default = "";
+      description = ''
+        Run the daemon as a different group.
+      '';
+    };
+
+    path = mkOption {
+      default = [];
+      description = ''
+        Packages added to the job's <envar>PATH</envar> environment variable.
+        Both the <filename>bin</filename> and <filename>sbin</filename>
+        subdirectories of each package are added.
+      '';
+    };
+
+  };
+
+
+  upstartJob = { name, config, ... }: {
+
+    options = {
+
+      unit = mkOption {
+        default = makeUnit config;
+        description = "Generated definition of the systemd unit corresponding to this job.";
+      };
+
+    };
+
+    config = {
+
+      # The default name is the name extracted from the attribute path.
+      name = mkDefaultValue name;
+
+    };
+
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    jobs = mkOption {
+      default = {};
+      description = ''
+        This option defines the system jobs started and managed by the
+        Upstart daemon.
+      '';
+      type = types.loaOf types.optionSet;
+      options = [ jobOptions upstartJob ];
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    systemd.services =
+      flip mapAttrs' config.jobs (name: job:
+        nameValuePair job.name job.unit);
+
+  };
+
+}