1
1
mirror of https://github.com/NixOS/mobile-nixos.git synced 2025-01-07 12:11:28 +03:00

Merge pull request #488 from samueldr-wip/feature/drop-in-stage-1-integration

Drop-in stage-1 integration [and better support for modular kernels]
This commit is contained in:
Samuel Dionne-Riel 2022-05-29 22:48:20 -04:00 committed by GitHub
commit 271b9106ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 573 additions and 266 deletions

View File

@ -80,6 +80,17 @@ module Mounting
Tasks::Luks.new(info["device"], mapper)
end
end
def self.mountpoint?(path)
begin
parent_path = File.dirname(path)
mp_stat = File.lstat(path)
pa_stat = File.lstat(parent_path)
mp_stat.dev == pa_stat.dev && mp_stat.ino == pa_stat.ino || mp_stat.dev != pa_stat.dev
rescue Errno::ENOENT
false
end
end
end

View File

@ -77,10 +77,13 @@ module System
# Discovers the location of given program name.
def self.which(program_name)
ENV["PATH"].split(":").each do |path|
(ENV["PATH"] or "").split(":").each do |path|
full = File.join(path, program_name)
return full if File.stat(full).executable? && !File.directory?(full)
if File.exists?(full) && !File.directory?(full) && File.stat(full).executable? then
return full
end
end
nil
end
def self.write(file, contents)
@ -178,6 +181,13 @@ module System
args << source
args << dest
# The kernel module for the filesystem may need to be probed.
begin
System.run("modprobe", type)
rescue System::CommandError
$logger.warn("Kernel filesystem module “#{type}” failed to load.")
end
# We may have some mountpoints already mounted from, e.g. early logging in
# /run/log... If we're not careful we will mount over the existing mount
# point and hide the resulting files.

View File

@ -72,7 +72,7 @@ module Tasks
# to be ran.
# Serves nothing to point to tasks depending on other tasks.
failed_tasks = todo.reject(&:depends_on_any_unfulfilled_task?)
failed_dependencies = failed_tasks.map(&:dependencies).inject(:+).uniq
failed_dependencies = failed_tasks.map(&:dependencies).inject(:+).uniq.reject(&:fulfilled?)
if elapsed > HUNG_BOOT_NOTIFICATION
label = "#{failed_tasks.length} tasks are waiting on #{failed_dependencies.length} unique dependencies.\n\n" +
@ -82,15 +82,22 @@ module Tasks
end
if elapsed > HUNG_BOOT_TIMEOUT
# Building this message is not pretty!
msg =
msg = [
"",
"#{failed_tasks.length} #{if failed_tasks.length == 1 then "task" else "tasks" end} " +
"did not run within #{HUNG_BOOT_TIMEOUT} seconds.\n" +
"\n" +
"did not run within #{HUNG_BOOT_TIMEOUT} seconds.\n",
"#{failed_dependencies.length} #{if failed_dependencies.length == 1 then "dependency" else "dependencies" end} " +
"could not resolve:\n" +
failed_dependencies.map(&:pretty_name).join("\n") +
"\n"
"could not resolve:\n",
failed_tasks.map do |task|
[
" - #{task.name}",
task.dependencies.map do |dep|
" - #{dep.pretty_name} [#{if dep.fulfilled? then "ok" else " " end}]"
end.join("\n")
].join("\n")
end.join("\n"),
""
].join("\n")
# Fail with a black backdrop, and force the message to stay up 60s
System.failure("TASKS_HANG_TIMEOUT", "Hung Tasks", msg, color: "000000", delay: 60)

View File

@ -31,6 +31,12 @@ class Tasks::Luks < Task
add_dependency(:Devices, source)
add_dependency(:Mount, "/run")
add_dependency(:Target, :Environment)
# Or else we can't get the passphrase!!
# TODO: instead of depending on the Splash Task, depend on a new type of
# dependency describing a "user input", which would be provided on
# the "message bus"
# e.g. `add_dependency(:Ask, :passphrase, "Passphrase for #{mapper}")`
add_dependency(:Task, Tasks::Splash.instance)
self.class.register(@mapper, self)
end

View File

@ -3,10 +3,14 @@ class Tasks::Modules < Task
def initialize(*modules)
add_dependency(:Files, MODULES_PATH)
add_dependency(:Target, :Environment)
add_dependency(:Mount, "/proc")
# May be required for input or display modules
Targets[:Graphics].add_dependency(:Task, self)
@modules = modules
end
def run()
System.write("/proc/sys/kernel/modprobe", System.which("modprobe"))
@modules.each do |mod|
begin
System.run("modprobe", mod)

View File

@ -52,6 +52,19 @@ class Tasks::Mount < Task
@named[:type]
end
def refresh_lvm()
unless System.which("lvm").nil? then
begin
ENV["LVM_SUPPRESS_FD_WARNINGS"] = "1"
System.run("lvm", "vgchange", "--activate=y")
ENV["LVM_SUPPRESS_FD_WARNINGS"] = nil
System.run("udevadm", "trigger", "--action=add")
rescue System::CommandError => e
$logger.info(e.to_s)
end
end
end
def name()
"#{super}(#{source}, #{mount_point}, #{@named.inspect})"
end
@ -67,7 +80,15 @@ module Dependencies
unless task
$logger.warn("Missing Mount task for mount point #{@mount_point}")
end
task && task.ran
# Already mounted?
# (Could be e.g. mounted beforehand or mounted by the kernel implicitly)
return true if Mounting.mountpoint?(@mount_point)
if task
task.refresh_lvm
task.ran
end
end
def depends_on?(other)

View File

@ -28,6 +28,9 @@ class Tasks::Splash < SingletonTask
# Implementation details-y; ask for the splash applet to be exited.
def quit(reason, sticky: nil)
return if @pid.nil?
count = 0
# Ensures the progress is shown
Progress.update({progress: 100, label: reason})
@ -44,6 +47,12 @@ class Tasks::Splash < SingletonTask
# Leave some breathing room to the CPU!
sleep(0.1)
count += 1
if count > 60 # 10 seconds~ish
$logger.fatal("Splash applet would not quit by itself...")
kill
break
end
end
@pid = nil

View File

@ -6,6 +6,7 @@ class Tasks::SwitchRoot < SingletonTask
SYSTEM_MOUNT_POINT = "/mnt"
def initialize()
add_dependency(:Task, Tasks::Splash.instance)
add_dependency(:Target, :SwitchRoot)
@target = SYSTEM_MOUNT_POINT
end
@ -92,6 +93,13 @@ class Tasks::SwitchRoot < SingletonTask
return generation_parameter.split("=", 2).last
end
# Given as a command-line option, from the bootloader (replacement for NixOS's stage-1)
init_parameter = System.cmdline().grep(/^init=/).first
unless init_parameter.nil?
init_parameter = init_parameter.split("=", 2).last
return init_parameter.rpartition("/").first
end
# The default generation
if File.symlink?(File.join(@target, DEFAULT_SYSTEM_LINK))
return DEFAULT_SYSTEM_LINK

View File

@ -11,6 +11,9 @@ class Tasks::UDev < SingletonTask
# It is preferred to depend on the specific device rather than this target.
Targets[:Devices].add_dependency(:Task, self)
Targets[:SwitchRoot].add_dependency(:Task, self)
# May be required for input
Targets[:Graphics].add_dependency(:Task, self)
end
def udevadm(*args)

View File

@ -17,27 +17,21 @@
};
boot.kernelParams = [
"vt.global_cursor_default=0"
"fbcon=vc:2-6"
"console=tty0"
];
mobile.system.type = "uefi";
mobile.boot.stage-1 = {
kernel = {
package = let inherit (pkgs.linuxPackages_5_4) kernel; in
kernel.overrideAttrs({passthru ? {}, ...}: {
# Using `kernel.passthru` as overrideAttrs on kernel derivations
# does not work as expected.
# See https://github.com/NixOS/nixpkgs/issues/111504
passthru = kernel.passthru // {
file = "bzImage";
};
})
;
modular = true;
# Rely on upstream NixOS modules by default for generic UEFI systems.
useNixOSKernel = lib.mkDefault true;
# Sync with <nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-base.nix>
# and with <nixpkgs/nixos/modules/system/boot/kernel.nix>
modules = [
# FIXME: enable only when building generic images (e.g. USB images)
additionalModules = [
# Some SATA/PATA stuff.
"ahci"
"sata_nv"
@ -119,5 +113,5 @@
};
};
mobile.quirks.supportsStage-0 = true;
mobile.quirks.supportsStage-0 = lib.mkDefault true;
}

View File

@ -129,4 +129,7 @@ in
# It's only noise, and the current stage-0 is not able to boot anything else
# than a system it was built for anyway.
mobile.quirks.supportsStage-0 = lib.mkForce false;
# Skip a long-running build for the documentation HTML.
documentation.enable = false;
}

View File

@ -0,0 +1,22 @@
{ lib, ... }:
{
# This system is not expected to be bootable.
fileSystems = {
"/" = {
device = "tmpfs";
fsType = "tmpfs";
};
};
boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = false;
# Documentation build will be different, so comparing `toplevel` would fail.
# Different why? New options!
documentation.enable = lib.mkOverride 10 false;
# Unimportant, but keeps the eval log cleaner.
system.stateVersion = "22.05";
# ¯\_(ツ)_/¯
services.getty.autologinUser = "root";
}

View File

@ -0,0 +1,52 @@
{ pkgs ? (import ../../../pkgs.nix {})
}@args':
let args = args' // { inherit pkgs; }; in
let
eval = configuration: import (pkgs.path + "/nixos") {
configuration = {
imports = [ configuration ];
};
};
# A "clean" NixOS eval
nixos-eval = eval {
imports = [
./configuration.nix
];
};
# A Mobile NixOS eval that should be a no-op
mobile-nixos-eval = eval {
imports = [
./configuration.nix
(import ../../../lib/configuration.nix { })
];
mobile.enable = false;
};
# A Mobile NixOS eval that should be a no-op
mobile-nixos-stage-1-eval = eval {
imports = [
./configuration.nix
(import ../../../lib/configuration.nix { })
];
mobile.enable = false;
mobile.boot.stage-1.enable = true;
};
in
{
inherit
nixos-eval
mobile-nixos-eval
mobile-nixos-stage-1-eval
;
# Use this output to check that the product works as expected.
# (The bogus rootfs will be overriden by the VM config.)
default =
assert nixos-eval.config.system.build.toplevel == mobile-nixos-eval.config.system.build.toplevel;
assert nixos-eval.config.system.build.vm == mobile-nixos-eval.config.system.build.vm;
mobile-nixos-eval.config.system.build.vm
;
mobile-nixos-stage-1 = mobile-nixos-stage-1-eval.config.system.build.vm;
}

View File

@ -0,0 +1,8 @@
#!/usr/bin/env nix-shell
#!nix-shell -p nix-diff -i bash
this_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd)"
nix-diff \
"$(nix-instantiate "$this_dir"/default.nix -A nixos-eval.config.system.build.toplevel)" \
"$(nix-instantiate "$this_dir"/default.nix -A mobile-nixos-eval.config.system.build.toplevel)"

View File

@ -12,11 +12,15 @@
# }
# ```
{ device }:
{ device ? null }:
{
imports = [
(import (../devices + "/${device}"))
]
++ import ../modules/module-list.nix;
imports =
(
if device == null
then []
else [ (import (../devices + "/${device}")) ]
)
++ import ../modules/module-list.nix
;
}

View File

@ -1,25 +0,0 @@
# This module, while seemingly having a funny name, has a truthful name.
# It dis(assembles) the integration within the NixOS module system that is
# causing issues for mobile-nixos.
#
# In reality, all fixups made here is a call for a better isolation of the
# modules they target. Let's wait until this is more stable, and add required
# fixes to the NixOS modules system as needed ☺.
{lib, config, ...}:
{
imports = [
./initrd.nix
];
config = lib.mkMerge [
{
# I don't know why, but the documentation has issues with the
# soc options when building other than qemu_x86_64
# And here the installation-device profile is a bit annoying.
# Let's ultra-diable the documentation and nixos manual.
documentation.enable = lib.mkOverride 10 false;
}
];
}

View File

@ -1,21 +0,0 @@
{ pkgs, lib, config, ... }:
let
dummy = pkgs.runCommandNoCC "dummy" {} "touch $out";
in
{
disabledModules = [
"tasks/encrypted-devices.nix"
"tasks/filesystems/zfs.nix"
];
config = {
# This isn't even used in our initrd...
boot.supportedFilesystems = lib.mkOverride 10 [ ];
boot.initrd.supportedFilesystems = lib.mkOverride 10 [];
# And disable the initrd outright!
boot.initrd.enable = false;
system.build.initialRamdiskSecretAppender = dummy;
};
}

35
modules/bootloader.nix Normal file
View File

@ -0,0 +1,35 @@
{ config, lib, pkgs, ... }:
let
inherit (config.boot) growPartition;
inherit (lib) mkIf mkOption optionalString types;
inherit (config.mobile._internal) compressLargeArtifacts;
inherit (pkgs) buildPackages;
rootfsLabel = config.mobile.generatedFilesystems.rootfs.label;
in
{
options = {
mobile = {
bootloader = {
enable = mkOption {
type = types.bool;
default = config.mobile.enable;
description = ''
Whether the bootloader **configuration** for Mobile NixOS
is enabled.
Installation and management of the Mobile NixOS bootloading
components is not implemented at this point in time.
(e.g. flashing boot.img on android, boot partition on other systems.)
'';
};
};
};
};
config = mkIf (config.mobile.bootloader.enable) {
boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = false;
};
}

View File

@ -14,4 +14,7 @@ in
};
};
};
config = {
documentation.nixos.options.splitBuild = false;
};
}

View File

@ -9,9 +9,15 @@ in
options.mobile.hardware.screen = {
width = mkOption {
type = types.int;
description = ''
Width of the device's display.
'';
};
height = mkOption {
type = types.int;
description = ''
Height of the device's display.
'';
};
};
}

View File

@ -1,7 +1,7 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkOption types;
inherit (lib) mkIf mkOption mkOptionDefault types;
cfg = config.mobile.hardware;
in
{
@ -20,5 +20,14 @@ in
{ assertion = cfg.socs ? ${cfg.soc}; message = "Cannot enable SOC ${cfg.soc}; it is unknown.";}
];
mobile.hardware.socs."${cfg.soc}".enable = true;
# When evaluating with the Mobile NixOS defaults disabled, we want
# to use the a generic type so evaluation can continue.
# Otherwise we want to error on an unset value if not set.
mobile.hardware.soc = mkIf (!config.mobile.enable) (
mkOptionDefault (
"generic-${config.nixpkgs.localSystem.parsed.cpu.name}"
)
);
};
}

View File

@ -3,10 +3,13 @@
let
inherit (lib)
literalExpression
mergeEqualOption
mkDefault
mkIf
mkMerge
mkOption
mkOverride
types
;
cfg = config.mobile.boot.stage-1.kernel;
@ -14,9 +17,9 @@ let
modulesClosure = pkgs.makeModulesClosure {
kernel = cfg.package;
allowMissing = true;
allowMissing = cfg.allowMissingModules;
rootModules = cfg.modules ++ cfg.additionalModules;
firmware = cfg.firmwares;
firmware = config.hardware.firmware;
};
inherit (config.mobile.quirks) supportsStage-0;
@ -25,6 +28,16 @@ in
# Note: These options are provided *instead* of `boot.initrd.*`, as we are
# not re-using the `initrd` options.
options.mobile.boot.stage-1.kernel = {
useNixOSKernel = mkOption {
type = types.bool;
default = !config.mobile.enable;
defaultText = literalExpression "!config.mobile.enable";
description = ''
Whether Mobile NixOS relies on upstream NixOS settings for kernel config.
Enable this when using the NixOS machinery for kernels.
'';
};
modular = mkOption {
type = types.bool;
default = false;
@ -52,11 +65,11 @@ in
They will not be modprobed.
'';
};
firmwares = mkOption {
type = types.listOf types.str;
default = [];
allowMissingModules = mkOption {
type = types.bool;
default = true;
description = ''
Firmwares to add to the closure.
Chooses whether the modules closure build fails if a module is missing.
'';
};
# We cannot use `linuxPackagesFor` as older kernels cause eval-time assertions...
@ -73,50 +86,94 @@ in
};
};
config.mobile.boot.stage-1 = (mkIf cfg.modular {
firmware = [ modulesClosure ];
contents = [
{ object = "${modulesClosure}/lib/modules"; symlink = "/lib/modules"; }
];
kernel.modules = [
# Basic always-needed kernel modules.
"loop"
config = mkMerge [
# This can always be configured, as it does not affect the NixOS configuration.
{
mobile.boot.stage-1 = (mkIf cfg.modular {
firmware = [ modulesClosure ];
contents = [
{ object = "${modulesClosure}/lib/modules"; symlink = "/lib/modules"; }
];
kernel.modules = [
# Basic always-needed kernel modules.
"loop"
"uinput"
"evdev"
];
kernel.additionalModules = [
# Filesystems
"nls_cp437"
"nls_iso8859-1"
"fat"
"vfat"
# Filesystems
"nls_cp437"
"nls_iso8859-1"
"fat"
"vfat"
"ext4"
"crc32c"
];
});
}
{
mobile.boot.stage-1 = (mkIf (!cfg.modular) {
contents = [
# Link an empty `/lib/modules` for the Modules task.
# This is better than implementing conditional loading of the task
# as the task is now always exercised.
{
object =
let
nullModules = pkgs.callPackage (
{ runCommandNoCC, ... }:
runCommandNoCC "null-modules" { } ''
mkdir -p $out/lib/modules
''
) {};
in
"${nullModules}/lib/modules"
;
symlink = "/lib/modules";
}
];
});
}
# Options affecting the NixOS configuration
(mkIf (!cfg.useNixOSKernel) {
boot.kernelPackages = mkDefault (
if (supportsStage-0 && config.mobile.rootfs.shared.enabled) || cfg.package == null
then let
self = {
# This must look legit enough so that NixOS thinks it's a kernel attrset.
stdenv = pkgs.stdenv;
# callPackage so that override / overrideAttrs exist.
kernel = pkgs.callPackage (
{ runCommandNoCC, ... }: runCommandNoCC "null-kernel" { version = "99"; } "mkdir $out; touch $out/'<no-kernel>'"
) {};
# Fake having `extend` available... probably dumb... but is it more
# dumb than faking a kernelPackages package set for eval??
extend = _: self;
};
in self
else (pkgs.recurseIntoAttrs (pkgs.linuxPackagesFor cfg.package))
);
"ext4"
"crc32c"
];
});
system.boot.loader.kernelFile = mkIf (cfg.package != null && cfg.package ? file) (
mkDefault cfg.package.file
);
config.boot.kernelPackages = mkDefault (
if (supportsStage-0 && config.mobile.rootfs.shared.enabled) || cfg.package == null
then let
self = {
# This must look legit enough so that NixOS thinks it's a kernel attrset.
stdenv = pkgs.stdenv;
# callPackage so that override / overrideAttrs exist.
kernel = pkgs.callPackage (
{ runCommandNoCC, ... }: runCommandNoCC "dummy" { version = "99"; } "mkdir $out; touch $out/dummy"
) {};
# Fake having `extend` available... probably dumb... but is it more
# dumb than faking a kernelPackages package set for eval??
extend = _: self;
# Disable kernel config checks as it's EXTREMELY nixpkgs-kernel centric.
# We're duplicating that good work for the time being.
system.requiredKernelConfig = lib.mkForce [];
})
(mkIf (cfg.useNixOSKernel) {
mobile.boot.stage-1 = {
kernel = {
package = config.boot.kernelPackages.kernel;
modular = true;
# Use the modules described by the NixOS config.
modules = config.boot.initrd.kernelModules ++ [ "uinput" "evdev" ];
additionalModules = config.boot.initrd.availableKernelModules;
};
};
in self
else (pkgs.recurseIntoAttrs (pkgs.linuxPackagesFor cfg.package))
);
config.system.boot.loader.kernelFile = mkIf (cfg.package != null && cfg.package ? file) (
mkDefault cfg.package.file
);
# Disable kernel config checks as it's EXTREMELY nixpkgs-kernel centric.
# We're duplicating that good work for the time being.
config.system.requiredKernelConfig = lib.mkForce [];
})
];
}

View File

@ -9,7 +9,7 @@ in
options.mobile.boot.stage-1.bootlog = {
enable = mkOption {
type = types.bool;
default = true;
default = config.mobile.boot.stage-1.enable;
description = ''
Enables bootlogd logging multiplexer.
'';

View File

@ -46,8 +46,8 @@ let
# TODO: define as an option
withStrace = false;
device_config = config.mobile.device;
device_name = device_config.name;
inherit (config.mobile) device;
device_name = device.name;
stage-1 = config.mobile.boot.stage-1;
@ -193,7 +193,7 @@ let
};
initrd = makeInitrd {
name = "initrd-${device_config.name}";
name = "mobile-nixos-initrd-${device_name}";
inherit contents;
compressor = {
@ -207,7 +207,7 @@ let
};
# ncdu -f result/initrd.ncdu
initrd-meta = pkgs.runCommandNoCC "initrd-${device_config.name}-meta" {
initrd-meta = pkgs.runCommandNoCC "initrd-${device_name}-meta" {
nativeBuildInputs = with pkgs.buildPackages; [
ncdu
cpio
@ -230,6 +230,16 @@ let
in
{
options = {
mobile.boot.stage-1.enable = mkOption {
type = types.bool;
default = config.mobile.enable;
description = ''
Whether to use the Mobile NixOS stage-1 implementation or not.
This will forcible override the NixOS stage-1 when enabled.
'';
};
mobile.boot.stage-1.stage = mkOption {
type = types.enum [ 0 1 ];
default = 1;
@ -337,7 +347,17 @@ in
};
};
config = {
config = mkIf config.mobile.boot.stage-1.enable {
boot.initrd.enable = false;
# This isn't even used in our initrd...
boot.supportedFilesystems = lib.mkOverride 10 [ ];
boot.initrd.supportedFilesystems = lib.mkOverride 10 [];
system.build.initialRamdiskSecretAppender =
pkgs.runCommandNoCC "noopRamdiskSecretAppender" {} "touch $out"
;
mobile.outputs = {
inherit
extraUtils
@ -350,14 +370,14 @@ in
# default NixOS outputs. Do not refer to this in Mobile NixOS.
system.build.initialRamdisk =
if config.mobile.rootfs.shared.enabled
then pkgs.runCommandNoCC "dummy" {} "touch $out"
then pkgs.runCommandNoCC "nullInitialRamdisk" {} "touch $out"
else initrd
;
mobile.boot.stage-1.bootConfig = {
inherit (config.mobile.boot.stage-1) stage;
device = {
inherit (device_config) name;
name = device_name;
};
kernel = {
inherit (config.mobile.boot.stage-1.kernel) modules;

View File

@ -36,4 +36,10 @@ with lib;
'';
};
};
config = mkIf (!config.mobile.enable) {
mobile.device.name = mkOptionDefault "generic";
mobile.device.identity.name = mkOptionDefault "generic";
mobile.device.identity.manufacturer = mkOptionDefault "generic";
};
}

22
modules/mobile.nix Normal file
View File

@ -0,0 +1,22 @@
{ lib, ... }:
let
inherit (lib)
mkOption
types
;
in
{
options = {
mobile = {
enable = mkOption {
type = types.bool;
default = true;
description = ''
When this setting is set to false, including Mobile
NixOS in your NixOS configuration should be a no-op.
'';
};
};
};
}

View File

@ -1,9 +1,9 @@
# Then add our additional modules.
# Keep this list `:sort`ed.
[
./_nixos-disintegration
./adb.nix
./boot-initrd.nix
./bootloader.nix
./cross-workarounds.nix
./devices-metadata.nix
./documentation.nix
@ -35,6 +35,7 @@
./kernel-config.nix
./lib.nix
./luks.nix
./mobile.nix
./mobile-device.nix
./nixpkgs.nix
./outputs.nix

View File

@ -4,90 +4,103 @@
let
inherit (config.boot) growPartition;
inherit (lib) optionalString types;
inherit (lib) mkIf mkOption optionalString types;
inherit (config.mobile._internal) compressLargeArtifacts;
inherit (pkgs) buildPackages;
rootfsLabel = config.mobile.generatedFilesystems.rootfs.label;
in
{
boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = false;
boot.growPartition = lib.mkDefault true;
mobile.generatedFilesystems.rootfs = lib.mkDefault {
type = "ext4";
label = "NIXOS_SYSTEM";
id = "44444444-4444-4444-8888-888888888888";
populateCommands =
let
closureInfo = pkgs.buildPackages.closureInfo { rootPaths = config.system.build.toplevel; };
in
''
mkdir -p ./nix/store
echo "Copying system closure..."
while IFS= read -r path; do
echo " Copying $path"
cp -prf "$path" ./nix/store
done < "${closureInfo}/store-paths"
echo "Done copying system closure..."
cp -v ${closureInfo}/registration ./nix-path-registration
'';
# Give some headroom for initial mounting.
extraPadding = pkgs.imageBuilder.size.MiB 20;
# FIXME: See #117, move compression into the image builder.
# Zstd can take a long time to complete successfully at high compression
# levels. Increasing the compression level could lead to timeouts.
postProcess = optionalString compressLargeArtifacts ''
(PS4=" $ "; set -x
PATH="$PATH:${buildPackages.zstd}/bin"
cd $out
ls -lh
time zstd -10 --rm "$filename"
ls -lh
)
'' + ''
(PS4=" $ "; set -x
mkdir $out/nix-support
cat <<EOF > $out/nix-support/hydra-build-products
file rootfs${optionalString compressLargeArtifacts "-zstd"} $out/$filename${optionalString compressLargeArtifacts ".zst"}
EOF
)
'';
zstd = compressLargeArtifacts;
};
boot.postBootCommands = ''
# On the first boot do some maintenance tasks
if [ -f /nix-path-registration ]; then
# Register the contents of the initial Nix store
${config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
# Prevents this from running on later boots.
rm -f /nix-path-registration
fi
'';
fileSystems = {
"/" = lib.mkDefault {
device = "/dev/disk/by-label/${rootfsLabel}";
fsType = "ext4";
autoResize = true;
options = {
mobile = {
rootfs = {
enableDefaultConfiguration = mkOption {
type = types.bool;
default = config.mobile.enable;
description = ''
Whether some of the rootfs configuration is managed by Mobile NixOS or not.
'';
};
};
};
};
# FIXME: Move this in a proper module + task for the filesystem.
# This is a "wrong" assumption, that only holds through since we are setting
# fileSystems."/".autoResize to true here.
mobile.boot.stage-1.extraUtils = with pkgs; [
{ package = e2fsprogs; }
];
config = mkIf (config.mobile.rootfs.enableDefaultConfiguration) {
boot.growPartition = lib.mkDefault true;
mobile.generatedFilesystems.rootfs = lib.mkDefault {
type = "ext4";
label = "NIXOS_SYSTEM";
id = "44444444-4444-4444-8888-888888888888";
populateCommands =
let
closureInfo = pkgs.buildPackages.closureInfo { rootPaths = config.system.build.toplevel; };
in
''
mkdir -p ./nix/store
echo "Copying system closure..."
while IFS= read -r path; do
echo " Copying $path"
cp -prf "$path" ./nix/store
done < "${closureInfo}/store-paths"
echo "Done copying system closure..."
cp -v ${closureInfo}/registration ./nix-path-registration
'';
# Give some headroom for initial mounting.
extraPadding = pkgs.imageBuilder.size.MiB 20;
# FIXME: See #117, move compression into the image builder.
# Zstd can take a long time to complete successfully at high compression
# levels. Increasing the compression level could lead to timeouts.
postProcess = optionalString compressLargeArtifacts ''
(PS4=" $ "; set -x
PATH="$PATH:${buildPackages.zstd}/bin"
cd $out
ls -lh
time zstd -10 --rm "$filename"
ls -lh
)
'' + ''
(PS4=" $ "; set -x
mkdir $out/nix-support
cat <<EOF > $out/nix-support/hydra-build-products
file rootfs${optionalString compressLargeArtifacts "-zstd"} $out/$filename${optionalString compressLargeArtifacts ".zst"}
EOF
)
'';
zstd = compressLargeArtifacts;
};
boot.postBootCommands = ''
# On the first boot do some maintenance tasks
if [ -f /nix-path-registration ]; then
# Register the contents of the initial Nix store
${config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
# Prevents this from running on later boots.
rm -f /nix-path-registration
fi
'';
fileSystems = {
"/" = lib.mkDefault {
device = "/dev/disk/by-label/${rootfsLabel}";
fsType = "ext4";
autoResize = true;
};
};
# FIXME: Move this in a proper module + task for the filesystem.
# This is a "wrong" assumption, that only holds through since we are setting
# fileSystems."/".autoResize to true here.
mobile.boot.stage-1.extraUtils = with pkgs; [
{ package = e2fsprogs; }
];
};
}

View File

@ -20,8 +20,8 @@ in
config = mkIf enabled {
# Ensure we don't bring a kernel or initrd into the system.
system.boot.loader.kernelFile = "dummy";
system.boot.loader.initrdFile = "dummy";
system.boot.loader.kernelFile = "<no-kernel>";
system.boot.loader.initrdFile = "<no-initrd>";
# And totally obliterate device-specific files from stage-2.
system.extraSystemBuilderCmds = ''

View File

@ -13,7 +13,7 @@ let
'';
in
{
config = mkIf (!config.mobile.rootfs.shared.enabled) {
config = mkIf (config.mobile.enable && !config.mobile.rootfs.shared.enabled) {
system.extraSystemBuilderCmds = ''
echo ":: Adding Mobile NixOS information to the build..."
(

View File

@ -7,6 +7,8 @@ let
# The platform selected by the configuration
selectedPlatform = lib.systems.elaborate cfg.system;
isCross = selectedPlatform.system != localSystem.system;
in
{
options.mobile = {
@ -33,20 +35,11 @@ in
}
];
nixpkgs.crossSystem = lib.mkIf
(
let
result = selectedPlatform.system != localSystem.system;
in
builtins.trace
"Building with crossSystem?: ${selectedPlatform.system} != ${localSystem.system} ${if result then "we are" else "we're not"}."
result
)
(
builtins.trace
" crossSystem: config: ${selectedPlatform.config}"
selectedPlatform
)
;
nixpkgs.crossSystem = lib.mkIf isCross (
builtins.trace ''
Building with crossSystem?: ${selectedPlatform.system} != ${localSystem.system} ${if isCross then "we are" else "we're not"}.
crossSystem: config: ${selectedPlatform.config}''
selectedPlatform
);
};
}

View File

@ -49,5 +49,12 @@ in
message = "Cannot build unexpected system type: ${system_type}.\n Known types: ${lib.concatStringsSep ", " known_system_types}";
}
];
# When evaluating with the Mobile NixOS defaults disabled, we want
# to use the `none` type so evaluation can continue.
# Otherwise we want to error on an unset value if not set.
mobile.system.type = mkIf (!config.mobile.enable) (
lib.mkOptionDefault "none"
);
};
}

View File

@ -1,4 +1,5 @@
{ lib
, stdenv
, fetchurl
, runCommandNoCC
, initrd
@ -36,7 +37,7 @@ let
};
# Kernel used in kpart.
kernel_file = "${kernel}/${kernel.file}";
kernel_file = "${kernel}/${if kernel ? file then kernel.file else stdenv.hostPlatform.linux-kernel.target}";
# Kernel command line for vbutil_kernel.
kpart_config = writeTextFile {

View File

@ -10,7 +10,7 @@ let
inherit (cfg) soc;
deviceName = config.mobile.device.name;
kernel = stage-0.mobile.boot.stage-1.kernel.package;
kernel_file = "${kernel}/${kernel.file}";
kernel_file = "${kernel}/${if kernel ? file then kernel.file else pkgs.stdenv.hostPlatform.linux-kernel.target}";
# Look-up table to translate from targetPlatform to U-Boot names.
ubootPlatforms = {

View File

@ -10,7 +10,7 @@ let
cfg = config.mobile.quirks.uefi;
deviceName = config.mobile.device.name;
kernel = stage-0.mobile.boot.stage-1.kernel.package;
kernelFile = "${kernel}/${kernel.file}";
kernelFile = "${kernel}/${if kernel ? file then kernel.file else pkgs.stdenv.hostPlatform.linux-kernel.target}";
# Look-up table to translate from targetPlatform to U-Boot names.
uefiPlatforms = {

View File

@ -4,7 +4,7 @@ let
# This particular VM module is only enabled for the uefi system type.
enabled = config.mobile.system.type == "uefi";
inherit (lib) mkAfter mkIf mkOption types;
inherit (lib) mkAfter mkIf mkMerge mkOption types;
inherit (config.mobile) device hardware;
inherit (config.mobile.boot) stage-1;
inherit (config.mobile.outputs.uefi) disk-image;
@ -16,6 +16,15 @@ in
{
options = {
mobile = {
quirks.uefi.enableVM = mkOption {
type = types.bool;
default = false;
internal = true;
description = ''
Internal switch to select whether the `outputs.uefi.vm` value points
to the composeConfig usage, or to the actual output.
'';
};
outputs = {
uefi = {
vm = mkOption {
@ -29,44 +38,53 @@ in
};
};
};
config = mkIf enabled {
boot.kernelParams = mkAfter [
"console=ttyS0"
];
config = mkMerge [
(mkIf config.mobile.quirks.uefi.enableVM {
boot.kernelParams = mkAfter [
"console=ttyS0"
];
mobile.boot.stage-1.kernel.modules = [
# Networking
"e1000"
mobile.boot.stage-1.kernel.modules = [
# Networking
"e1000"
# Input within X11
"uinput" "evdev"
# Input within X11
"uinput" "evdev"
# Video
"bochs_drm"
];
mobile.outputs.uefi = {
vm = pkgs.writeShellScript "run-vm-${device.name}" ''
ARGS=(
-enable-kvm
-bios "${pkgs.OVMF.fd}/FV/OVMF.fd"
-m "${ram}M"
-serial "mon:stdio"
-drive "file=${disk-image}/${disk-image.filename},format=raw,snapshot=on"
# Video
"bochs_drm"
];
mobile.outputs.uefi = {
vm = pkgs.writeShellScript "run-vm-${device.name}" ''
ARGS=(
-enable-kvm
-bios "${pkgs.OVMF.fd}/FV/OVMF.fd"
-m "${ram}M"
-serial "mon:stdio"
-drive "file=${disk-image}/${disk-image.filename},format=raw,snapshot=on"
-device "VGA,edid=on,xres=${xres},yres=${yres}"
-device "usb-ehci"
-device "usb-kbd"
-device "usb-tablet"
-device "VGA,edid=on,xres=${xres},yres=${yres}"
-device "usb-ehci"
-device "usb-kbd"
-device "usb-tablet"
-device "e1000,netdev=net0"
-netdev "user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23,net=172.16.42.0/24,dhcpstart=172.16.42.1"
-device "e1000,netdev=net0"
-netdev "user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23,net=172.16.42.0/24,dhcpstart=172.16.42.1"
-device "e1000,netdev=user.0"
-netdev "user,id=user.0"
)
-device "e1000,netdev=user.0"
-netdev "user,id=user.0"
)
${pkgs.qemu}/bin/qemu-system-${pkgs.stdenv.targetPlatform.qemuArch} "''${ARGS[@]}" "''${@}"
'';
};
};
${pkgs.qemu}/bin/qemu-system-${pkgs.stdenv.targetPlatform.qemuArch} "''${ARGS[@]}" "''${@}"
'';
};
})
(mkIf (!config.mobile.quirks.uefi.enableVM) {
mobile.outputs.uefi.vm = (config.lib.mobile-nixos.composeConfig {
config = {
mobile.quirks.uefi.enableVM = true;
};
}).config.mobile.outputs.uefi.vm;
})
];
}