mirror of
https://github.com/NixOS/mobile-nixos.git
synced 2024-12-26 09:26:14 +03:00
Merge branch 'feature/stage-2'
This commit is contained in:
commit
7f5266de24
22
README.md
22
README.md
@ -1,38 +1,32 @@
|
||||
Mobile NixOS
|
||||
============
|
||||
|
||||
An overlay for building stuff.
|
||||
|
||||
This is a work-in-progress.
|
||||
*This is expected to be built against the nixos-unstable for now.*
|
||||
|
||||
|
||||
WIP notes
|
||||
---------
|
||||
|
||||
```
|
||||
nix-build --argstr device asus-z00t -A all
|
||||
# Maybe `nix copy ./result --to ssh://another-host`
|
||||
adb wait-for-device && adb reboot bootloader
|
||||
fastboot boot result # or full path
|
||||
# getting adb and fastboot working is left as an exercise to the reader.
|
||||
```
|
||||
|
||||
Alternatively, helpers under `bin` can be used. They mostly pave over
|
||||
the nix CLI to provide one-liners and one-parameter helpers.
|
||||
|
||||
```
|
||||
# Builds -A all for device_name $1
|
||||
bin/build asus-z00t
|
||||
nix-build --argstr device asus-z00t -A build.android-bootimg
|
||||
```
|
||||
|
||||
### Booting qemu
|
||||
|
||||
```
|
||||
bin/build qemu-x86_64
|
||||
bin/boot-qemu
|
||||
```
|
||||
The qemu target has a `vm` build output, which results in a script that will
|
||||
automatically start the "virtual device".
|
||||
|
||||
This currently does not build using 18.03 and may never (18.09 may release before!)
|
||||
```
|
||||
nix-build -I --argstr device qemu-x86_64 -A build.vm
|
||||
./result
|
||||
```
|
||||
|
||||
### `local.nix`
|
||||
|
||||
|
BIN
artwork/splash.stage-0.png
Normal file
BIN
artwork/splash.stage-0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
artwork/splash.stage-0.xcf
Normal file
BIN
artwork/splash.stage-0.xcf
Normal file
Binary file not shown.
BIN
artwork/splash.stage-1.png
Normal file
BIN
artwork/splash.stage-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -p qemu -p ruby -i ruby
|
||||
|
||||
# Boots a given `result` symlink on a remote system.
|
||||
# This is mainly used to speed-up development where
|
||||
# an aarch64 host is used to build the system, but the
|
||||
# target device is plugged into another system
|
||||
|
||||
require_relative "../lib/rb/all.rb"
|
||||
require "fileutils"
|
||||
include FileUtils
|
||||
|
||||
# This assumes `result` is boot.img.
|
||||
STORE_PATH = File.readlink("result")
|
||||
|
||||
# FIXME : read-only doesn't work...
|
||||
$copy_readwrite = true;
|
||||
|
||||
begin
|
||||
disk_image = Dir.glob(File.join(File.readlink("result"), "system", "sd-image", "*.img")).first
|
||||
if $copy_readwrite then
|
||||
DISK_IMAGE = "fs.img"
|
||||
cp(disk_image, DISK_IMAGE)
|
||||
chmod("u=rw", DISK_IMAGE)
|
||||
else
|
||||
DISK_IMAGE = disk_image
|
||||
end
|
||||
end
|
||||
|
||||
unless ARGV.count == 0 then
|
||||
puts "Usage: bin/boot-eqmu"
|
||||
exit 1
|
||||
end
|
||||
|
||||
KERNEL_INITRD = "result/kernel-initrd"
|
||||
|
||||
args = []
|
||||
args.push("qemu-system-x86_64")
|
||||
args.push("-kernel", "#{KERNEL_INITRD}/kernel")
|
||||
args.push("-initrd", "#{KERNEL_INITRD}/initrd")
|
||||
args.push("-append", File.read("#{KERNEL_INITRD}/cmdline.txt"))
|
||||
args.push("-m", "#{File.read("#{KERNEL_INITRD}/ram.txt")}M")
|
||||
args.push("-serial", "stdio")
|
||||
args.push("-drive", "file=#{DISK_IMAGE},format=raw#{if $copy_readwrite then "" else ",readonly" end}")
|
||||
args.push("-device", "e1000,netdev=net0")
|
||||
args.push("-netdev", "user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23,net=172.16.42.0/24,dhcpstart=172.16.42.1")
|
||||
|
||||
run(*args, exec: true)
|
||||
|
||||
# vim: ft=ruby
|
26
bin/build
26
bin/build
@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -p ruby -i ruby
|
||||
|
||||
require_relative "../lib/rb/all.rb"
|
||||
|
||||
# Given a device name, instantiates `nix-build` to build
|
||||
# everything needed to boot on that device.
|
||||
|
||||
if ARGV.count < 1 then
|
||||
puts "Usage: bin/build <device-name>"
|
||||
exit 1
|
||||
end
|
||||
|
||||
DEVICE = ARGV.shift
|
||||
NIXPKGS=File.join(*__dir__.split("/")[0..-2], "nixpkgs")
|
||||
|
||||
run(
|
||||
"env", "-i",
|
||||
"nix-build", "-A", "all",
|
||||
"-I", "nixpkgs=#{NIXPKGS}",
|
||||
"--argstr", "device", DEVICE,
|
||||
*ARGV,
|
||||
exec: true
|
||||
)
|
||||
|
||||
# vim: ft=ruby
|
6
bin/ssh-initrd
Executable file
6
bin/ssh-initrd
Executable file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PS4=" $ "
|
||||
set -eux
|
||||
|
||||
exec ssh -o UserKnownHostsFile=/dev/null root@172.16.42.1
|
70
default.nix
70
default.nix
@ -1,31 +1,75 @@
|
||||
# This entry points allows calling `nix-build -A` with
|
||||
# anything defined in the overlay (or the host system).
|
||||
# Selection of the device can be made either through the environment or through
|
||||
# using `--argstr device [...]`.
|
||||
let deviceFromEnv = builtins.getEnv "MOBILE_NIXOS_DEVICE"; in
|
||||
|
||||
{
|
||||
device ?
|
||||
# "a" nixpkgs we're using for its lib.
|
||||
pkgs' ? import <nixpkgs> {}
|
||||
|
||||
# The identifier of the device this should be built for.
|
||||
, device ?
|
||||
if deviceFromEnv == ""
|
||||
then throw "Please pass a device name or set the MOBILE_NIXOS_DEVICE environment variable."
|
||||
else deviceFromEnv
|
||||
}:
|
||||
with import <nixpkgs> {};
|
||||
let
|
||||
# Evaluation doesn't actually use the overlay.
|
||||
# The overlay has been re-defined in the modules system.
|
||||
eval = import ./lib/eval-config.nix {
|
||||
inherit (pkgs'.lib) optional;
|
||||
|
||||
# Evaluates NixOS, mobile-nixos and the device config with the given
|
||||
# additional modules.
|
||||
evalWith = modules: import ./lib/eval-config.nix {
|
||||
modules = [
|
||||
(import (./. + "/devices/${device}" ))
|
||||
]
|
||||
# TODO : allow loading from elsewhere through ENV
|
||||
++ lib.optional (builtins.pathExists ./local.nix) (import (./local.nix ))
|
||||
;
|
||||
] ++ modules;
|
||||
};
|
||||
|
||||
# Eval with `local.nix` if available.
|
||||
# This eval is the "WIP" eval. What's usually built with `nix-build`.
|
||||
eval = evalWith (optional (builtins.pathExists ./local.nix) (import (./local.nix )));
|
||||
|
||||
# This is used by the `-A installer` shortcut.
|
||||
installer-eval = evalWith [
|
||||
./profiles/installer.nix
|
||||
];
|
||||
in
|
||||
{
|
||||
inherit (eval.config.system.build) all;
|
||||
# The build artifacts from the modules system.
|
||||
inherit (eval.config.system) build;
|
||||
|
||||
# The evaluated config
|
||||
inherit (eval) config;
|
||||
|
||||
# The final pkgs set, usable as -A pkgs.[...] on the CLI.
|
||||
inherit (eval) pkgs;
|
||||
|
||||
# The whole (default) eval
|
||||
inherit eval;
|
||||
|
||||
# Shortcut to allow building `nixos` from the same channel revision.
|
||||
# This is used by `./nixos/default.nix`
|
||||
# Any time `nix-build nixos` is used upstream, it can be used here.
|
||||
nixos = (import (path + "/nixos"));
|
||||
nixos = import <nixpkgs/nixos>;
|
||||
|
||||
# `mobile-installer` will, when possible, contain the installer build for the
|
||||
# given system. It usually is an alias for a disk-image type build.
|
||||
installer = installer-eval.config.system.build.mobile-installer;
|
||||
|
||||
# Evaluating this whole set is counter-productive.
|
||||
# It'll put a *bunch* of build products from the misc. inherits we added.
|
||||
|
||||
# (We're also using `device` to force the other throw to happen first.)
|
||||
# TODO : We may want to produce an internal list of available outputs, so that
|
||||
# each platform can document what it makes available. This would allow
|
||||
# the message to be more user-friendly by displaying a choice.
|
||||
__please-fail = throw ''
|
||||
Cannot directly build for ${device}...
|
||||
|
||||
Building this whole set is counter-productive, and not likely to be what
|
||||
is desired.
|
||||
|
||||
You can try to build the `installer` attribute (-A installer) if your system
|
||||
provides an installer.
|
||||
|
||||
Please refer to your platform's documentation for usage.
|
||||
'';
|
||||
}
|
||||
|
@ -14,5 +14,5 @@
|
||||
};
|
||||
};
|
||||
|
||||
mobile.system.type = "android-bootimg";
|
||||
mobile.system.type = "android";
|
||||
}
|
||||
|
@ -19,5 +19,5 @@
|
||||
};
|
||||
};
|
||||
|
||||
mobile.system.type = "android-bootimg";
|
||||
mobile.system.type = "android";
|
||||
}
|
||||
|
13
devices/asus-z00t/kernel/01_more_precise_arch.patch
Normal file
13
devices/asus-z00t/kernel/01_more_precise_arch.patch
Normal file
@ -0,0 +1,13 @@
|
||||
diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile
|
||||
index abb79b3cfcfe..a0f8d8f9e88a 100644
|
||||
--- a/arch/arm64/crypto/Makefile
|
||||
+++ b/arch/arm64/crypto/Makefile
|
||||
@@ -36,7 +36,7 @@ CFLAGS_aes-glue-ce.o := -DUSE_V8_CRYPTO_EXTENSIONS
|
||||
|
||||
obj-$(CONFIG_CRYPTO_CRC32_ARM64) += crc32-arm64.o
|
||||
|
||||
-CFLAGS_crc32-arm64.o := -mcpu=generic+crc
|
||||
+CFLAGS_crc32-arm64.o := -march=armv8-a+crc
|
||||
|
||||
$(obj)/aes-glue-%.o: $(src)/aes-glue.c FORCE
|
||||
$(call if_changed_rule,cc_o_c)
|
@ -21,6 +21,7 @@
|
||||
|
||||
patches = [
|
||||
./0001-Porting-changes-found-in-LineageOS-android_kernel_cy.patch
|
||||
./01_more_precise_arch.patch
|
||||
./01_fix_gcc6_errors.patch
|
||||
./02_mdss_fb_refresh_rate.patch
|
||||
./05_dtb-fix.patch
|
||||
|
@ -38,5 +38,5 @@
|
||||
};
|
||||
};
|
||||
|
||||
mobile.system.type = "android-bootimg";
|
||||
mobile.system.type = "android";
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
# This is because motorola ships an armv7l userspace from stock ROM.
|
||||
#
|
||||
# in local.nix:
|
||||
# mobile.system.platform = lib.mkForce "armv7a-linux";
|
||||
# mobile.system.system = lib.mkForce "armv7l-linux";
|
||||
#
|
||||
|
||||
let
|
||||
|
@ -1,7 +1,5 @@
|
||||
# This file is based on <nixpkgs/nixos/lib/eval-config.nix>.
|
||||
|
||||
# From a device configuration, build an initrd.
|
||||
|
||||
{ # !!! system can be set modularly, would be nice to remove
|
||||
system ? builtins.currentSystem
|
||||
, # !!! is this argument needed any more? The pkgs argument can
|
||||
|
46
lib/image-builder/default.nix
Normal file
46
lib/image-builder/default.nix
Normal file
@ -0,0 +1,46 @@
|
||||
{ lib, newScope }:
|
||||
|
||||
let
|
||||
inherit (lib) makeScope;
|
||||
in
|
||||
makeScope newScope (self:
|
||||
let
|
||||
inherit (self) callPackage;
|
||||
in
|
||||
# Note: Prefer using `self.something.deep` rather than making `something` a
|
||||
# recursive set. Otherwise it won't override as expected.
|
||||
{
|
||||
makeFilesystem = callPackage ./makeFilesystem.nix {};
|
||||
|
||||
# All known supported filesystems for image generation.
|
||||
# Use stand-alone (outside of a disk image) is supported.
|
||||
fileSystem = {
|
||||
makeExt4 = callPackage ./makeExt4.nix {};
|
||||
makeFAT32 = callPackage ./makeFAT32.nix {};
|
||||
# Specialization of `makeFAT32` with (1) filesystemType showing as ESP,
|
||||
# and (2) the name defaults to ESP.
|
||||
makeESP = args: self.fileSystem.makeFAT32 ({ name = "ESP"; filesystemType = "ESP"; } // args);
|
||||
};
|
||||
|
||||
gap = length: {
|
||||
inherit length;
|
||||
isGap = true;
|
||||
};
|
||||
|
||||
# All supported disk formats for image generation.
|
||||
diskImage = {
|
||||
makeMBR = callPackage ./makeMBR.nix {};
|
||||
makeGPT = callPackage ./makeGPT.nix {};
|
||||
};
|
||||
|
||||
# Don't do maths yourselves, just use the helpers.
|
||||
# Yes, this is the bibytes family of units.
|
||||
# (This is fine as rec; it won't be overriden.)
|
||||
size = rec {
|
||||
TiB = x: 1024 * (GiB x);
|
||||
GiB = x: 1024 * (MiB x);
|
||||
MiB = x: 1024 * (KiB x);
|
||||
KiB = x: 1024 * x;
|
||||
};
|
||||
}
|
||||
)
|
19
lib/image-builder/in-depth-tests/README.md
Normal file
19
lib/image-builder/in-depth-tests/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
In-depth tests
|
||||
==============
|
||||
|
||||
Those are rather bulky integration-type tests. They are not ran by default, but
|
||||
should be used to better test the image builder infrastructure.
|
||||
|
||||
Changes to the infra should be passed through this test suite in addition to the
|
||||
slimmer usual tests suite.
|
||||
|
||||
> **Tip:**
|
||||
>
|
||||
> From the image-builder infra directory, run the following.
|
||||
>
|
||||
> ```
|
||||
> nix-build in-depth-tests/[...].nix -I nixpkgs-overlays=$PWD/lib/tests/test-overlay.nix
|
||||
> ```
|
||||
>
|
||||
> This allows you to track the bigger builds more easily.
|
||||
|
96
lib/image-builder/in-depth-tests/raspi-like.nix
Normal file
96
lib/image-builder/in-depth-tests/raspi-like.nix
Normal file
@ -0,0 +1,96 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
inherit (pkgs) imageBuilder ubootTools;
|
||||
|
||||
configTxt = pkgs.writeText "config.txt" ''
|
||||
kernel=u-boot-rpi3.bin
|
||||
|
||||
# Boot in 64-bit mode.
|
||||
arm_control=0x200
|
||||
|
||||
# Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
|
||||
# when attempting to show low-voltage or overtemperature warnings.
|
||||
avoid_warnings=1
|
||||
'';
|
||||
|
||||
scrTxt = pkgs.writeText "uboot.scr.txt" ''
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
|
||||
echo " **"
|
||||
echo " ** Image Builder sanity checks!"
|
||||
echo " ** This will appear to freeze since the kernel is not built with the VC4 kernel built-in."
|
||||
echo " ** Stay assured, the kernel should be panicking anyway since there is no initrd, no init, and no useful FS."
|
||||
echo " **"
|
||||
|
||||
echo
|
||||
|
||||
load $devtype $devnum:$distro_bootpart $kernel_addr_r boot/kernel
|
||||
booti $kernel_addr_r
|
||||
'';
|
||||
|
||||
scr = pkgs.runCommandNoCC "uboot-script" {} ''
|
||||
mkdir -p $out
|
||||
${ubootTools}/bin/mkimage \
|
||||
-A arm64 \
|
||||
-O linux \
|
||||
-T script \
|
||||
-C none \
|
||||
-n ${scrTxt} -d ${scrTxt} \
|
||||
$out/boot.scr
|
||||
'';
|
||||
|
||||
# Here, we built a fictitious system cloning the AArch64 sd-image setup.
|
||||
# The chosen derivations are known to build fully when cross-compiled.
|
||||
pkgsAArch64 = (if pkgs.stdenv.isAarch64 then pkgs else pkgs.pkgsCross.aarch64-multiplatform);
|
||||
|
||||
# The kernel for the device.
|
||||
kernel = pkgsAArch64.linux_rpi;
|
||||
|
||||
# TODO: for completeness' sake an initrd with the vc4 driver should be built
|
||||
# to show that this works as a self-contained demo.
|
||||
in
|
||||
|
||||
with imageBuilder;
|
||||
|
||||
/**
|
||||
* This disk image is built to be functionally compatible with the usual `sd_image`
|
||||
* from NixOS, but *it is not* an actual `sd_image` compatible system.
|
||||
*
|
||||
* The main thing it aims to do is *minimally* create a bootable system.
|
||||
*/
|
||||
diskImage.makeMBR {
|
||||
name = "diskimage";
|
||||
diskID = "01234567";
|
||||
|
||||
partitions = [
|
||||
(gap (size.MiB 10))
|
||||
(fileSystem.makeFAT32 {
|
||||
# Size-less
|
||||
name = "FIRMWARE";
|
||||
partitionID = "ABADF00D";
|
||||
extraPadding = size.MiB 10;
|
||||
populateCommands = ''
|
||||
(
|
||||
src=${pkgsAArch64.raspberrypifw}/share/raspberrypi/boot
|
||||
cp $src/bootcode.bin $src/fixup*.dat $src/start*.elf ./
|
||||
cp ${pkgsAArch64.ubootRaspberryPi3_64bit}/u-boot.bin ./u-boot-rpi3.bin
|
||||
cp ${configTxt} ./config.txt
|
||||
)
|
||||
'';
|
||||
})
|
||||
(fileSystem.makeExt4 {
|
||||
bootable = true;
|
||||
name = "NIXOS";
|
||||
partitionID = "44444444-4444-4444-8888-888888888888";
|
||||
populateCommands = ''
|
||||
mkdir -p ./boot
|
||||
cp ${kernel}/Image ./boot/kernel
|
||||
cp ${scr}/boot.scr ./boot/boot.scr
|
||||
'';
|
||||
})
|
||||
];
|
||||
}
|
87
lib/image-builder/in-depth-tests/size/ext4.nix
Normal file
87
lib/image-builder/in-depth-tests/size/ext4.nix
Normal file
@ -0,0 +1,87 @@
|
||||
# Ensures we can fit stuff in an ext4 image.
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
inherit (pkgs) imageBuilder;
|
||||
makeNull = size: let
|
||||
filename = "null.img";
|
||||
filesystemType = "FAT32"; # meh, good enough
|
||||
in
|
||||
''
|
||||
mkdir -p $out
|
||||
dd if=/dev/zero of=./${toString size}.img bs=${toString size} count=1
|
||||
'';
|
||||
in
|
||||
|
||||
with imageBuilder;
|
||||
|
||||
{
|
||||
eight = fileSystem.makeExt4 {
|
||||
name = "eight";
|
||||
partitionID = "44444444-4444-4444-0000-000000000008";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 8)}
|
||||
'';
|
||||
};
|
||||
|
||||
eleven = fileSystem.makeExt4 {
|
||||
name = "eleven";
|
||||
partitionID = "44444444-4444-4444-0000-000000000011";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 11)}
|
||||
'';
|
||||
};
|
||||
|
||||
sixteen = fileSystem.makeExt4 {
|
||||
name = "sixteen";
|
||||
partitionID = "44444444-4444-4444-0000-000000000016";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 16)}
|
||||
'';
|
||||
};
|
||||
|
||||
one_twenty_eight = fileSystem.makeExt4 {
|
||||
name = "one_twenty_eight";
|
||||
partitionID = "44444444-4444-4444-0000-000000000128";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 128)}
|
||||
'';
|
||||
};
|
||||
|
||||
two_fifty_six = fileSystem.makeExt4 {
|
||||
name = "two_fifty_six";
|
||||
partitionID = "44444444-4444-4444-0000-000000000256";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 256)}
|
||||
'';
|
||||
};
|
||||
|
||||
five_twelve = fileSystem.makeExt4 {
|
||||
name = "five_twelve";
|
||||
partitionID = "44444444-4444-4444-0000-000000000512";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 512)}
|
||||
'';
|
||||
};
|
||||
|
||||
with_space = fileSystem.makeExt4 {
|
||||
name = "with_space";
|
||||
partitionID = "44444444-4444-4444-0000-000000000005";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 5)}
|
||||
'';
|
||||
extraPadding = size.MiB 10;
|
||||
};
|
||||
|
||||
# Fills 512 MiB (the downard slump in the high fudge factor) with 512 1MiB
|
||||
# files so we ensure the filesystem overhead is accounted for.
|
||||
multiple-files = fileSystem.makeExt4 {
|
||||
name = "multiple-files";
|
||||
partitionID = "44444444-4444-4444-0000-000000000512";
|
||||
populateCommands = ''
|
||||
for i in {1..512}; do
|
||||
dd if=/dev/zero of=./$i.img bs=${toString (imageBuilder.size.MiB 1)} count=1
|
||||
done
|
||||
'';
|
||||
};
|
||||
}
|
5
lib/image-builder/lib/overlay.nix
Normal file
5
lib/image-builder/lib/overlay.nix
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
(self: super: { imageBuilder = self.callPackage ../. {}; })
|
||||
# All the software will be upstreamed with NixOS when upstreaming the library.
|
||||
(import ../../../overlay/overlay.nix)
|
||||
]
|
18
lib/image-builder/lib/tests/test-overlay.nix
Normal file
18
lib/image-builder/lib/tests/test-overlay.nix
Normal file
@ -0,0 +1,18 @@
|
||||
# Adds the imageBuilder overlay.
|
||||
(import ../overlay.nix) ++
|
||||
[
|
||||
# Makes the imageBuilder build impure to force rebuilds to more easily test
|
||||
# reproducibility of outputs.
|
||||
(self: super:
|
||||
let
|
||||
inherit (self.lib.attrsets) mapAttrs;
|
||||
in
|
||||
{
|
||||
imageBuilder = super.imageBuilder.overrideScope'(self: super: {
|
||||
makeFilesystem = args: super.makeFilesystem (args // {
|
||||
REBUILD = "# ${toString builtins.currentTime}";
|
||||
});
|
||||
});
|
||||
}
|
||||
)
|
||||
]
|
61
lib/image-builder/lib/tests/verify.rb
Executable file
61
lib/image-builder/lib/tests/verify.rb
Executable file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "shellwords"
|
||||
require "open3"
|
||||
|
||||
# Test harness to validate results of a `nix-build`.
|
||||
# This script will `#load()` the given script.
|
||||
|
||||
if ARGV.length < 2 then
|
||||
abort "Usage: verify.rb <script> <result>"
|
||||
end
|
||||
|
||||
$failures = []
|
||||
$script = ARGV.shift
|
||||
$result = ARGV.shift
|
||||
|
||||
module Helpers
|
||||
def compare_output(cmd, expected, message: nil)
|
||||
expected ||= ""
|
||||
message ||= "Command `#{cmd.shelljoin}` has unexpected output:"
|
||||
expected = expected.strip
|
||||
out = `#{cmd.shelljoin}`.strip
|
||||
|
||||
unless out == expected then
|
||||
$failures << "#{message}:\n\tGot: #{out}\n\tExpected #{expected}"
|
||||
end
|
||||
end
|
||||
|
||||
def sha256sum(filename, expected)
|
||||
filename = File.join($result, filename)
|
||||
compare_output(
|
||||
["nix-hash", "--flat", "--type", "sha256", filename], expected,
|
||||
message: "File #{filename.shellescape} has unexpected hash",
|
||||
)
|
||||
end
|
||||
|
||||
def file(filename, expected)
|
||||
filename = File.join($result, filename)
|
||||
compare_output(
|
||||
["file", "--dereference", "--brief", filename], expected,
|
||||
message: "File #{filename.shellescape} has unexpected file type",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
include Helpers
|
||||
|
||||
# Executes the script
|
||||
load($script)
|
||||
|
||||
at_exit do
|
||||
if $failures.length > 0 then
|
||||
puts "Verification failed:"
|
||||
$failures.each do |failure|
|
||||
puts " → #{failure}"
|
||||
end
|
||||
exit 1
|
||||
end
|
||||
|
||||
exit 0
|
||||
end
|
113
lib/image-builder/makeExt4.nix
Normal file
113
lib/image-builder/makeExt4.nix
Normal file
@ -0,0 +1,113 @@
|
||||
{ lib, imageBuilder, libfaketime, e2fsprogs, make_ext4fs }:
|
||||
|
||||
/* */ let scope = { "fileSystem.makeExt4" =
|
||||
|
||||
let
|
||||
inherit (lib.strings) splitString;
|
||||
inherit (imageBuilder) makeFilesystem;
|
||||
|
||||
# Bash doesn't do floating point representations. Multiplications and divisions
|
||||
# are handled with enough precision that we can multiply and divide to get a precision.
|
||||
precision = 1000;
|
||||
|
||||
first = list: lib.lists.last (lib.lists.reverseList list);
|
||||
chopDecimal = f: first (splitString "." (toString f));
|
||||
makeFudge = f: toString (chopDecimal (f * precision));
|
||||
|
||||
# This applies only to 256MiB and greater.
|
||||
# For smaller than 256MiB images the overhead from the FS is much greater.
|
||||
# This will also let *some* slack space at the end at greater sizes.
|
||||
# This is the value at 512MiB where it goes slightly down compared to 256MiB.
|
||||
fudgeFactor = makeFudge 0.05208587646484375;
|
||||
|
||||
# This table was built using a script that built an image with `make_ext4fs`
|
||||
# for the given size in MiB, and recorded the available size according to `df`.
|
||||
smallFudgeLookup = lib.strings.concatStringsSep "\n" (lib.lists.reverseList(
|
||||
lib.attrsets.mapAttrsToList (size: factor: ''
|
||||
elif (( size > ${toString size} )); then
|
||||
fudgeFactor=${toString factor}
|
||||
'') {
|
||||
"${toString (imageBuilder.size.MiB 5)}" = makeFudge 0.84609375;
|
||||
"${toString (imageBuilder.size.MiB 8)}" = makeFudge 0.5419921875;
|
||||
"${toString (imageBuilder.size.MiB 16)}" = makeFudge 0.288818359375;
|
||||
"${toString (imageBuilder.size.MiB 32)}" = makeFudge 0.1622314453125;
|
||||
"${toString (imageBuilder.size.MiB 64)}" = makeFudge 0.09893798828125;
|
||||
"${toString (imageBuilder.size.MiB 128)}" = makeFudge 0.067291259765625;
|
||||
"${toString (imageBuilder.size.MiB 256)}" = makeFudge 0.0518646240234375;
|
||||
}
|
||||
));
|
||||
|
||||
minimumSize = imageBuilder.size.MiB 5;
|
||||
in
|
||||
{ partitionID
|
||||
, blockSize ? imageBuilder.size.KiB 4
|
||||
, ... } @ args:
|
||||
makeFilesystem (args // {
|
||||
filesystemType = "ext4";
|
||||
|
||||
inherit blockSize minimumSize;
|
||||
|
||||
nativeBuildInputs = [
|
||||
e2fsprogs
|
||||
make_ext4fs
|
||||
libfaketime
|
||||
];
|
||||
|
||||
filesystemPhase = ''
|
||||
:
|
||||
'';
|
||||
|
||||
computeMinimalSize = ''
|
||||
# `local size` is in bytes.
|
||||
|
||||
# We don't have a static reserved factor figured out. It is rather hard with
|
||||
# ext4fs as there are multiple factors increasing the overhead.
|
||||
local reservedSize=0
|
||||
local fudgeFactor=${toString fudgeFactor}
|
||||
|
||||
# Instead we rely on a lookup table. See how it is built in the derivation file.
|
||||
if (( size < ${toString (imageBuilder.size.MiB 256)} )); then
|
||||
echo "$size is smaller than 256MiB; using the lookup table." 1>&2
|
||||
|
||||
# A bit of a hack, though allows us to build the lookup table using only
|
||||
# elifs.
|
||||
if false; then
|
||||
:
|
||||
${smallFudgeLookup}
|
||||
else
|
||||
# The data is smaller than 5MiB... The filesystem image size will likely
|
||||
# not be able to accomodate... here we handle it in another way.
|
||||
fudgeFactor=0
|
||||
echo "Fudge factor skipped for extra small partition. Instead increasing by a fixed amount." 1>&2
|
||||
size=$(( size + ${toString minimumSize}))
|
||||
fi
|
||||
fi
|
||||
|
||||
local reservedSize=$(( size * $fudgeFactor / ${toString precision} ))
|
||||
|
||||
echo "Fudge factor: $fudgeFactor / ${toString precision}" 1>&2
|
||||
echo -n "Adding reservedSize: $size + $reservedSize = " 1>&2
|
||||
size=$((size + reservedSize))
|
||||
echo "$size" 1>&2
|
||||
'';
|
||||
|
||||
copyPhase = ''
|
||||
faketime -f "1970-01-01 00:00:00" \
|
||||
make_ext4fs \
|
||||
-b $blockSize \
|
||||
-L $partName \
|
||||
-l $size \
|
||||
-U $partitionID \
|
||||
"$img" \
|
||||
.
|
||||
'';
|
||||
|
||||
checkPhase = ''
|
||||
'';
|
||||
# FIXME:
|
||||
# Padding at end of inode bitmap is not set. Fix? no
|
||||
# exit code
|
||||
#EXT2FS_NO_MTAB_OK=yes fsck.ext4 -n -f $img
|
||||
})
|
||||
|
||||
/* */ ;}; in scope."fileSystem.makeExt4"
|
102
lib/image-builder/makeFAT32.nix
Normal file
102
lib/image-builder/makeFAT32.nix
Normal file
@ -0,0 +1,102 @@
|
||||
{ lib, imageBuilder, dosfstools, mtools, libfaketime}:
|
||||
|
||||
/* */ let scope = { "fileSystem.makeFAT32" =
|
||||
|
||||
let
|
||||
inherit (lib.strings) splitString;
|
||||
inherit (imageBuilder) makeFilesystem;
|
||||
# The default from `mkfs.fat`.
|
||||
reservedSectors = 32;
|
||||
# The default from `mkfs.fat`.
|
||||
hiddenSectors = 0;
|
||||
|
||||
# The default from `mkfs.fat`.
|
||||
numberOfFats = 2;
|
||||
# Extra padding per FAT, a constant in code
|
||||
fatPadding = 4;
|
||||
|
||||
# I have not been able to validate that it could be different from 1 for FAT32.
|
||||
# It seems the different values (e.g. 4) are for FAT12 and FAT16.
|
||||
# This is the only "bad" assumption here.
|
||||
clusterSize = 1;
|
||||
|
||||
# Bash doesn't do floating point representations. Multiplications and divisions
|
||||
# are handled with enough precision that we can multiply and divide to get a precision.
|
||||
precision = 1000;
|
||||
|
||||
first = list: lib.lists.last (lib.lists.reverseList list);
|
||||
chopDecimal = f: first (splitString "." (toString f));
|
||||
in
|
||||
{ partitionID
|
||||
# These defaults are assuming small~ish FAT32 filesystems are generated.
|
||||
, blockSize ? 512
|
||||
, sectorSize ? 512
|
||||
, ... } @ args:
|
||||
makeFilesystem (args // {
|
||||
# FAT32 can be used for ESP. Let's make this obvious.
|
||||
filesystemType = if args ? filesystemType then args.filesystemType else "FAT32";
|
||||
|
||||
inherit blockSize sectorSize;
|
||||
minimumSize = imageBuilder.size.KiB 500;
|
||||
|
||||
nativeBuildInputs = [
|
||||
libfaketime
|
||||
dosfstools
|
||||
mtools
|
||||
];
|
||||
|
||||
computeMinimalSize = ''
|
||||
# `local size` is in bytes.
|
||||
|
||||
# This amount is a static amount of reserved space.
|
||||
local static_reserved=${toString ( (reservedSectors + hiddenSectors) * sectorSize )}
|
||||
|
||||
# This is a constant representing the relative reserved space ratio.
|
||||
local relative_reserved=${
|
||||
chopDecimal (
|
||||
precision - (
|
||||
1.0 * sectorSize / ((clusterSize * sectorSize) + (numberOfFats * fatPadding))
|
||||
# ^ forces floating point
|
||||
) * precision
|
||||
)
|
||||
}
|
||||
# Rounds up the likely truncated result. At worst it's a bit more space.
|
||||
(( relative_reserved++ ))
|
||||
|
||||
echo "static_reserved=$static_reserved" 1>&2
|
||||
echo "relative_reserved=$relative_reserved" 1>&2
|
||||
|
||||
local reservedSize=$(( (static_reserved + size) * relative_reserved / ${toString precision} + static_reserved ))
|
||||
|
||||
echo -n "Adding reservedSize: $size + $reservedSize = " 1>&2
|
||||
size=$((size + reservedSize))
|
||||
echo "$size" 1>&2
|
||||
'';
|
||||
|
||||
filesystemPhase = ''
|
||||
faketime -f "1970-01-01 00:00:00" mkfs.vfat \
|
||||
-R ${toString reservedSectors} \
|
||||
-h ${toString hiddenSectors} \
|
||||
-s ${toString (blockSize / sectorSize)} \
|
||||
-S ${toString sectorSize} \
|
||||
-i $partitionID \
|
||||
-n $partName \
|
||||
"$img"
|
||||
'';
|
||||
|
||||
copyPhase = ''
|
||||
for f in ./* ./.*; do
|
||||
if [[ "$f" != "./." && "$f" != "./.." ]]; then
|
||||
faketime -f "1970-01-01 00:00:00" \
|
||||
mcopy -psv -i "$img" "$f" ::
|
||||
fi
|
||||
done
|
||||
'';
|
||||
|
||||
checkPhase = ''
|
||||
# Always verify FS
|
||||
fsck.vfat -vn "$img"
|
||||
'';
|
||||
})
|
||||
|
||||
/* */ ;}; in scope."fileSystem.makeFAT32"
|
152
lib/image-builder/makeFilesystem.nix
Normal file
152
lib/image-builder/makeFilesystem.nix
Normal file
@ -0,0 +1,152 @@
|
||||
{ stdenvNoCC, lib, writeText }:
|
||||
|
||||
/* */ let scope = { "fileSystem.makeFilesystem" =
|
||||
|
||||
let
|
||||
inherit (lib) optionals optionalString assertMsg;
|
||||
in
|
||||
|
||||
{
|
||||
name
|
||||
# Size (in bytes) the filesystem image will be given.
|
||||
# When size is not given, it is assumed that `populateCommands` will populate
|
||||
# the filesystem, and the size will be derived (see computeMinimalSize).
|
||||
, size ? null
|
||||
|
||||
# The populate commands are executed in a subshell. The CWD at the star is the
|
||||
# public API to know where to add files that will be added to the image.
|
||||
, populateCommands ? null
|
||||
|
||||
# Used with the assumption that files are rounded up to blockSize increments.
|
||||
, blockSize
|
||||
|
||||
# Additional commands to compute a required increase in size to fit files.
|
||||
, computeMinimalSize ? null
|
||||
|
||||
# When automatic sizing is used, additional amount of bytes to pad the image by.
|
||||
, extraPadding ? 0
|
||||
, ...
|
||||
} @ args:
|
||||
|
||||
assert lib.asserts.assertMsg
|
||||
(size !=null || populateCommands != null)
|
||||
"Either a size or populateCommands needs to be given to build a filesystem.";
|
||||
|
||||
let
|
||||
partName = name;
|
||||
in
|
||||
stdenvNoCC.mkDerivation (args // rec {
|
||||
# Do not inherit `size`; we don't want to accidentally use it. The `size` can
|
||||
# be dynamic depending on the contents.
|
||||
inherit partName blockSize;
|
||||
|
||||
name = "partition-${partName}";
|
||||
filename = "${partName}.img";
|
||||
img = "${placeholder "out"}/${filename}";
|
||||
|
||||
nativeBuildInputs = [
|
||||
] ++ optionals (args ? nativeBuildInputs) args.nativeBuildInputs;
|
||||
|
||||
buildCommand = ''
|
||||
adjust-minimal-size() {
|
||||
size="$1"
|
||||
|
||||
echo "$size"
|
||||
}
|
||||
|
||||
compute-minimal-size() {
|
||||
local size=0
|
||||
(
|
||||
cd files
|
||||
# Size rounded in blocks. This assumes all files are to be rounded to a
|
||||
# multiple of blockSize.
|
||||
# Use of `--apparent-size` is to ensure we don't get the block size of the underlying FS.
|
||||
# Use of `--block-size` is to get *our* block size.
|
||||
size=$(find . ! -type d -exec 'du' '--apparent-size' '--block-size' "$blockSize" '{}' ';' | cut -f1 | sum-lines)
|
||||
echo "Reserving $size sectors for files..." 1>&2
|
||||
|
||||
# Adds one blockSize per directory, they do take some place, in the end.
|
||||
# FIXME: write test to confirm this assumption
|
||||
local directories=$(find . -type d | wc -l)
|
||||
echo "Reserving $directories sectors for directories..." 1>&2
|
||||
|
||||
size=$(( directories + size ))
|
||||
|
||||
size=$((size * blockSize))
|
||||
|
||||
${if computeMinimalSize == null then "" else computeMinimalSize}
|
||||
|
||||
size=$(( size + ${toString extraPadding} ))
|
||||
|
||||
echo "$size"
|
||||
)
|
||||
}
|
||||
|
||||
sum-lines() {
|
||||
local acc=0
|
||||
while read -r number; do
|
||||
acc=$((acc+number))
|
||||
done
|
||||
|
||||
echo "$acc"
|
||||
}
|
||||
|
||||
|
||||
# The default stdenv/generic clashes with `runHook`.
|
||||
# It doesn't override as expected.
|
||||
unset -f checkPhase
|
||||
|
||||
mkdir -p $out
|
||||
mkdir -p files
|
||||
|
||||
${optionalString (populateCommands != null) ''
|
||||
echo
|
||||
echo "Populating disk image"
|
||||
echo
|
||||
(
|
||||
cd files
|
||||
${populateCommands}
|
||||
)
|
||||
''}
|
||||
${optionalString (size == null) ''
|
||||
size=$(compute-minimal-size)
|
||||
''}
|
||||
|
||||
if (( size < minimumSize )); then
|
||||
size=$minimumSize
|
||||
echo "WARNING: the '$partName' partition was too small, size increased to $minimumSize bytes."
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Building partition ${partName}"
|
||||
echo "With ${if size == null
|
||||
then "automatic size ($size bytes)"
|
||||
else "$size bytes"
|
||||
}"
|
||||
echo
|
||||
|
||||
echo " -> Allocating space"
|
||||
truncate -s $size "$img"
|
||||
|
||||
echo " -> Making filesystem"
|
||||
runHook filesystemPhase
|
||||
|
||||
echo " -> Copying files"
|
||||
(
|
||||
cd files
|
||||
runHook copyPhase
|
||||
)
|
||||
|
||||
echo " -> Checking filesystem"
|
||||
echo "$checkPhase"
|
||||
runHook checkPhase
|
||||
|
||||
'';
|
||||
|
||||
})
|
||||
# mkdir -p $out/nix-support
|
||||
# cat ${writeText "${name}-metadata" (builtins.toJSON {
|
||||
# inherit size;
|
||||
# })} > $out/nix-support/partition-metadata.json
|
||||
|
||||
/* */ ;}; in scope."fileSystem.makeFilesystem"
|
159
lib/image-builder/makeGPT.nix
Normal file
159
lib/image-builder/makeGPT.nix
Normal file
@ -0,0 +1,159 @@
|
||||
{ stdenvNoCC, lib
|
||||
, imageBuilder
|
||||
, utillinux
|
||||
}:
|
||||
|
||||
/* */ let scope = { "diskImage.makeGPT" =
|
||||
|
||||
let
|
||||
inherit (lib) concatMapStringsSep optionalString;
|
||||
|
||||
# List of known mappings of GPT partition types to filesystems.
|
||||
# This is not exhaustive, only used as a default.
|
||||
types = {
|
||||
"FAT32" = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
|
||||
"ESP" = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B";
|
||||
"ext2" = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
|
||||
"ext3" = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
|
||||
"ext4" = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
|
||||
};
|
||||
in
|
||||
{
|
||||
name
|
||||
, partitions
|
||||
, diskID
|
||||
}:
|
||||
|
||||
let
|
||||
_name = name;
|
||||
|
||||
eachPart = partitions: fn: (
|
||||
concatMapStringsSep "\n" (partition:
|
||||
fn partition
|
||||
) partitions);
|
||||
|
||||
# Default alignment.
|
||||
alignment = toString (imageBuilder.size.MiB 1);
|
||||
|
||||
image = partition:
|
||||
if lib.isDerivation partition then
|
||||
"${partition}/${partition.filename}"
|
||||
else
|
||||
partition.filename
|
||||
;
|
||||
in
|
||||
stdenvNoCC.mkDerivation rec {
|
||||
name = "disk-image-${_name}";
|
||||
filename = "${_name}.img";
|
||||
img = "${placeholder "out"}/${filename}";
|
||||
|
||||
nativeBuildInputs = [
|
||||
utillinux
|
||||
];
|
||||
|
||||
buildCommand = let
|
||||
# This fragment is used to compute the (aligned) size of the partition.
|
||||
# It is used *only* to track the tally of the space used, thus the starting
|
||||
# offset of the next partition. The filesystem sizes are untouched.
|
||||
sizeFragment = partition: ''
|
||||
start=$totalSize
|
||||
${
|
||||
if partition ? length then
|
||||
''size=$((${toString partition.length}))''
|
||||
else
|
||||
''size=$(($(du --apparent-size -B 512 "$input_img" | awk '{ print $1 }') * 512))''
|
||||
}
|
||||
size=$(( $(if (($size % ${alignment})); then echo 1; else echo 0; fi ) + size / ${alignment} ))
|
||||
size=$(( size * ${alignment} ))
|
||||
totalSize=$(( totalSize + size ))
|
||||
echo "Partition: start $start | size $size | totalSize $totalSize"
|
||||
'';
|
||||
|
||||
# This fragment is used to add the desired gap to `totalSize`.
|
||||
# We're setting `start` and `size` only to mirror the information shown
|
||||
# for partitions.
|
||||
# Do note that gaps are always aligned, so two gaps sized half the alignment
|
||||
# would create 2× the space expected.
|
||||
# What may *instead* be done at one point is always align `start` for partitions.
|
||||
gapFragment = partition: ''
|
||||
start=$totalSize
|
||||
size=${toString partition.length}
|
||||
size=$(( $(if (($size % ${alignment})); then echo 1; else echo 0; fi ) + size / ${alignment} ))
|
||||
totalSize=$(( totalSize + size ))
|
||||
echo "Gap: start $start | size $size | totalSize $totalSize"
|
||||
'';
|
||||
in ''
|
||||
mkdir -p $out
|
||||
|
||||
cat <<EOF > script.sfdisk
|
||||
label: gpt
|
||||
grain: 1024
|
||||
label-id: ${diskID}
|
||||
EOF
|
||||
|
||||
totalSize=${alignment}
|
||||
echo
|
||||
echo "Gathering information about partitions."
|
||||
${eachPart partitions (partition:
|
||||
if partition ? isGap && partition.isGap then
|
||||
(gapFragment partition)
|
||||
else
|
||||
''
|
||||
input_img="${image partition}"
|
||||
${sizeFragment partition}
|
||||
echo " -> ${partition.name}: $size / ${if partition ? filesystemType then partition.filesystemType else ""}"
|
||||
|
||||
(
|
||||
# The size is /1024; otherwise it's in sectors.
|
||||
echo -n 'start='"$((start/1024))"'KiB'
|
||||
echo -n ', size='"$((size/1024))"'KiB'
|
||||
echo -n ', type=${
|
||||
if partition ? partitionType then
|
||||
partition.partitionType
|
||||
else
|
||||
types.${partition.filesystemType}
|
||||
}'
|
||||
${optionalString (partition ? bootable && partition.bootable)
|
||||
"echo -n ', bootable'"}
|
||||
echo "" # Finishes the command
|
||||
) >> script.sfdisk
|
||||
''
|
||||
)}
|
||||
|
||||
# Allow space for alignment + secondary partition table / header.
|
||||
totalSize=$(( totalSize + ${alignment} ))
|
||||
|
||||
echo "--- script ----"
|
||||
cat script.sfdisk
|
||||
echo "--- script ----"
|
||||
|
||||
echo
|
||||
echo "Making image, $totalSize bytes..."
|
||||
truncate -s $((totalSize)) $img
|
||||
sfdisk $img < script.sfdisk
|
||||
|
||||
totalSize=${alignment}
|
||||
echo
|
||||
echo "Writing partitions into image"
|
||||
${eachPart partitions (partition:
|
||||
if partition ? isGap && partition.isGap then
|
||||
(gapFragment partition)
|
||||
else
|
||||
''
|
||||
input_img="${image partition}"
|
||||
${sizeFragment partition}
|
||||
echo " -> ${partition.name}: $size / ${if partition ? filesystemType then partition.filesystemType else ""}"
|
||||
|
||||
echo "$start / $size"
|
||||
dd conv=notrunc if=$input_img of=$img seek=$((start/512)) count=$((size/512)) bs=512
|
||||
''
|
||||
)}
|
||||
|
||||
echo
|
||||
echo "Information about the image:"
|
||||
ls -lh $img
|
||||
sfdisk -V --list $img
|
||||
'';
|
||||
}
|
||||
|
||||
/* */ ;}; in scope."diskImage.makeGPT"
|
139
lib/image-builder/makeMBR.nix
Normal file
139
lib/image-builder/makeMBR.nix
Normal file
@ -0,0 +1,139 @@
|
||||
{ stdenvNoCC, lib
|
||||
, imageBuilder
|
||||
, utillinux
|
||||
}:
|
||||
|
||||
/* */ let scope = { "diskImage.makeMBR" =
|
||||
|
||||
let
|
||||
inherit (lib) concatMapStringsSep optionalString;
|
||||
|
||||
# List of known mappings of MBR partition types to filesystems.
|
||||
types = {
|
||||
"FAT32" = "b";
|
||||
"ESP" = "ef";
|
||||
"ext2" = "83";
|
||||
"ext3" = "83";
|
||||
"ext4" = "83";
|
||||
};
|
||||
in
|
||||
{
|
||||
name
|
||||
, partitions
|
||||
# Without the prefixed `0x`
|
||||
, diskID
|
||||
}:
|
||||
|
||||
let
|
||||
_name = name;
|
||||
|
||||
eachPart = partitions: fn: (
|
||||
concatMapStringsSep "\n" (partition:
|
||||
fn partition
|
||||
) partitions);
|
||||
|
||||
# Default alignment.
|
||||
alignment = toString (imageBuilder.size.MiB 1);
|
||||
in
|
||||
stdenvNoCC.mkDerivation rec {
|
||||
name = "disk-image-${_name}";
|
||||
filename = "${_name}.img";
|
||||
img = "${placeholder "out"}/${filename}";
|
||||
|
||||
nativeBuildInputs = [
|
||||
utillinux
|
||||
];
|
||||
|
||||
buildCommand = let
|
||||
# This fragment is used to compute the (aligned) size of the partition.
|
||||
# It is used *only* to track the tally of the space used, thus the starting
|
||||
# offset of the next partition. The filesystem sizes are untouched.
|
||||
sizeFragment = ''
|
||||
start=$totalSize
|
||||
size=$(($(du --apparent-size -B 512 "$input_img" | awk '{ print $1 }') * 512))
|
||||
size=$(( $(if (($size % ${alignment})); then echo 1; else echo 0; fi ) + size / ${alignment} ))
|
||||
size=$(( size * ${alignment} ))
|
||||
totalSize=$(( totalSize + size ))
|
||||
echo "Partition: start $start | size $size | totalSize $totalSize"
|
||||
'';
|
||||
|
||||
# This fragment is used to add the desired gap to `totalSize`.
|
||||
# We're setting `start` and `size` only to mirror the information shown
|
||||
# for partitions.
|
||||
# Do note that gaps are always aligned, so two gaps sized half the alignment
|
||||
# would create 2× the space expected.
|
||||
# What may *instead* be done at one point is always align `start` for partitions.
|
||||
gapFragment = partition: ''
|
||||
start=$totalSize
|
||||
size=${toString partition.length}
|
||||
size=$(( $(if (($size % ${alignment})); then echo 1; else echo 0; fi ) + size / ${alignment} ))
|
||||
totalSize=$(( totalSize + size ))
|
||||
echo "Gap: start $start | size $size | totalSize $totalSize"
|
||||
'';
|
||||
in ''
|
||||
mkdir -p $out
|
||||
|
||||
cat <<EOF > script.sfdisk
|
||||
label: dos
|
||||
grain: 1024
|
||||
label-id: 0x${diskID}
|
||||
EOF
|
||||
|
||||
totalSize=${alignment}
|
||||
echo
|
||||
echo "Gathering information about partitions."
|
||||
${eachPart partitions (partition:
|
||||
if partition ? isGap && partition.isGap then
|
||||
(gapFragment partition)
|
||||
else
|
||||
''
|
||||
input_img="${partition}/${partition.filename}"
|
||||
${sizeFragment}
|
||||
echo " -> ${partition.name}: $size / ${partition.filesystemType}"
|
||||
|
||||
(
|
||||
# The size is /1024; otherwise it's in sectors.
|
||||
echo -n 'start='"$((start/1024))"'KiB'
|
||||
echo -n ', size='"$((size/1024))"'KiB'
|
||||
echo -n ', type=${types."${partition.filesystemType}"}'
|
||||
${optionalString (partition ? bootable && partition.bootable)
|
||||
"echo -n ', bootable'"}
|
||||
echo "" # Finishes the command
|
||||
) >> script.sfdisk
|
||||
''
|
||||
)}
|
||||
|
||||
echo "--- script ----"
|
||||
cat script.sfdisk
|
||||
echo "--- script ----"
|
||||
|
||||
echo
|
||||
echo "Making image, $totalSize bytes..."
|
||||
truncate -s $((totalSize)) $img
|
||||
sfdisk $img < script.sfdisk
|
||||
|
||||
totalSize=${alignment}
|
||||
echo
|
||||
echo "Writing partitions into image"
|
||||
${eachPart partitions (partition:
|
||||
if partition ? isGap && partition.isGap then
|
||||
(gapFragment partition)
|
||||
else
|
||||
''
|
||||
input_img="${partition}/${partition.filename}"
|
||||
${sizeFragment}
|
||||
echo " -> ${partition.name}: $size / ${partition.filesystemType}"
|
||||
|
||||
echo "$start / $size"
|
||||
dd conv=notrunc if=$input_img of=$img seek=$((start/512)) count=$((size/512)) bs=512
|
||||
''
|
||||
)}
|
||||
|
||||
echo
|
||||
echo "Information about the image:"
|
||||
ls -lh $img
|
||||
sfdisk -V --list $img
|
||||
'';
|
||||
}
|
||||
|
||||
/* */ ;}; in scope."diskImage.makeMBR"
|
144
lib/image-builder/test.rb
Executable file
144
lib/image-builder/test.rb
Executable file
@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
# All dependencies `verify` will need too.
|
||||
#!nix-shell --pure -p nix -p ruby -p file -i ruby
|
||||
|
||||
require "tmpdir"
|
||||
require "open3"
|
||||
require "json"
|
||||
# require "fileutils"
|
||||
|
||||
prefix = File.join(__dir__, "tests")
|
||||
NIX_PATH = "nixpkgs-overlays=#{__dir__}/lib/tests/test-overlay.nix:nixpkgs=channel:nixos-19.03"
|
||||
|
||||
# Default directives for the test.
|
||||
DEFAULT_DIRECTIVES = {
|
||||
# Default is to succeed.
|
||||
status: 0,
|
||||
|
||||
# Nothing to grep for in particular.
|
||||
# (Caution! Successes are likely not to have logs!)
|
||||
grep: nil,
|
||||
}
|
||||
|
||||
Env = {
|
||||
"NIX_PATH" => NIX_PATH,
|
||||
}
|
||||
|
||||
tests =
|
||||
if ARGV.count > 0 then
|
||||
ARGV
|
||||
else
|
||||
# Assumes all nix files in `./tests` are tests to `nix-build`.
|
||||
Dir.glob(File.join(prefix, "**/*.nix"))
|
||||
end
|
||||
|
||||
tests.sort!
|
||||
|
||||
$exit = 0
|
||||
$failed_tests = []
|
||||
|
||||
puts ""
|
||||
puts "Tests"
|
||||
puts "====="
|
||||
puts ""
|
||||
|
||||
tests.each_with_index do |file, index|
|
||||
short_name = file.sub("#{prefix}/", "")
|
||||
print "Running test #{(index+1).to_s.rjust(tests.length.to_s.length)}/#{tests.length} '#{short_name}' "
|
||||
|
||||
failures = []
|
||||
|
||||
# Reads the first line of the file. It may hold a json snippet
|
||||
# configuring the expected test results.
|
||||
directives = File.read(file).split("\n").first || ""
|
||||
directives = DEFAULT_DIRECTIVES.merge(
|
||||
if directives.match(/^\s*#\s*expect:/i) then
|
||||
# Parse...
|
||||
JSON.parse(
|
||||
# Everything after the first colon as json
|
||||
directives.split(":", 2).last,
|
||||
symbolize_names: true,
|
||||
)
|
||||
else
|
||||
{}
|
||||
end
|
||||
)
|
||||
|
||||
# The result symlink is in a temp directory...
|
||||
Dir.mktmpdir("image-builder-test") do |dir|
|
||||
result = File.join(dir, "result")
|
||||
|
||||
# TODO : figure out how to keep stdout/stderr synced but logged separately.
|
||||
log, status = Open3.capture2e(Env, "nix-build", "--show-trace", "--out-link", result, file)
|
||||
|
||||
unless status.exitstatus == directives[:status]
|
||||
failures << "Build exited with status #{status.exitstatus} expected #{directives[:status]}."
|
||||
end
|
||||
|
||||
if directives[:grep] then
|
||||
unless log.match(directives[:grep])
|
||||
failures << "Output log did not match `#{directives[:grep]}`."
|
||||
end
|
||||
end
|
||||
|
||||
# Do we test further with the test scripts?
|
||||
if failures.length == 0 and status.success? then
|
||||
script = file.sub(/\.nix$/, ".rb")
|
||||
if File.exists?(script) then
|
||||
log, status = Open3.capture2e(Env, File.join(__dir__, "lib/tests/verify.rb"), script, result)
|
||||
end
|
||||
|
||||
unless status.exitstatus == 0
|
||||
failures << "Verification exited with status #{status.exitstatus} expected 0."
|
||||
end
|
||||
|
||||
store_path = File.readlink(result)
|
||||
end
|
||||
|
||||
if failures.length == 0 then
|
||||
puts "[Success] (#{store_path || "no output"}) "
|
||||
else
|
||||
$exit = 1
|
||||
$failed_tests << file
|
||||
puts "[Failed] (#{store_path || "no output"}) "
|
||||
puts ""
|
||||
puts "Failures:"
|
||||
failures.each do |failure|
|
||||
puts " - #{failure}"
|
||||
end
|
||||
puts ""
|
||||
puts "Directives:"
|
||||
puts ""
|
||||
puts "```"
|
||||
puts "#{directives.inspect}"
|
||||
puts "```"
|
||||
|
||||
puts ""
|
||||
puts "Output from the test:"
|
||||
puts ""
|
||||
puts "````"
|
||||
puts "#{log}"
|
||||
puts "````"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts ""
|
||||
puts "* * *"
|
||||
puts ""
|
||||
puts "Test summary"
|
||||
puts "============"
|
||||
puts ""
|
||||
|
||||
puts "#{tests.length - $failed_tests.length}/#{tests.length} tests successful."
|
||||
|
||||
if $failed_tests.length > 0 then
|
||||
puts ""
|
||||
puts "Failed tests:\n"
|
||||
$failed_tests.each do |filename|
|
||||
puts " - #{filename.sub(/^#{__dir__}/, ".")}"
|
||||
end
|
||||
puts ""
|
||||
end
|
||||
|
||||
exit $exit
|
50
lib/image-builder/tests/alignment/aligned.nix
Normal file
50
lib/image-builder/tests/alignment/aligned.nix
Normal file
@ -0,0 +1,50 @@
|
||||
# Verifies that filesystems sized to be aligned works.
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
inherit (pkgs) imageBuilder;
|
||||
makeNull = size: pkgs.runCommandNoCC "filesystems-test" {
|
||||
filename = "null.img";
|
||||
filesystemType = "FAT32"; # meh, good enough
|
||||
} ''
|
||||
mkdir -p $out
|
||||
dd if=/dev/zero of=$out/$filename bs=${toString size} count=1
|
||||
'';
|
||||
in
|
||||
|
||||
with imageBuilder;
|
||||
|
||||
{
|
||||
one = diskImage.makeMBR {
|
||||
name = "diskimage";
|
||||
diskID = "012345678";
|
||||
partitions = [
|
||||
(makeNull (size.MiB 1))
|
||||
(makeNull (size.MiB 1))
|
||||
];
|
||||
};
|
||||
nine = diskImage.makeMBR {
|
||||
name = "diskimage";
|
||||
diskID = "012345678";
|
||||
partitions = [
|
||||
(makeNull (size.MiB 9))
|
||||
(makeNull (size.MiB 9))
|
||||
];
|
||||
};
|
||||
ten = diskImage.makeMBR {
|
||||
name = "diskimage";
|
||||
diskID = "012345678";
|
||||
partitions = [
|
||||
(makeNull (size.MiB 10))
|
||||
(makeNull (size.MiB 10))
|
||||
];
|
||||
};
|
||||
eleven = diskImage.makeMBR {
|
||||
name = "diskimage";
|
||||
diskID = "012345678";
|
||||
partitions = [
|
||||
(makeNull (size.MiB 11))
|
||||
(makeNull (size.MiB 11))
|
||||
];
|
||||
};
|
||||
}
|
42
lib/image-builder/tests/alignment/unaligned.nix
Normal file
42
lib/image-builder/tests/alignment/unaligned.nix
Normal file
@ -0,0 +1,42 @@
|
||||
# Verifies that filesystems sized to be unaligned will work.
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
inherit (pkgs) imageBuilder;
|
||||
makeNull = size: pkgs.runCommandNoCC "filesystems-test" {
|
||||
filename = "null.img";
|
||||
filesystemType = "FAT32"; # meh, good enough
|
||||
} ''
|
||||
mkdir -p $out
|
||||
dd if=/dev/zero of=$out/$filename bs=${toString size} count=1
|
||||
'';
|
||||
in
|
||||
|
||||
with imageBuilder;
|
||||
|
||||
# Through empirical testing, it was found out that the defaults from `sfdisk`
|
||||
# are not as documented.
|
||||
# First of all, on small disks no alignment will be made.
|
||||
# Starting with 3MiB (empirically derived) alignment will be made.
|
||||
# The alignment is documented as being based on the I/O limits. It seems like
|
||||
# for files it ends up causing alignments at the 2MiB boundaries.
|
||||
# Such, `grain: 1024` has to be set to configure sfdisk for the sane default
|
||||
# documented in its manpage
|
||||
#
|
||||
# > grain Specify minimal size in bytes used to calculate partitions alignment.
|
||||
# > The default is 1MiB and it's strongly recommended to use the default.
|
||||
# > Do not modify this variable if you're not sure.
|
||||
#
|
||||
# The default is *not* 1MiB and will break the generation of images if it tries
|
||||
# to align a small partition at the very end of the disk, when the disk is sized
|
||||
# just right to fit.
|
||||
#
|
||||
# This is what this test validates.
|
||||
diskImage.makeMBR {
|
||||
name = "diskimage";
|
||||
diskID = "012345678";
|
||||
partitions = [
|
||||
(makeNull (size.MiB 3))
|
||||
(makeNull (size.MiB 1))
|
||||
];
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
# Expect: { "status": 1, "grep": "Either a size or populateCommands needs to be given to build a filesystem." }
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
inherit (pkgs) imageBuilder;
|
||||
in
|
||||
|
||||
with imageBuilder;
|
||||
|
||||
fileSystem.makeFAT32 {
|
||||
name = "whatever";
|
||||
partitionID = "0123456789ABCDEF";
|
||||
}
|
35
lib/image-builder/tests/filesystems/default.nix
Normal file
35
lib/image-builder/tests/filesystems/default.nix
Normal file
@ -0,0 +1,35 @@
|
||||
# Tests all known filesystems as empty, with defaults.
|
||||
# This test helps ensure the basic interface stays stable, and works.
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
inherit (pkgs) imageBuilder;
|
||||
inherit (pkgs.lib.attrsets) mapAttrsToList;
|
||||
inherit (pkgs.lib.strings) concatStringsSep removePrefix;
|
||||
IDs = {
|
||||
FAT32 = "0123456789ABCDEF";
|
||||
ESP = "0123456789ABCDEF";
|
||||
Ext4 = "44444444-4444-4444-1324-123456789098";
|
||||
};
|
||||
in
|
||||
|
||||
with imageBuilder;
|
||||
|
||||
let
|
||||
cmds =
|
||||
mapAttrsToList (fn_name: fn:
|
||||
let
|
||||
fs = fn rec {
|
||||
name = removePrefix "make" fn_name;
|
||||
size = imageBuilder.size.MiB 10;
|
||||
partitionID = IDs."${name}";
|
||||
};
|
||||
in
|
||||
''
|
||||
ln -s ${fs}/${fs.filename} $out/
|
||||
'') fileSystem;
|
||||
in
|
||||
pkgs.runCommandNoCC "filesystems-test" {} ''
|
||||
mkdir -p $out/
|
||||
${concatStringsSep "\n" cmds}
|
||||
''
|
19
lib/image-builder/tests/filesystems/default.rb
Normal file
19
lib/image-builder/tests/filesystems/default.rb
Normal file
@ -0,0 +1,19 @@
|
||||
hashes = {
|
||||
"ESP.img" => "f9b39d98bfb797b050467ca5214671b5b427b7896cae44d27d2fc8dbceaccd88",
|
||||
"FAT32.img" => "79028d8af97ae4400ea2ab36d34e2a80684c9f8d31ea75e3f54d908c75adc3a4",
|
||||
"Ext4.img" => "8182df3038f43cdf2aba6f3980d9fa794affab0f040f9c07450ebbf0d3d8c2ad",
|
||||
}
|
||||
|
||||
filetypes = {
|
||||
"ESP.img" => 'DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", reserved sectors 32, root entries 512, sectors 20480 (volumes <=32 MB), Media descriptor 0xf8, sectors/FAT 80, sectors/track 32, heads 64, serial number 0x89abcdef, label: "ESP ", FAT (16 bit)',
|
||||
"FAT32.img" => 'DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", reserved sectors 32, root entries 512, sectors 20480 (volumes <=32 MB), Media descriptor 0xf8, sectors/FAT 80, sectors/track 32, heads 64, serial number 0x89abcdef, label: "FAT32 ", FAT (16 bit)',
|
||||
"Ext4.img" => 'Linux rev 1.0 ext4 filesystem data, UUID=44444444-4444-4444-1324-123456789098, volume name "Ext4" (extents) (large files)'
|
||||
}
|
||||
|
||||
# By globbing on the output, we can validate all built images are verified.
|
||||
# The builder should have built everything under `fileSystems`.
|
||||
Dir.glob(File.join($result, "**/*")) do |file|
|
||||
name = File.basename(file)
|
||||
sha256sum(name, hashes[name])
|
||||
file(name, filetypes[name])
|
||||
end
|
39
lib/image-builder/tests/filesystems/default_with_files.nix
Normal file
39
lib/image-builder/tests/filesystems/default_with_files.nix
Normal file
@ -0,0 +1,39 @@
|
||||
# Tests all known filesystems with files.
|
||||
# This test helps ensure the basic interface stays stable, and works.
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
inherit (pkgs) imageBuilder;
|
||||
inherit (pkgs.lib.attrsets) mapAttrsToList;
|
||||
inherit (pkgs.lib.strings) concatStringsSep removePrefix;
|
||||
IDs = {
|
||||
FAT32 = "0123456789ABCDEF";
|
||||
ESP = "0123456789ABCDEF";
|
||||
Ext4 = "44444444-4444-4444-1324-123456789098";
|
||||
};
|
||||
in
|
||||
|
||||
with imageBuilder;
|
||||
|
||||
let
|
||||
cmds =
|
||||
mapAttrsToList (fn_name: fn:
|
||||
let
|
||||
fs = fn rec {
|
||||
name = removePrefix "make" fn_name;
|
||||
partitionID = IDs."${name}";
|
||||
populateCommands = ''
|
||||
echo "I am ${name}." > file
|
||||
ls -lA
|
||||
'';
|
||||
};
|
||||
in
|
||||
''
|
||||
ln -s ${fs}/${fs.filename} $out/
|
||||
'') fileSystem;
|
||||
in
|
||||
pkgs.runCommandNoCC "filesystems-test" {} ''
|
||||
mkdir -p $out/
|
||||
${concatStringsSep "\n" cmds}
|
||||
''
|
||||
|
19
lib/image-builder/tests/filesystems/default_with_files.rb
Normal file
19
lib/image-builder/tests/filesystems/default_with_files.rb
Normal file
@ -0,0 +1,19 @@
|
||||
hashes = {
|
||||
"ESP.img" => "d70b24594f0615b83fddb8608b4819432380359bc5c6763d7c318e0c3233ea64",
|
||||
"FAT32.img" => "73ab1acd845fd0f7f5ddd30edea074f0b14bf4f2b4137f904a90939f1cb2ac8d",
|
||||
"Ext4.img" => "56181a43406c4ba731ed14b3ed32ed6c169b1c2abfe385eef989faef55e3cd07",
|
||||
}
|
||||
|
||||
filetypes = {
|
||||
"ESP.img" => 'DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", reserved sectors 32, root entries 512, sectors 1000 (volumes <=32 MB), Media descriptor 0xf8, sectors/FAT 3, sectors/track 32, heads 64, serial number 0x89abcdef, label: "ESP ", FAT (12 bit)',
|
||||
"FAT32.img" => 'DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", reserved sectors 32, root entries 512, sectors 1000 (volumes <=32 MB), Media descriptor 0xf8, sectors/FAT 3, sectors/track 32, heads 64, serial number 0x89abcdef, label: "FAT32 ", FAT (12 bit)',
|
||||
"Ext4.img" => 'Linux rev 1.0 ext4 filesystem data, UUID=44444444-4444-4444-1324-123456789098, volume name "Ext4" (extents) (large files)'
|
||||
}
|
||||
|
||||
# By globbing on the output, we can validate all built images are verified.
|
||||
# The builder should have built everything under `fileSystems`.
|
||||
Dir.glob(File.join($result, "**/*")) do |file|
|
||||
name = File.basename(file)
|
||||
sha256sum(name, hashes[name])
|
||||
file(name, filetypes[name])
|
||||
end
|
70
lib/image-builder/tests/size/ext4.nix
Normal file
70
lib/image-builder/tests/size/ext4.nix
Normal file
@ -0,0 +1,70 @@
|
||||
# Ensures we can fit stuff in an ext4 image.
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
inherit (pkgs) imageBuilder;
|
||||
makeNull = size: let
|
||||
filename = "null.img";
|
||||
filesystemType = "FAT32"; # meh, good enough
|
||||
in
|
||||
''
|
||||
mkdir -p $out
|
||||
dd if=/dev/zero of=./${toString size}.img bs=${toString size} count=1
|
||||
'';
|
||||
in
|
||||
|
||||
with imageBuilder;
|
||||
|
||||
{
|
||||
one = fileSystem.makeExt4 {
|
||||
name = "one";
|
||||
partitionID = "44444444-4444-4444-0000-000000000001";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 1)}
|
||||
'';
|
||||
};
|
||||
two = fileSystem.makeExt4 {
|
||||
name = "two";
|
||||
partitionID = "44444444-4444-4444-0000-000000000002";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 2)}
|
||||
'';
|
||||
};
|
||||
three = fileSystem.makeExt4 {
|
||||
name = "three";
|
||||
partitionID = "44444444-4444-4444-0000-000000000003";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 3)}
|
||||
'';
|
||||
};
|
||||
four = fileSystem.makeExt4 {
|
||||
name = "four";
|
||||
partitionID = "44444444-4444-4444-0000-000000000004";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 4)}
|
||||
'';
|
||||
};
|
||||
five = fileSystem.makeExt4 {
|
||||
name = "five";
|
||||
partitionID = "44444444-4444-4444-0000-000000000005";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 5)}
|
||||
'';
|
||||
};
|
||||
# This is the boundary where otherwise it would begin to fail.
|
||||
five_plus_one = fileSystem.makeExt4 {
|
||||
name = "five_plus_one";
|
||||
partitionID = "44444444-4444-4444-0001-000000000005";
|
||||
populateCommands = ''
|
||||
${makeNull ((imageBuilder.size.MiB 5) + 1)}
|
||||
'';
|
||||
};
|
||||
six = fileSystem.makeExt4 {
|
||||
name = "six";
|
||||
partitionID = "44444444-4444-4444-0000-000000000006";
|
||||
populateCommands = ''
|
||||
${makeNull (imageBuilder.size.MiB 6)}
|
||||
'';
|
||||
};
|
||||
# For bigger tests, see in-depth-tests
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
ROOT = File.join(__dir__, "..", "..")
|
||||
require_relative "run.rb"
|
@ -1,12 +0,0 @@
|
||||
require "shellwords"
|
||||
|
||||
def run(*cmd, exec: false)
|
||||
puts " $ #{cmd.shelljoin}"
|
||||
|
||||
if exec then
|
||||
Kernel.exec(*cmd)
|
||||
else
|
||||
system(*cmd)
|
||||
end
|
||||
exit $?.exitstatus unless $?.exitstatus == 0
|
||||
end
|
26
modules/_nixos-disintegration/default.nix
Normal file
26
modules/_nixos-disintegration/default.nix
Normal file
@ -0,0 +1,26 @@
|
||||
# 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;
|
||||
services.nixosManual.showManual = lib.mkOverride 10 false;
|
||||
}
|
||||
];
|
||||
}
|
20
modules/_nixos-disintegration/initrd.nix
Normal file
20
modules/_nixos-disintegration/initrd.nix
Normal file
@ -0,0 +1,20 @@
|
||||
{lib, config, ...}:
|
||||
|
||||
# FIXME: instead of setting `boot.isContainer`, let's instead disable the whole
|
||||
# stage-1 module and re-implement the options as needed.
|
||||
# → Maybe we can even import it ourselves, to get to its `options` attribute?
|
||||
{
|
||||
disabledModules = [
|
||||
<nixpkgs/nixos/modules/tasks/encrypted-devices.nix>
|
||||
<nixpkgs/nixos/modules/tasks/filesystems/zfs.nix>
|
||||
];
|
||||
|
||||
config = {
|
||||
# This isn't even used in our initrd...
|
||||
boot.supportedFilesystems = lib.mkOverride 10 [ ];
|
||||
boot.initrd.supportedFilesystems = lib.mkOverride 10 [];
|
||||
|
||||
# Co-opting this setting to disable the upstream NixOS stage-1.
|
||||
boot.isContainer = true;
|
||||
};
|
||||
}
|
@ -15,6 +15,6 @@ in
|
||||
};
|
||||
|
||||
config = {
|
||||
mobile.system.platform = lib.mkIf cfg.generic-x86_64.enable "x86_64-linux";
|
||||
mobile.system.system = lib.mkIf cfg.generic-x86_64.enable "x86_64-linux";
|
||||
};
|
||||
}
|
||||
|
@ -27,19 +27,19 @@ in
|
||||
config = mkMerge [
|
||||
{
|
||||
mobile = mkIf cfg.qualcomm-msm8939.enable {
|
||||
system.platform = "aarch64-linux";
|
||||
system.system = "aarch64-linux";
|
||||
quirks.qualcomm.msm-fb-handle.enable = true;
|
||||
};
|
||||
}
|
||||
{
|
||||
mobile = mkIf cfg.qualcomm-msm8953.enable {
|
||||
system.platform = "aarch64-linux";
|
||||
system.system = "aarch64-linux";
|
||||
quirks.qualcomm.msm-fb-handle.enable = true;
|
||||
};
|
||||
}
|
||||
{
|
||||
mobile = mkIf cfg.qualcomm-apq8064-1aa.enable {
|
||||
system.platform = "armv7a-linux";
|
||||
system.system = "armv7l-linux";
|
||||
quirks.qualcomm.msm-fb-refresher.enable = true;
|
||||
};
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ in
|
||||
config = mkMerge [
|
||||
{
|
||||
mobile = mkIf cfg.rockchip-op1.enable {
|
||||
system.platform = "aarch64-linux";
|
||||
system.system = "aarch64-linux";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
@ -1,8 +1,7 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
cfg = config.mobile.hardware;
|
||||
in
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ with import ./initrd-order.nix;
|
||||
|
||||
let
|
||||
cfg = config.mobile.boot.stage-1.splash;
|
||||
image = name: ../artwork + "/${name}.png";
|
||||
mkSplash = at: name:
|
||||
{
|
||||
init = lib.mkOrder at ''
|
||||
@ -12,7 +13,7 @@ let
|
||||
'';
|
||||
contents = [
|
||||
{
|
||||
object = (builtins.path { path = ../artwork + "/${name}.png"; });
|
||||
object = (builtins.path { path = image name; });
|
||||
symlink = "/${name}.png";
|
||||
}
|
||||
];
|
||||
@ -48,8 +49,18 @@ in
|
||||
];
|
||||
}
|
||||
|
||||
(mkSplash AFTER_FRAMEBUFFER_INIT "loading")
|
||||
(mkSplash (READY_INIT - 1) "splash")
|
||||
# This is as early as we can splash...
|
||||
(mkSplash AFTER_FRAMEBUFFER_INIT "splash.stage-0")
|
||||
# Though there's still some setting-up in stage-1,
|
||||
# This is where "init is ready".
|
||||
(mkSplash (READY_INIT - 1) "splash.stage-1")
|
||||
(mkIf cfg.rgb-debug (mkSplash (READY_INIT) "rgb-debug"))
|
||||
]);
|
||||
|
||||
# This happens in stage-2. This is why we're not using `mkSplash`.
|
||||
# This is the earliest in stage-2 we can show, for vt-less devices, that
|
||||
# stage-2 is really happening.
|
||||
config.boot.postBootCommands = ''
|
||||
${pkgs.ply-image}/bin/ply-image --clear=0x000000 ${image "splash.stage-2"} > /dev/null 2>&1
|
||||
'';
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ with import ./initrd-order.nix;
|
||||
|
||||
let
|
||||
cfg = config.mobile.boot.stage-1;
|
||||
system_type = config.mobile.system.type;
|
||||
in
|
||||
{
|
||||
# FIXME Generic USB gadget support to come.
|
||||
@ -26,7 +27,7 @@ in
|
||||
};
|
||||
adbd = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
default = system_type == "android";
|
||||
description = ''
|
||||
Enables adbd on the device.
|
||||
'';
|
||||
|
9
modules/initrd.nix
Normal file
9
modules/initrd.nix
Normal file
@ -0,0 +1,9 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
let
|
||||
device_config = config.mobile.device;
|
||||
stage-1 = config.mobile.boot.stage-1;
|
||||
in
|
||||
{
|
||||
system.build.initrd = pkgs.callPackage ../systems/initrd.nix { inherit device_config stage-1; };
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
# Keep sorted, <nixpkgs> imports first.
|
||||
let
|
||||
# This is only used to get the path to nixpkgs.
|
||||
# This one shouldn't affect cross-compiling.
|
||||
nixpkgs = (import <nixpkgs> {}).path;
|
||||
in
|
||||
# Import the upstream module-list.
|
||||
# FIXME : This won't allow importing `mobile-nixos` into /etc/configuration.nix
|
||||
(import <nixpkgs/nixos/modules/module-list.nix>) ++
|
||||
|
||||
# Then add our additional modules.
|
||||
# Keep this list `:sort`ed.
|
||||
[
|
||||
(nixpkgs + "/nixos/modules/misc/nixpkgs.nix")
|
||||
(nixpkgs + "/nixos/modules/misc/assertions.nix")
|
||||
./_nixos-disintegration
|
||||
./boot-initrd.nix
|
||||
./hardware-generic.nix
|
||||
./hardware-qualcomm.nix
|
||||
@ -27,11 +26,11 @@ in
|
||||
./initrd-ssh.nix
|
||||
./initrd-telnet.nix
|
||||
./initrd-usb.nix
|
||||
./initrd.nix
|
||||
./mobile-device.nix
|
||||
./nixpkgs.nix
|
||||
./quirks-qualcomm.nix
|
||||
./stage-2.nix
|
||||
./system-build.nix
|
||||
./system-target.nix
|
||||
./system-types.nix
|
||||
]
|
||||
|
@ -1,21 +1,36 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
# FIXME: Add hook for mounting, right now it's hardcoded to only mount "/".
|
||||
# (This'll allow complex schemes like LVM)
|
||||
# FIXME: Move udev stuff out.
|
||||
|
||||
let
|
||||
rootfs = config.fileSystems."/".device;
|
||||
in
|
||||
with import ./initrd-order.nix;
|
||||
{
|
||||
mobile.boot.stage-1.init = lib.mkOrder SWITCH_ROOT_INIT ''
|
||||
mobile.boot.stage-1.init =
|
||||
lib.mkOrder SWITCH_ROOT_INIT ''
|
||||
set -x
|
||||
targetRoot=/mnt
|
||||
|
||||
_fs_id() {
|
||||
blkid | grep ' LABEL="'"$1"'" ' | cut -d':' -f1
|
||||
}
|
||||
# FIXME : udev stuff out of here...
|
||||
systemd-udevd --daemon
|
||||
udevadm trigger --action=add
|
||||
udevadm settle
|
||||
|
||||
targetRoot=/mnt
|
||||
|
||||
_init_path() {
|
||||
local _system=""
|
||||
# IF no /nix/var...
|
||||
if [ -e "$targetRoot/nix/var/nix/profiles/system" ]; then
|
||||
_system="$targetRoot/nix/var/nix/profiles/system"
|
||||
|
||||
# Using -L is required, as the link chain is most likely dangling.
|
||||
if [ -L "$targetRoot/nix/var/nix/profiles/system" ]; then
|
||||
# There is a system symlink, use it.
|
||||
# What's that strange dance? We're canonicalizing one level deep of an
|
||||
# absolute symlink that we can't easily canonicalize otherwise.
|
||||
_system=$(cd $targetRoot/nix/var/nix/profiles/; readlink $(readlink system))
|
||||
elif [ -e "$targetRoot/nix-path-registration" ]; then
|
||||
# Otherwise, try to find one in nix-path-registration.
|
||||
_system="$(grep '^/nix/store/[a-z0-9]\+-nixos-system-' $targetRoot/nix-path-registration | head -1)"
|
||||
else
|
||||
echo "!!!!!"
|
||||
@ -29,23 +44,14 @@ with import ./initrd-order.nix;
|
||||
echo "!!!!!"
|
||||
echo "!!!!!"
|
||||
sleep 2m
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$_system/init"
|
||||
}
|
||||
|
||||
_fs_id NIXOS_SD
|
||||
_fs_id NIXOS_BOOT
|
||||
# FIXME : LESS FLIMSY!
|
||||
mkdir -p $targetRoot
|
||||
mount $(_fs_id NIXOS_SD) $targetRoot
|
||||
|
||||
# mkdir -p $targetRoot/boot
|
||||
# mount $(_fs_id NIXOS_BOOT) $targetRoot/boot
|
||||
|
||||
# mount "$(blkid | grep ' LABEL="'"NIXOS_SD"'" ' | cut -d':' -f1)" /mnt
|
||||
# mkdir -p /mnt/boot/
|
||||
# mount "$(blkid | grep ' LABEL="'"NIXOS_BOOT"'" ' | cut -d':' -f1)" /mnt/boot
|
||||
mount "${rootfs}" $targetRoot
|
||||
|
||||
echo ""
|
||||
echo "***"
|
||||
@ -55,12 +61,17 @@ with import ./initrd-order.nix;
|
||||
echo "***"
|
||||
echo ""
|
||||
|
||||
|
||||
for mp in /proc /sys /dev /run; do
|
||||
mkdir -m 0755 -p $targetRoot/$mp
|
||||
mount --move $mp $targetRoot/$mp
|
||||
done
|
||||
|
||||
# TODO : hook "AT" switch root
|
||||
|
||||
# FIXME : udev stuff out of here...
|
||||
# Stop udevd.
|
||||
udevadm control --exit
|
||||
|
||||
exec env -i $(type -P switch_root) $targetRoot $(_init_path)
|
||||
'';
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
options.system.build = mkOption {
|
||||
internal = true;
|
||||
description = ''
|
||||
Where the result will be put into.
|
||||
This ends up building `all`.
|
||||
'';
|
||||
};
|
||||
}
|
@ -1,24 +1,35 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
# FIXME : current implementation only works for native x86_64 built hosts.
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.mobile.system;
|
||||
|
||||
target_types = {
|
||||
aarch64-linux = lib.systems.examples.aarch64-multiplatform;
|
||||
armv7a-linux = lib.systems.examples.armv7l-hf-multiplatform;
|
||||
x86_64-linux = { config = "x86_64-unknown-linux-gnu"; };
|
||||
# Mapping from system types to config types
|
||||
# A simplified view of <nixpkgs/lib/systems/examples.nix>
|
||||
config_types = {
|
||||
aarch64-linux = "aarch64-unknown-linux-gnu";
|
||||
armv7l-linux = "armv7l-unknown-linux-gnueabihf";
|
||||
x86_64-linux = "x86_64-unknown-linux-gnu";
|
||||
};
|
||||
|
||||
# Hmmm, this doesn't feel right, but it does work.
|
||||
host_platform = (import <nixpkgs> {}).buildPackages.hostPlatform;
|
||||
# Derived from config_types
|
||||
target_types = lib.attrNames config_types;
|
||||
|
||||
# Builds the expected "platform" set for cross-compilation from the given
|
||||
# system name.
|
||||
selectPlatform = system: {
|
||||
inherit system;
|
||||
platform = lib.systems.platforms.selectBySystem system;
|
||||
config = config_types.${system};
|
||||
};
|
||||
|
||||
# The platform selected by the configuration
|
||||
selectedPlatform = selectPlatform cfg.system;
|
||||
in
|
||||
{
|
||||
options.mobile = {
|
||||
system.platform = mkOption {
|
||||
type = types.enum (lib.attrNames target_types);
|
||||
system.system = mkOption {
|
||||
type = types.enum target_types;
|
||||
description = ''
|
||||
Defines the kind of target architecture system the device is.
|
||||
|
||||
@ -30,14 +41,25 @@ in
|
||||
config = {
|
||||
assertions = [
|
||||
{
|
||||
assertion = pkgs.targetPlatform.system == cfg.platform;
|
||||
message = "pkgs.targetPlatform.system expected to be `${cfg.platform}`, is `${pkgs.targetPlatform.system}`";
|
||||
assertion = pkgs.targetPlatform.system == cfg.system;
|
||||
message = "pkgs.targetPlatform.system expected to be `${cfg.system}`, is `${pkgs.targetPlatform.system}`";
|
||||
}
|
||||
];
|
||||
|
||||
nixpkgs.crossSystem = lib.mkIf
|
||||
( target_types.${cfg.platform}.config != host_platform.config )
|
||||
(target_types.${cfg.platform} // { system = cfg.platform; }) # FIXME : WHY? I didn't need to add system before :/
|
||||
(
|
||||
let
|
||||
result = selectedPlatform.system != builtins.currentSystem;
|
||||
in
|
||||
builtins.trace
|
||||
"Building with crossSystem?: ${selectedPlatform.system} != ${builtins.currentSystem} → ${if result then "we are" else "we're not"}."
|
||||
result
|
||||
)
|
||||
(
|
||||
builtins.trace
|
||||
" crossSystem: config: ${selectedPlatform.config}"
|
||||
selectedPlatform
|
||||
)
|
||||
;
|
||||
};
|
||||
}
|
||||
|
@ -6,45 +6,27 @@ let
|
||||
failed = map (x: x.message) (filter (x: !x.assertion) config.assertions);
|
||||
|
||||
system_type = config.mobile.system.type;
|
||||
device_config = config.mobile.device;
|
||||
hardware_config = config.mobile.hardware;
|
||||
stage-1 = config.mobile.boot.stage-1;
|
||||
|
||||
build_types = {
|
||||
android-device = pkgs.callPackage ../systems/android-device.nix {
|
||||
inherit config;
|
||||
};
|
||||
android-bootimg = pkgs.callPackage ../systems/bootimg.nix {
|
||||
inherit device_config;
|
||||
# XXX : this feels like a hack
|
||||
initrd = pkgs.callPackage ../systems/initrd.nix { inherit device_config stage-1; };
|
||||
};
|
||||
depthcharge = pkgs.callPackage ../systems/depthcharge {
|
||||
inherit device_config;
|
||||
initrd = pkgs.callPackage ../systems/initrd.nix { inherit device_config stage-1; };
|
||||
};
|
||||
kernel-initrd = pkgs.linkFarm "${device_config.name}-build" [
|
||||
{
|
||||
name = "kernel-initrd";
|
||||
path = pkgs.callPackage ../systems/kernel-initrd.nix {
|
||||
# FIXME this all feels a bit not enough generic.
|
||||
inherit device_config hardware_config;
|
||||
initrd = pkgs.callPackage ../systems/initrd.nix { inherit device_config stage-1; };
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "system";
|
||||
# Equivalent to:
|
||||
# → nix-build nixos -I nixos-config=system-image.nix -A config.system.build.sdImage
|
||||
path = ((import (pkgs.path + "/nixos")) { configuration = ../system-image.nix; }).config.system.build.sdImage;
|
||||
}
|
||||
];
|
||||
};
|
||||
known_system_types = config.mobile.system.types;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../systems/rootfs.nix
|
||||
./system-types/depthcharge.nix
|
||||
./system-types/kernel-initrd.nix
|
||||
./system-types/android.nix
|
||||
];
|
||||
|
||||
options.mobile = {
|
||||
system.types = mkOption {
|
||||
type = types.listOf types.str;
|
||||
internal = true;
|
||||
description = ''
|
||||
Registry of system types.
|
||||
'';
|
||||
};
|
||||
system.type = mkOption {
|
||||
type = types.enum (lib.attrNames build_types);
|
||||
type = types.enum known_system_types;
|
||||
description = ''
|
||||
Defines the kind of system the device is.
|
||||
|
||||
@ -57,15 +39,11 @@ in
|
||||
config = {
|
||||
assertions = [
|
||||
# While the enum type is enough to implement value safety, this will help
|
||||
# when implementing new platforms and not implementing them in build_types.
|
||||
{ assertion = build_types ? ${system_type}; message = "Cannot build unexpected system type: ${system_type}.";}
|
||||
# when implementing new platforms and not implementing them in known_system_types.
|
||||
{
|
||||
assertion = lib.lists.any (x: x == system_type) known_system_types;
|
||||
message = "Cannot build unexpected system type: ${system_type}.\n Known types: ${lib.concatStringsSep ", " known_system_types}";
|
||||
}
|
||||
];
|
||||
system = {
|
||||
build =
|
||||
if failed == [] then
|
||||
build_types."${system_type}"
|
||||
else throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: " → ${x}") failed)}\n"
|
||||
;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
32
modules/system-types/android.nix
Normal file
32
modules/system-types/android.nix
Normal file
@ -0,0 +1,32 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
device_config = config.mobile.device;
|
||||
device_name = device_config.name;
|
||||
enabled = config.mobile.system.type == "android";
|
||||
|
||||
inherit (config.system.build) rootfs;
|
||||
|
||||
android-bootimg = pkgs.callPackage ../../systems/bootimg.nix {
|
||||
inherit device_config;
|
||||
initrd = config.system.build.initrd;
|
||||
};
|
||||
|
||||
android-device = pkgs.runCommandNoCC "android-device-${device_name}" {} ''
|
||||
mkdir -p $out
|
||||
ln -s ${rootfs}/${rootfs.filename} $out/system.img
|
||||
ln -s ${android-bootimg} $out/boot.img
|
||||
'';
|
||||
in
|
||||
{
|
||||
config = lib.mkMerge [
|
||||
{ mobile.system.types = [ "android" ]; }
|
||||
|
||||
(lib.mkIf enabled {
|
||||
system.build = {
|
||||
inherit android-bootimg android-device;
|
||||
mobile-installer = throw "No installer yet...";
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
25
modules/system-types/depthcharge.nix
Normal file
25
modules/system-types/depthcharge.nix
Normal file
@ -0,0 +1,25 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
device_config = config.mobile.device;
|
||||
enabled = config.mobile.system.type == "depthcharge";
|
||||
|
||||
disk-image = pkgs.callPackage ../../systems/depthcharge {
|
||||
inherit device_config;
|
||||
initrd = config.system.build.initrd;
|
||||
system = config.system.build.rootfs;
|
||||
};
|
||||
in
|
||||
{
|
||||
config = lib.mkMerge [
|
||||
{ mobile.system.types = [ "depthcharge" ]; }
|
||||
|
||||
(lib.mkIf enabled {
|
||||
system.build = {
|
||||
inherit disk-image;
|
||||
# installer shortcut; it's a depthcharge disk-image build.
|
||||
mobile-installer = disk-image;
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
56
modules/system-types/kernel-initrd.nix
Normal file
56
modules/system-types/kernel-initrd.nix
Normal file
@ -0,0 +1,56 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
device_config = config.mobile.device;
|
||||
device_name = device_config.name;
|
||||
hardware_config = config.mobile.hardware;
|
||||
rootfs = config.system.build.rootfs;
|
||||
enabled = config.mobile.system.type == "kernel-initrd";
|
||||
|
||||
kernel-initrd = pkgs.callPackage ../../systems/kernel-initrd.nix {
|
||||
inherit device_config hardware_config;
|
||||
initrd = config.system.build.initrd;
|
||||
};
|
||||
|
||||
system = pkgs.linkFarm "${device_config.name}-build" [
|
||||
{
|
||||
name = "kernel-initrd";
|
||||
path = "kernel-initrd";
|
||||
}
|
||||
{
|
||||
name = "system";
|
||||
path = rootfs;
|
||||
}
|
||||
];
|
||||
in
|
||||
{
|
||||
config = lib.mkMerge [
|
||||
{ mobile.system.types = [ "kernel-initrd" ]; }
|
||||
|
||||
(lib.mkIf enabled {
|
||||
system.build = {
|
||||
inherit system;
|
||||
mobile-installer = system;
|
||||
vm = pkgs.writeScript "run-vm-${device_name}" ''
|
||||
#!${pkgs.runtimeShell}
|
||||
PS4=" $ "
|
||||
set -eux
|
||||
|
||||
cp -f ${rootfs}/*.img fs.img
|
||||
chmod +rw fs.img
|
||||
|
||||
qemu-system-x86_64 \
|
||||
-enable-kvm \
|
||||
-kernel "${kernel-initrd}/kernel" \
|
||||
-initrd "${kernel-initrd}/initrd" \
|
||||
-append "$(cat "${kernel-initrd}/cmdline.txt")" \
|
||||
-m "$(cat "${kernel-initrd}/ram.txt")M" \
|
||||
-serial "stdio" \
|
||||
-drive "file=fs.img,format=raw" \
|
||||
-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"
|
||||
'';
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
@ -1 +0,0 @@
|
||||
((import ../.) { device = "qemu-x86_64"; }).nixos
|
@ -0,0 +1,82 @@
|
||||
From 5c2e0b7b5911477dcc4dedfd9910e28e9af65a91 Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Dionne-Riel <samuel@dionne-riel.com>
|
||||
Date: Thu, 29 Aug 2019 15:07:36 -0400
|
||||
Subject: [PATCH 1/2] uuid: add facilities to parse and print UUIDs
|
||||
|
||||
Signed-off-by: Samuel Dionne-Riel <samuel@dionne-riel.com>
|
||||
---
|
||||
uuid.c | 43 +++++++++++++++++++++++++++++++++++++++++++
|
||||
uuid.h | 6 ++++++
|
||||
2 files changed, 49 insertions(+)
|
||||
|
||||
diff --git a/uuid.c b/uuid.c
|
||||
index 7ded43a..47ef097 100644
|
||||
--- a/uuid.c
|
||||
+++ b/uuid.c
|
||||
@@ -59,3 +59,46 @@ void generate_uuid(const char *namespace, const char *name, u8 result[16])
|
||||
uuid->clk_seq_hi_res &= ~(1 << 6);
|
||||
uuid->clk_seq_hi_res |= 1 << 7;
|
||||
}
|
||||
+
|
||||
+void parse_uuid(const char *input, uint8_t result [16])
|
||||
+{
|
||||
+ int u = 0;
|
||||
+ if (strlen(input) != UUID_STR_LEN-1) {
|
||||
+ fprintf(stderr, "UUID MUST be in the format 00112233-4455-6677-8899-AABBCCDDEEFF.\n");
|
||||
+ abort();
|
||||
+ }
|
||||
+ for (int i = 0; i < UUID_STR_LEN - 1; i++) {
|
||||
+ if (i == 8 || i == 13 || i == 18 || i == 23) {
|
||||
+ /* Skip hyphens */
|
||||
+ }
|
||||
+ else {
|
||||
+ char pair[3] = "00";
|
||||
+ strncpy(pair, input + i, 2);
|
||||
+ result[u] = strtol(pair, NULL, 16);
|
||||
+ /* This handled a pair, let's move one more further along. */
|
||||
+ i++;
|
||||
+ /* Move to the next byte. */
|
||||
+ u++;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void uuid_to_string(const uint8_t uuid [16], char result [UUID_STR_LEN])
|
||||
+{
|
||||
+ /* Index in uuid */
|
||||
+ int u = 0;
|
||||
+ for (int i = 0; i < UUID_STR_LEN-1; i++) {
|
||||
+ /* Handles separators */
|
||||
+ if (i == 8 || i == 13 || i == 18 || i == 23) {
|
||||
+ strcpy(result+i, "-");
|
||||
+ }
|
||||
+ else {
|
||||
+ /* This writes \0 at the end of every invocation */
|
||||
+ snprintf(result+i, 3, "%02X", uuid[u]);
|
||||
+ /* This handled a pair, let's move one more further along. */
|
||||
+ i++;
|
||||
+ /* Move to the next byte. */
|
||||
+ u++;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/uuid.h b/uuid.h
|
||||
index ff1b438..21ba165 100644
|
||||
--- a/uuid.h
|
||||
+++ b/uuid.h
|
||||
@@ -19,6 +19,12 @@
|
||||
|
||||
#include "ext4_utils.h"
|
||||
|
||||
+#define UUID_STR_LEN 36+1
|
||||
+
|
||||
void generate_uuid(const char *namespace, const char *name, u8 result[16]);
|
||||
|
||||
+void parse_uuid(const char *input, uint8_t result [16]);
|
||||
+
|
||||
+void uuid_to_string(const uint8_t uuid [16], char result [UUID_STR_LEN]);
|
||||
+
|
||||
#endif
|
||||
--
|
||||
2.19.2
|
||||
|
@ -0,0 +1,129 @@
|
||||
From 71bc2e8b772695c00b2ed550565724afddd6fb7e Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Dionne-Riel <samuel@dionne-riel.com>
|
||||
Date: Thu, 29 Aug 2019 15:11:04 -0400
|
||||
Subject: [PATCH 2/2] make_ext4fs: allow setting a specific UUID
|
||||
|
||||
The UUID was previously only reproducibly generated using a combination
|
||||
of a fixed namespace and the label of the partition. This change allows
|
||||
projects to specify a desired UUID instead.
|
||||
|
||||
Signed-off-by: Samuel Dionne-Riel <samuel@dionne-riel.com>
|
||||
---
|
||||
ext4_sb.h | 3 +++
|
||||
ext4_utils.c | 13 ++++++++++++-
|
||||
make_ext4fs.c | 5 +++++
|
||||
make_ext4fs_main.c | 9 +++++++--
|
||||
4 files changed, 27 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/ext4_sb.h b/ext4_sb.h
|
||||
index d6416a7..68f5cb1 100644
|
||||
--- a/ext4_sb.h
|
||||
+++ b/ext4_sb.h
|
||||
@@ -18,6 +18,7 @@
|
||||
#define _EXT4_UTILS_EXT4_SB_H_
|
||||
|
||||
#include "ext4_kernel_headers.h"
|
||||
+#include <stdbool.h>
|
||||
|
||||
#define EXT4_SUPER_MAGIC 0xEF53
|
||||
|
||||
@@ -42,6 +43,8 @@ struct fs_info {
|
||||
uint32_t reserve_pcnt;
|
||||
const char *label;
|
||||
uint8_t no_journal;
|
||||
+ bool with_uuid;
|
||||
+ uint8_t uuid[16];
|
||||
};
|
||||
|
||||
int ext4_parse_sb(struct ext4_super_block *sb, struct fs_info *info);
|
||||
diff --git a/ext4_utils.c b/ext4_utils.c
|
||||
index 1a886d7..e8e60fb 100644
|
||||
--- a/ext4_utils.c
|
||||
+++ b/ext4_utils.c
|
||||
@@ -221,7 +221,13 @@ void ext4_fill_in_sb()
|
||||
sb->s_feature_compat = info.feat_compat;
|
||||
sb->s_feature_incompat = info.feat_incompat;
|
||||
sb->s_feature_ro_compat = info.feat_ro_compat;
|
||||
- generate_uuid("extandroid/make_ext4fs", info.label, sb->s_uuid);
|
||||
+ if (info.with_uuid) {
|
||||
+ memset(sb->s_uuid, 0, sizeof(sb->s_uuid));
|
||||
+ memcpy(sb->s_uuid, info.uuid, sizeof(sb->s_uuid));
|
||||
+ }
|
||||
+ else {
|
||||
+ generate_uuid("extandroid/make_ext4fs", info.label, sb->s_uuid);
|
||||
+ }
|
||||
memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
|
||||
strncpy(sb->s_volume_name, info.label, sizeof(sb->s_volume_name));
|
||||
memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
|
||||
@@ -521,6 +527,11 @@ int read_ext(int fd, int verbose)
|
||||
printf(" Inodes per group: %d\n", info.inodes_per_group);
|
||||
printf(" Inode size: %d\n", info.inode_size);
|
||||
printf(" Label: %s\n", info.label);
|
||||
+ if (info.with_uuid) {
|
||||
+ char uuid[UUID_STR_LEN] = "";
|
||||
+ uuid_to_string(info.uuid, uuid);
|
||||
+ printf(" UUID: %s\n", uuid);
|
||||
+ }
|
||||
printf(" Blocks: %"PRIu64"\n", aux_info.len_blocks);
|
||||
printf(" Block groups: %d\n", aux_info.groups);
|
||||
printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
|
||||
diff --git a/make_ext4fs.c b/make_ext4fs.c
|
||||
index 051052b..ac38d15 100644
|
||||
--- a/make_ext4fs.c
|
||||
+++ b/make_ext4fs.c
|
||||
@@ -451,6 +451,11 @@ int make_ext4fs_internal(int fd, const char *_directory,
|
||||
printf(" Inode size: %d\n", info.inode_size);
|
||||
printf(" Journal blocks: %d\n", info.journal_blocks);
|
||||
printf(" Label: %s\n", info.label);
|
||||
+ if (info.with_uuid) {
|
||||
+ char uuid[UUID_STR_LEN] = "";
|
||||
+ uuid_to_string(info.uuid, uuid);
|
||||
+ printf(" UUID: %s\n", uuid);
|
||||
+ }
|
||||
|
||||
ext4_create_fs_aux_info();
|
||||
|
||||
diff --git a/make_ext4fs_main.c b/make_ext4fs_main.c
|
||||
index 88254c3..e957a03 100644
|
||||
--- a/make_ext4fs_main.c
|
||||
+++ b/make_ext4fs_main.c
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "ext4_utils.h"
|
||||
#include "canned_fs_config.h"
|
||||
+#include "uuid.h"
|
||||
|
||||
extern struct fs_info info;
|
||||
|
||||
@@ -35,7 +36,7 @@ static void usage(char *path)
|
||||
{
|
||||
fprintf(stderr, "%s [ -l <len> ] [ -j <journal size> ] [ -b <block_size> ]\n", basename(path));
|
||||
fprintf(stderr, " [ -g <blocks per group> ] [ -i <inodes> ] [ -I <inode size> ]\n");
|
||||
- fprintf(stderr, " [ -m <reserved blocks percent> ] [ -L <label> ] [ -f ]\n");
|
||||
+ fprintf(stderr, " [ -m <reserved blocks percent> ] [ -L <label> ] [ -U <uuid> ] [ -f ]\n");
|
||||
fprintf(stderr, " [ -S file_contexts ] [ -C fs_config ] [ -T timestamp ]\n");
|
||||
fprintf(stderr, " [ -z | -s ] [ -w ] [ -c ] [ -J ] [ -v ] [ -B <block_list_file> ]\n");
|
||||
fprintf(stderr, " <filename> [<directory>]\n");
|
||||
@@ -58,7 +59,7 @@ int main(int argc, char **argv)
|
||||
time_t fixed_time = -1;
|
||||
FILE* block_list_file = NULL;
|
||||
|
||||
- while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:T:C:B:m:fwzJsctv")) != -1) {
|
||||
+ while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:T:C:B:m:U:fwzJsctv")) != -1) {
|
||||
switch (opt) {
|
||||
case 'l':
|
||||
info.len = parse_num(optarg);
|
||||
@@ -81,6 +82,10 @@ int main(int argc, char **argv)
|
||||
case 'L':
|
||||
info.label = optarg;
|
||||
break;
|
||||
+ case 'U':
|
||||
+ info.with_uuid = true;
|
||||
+ parse_uuid(optarg, info.uuid);
|
||||
+ break;
|
||||
case 'f':
|
||||
force = 1;
|
||||
break;
|
||||
--
|
||||
2.19.2
|
||||
|
34
overlay/make_ext4fs/default.nix
Normal file
34
overlay/make_ext4fs/default.nix
Normal file
@ -0,0 +1,34 @@
|
||||
{ stdenv, fetchgit, zlib }:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "make_ext4fs";
|
||||
version = "unstable-2017-05-21";
|
||||
|
||||
src = fetchgit {
|
||||
url = "git://git.openwrt.org/project/make_ext4fs.git";
|
||||
rev = "eebda1d55d9701ace2700d7ae461697fadf52d1f";
|
||||
sha256 = "03lqd5qy3nli9mmnlbgxwsplwz8v10cyjyzl1fxcfz8jvzr00c61";
|
||||
};
|
||||
|
||||
patches = [
|
||||
./0001-uuid-add-facilities-to-parse-and-print-UUIDs.patch
|
||||
./0002-make_ext4fs-allow-setting-a-specific-UUID.patch
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
zlib
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp make_ext4fs $out/bin
|
||||
'';
|
||||
|
||||
meta = with stdenv.lib; {
|
||||
homepage = https://git.openwrt.org/?p=project/make_ext4fs.git;
|
||||
description = "Standalone fork of Android make_ext4fs utility";
|
||||
license = licenses.asl20;
|
||||
platforms = platforms.linux;
|
||||
maintainers = [ maintainers.samueldr ];
|
||||
};
|
||||
}
|
@ -53,6 +53,13 @@ in
|
||||
p9_fixes
|
||||
];
|
||||
|
||||
#
|
||||
# New software to upstream
|
||||
# ------------------------
|
||||
#
|
||||
|
||||
make_ext4fs = callPackage ./make_ext4fs {};
|
||||
|
||||
#
|
||||
# Fixes to upstream
|
||||
# -----------------
|
||||
@ -60,33 +67,6 @@ in
|
||||
# All that follows will have to be cleaned and then upstreamed.
|
||||
#
|
||||
|
||||
fbterm = super.fbterm.overrideDerivation(oldAttrs: with self; {
|
||||
# Adds missing nativeBuildInputs (they're only buildInputs in nixpkgs).
|
||||
nativeBuildInputs = [ pkgconfig ncurses binutils ];
|
||||
# Futhermore, this patch is needed for compilation.
|
||||
patches = [
|
||||
(fetchpatch {
|
||||
name = "0001-fbio.cpp-improxy.cpp-fbterm.cpp-fix-musl-compile.patch";
|
||||
url = "https://raw.githubusercontent.com/buildroot/buildroot/master/package/fbterm/0001-fbio.cpp-improxy.cpp-fbterm.cpp-fix-musl-compile.patch";
|
||||
sha256 = "10dgpsym0nhsxzjbi0dbp1y5h2a1b7srsf9l09j9g10ia31ljbs3";
|
||||
})
|
||||
]
|
||||
++ oldAttrs.patches
|
||||
;
|
||||
});
|
||||
|
||||
freetype = super.freetype.overrideDerivation(oldAttrs: with self;{
|
||||
# ./configure doesn't detect the native compiler properly.
|
||||
CC_BUILD = "${buildPackages.stdenv.cc}/bin/cc";
|
||||
});
|
||||
|
||||
libdrm = super.libdrm.overrideAttrs(oldAttrs: {
|
||||
# valgrind won't build cross.
|
||||
buildInputs = builtins.filter (
|
||||
input: input != self.valgrind-light
|
||||
) oldAttrs.buildInputs;
|
||||
});
|
||||
|
||||
vboot_reference = super.vboot_reference.overrideAttrs(attrs: {
|
||||
# https://github.com/NixOS/nixpkgs/pull/69039
|
||||
postPatch = ''
|
||||
@ -103,4 +83,6 @@ in
|
||||
stdenv = with self; overrideCC stdenv buildPackages.gcc6;
|
||||
};
|
||||
};
|
||||
|
||||
imageBuilder = callPackage ../lib/image-builder {};
|
||||
}
|
||||
|
24
profiles/installer.nix
Normal file
24
profiles/installer.nix
Normal file
@ -0,0 +1,24 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
ifSystem = type: lib.mkIf (config.mobile.system.type == type);
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
<nixpkgs/nixos/modules/profiles/installation-device.nix>
|
||||
];
|
||||
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
# This seems counter-intuitive to me, but networkmanager requires that
|
||||
# this is set to false.
|
||||
networking.wireless.enable = false;
|
||||
networking.networkmanager.enable = true;
|
||||
}
|
||||
(ifSystem "depthcharge" {
|
||||
environment.systemPackages = with pkgs; [
|
||||
vboot_reference
|
||||
];
|
||||
})
|
||||
];
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
#
|
||||
# nix-build nixos -I nixos-config=system-image.nix -A config.system.build.sdImage
|
||||
#
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (import <nixpkgs> {}) path;
|
||||
|
||||
extlinux-conf-builder =
|
||||
import (path + "/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix") {
|
||||
inherit pkgs;
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(path + "/nixos/modules/installer/cd-dvd/sd-image.nix")
|
||||
(path + "/nixos/modules/profiles/base.nix")
|
||||
(path + "/nixos/modules/profiles/installation-device.nix")
|
||||
];
|
||||
boot.loader.grub.enable = false;
|
||||
boot.loader.generic-extlinux-compatible.enable = true;
|
||||
|
||||
# FIXME: this probably should be in installation-device.nix
|
||||
|
||||
sdImage = {
|
||||
populateBootCommands = ''
|
||||
${extlinux-conf-builder} -t 3 -c ${config.system.build.toplevel} -d ./boot
|
||||
'';
|
||||
};
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
{
|
||||
config
|
||||
, pkgs
|
||||
}:
|
||||
with pkgs;
|
||||
let
|
||||
device_config = config.mobile.device;
|
||||
hardware_config = config.mobile.hardware;
|
||||
|
||||
inherit (config.mobile.boot) stage-1;
|
||||
|
||||
inherit (hardware_config) ram;
|
||||
device_name = device_config.name;
|
||||
device_info = device_config.info;
|
||||
|
||||
android-bootimg = pkgs.callPackage ./bootimg.nix {
|
||||
inherit device_config;
|
||||
initrd = pkgs.callPackage ./initrd.nix { inherit device_config stage-1; };
|
||||
};
|
||||
android-system =
|
||||
(
|
||||
(import (pkgs.path + "/nixos"))
|
||||
{
|
||||
#system = "armv7l-linux";
|
||||
#system = config.nixpkgs.pkgs.targetPlatform.system;
|
||||
configuration = import (./system-android.nix) { mobile_config = config; };
|
||||
}
|
||||
).config.system.build.systemImage
|
||||
;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
name = "nixos-mobile_${device_name}_combined";
|
||||
|
||||
src = builtins.filterSource (path: type: false) ./.;
|
||||
unpackPhase = "true";
|
||||
|
||||
buildInputs = [
|
||||
linux
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/
|
||||
cp ${android-bootimg} $out/boot.img
|
||||
cp ${android-system} $out/system.img
|
||||
'';
|
||||
}
|
@ -2,6 +2,9 @@
|
||||
, fetchurl
|
||||
, runCommandNoCC
|
||||
, initrd
|
||||
, system
|
||||
, imageBuilder
|
||||
, lib
|
||||
|
||||
, dtc
|
||||
, ubootTools
|
||||
@ -11,22 +14,48 @@
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (imageBuilder) size;
|
||||
inherit (imageBuilder.diskImage) makeGPT;
|
||||
|
||||
# https://www.chromium.org/chromium-os/chromiumos-design-docs/disk-format
|
||||
# This doesn't fit into the generic makeGPT, some of those are really specific
|
||||
# to depthcharge.
|
||||
GPT_ENTRY_TYPES = {
|
||||
UNUSED = "00000000-0000-0000-0000-000000000000";
|
||||
EFI = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B";
|
||||
CHROMEOS_FIRMWARE = "CAB6E88E-ABF3-4102-A07A-D4BB9BE3C1D3";
|
||||
CHROMEOS_KERNEL = "FE3A2A5D-4F32-41A7-B725-ACCC3285A309";
|
||||
CHROMEOS_ROOTFS = "3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC";
|
||||
CHROMEOS_RESERVED = "2E0A753D-9E48-43B0-8337-B15192CB1B5E";
|
||||
LINUX_DATA = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
|
||||
LINUX_FS = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
|
||||
};
|
||||
|
||||
inherit (device_info) arch kernel kernel_cmdline dtbs;
|
||||
|
||||
device_info = device_config.info;
|
||||
device_name = device_config.name;
|
||||
|
||||
# Kernel used in kpart.
|
||||
kernel_file = "${kernel}/${kernel.file}";
|
||||
|
||||
# Kernel command line for vbutil_kernel.
|
||||
kpart_config = writeTextFile {
|
||||
name = "kpart-config-${device_name}";
|
||||
text = kernel_cmdline;
|
||||
};
|
||||
|
||||
# Name used for some image file output.
|
||||
name = "mobile-nixos-${device_name}";
|
||||
|
||||
# https://github.com/thefloweringash/kevin-nix/issues/3
|
||||
make-kernel-its = fetchurl {
|
||||
url = "https://raw.githubusercontent.com/thefloweringash/kevin-nix/e4156870bdb0a374b92c2291e5061d2c1a6c14b3/modules/make-kernel-its.sh";
|
||||
sha256 = "05918hcmrgrj71hiq460gpzz8lngz2ccf617m9p4c82s43v4agmg";
|
||||
};
|
||||
|
||||
kpart = runCommandNoCC "depthcharge-${device_name}" {
|
||||
# The image file containing the kernel and initrd.
|
||||
kpart = runCommandNoCC "kpart-${device_name}" {
|
||||
nativeBuildInputs = [
|
||||
dtc
|
||||
ubootTools
|
||||
@ -62,6 +91,42 @@ let
|
||||
--config ${kpart_config} \
|
||||
--pack $out/kpart
|
||||
'';
|
||||
|
||||
# An "unfinished" disk image.
|
||||
# It's missing some minor cgpt magic.
|
||||
# FIXME : make(MBR|GPT) should have a postBuild hook to manipulate the image.
|
||||
image = makeGPT {
|
||||
inherit name;
|
||||
diskID = "44444444-4444-4444-8888-888888888888";
|
||||
partitions = [
|
||||
{
|
||||
name = "kernel";
|
||||
filename = "${kpart}/kpart";
|
||||
partitionType = GPT_ENTRY_TYPES.CHROMEOS_KERNEL;
|
||||
length = size.MiB 64;
|
||||
}
|
||||
system
|
||||
];
|
||||
};
|
||||
in
|
||||
# FIXME: produce more than the kernel partition.
|
||||
kpart
|
||||
# Takes the built image, and do some light editing using `cgpt`.
|
||||
# This uses some depthcharge-specific fields to make the image bootable.
|
||||
# FIXME : integrate into the makeGPT call with postBuild or something
|
||||
runCommandNoCC "depthcharge-${device_name}" { nativeBuildInputs = [ vboot_reference ]; } ''
|
||||
# Copy the generated image...
|
||||
# Note that while it's GPT, it's lacking some depthcharge magic attributes
|
||||
cp ${image}/${name}.img ./
|
||||
chmod +w ${name}.img
|
||||
|
||||
# Which is what we're adding back with cgpt!
|
||||
cgpt add ${lib.concatStringsSep " " [
|
||||
"-i 1" # Work on the first partition (instead of adding)
|
||||
"-S 1" # Mark as successful (so it'll be booted from)
|
||||
"-T 5" # Tries remaining
|
||||
"-P 10" # Priority
|
||||
"${name}.img"
|
||||
]}
|
||||
|
||||
mkdir -p $out
|
||||
cp ${name}.img $out/
|
||||
''
|
||||
|
@ -11,8 +11,18 @@
|
||||
|
||||
lib,
|
||||
mkExtraUtils,
|
||||
|
||||
# FIXME : udev specifics
|
||||
runCommandNoCC,
|
||||
udev,
|
||||
pkgs
|
||||
}:
|
||||
|
||||
# FIXME: get the udev specifics out of here.
|
||||
# The main issue is how `udevRules` needs a reference to `extraUtils`.
|
||||
# This means that `extraUtils` should be a build product of stage-1 in
|
||||
# system.build, that we can refer to when required.
|
||||
|
||||
let
|
||||
inherit (lib) optionals flatten;
|
||||
|
||||
@ -24,11 +34,74 @@ let
|
||||
busybox
|
||||
]
|
||||
++ optionals (stage-1 ? extraUtils) stage-1.extraUtils
|
||||
++ [{
|
||||
package = runCommandNoCC "empty" {} "mkdir -p $out";
|
||||
extraCommand =
|
||||
let
|
||||
inherit (pkgs) udev;
|
||||
in
|
||||
''
|
||||
# Copy udev.
|
||||
copy_bin_and_libs ${udev}/lib/systemd/systemd-udevd
|
||||
copy_bin_and_libs ${udev}/bin/udevadm
|
||||
for BIN in ${udev}/lib/udev/*_id; do
|
||||
copy_bin_and_libs $BIN
|
||||
done
|
||||
''
|
||||
;
|
||||
}]
|
||||
;
|
||||
};
|
||||
|
||||
shell = "${extraUtils}/bin/ash";
|
||||
|
||||
udevRules = runCommandNoCC "udev-rules" {
|
||||
allowedReferences = [ extraUtils ];
|
||||
preferLocalBuild = true;
|
||||
} ''
|
||||
mkdir -p $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/
|
||||
|
||||
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.coreutils}/bin/basename ${extraUtils}/bin/basename \
|
||||
--replace ${pkgs.utillinux}/bin/blkid ${extraUtils}/bin/blkid \
|
||||
--replace ${pkgs.lvm2}/sbin ${extraUtils}/bin \
|
||||
--replace ${pkgs.mdadm}/sbin ${extraUtils}/sbin \
|
||||
--replace ${pkgs.bash}/bin/sh ${extraUtils}/bin/sh \
|
||||
--replace ${udev}/bin/udevadm ${extraUtils}/bin/udevadm
|
||||
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
|
||||
''; # */
|
||||
|
||||
# Just to keep track of this bit.
|
||||
udevFragment = ''
|
||||
mkdir -p /etc/udev
|
||||
|
||||
ln -sfn ${udevRules} /etc/udev/rules.d
|
||||
'';
|
||||
|
||||
stage1 = writeScript "stage1" ''
|
||||
#!${shell}
|
||||
|
||||
@ -41,6 +114,7 @@ let
|
||||
ln -sv ${shell} /bin/sh
|
||||
|
||||
# ---- stage-1.init START ----
|
||||
${udevFragment}
|
||||
${stage-1.init}
|
||||
# ---- stage-1.init END ----
|
||||
'';
|
||||
|
83
systems/rootfs.nix
Normal file
83
systems/rootfs.nix
Normal file
@ -0,0 +1,83 @@
|
||||
# This builds a rootfs image (ext4) from the current configuration.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
boot.loader.grub.enable = false;
|
||||
boot.loader.generic-extlinux-compatible.enable = false;
|
||||
|
||||
system.build.rootfs =
|
||||
pkgs.imageBuilder.fileSystem.makeExt4 {
|
||||
name = "NIXOS_SYSTEM";
|
||||
partitionID = "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
|
||||
'';
|
||||
# FIXME : fixup the partition autoexpand.
|
||||
extraPadding = pkgs.imageBuilder.size.MiB 500;
|
||||
}
|
||||
;
|
||||
|
||||
# FIXME: this is not a rootfs!
|
||||
system.build.diskImage =
|
||||
pkgs.imageBuilder.diskImage.makeMBR {
|
||||
name = "mobile-nixos";
|
||||
diskID = "01234567";
|
||||
partitions = [
|
||||
# FIXME : initrd how?
|
||||
config.system.build.rootfs
|
||||
];
|
||||
}
|
||||
;
|
||||
|
||||
#pkgs.runCommandNoCC "mobile-nixos-rootfs" {} ''
|
||||
# echo "${config.system.build.toplevel}" > $out
|
||||
#'';
|
||||
|
||||
boot.postBootCommands = ''
|
||||
# On the first boot do some maintenance tasks
|
||||
if [ -f /nix-path-registration ]; then
|
||||
${""
|
||||
# TODO : optionally resize NIXOS_SYSTEM, depending on the target.
|
||||
# # Figure out device names for the boot device and root filesystem.
|
||||
# rootPart=$(readlink -f /dev/disk/by-label/NIXOS_SYSTEM)
|
||||
# bootDevice=$(lsblk -npo PKNAME $rootPart)
|
||||
|
||||
# # Resize the root partition and the filesystem to fit the disk
|
||||
# echo ",+," | sfdisk -N2 --no-reread $bootDevice
|
||||
# ${pkgs.parted}/bin/partprobe
|
||||
# ${pkgs.e2fsprogs}/bin/resize2fs $rootPart
|
||||
}
|
||||
|
||||
# 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 = {
|
||||
"/" = {
|
||||
# Expected to be installed as `system` partition on android-like.
|
||||
# Thus the name.
|
||||
# TODO: move into the android system type.
|
||||
device = "/dev/disk/by-label/NIXOS_SYSTEM";
|
||||
fsType = "ext4";
|
||||
};
|
||||
};
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
# This module creates a system.img file built and sized to flash to an android-device.
|
||||
#
|
||||
# The derivation for the image will be placed in
|
||||
# config.system.build.system_img
|
||||
|
||||
# FIXME : this is a bit of a hack.
|
||||
|
||||
{
|
||||
mobile_config
|
||||
}:
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
rootfsImage = import <nixpkgs/nixos/lib/make-ext4-fs.nix> {
|
||||
inherit pkgs;
|
||||
inherit (config.systemImage) storePaths;
|
||||
volumeLabel = "NIXOS_SD";
|
||||
};
|
||||
in
|
||||
{
|
||||
options.systemImage = {
|
||||
imageBaseName = mkOption {
|
||||
default = "nixos-system";
|
||||
description = ''
|
||||
Prefix of the name of the generated image file.
|
||||
'';
|
||||
};
|
||||
|
||||
storePaths = mkOption {
|
||||
type = with types; listOf package;
|
||||
example = literalExample "[ pkgs.stdenv ]";
|
||||
description = ''
|
||||
Derivations to be included in the Nix store in the generated SD image.
|
||||
'';
|
||||
};
|
||||
|
||||
bootSize = mkOption {
|
||||
type = types.int;
|
||||
default = 120;
|
||||
description = ''
|
||||
Size of the /boot partition, in megabytes.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
fileSystems = {
|
||||
"/" = {
|
||||
device = "/dev/disk/by-label/NIXOS_SD";
|
||||
fsType = "ext4";
|
||||
};
|
||||
};
|
||||
|
||||
boot.kernelPackages =
|
||||
lib.makeExtensible (self: with self; {
|
||||
inherit (mobile_config.mobile.device.info) kernel;
|
||||
})
|
||||
;#pkgs.linuxPackagesFor mobile_config.mobile.device.info.kernel;
|
||||
|
||||
systemImage.storePaths = [ config.system.build.toplevel ];
|
||||
|
||||
system.build.systemImage = rootfsImage;
|
||||
#pkgs.stdenv.mkDerivation {
|
||||
# name = "mobile-nixos_system-android_system.img";
|
||||
|
||||
# buildInputs = with pkgs; [ e2fsprogs mtools libfaketime utillinux ];
|
||||
|
||||
# buildCommand = ''
|
||||
# cp "${rootfsImage}" "$img"
|
||||
# '';
|
||||
#};
|
||||
|
||||
boot.postBootCommands = ''
|
||||
# On the first boot do some maintenance tasks
|
||||
if [ -f /nix-path-registration ]; then
|
||||
# Figure out device names for the boot device and root filesystem.
|
||||
rootPart=$(readlink -f /dev/disk/by-label/NIXOS_SD)
|
||||
# bootDevice=$(lsblk -npo PKNAME $rootPart)
|
||||
#
|
||||
# # Resize the root partition and the filesystem to fit the disk
|
||||
# echo ",+," | sfdisk -N2 --no-reread $bootDevice
|
||||
# ${pkgs.parted}/bin/partprobe
|
||||
${pkgs.e2fsprogs}/bin/resize2fs $rootPart
|
||||
|
||||
# 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
|
||||
'';
|
||||
|
||||
# FIXME https://github.com/ElvishJerricco/cross-nixos-aarch64/blob/master/configuration.nix
|
||||
security.polkit.enable = false;
|
||||
services.udisks2.enable = false;
|
||||
programs.command-not-found.enable = false;
|
||||
system.boot.loader.kernelFile = lib.mkForce "Image";
|
||||
services.nixosManual.enable = lib.mkOverride 0 false;
|
||||
nix.checkConfig = false;
|
||||
services.klogd.enable = false;
|
||||
|
||||
nixpkgs.crossSystem = mobile_config.nixpkgs.crossSystem;
|
||||
system.nixos.stateVersion = "18.09";
|
||||
|
||||
systemd.services.sshd.wantedBy = lib.mkOverride 0 ["multi-user.target"];
|
||||
|
||||
# https://github.com/ElvishJerricco/cross-nixos-aarch64/blob/master/sd-image-aarch64.nix
|
||||
boot.loader.grub.enable = false;
|
||||
boot.loader.generic-extlinux-compatible.enable = true;
|
||||
boot.consoleLogLevel = lib.mkDefault 7;
|
||||
users.extraUsers.root.initialHashedPassword = "";
|
||||
|
||||
# HACK!
|
||||
# Removing `(isYes "MODULES")` would be preferrable.
|
||||
system.requiredKernelConfig = lib.mkForce [];
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user