Skip to content

Commit 0724777

Browse files
committed
FDE port from x86 to nvidia
- Modified files: - targets/nvidia-jetson-orin/flake-module.nix - modules/reference/hardware/jetpack/nvidia-jetson-orin/partition-template.nix - modules/partitioning/deferred-disk-encryption.nix What This Change Set Achieves - Adds phase-1 full disk encryption (FDE) support for Orin AGX debug targets using deferred first-boot encryption. - Keeps existing Orin targets intact; introduces additive -fde-phase1 target variants. - Refactors flash flow so phase1 targets can produce working -flash-script / -flash-qspi artifacts. - Preserves default installer-marker behavior globally, while bypassing marker only for phase1 targets. Previously Added (same target file, same branch context) - Logging enabled for all Orin targets (copied from x86 template style) in targets/nvidia-jetson-orin/flake-module.nix. - GIVC enabled for AGX debug cross targets (agx-debug and agx-debug-nodemoapps) in targets/nvidia-jetson-orin/flake-module.nix. Detailed Changes - targets/nvidia-jetson-orin/flake-module.nix - Added phase1 generator functions: - generate-fde-phase1 - generate-fde-phase1-cross-from-x86_64 - New additive target variants for: - nvidia-jetson-orin-agx-debug-fde-phase1 - nvidia-jetson-orin-agx-debug-nodemoapps-fde-phase1 - plus their -from-x86_64 variants. - Phase1 target behavior: - Disables sd-image format modules. - Switches package output to system.build.ghafImage. - Imports disko+verity partition modules. - Enables deferred encryption. - Overrides encrypted device path to "/dev/disk/by-partlabel/APP". - Disables installer marker requirement for phase1 only. - Cross image-builder compatibility tuning: - Uses pkgs.buildPackages, enables binfmt, pins image-builder kernel packages to build-side kernel packages. - Adds host/build platform overrides for disko builder config. - Disk layout tuning for flash constraints: - Forces disk image size/LV sizing (58G image, 44G root, 8G swap, 2G persist). - Refactors flash package generation: - Introduces flashableCrossTargets = crossTargets ++ fdePhase1CrossTargets. - Flash artifacts are now emitted for both existing and phase1 cross targets. - modules/partitioning/deferred-disk-encryption.nix - Added new options: - ghaf.storage.encryption.lvmPartitionDevice (nullable string override) - ghaf.storage.encryption.requireInstallerMarker (bool, default true) - Updated device selection logic: - Uses override if provided, then verity path, then disko default. - Marker handling is now conditional: - Marker-check + marker-removal are wrapped under requireInstallerMarker. - Result: - Existing behavior unchanged by default. - Phase1 can bypass installer marker safely and target APP. - modules/reference/hardware/jetpack/nvidia-jetson-orin/partition-template.nix - Refactored image handling to support two source layouts: - Traditional sd-image (esp.offset/root.offset) - Disko raw image (disk1.raw) - For raw-image path: - Detects ESP/APP partitions via fdisk. - Extracts ESP and APP with dd. - Added conv=sparse for root extraction to avoid temporary-space exhaustion. - Added APP-size clamp to align with Jetson flash XML fixed upper bound: - Prevents GPT generation failure (End sector for APP ... expected ... actual: 0). Target/Artifact Matrix After Change - Existing targets remain unchanged. - New phase1 outputs include: - ...-fde-phase1 - ...-fde-phase1-from-x86_64 - ...-fde-phase1-from-x86_64-flash-script - ...-fde-phase1-from-x86_64-flash-qspi - same for ...-agx-debug-nodemoapps... Validation Performed - Evaluated phase1 config options successfully: - encryption enabled/deferred - lvmPartitionDevice resolves to /dev/disk/by-partlabel/APP - installer marker requirement false for phase1 - first-boot-encrypt initrd service enabled and wired - Built successfully in Docker Compose: - nvidia-jetson-orin-agx-debug-fde-phase1-from-x86_64 - nvidia-jetson-orin-agx-debug-nodemoapps-fde-phase1-from-x86_64 - ...-flash-script for both - ...-flash-qspi evaluates and builds to initrd-flash output - Fixed several intermediate blockers during validation: - exec-format issues in image builder - missing kernel module expectations in initrd - flash temp-space issues - APP GPT boundary mismatch Runtime/Operational Guidance - Recommended deployment flow for phase1: - dd phase1 disk1.raw to USB. - Flash only QSPI with ...-flash-qspi. - Boot with USB attached. - Why QSPI-only: - Avoids potential APP label ambiguity between internal and external media during deferred encryption. What Is Not Changed - Non-phase1 targets do not inherit deferred-encryption behavior. - Global default marker requirement remains enabled (true), preserving existing installer-based expectations outside phase1. Signed-off-by: vadik likholetov <vadikas@gmail.com>
1 parent b897227 commit 0724777

File tree

3 files changed

+169
-22
lines changed

3 files changed

+169
-22
lines changed

modules/partitioning/deferred-disk-encryption.nix

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ let
3232
# Determine the LVM partition device
3333
# This is the partition that will be encrypted
3434
lvmPartition =
35-
if config.ghaf.partitioning.verity.enable then
35+
if cfg.lvmPartitionDevice != null then
36+
cfg.lvmPartitionDevice
37+
else if config.ghaf.partitioning.verity.enable then
3638
# For verity setups, use persist partition
3739
"/dev/disk/by-partuuid/${config.image.repart.partitions."50-persist".repartConfig.UUID}"
3840
else
@@ -158,6 +160,7 @@ let
158160
fi
159161
160162
# Check for installer/completion markers on the ESP partition.
163+
${lib.optionalString cfg.requireInstallerMarker ''
161164
ESP_DEVICE=""
162165
for i in {1..10}; do
163166
ESP_DEVICE="$(lsblk -pn -o PATH,PARTLABEL | awk 'tolower($2) ~ /esp/ { print $1; exit }')"
@@ -182,6 +185,7 @@ let
182185
umount /mnt/esp
183186
exit 0
184187
fi
188+
''}
185189
186190
# Stop Plymouth to show encryption progress
187191
if command -v plymouth >/dev/null 2>&1; then
@@ -585,9 +589,11 @@ let
585589
echo ""
586590
587591
# Remove the installer marker so we don't run again if this fails.
588-
rm -f /mnt/esp/.ghaf-installer-encrypt
589-
umount /mnt/esp
590-
rmdir /mnt/esp
592+
${lib.optionalString cfg.requireInstallerMarker ''
593+
rm -f /mnt/esp/.ghaf-installer-encrypt
594+
umount /mnt/esp
595+
rmdir /mnt/esp
596+
''}
591597
592598
${
593599
if config.ghaf.profiles.debug.enable then
@@ -619,6 +625,18 @@ in
619625
{
620626
options.ghaf.storage.encryption = {
621627
deferred = mkEnableOption "Apply disk encryption on first boot instead of at image creation";
628+
629+
lvmPartitionDevice = lib.mkOption {
630+
type = lib.types.nullOr lib.types.str;
631+
default = null;
632+
description = "Override block device path to encrypt during deferred first-boot encryption.";
633+
};
634+
635+
requireInstallerMarker = lib.mkOption {
636+
type = lib.types.bool;
637+
default = true;
638+
description = "Require /.ghaf-installer-encrypt marker on ESP before running deferred first-boot encryption.";
639+
};
622640
};
623641

624642
config = mkIf (cfg.enable && cfg.deferred) {

modules/reference/hardware/jetpack/nvidia-jetson-orin/partition-template.nix

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ let
1313
# Using the same config for all orin boards (for now)
1414
# TODO should this be changed when NX added
1515
cfg = config.ghaf.hardware.nvidia.orin;
16-
17-
images = config.system.build.${config.formatAttr};
16+
sdImages = config.system.build.sdImage or null;
17+
ghafImages = config.system.build.ghafImage or null;
1818
partitionsEmmc = pkgs.writeText "sdmmc.xml" ''
1919
<partition name="master_boot_record" type="protective_master_boot_record">
2020
<allocation_policy> sequential </allocation_policy>
@@ -155,16 +155,55 @@ in
155155
#"${pkgs.pkgsBuildBuild.patch}/bin/patch" -p0 < ${./tegra2-mb2-bct-scr.patch}
156156
''
157157
+ lib.optionalString (!cfg.flashScriptOverrides.onlyQSPI) ''
158-
ESP_OFFSET=$(cat "${images}/esp.offset")
159-
ESP_SIZE=$(cat "${images}/esp.size")
160-
ROOT_OFFSET=$(cat "${images}/root.offset")
161-
ROOT_SIZE=$(cat "${images}/root.size")
162-
163-
img="${images}/sd-image/${config.image.fileName}"
164-
echo "Extracting ESP partition to $WORKDIR/bootloader/esp.img ..."
165-
dd if=<("${pkgs.pkgsBuildBuild.zstd}/bin/pzstd" -d "$img" -c) of="$WORKDIR/bootloader/esp.img" bs=512 iseek="$ESP_OFFSET" count="$ESP_SIZE"
166-
echo "Extracting root partition to $WORKDIR/root.img ..."
167-
dd if=<("${pkgs.pkgsBuildBuild.zstd}/bin/pzstd" -d "$img" -c) of="$WORKDIR/bootloader/root.img" bs=512 iseek="$ROOT_OFFSET" count="$ROOT_SIZE"
158+
SD_IMAGES_DIR="${if sdImages != null then sdImages else ""}"
159+
GHAF_IMAGES_DIR="${if ghafImages != null then ghafImages else ""}"
160+
161+
if [ -n "$SD_IMAGES_DIR" ] && [ -e "$SD_IMAGES_DIR/esp.offset" ] && [ -e "$SD_IMAGES_DIR/root.offset" ]; then
162+
ESP_OFFSET=$(cat "$SD_IMAGES_DIR/esp.offset")
163+
ESP_SIZE=$(cat "$SD_IMAGES_DIR/esp.size")
164+
ROOT_OFFSET=$(cat "$SD_IMAGES_DIR/root.offset")
165+
ROOT_SIZE=$(cat "$SD_IMAGES_DIR/root.size")
166+
167+
img=$(ls "$SD_IMAGES_DIR"/sd-image/*.img.zst | head -n 1)
168+
echo "Extracting ESP partition from sd-image to $WORKDIR/bootloader/esp.img ..."
169+
dd if=<("${pkgs.pkgsBuildBuild.zstd}/bin/pzstd" -d "$img" -c) of="$WORKDIR/bootloader/esp.img" bs=512 iseek="$ESP_OFFSET" count="$ESP_SIZE"
170+
echo "Extracting root partition from sd-image to $WORKDIR/root.img ..."
171+
dd if=<("${pkgs.pkgsBuildBuild.zstd}/bin/pzstd" -d "$img" -c) of="$WORKDIR/bootloader/root.img" bs=512 iseek="$ROOT_OFFSET" count="$ROOT_SIZE" conv=sparse
172+
elif [ -n "$GHAF_IMAGES_DIR" ] && [ -e "$GHAF_IMAGES_DIR/disk1.raw" ]; then
173+
img="$GHAF_IMAGES_DIR/disk1.raw"
174+
echo "Extracting ESP and APP partitions from disko raw image ..."
175+
176+
fdisk_output=$(${pkgs.pkgsBuildBuild.util-linux}/bin/fdisk -l "$img")
177+
178+
part_esp=$(${pkgs.pkgsBuildBuild.gawk}/bin/awk -v img="$img" '$1 == img "2" { print $2 " " $4; exit }' <<< "$fdisk_output")
179+
part_app=$(${pkgs.pkgsBuildBuild.gawk}/bin/awk -v img="$img" '$1 == img "3" { print $2 " " $4; exit }' <<< "$fdisk_output")
180+
181+
if [ -z "$part_esp" ] || [ -z "$part_app" ]; then
182+
echo "Failed to locate ESP/APP partitions in raw image"
183+
exit 1
184+
fi
185+
186+
ESP_OFFSET=$(echo "$part_esp" | ${pkgs.pkgsBuildBuild.gawk}/bin/awk '{print $1}')
187+
ESP_SIZE=$(echo "$part_esp" | ${pkgs.pkgsBuildBuild.gawk}/bin/awk '{print $2}')
188+
ROOT_OFFSET=$(echo "$part_app" | ${pkgs.pkgsBuildBuild.gawk}/bin/awk '{print $1}')
189+
ROOT_SIZE=$(echo "$part_app" | ${pkgs.pkgsBuildBuild.gawk}/bin/awk '{print $2}')
190+
191+
# Jetson AGX eMMC APP partition has a fixed upper bound in flash.xml.
192+
# Clamp extracted APP size to avoid invalid GPT generation.
193+
MAX_APP_END=119537630
194+
MAX_ROOT_SIZE=$((MAX_APP_END - ROOT_OFFSET + 1))
195+
if [ "$ROOT_SIZE" -gt "$MAX_ROOT_SIZE" ]; then
196+
ROOT_SIZE="$MAX_ROOT_SIZE"
197+
fi
198+
199+
echo "Extracting ESP partition from raw image to $WORKDIR/bootloader/esp.img ..."
200+
dd if="$img" of="$WORKDIR/bootloader/esp.img" bs=512 iseek="$ESP_OFFSET" count="$ESP_SIZE"
201+
echo "Extracting APP partition from raw image to $WORKDIR/root.img ..."
202+
dd if="$img" of="$WORKDIR/bootloader/root.img" bs=512 iseek="$ROOT_OFFSET" count="$ROOT_SIZE" conv=sparse
203+
else
204+
echo "Unsupported image layout: expected sd-image offsets or disk1.raw"
205+
exit 1
206+
fi
168207
169208
echo "Patching flash.xml with absolute paths to esp.img and root.img ..."
170209
"${pkgs.pkgsBuildBuild.gnused}/bin/sed" -i \

targets/nvidia-jetson-orin/flake-module.nix

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,10 @@ let
139139
// rec {
140140
name = tgt.name + "-from-x86_64";
141141
hostConfiguration = tgt.hostConfiguration.extendModules {
142-
modules = [ self.nixosModules.cross-compilation-from-x86_64 ] ++ lib.optionals enableGivcForAgxDebug [
142+
modules = [
143+
self.nixosModules.cross-compilation-from-x86_64
144+
]
145+
++ lib.optionals enableGivcForAgxDebug [
143146
{
144147
ghaf.givc.enable = lib.mkForce true;
145148
ghaf.givc.debug = lib.mkForce false;
@@ -149,24 +152,111 @@ let
149152
package = hostConfiguration.config.system.build.${hostConfiguration.config.formatAttr};
150153
};
151154

155+
generate-fde-phase1 =
156+
tgt:
157+
tgt
158+
// rec {
159+
name = tgt.name + "-fde-phase1";
160+
hostConfiguration = tgt.hostConfiguration.extendModules {
161+
modules = [
162+
(
163+
{ pkgs, ... }:
164+
{
165+
disabledModules = [
166+
(nixos-generators + "/format-module.nix")
167+
../../modules/reference/hardware/jetpack/nvidia-jetson-orin/format-module.nix
168+
];
169+
170+
imports = [
171+
self.nixosModules.disko-debug-partition
172+
self.nixosModules.verity-release-partition
173+
];
174+
175+
ghaf = {
176+
partitioning.disko.enable = lib.mkForce true;
177+
partitioning.disko.imageBuilder.compression = lib.mkForce "none";
178+
storage.encryption = {
179+
enable = lib.mkForce true;
180+
deferred = lib.mkForce true;
181+
lvmPartitionDevice = lib.mkForce "/dev/disk/by-partlabel/APP";
182+
requireInstallerMarker = lib.mkForce false;
183+
};
184+
virtualization.microvm.storeOnDisk = lib.mkForce true;
185+
};
186+
187+
boot.initrd.luks.cryptoModules = lib.mkForce [ ];
188+
boot.initrd.availableKernelModules = lib.mkForce [ ];
189+
boot.initrd.kernelModules = lib.mkForce [ ];
190+
191+
disko.imageBuilder = {
192+
pkgs = lib.mkForce pkgs.buildPackages;
193+
enableBinfmt = lib.mkForce true;
194+
kernelPackages = lib.mkForce pkgs.buildPackages.linuxPackages;
195+
extraConfig = {
196+
nixpkgs.hostPlatform = lib.mkOverride 0 pkgs.stdenv.hostPlatform;
197+
nixpkgs.buildPlatform = lib.mkOverride 0 pkgs.stdenv.buildPlatform;
198+
};
199+
};
200+
201+
disko.devices = {
202+
disk.disk1.imageSize = lib.mkForce "58G";
203+
lvm_vg.pool.lvs = {
204+
swap.size = lib.mkForce "8G";
205+
root.size = lib.mkForce "44G";
206+
persist.size = lib.mkForce "2G";
207+
};
208+
};
209+
}
210+
)
211+
];
212+
};
213+
package = hostConfiguration.config.system.build.ghafImage;
214+
};
215+
216+
generate-fde-phase1-cross-from-x86_64 =
217+
tgt:
218+
tgt
219+
// rec {
220+
name = tgt.name + "-from-x86_64";
221+
hostConfiguration = tgt.hostConfiguration.extendModules {
222+
modules = [ self.nixosModules.cross-compilation-from-x86_64 ];
223+
};
224+
package = hostConfiguration.config.system.build.ghafImage;
225+
};
226+
152227
# Add nodemoapps targets
153228
targets = target-configs ++ (map generate-nodemoapps target-configs);
154229
crossTargets = map generate-cross-from-x86_64 targets;
230+
fdePhase1Targets = map generate-fde-phase1 (
231+
builtins.filter (
232+
t:
233+
builtins.elem t.name [
234+
"nvidia-jetson-orin-agx-debug"
235+
"nvidia-jetson-orin-agx-debug-nodemoapps"
236+
]
237+
) targets
238+
);
239+
fdePhase1CrossTargets = map generate-fde-phase1-cross-from-x86_64 fdePhase1Targets;
240+
flashableCrossTargets = crossTargets ++ fdePhase1CrossTargets;
155241
in
156242
{
157243
flake = {
158244
nixosConfigurations = builtins.listToAttrs (
159-
map (t: lib.nameValuePair t.name t.hostConfiguration) (targets ++ crossTargets)
245+
map (t: lib.nameValuePair t.name t.hostConfiguration) (
246+
targets ++ crossTargets ++ fdePhase1Targets ++ fdePhase1CrossTargets
247+
)
160248
);
161249

162250
packages = {
163-
aarch64-linux = builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) targets);
251+
aarch64-linux = builtins.listToAttrs (
252+
map (t: lib.nameValuePair t.name t.package) (targets ++ fdePhase1Targets)
253+
);
164254
x86_64-linux =
165-
builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) crossTargets)
255+
builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) flashableCrossTargets)
166256
// builtins.listToAttrs (
167257
map (
168258
t: lib.nameValuePair "${t.name}-flash-script" t.hostConfiguration.pkgs.nvidia-jetpack.flashScript
169-
) crossTargets
259+
) flashableCrossTargets
170260
)
171261
// builtins.listToAttrs (
172262
map (
@@ -175,7 +265,7 @@ in
175265
(t.hostConfiguration.extendModules {
176266
modules = [ { ghaf.hardware.nvidia.orin.flashScriptOverrides.onlyQSPI = true; } ];
177267
}).pkgs.nvidia-jetpack.flashScript
178-
) crossTargets
268+
) flashableCrossTargets
179269
);
180270
};
181271
};

0 commit comments

Comments
 (0)