{ lib, config, pkgs, modulesPath, ... }: { options.ps4 = with lib; { enable = mkEnableOption "Build NixOS for Sony PlayStation 4."; usbImage = { imageName = mkOption { type = types.str; default = "${config.ps4.usbImage.imageBaseName}-${config.system.nixos.label}.img"; description = "Name of the generated image file."; }; imageBaseName = mkOption { type = types.str; default = "ps4nixos"; description = "Prefix of the name of the generated image file."; }; compressImage = mkOption { type = types.bool; default = false; description = "Compress the resulting image with zstd."; }; bootPartitionSize = mkOption { type = types.int; default = 128; description = "Size of the boot partition, in mebibytes (1024x1024 bytes)."; }; bootPartitionLabel = mkOption { type = types.str; default = "nixos-boot"; description = "Label to give the boot partition of the image."; }; rootPartitionLabel = mkOption { type = types.str; default = "nixos-root"; description = "Label to give the root partition of the image."; }; expandOnBoot = mkOption { type = types.bool; default = true; description = "Whether the image should expand the root partition on first boot."; }; firstbootFile = mkOption { type = types.str; default = "/firstboot"; description = '' Location of the file that allows commands to be run on first boot. If overriding fileSystems."/" then you should to set this to the root mount + /firstboot ''; }; }; }; config = let cfg = config.ps4; in lib.mkIf cfg.enable { fileSystems = { "/boot" = { device = "/dev/disk/by-label/${cfg.usbImage.bootPartitionLabel}"; fsType = "vfat"; options = [ "nofail" "noauto" ]; }; "/" = { device = "/dev/disk/by-label/${cfg.usbImage.rootPartitionLabel}"; fsType = "ext4"; }; }; boot.loader.grub.enable = lib.mkForce false; # from https://github.com/NixOS/nixpkgs/blob/e405f30513169feedb64b5c25e7b00242010af58/nixos/modules/installer/sd-card/sd-image.nix#L267 boot.postBootCommands = let expandOnBoot = lib.optionalString cfg.usbImage.expandOnBoot '' # Figure out device names for the boot device and root filesystem. rootPart=$(${pkgs.util-linux}/bin/findmnt -n -o SOURCE /) bootDevice=$(lsblk -npo PKNAME $rootPart) partNum=$(lsblk -npo MAJ:MIN $rootPart | ${pkgs.gawk}/bin/awk -F: '{print $2}') # Resize the root partition and the filesystem to fit the disk echo ",+," | sfdisk -N$partNum --no-reread $bootDevice ${pkgs.parted}/bin/partprobe ${pkgs.e2fsprogs}/bin/resize2fs $rootPart ''; inherit (cfg.usbImage) firstbootFile; in '' # On the first boot do some maintenance tasks if [ -f ${firstbootFile} ]; then set -euo pipefail set -x ${expandOnBoot} # TODO what does all this do? # Register the contents of the initial Nix store # ${config.nix.package.out}/bin/nix-store --load-db < ${firstbootFile} # 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 ${firstbootFile} fi ''; system.build.rootFileSystem = pkgs.callPackage (modulesPath + "/../lib/make-ext4-fs.nix") { storePaths = [ config.system.build.toplevel ]; volumeLabel = cfg.usbImage.rootPartitionLabel; populateImageCommands = '' cp ${config.system.build.toplevel}/init ./files/init touch ./files/firstboot ''; }; }; }