Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/src/content/docs/ghaf/overview/arch/secureboot.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,27 @@ efi-readvar -v PK
efi-readvar -v KEK
efi-readvar -v db
```

## Jetson Orin signed flashing workflow

Jetson Orin targets produce two independent build artifacts in CI:

1. The flash script (`nix build .#nvidia-jetson-orin-agx-debug-from-x86_64-flash-script`) which orchestrates NVIDIA's flashing tools.
2. The filesystem image (`nix build .#nvidia-jetson-orin-agx-debug-from-x86_64`) that contains the ESP and root partitions.

After the filesystem image is signed, pass its Nix store path directly to the flash helper:

```sh
SIGNED_SD_IMAGE=$(nix path-info .#nvidia-jetson-orin-agx-debug-from-x86_64)
./result/bin/flash-ghaf-host -s "$SIGNED_SD_IMAGE"
```

The `-s/--signed-sd-image` flag extracts `BOOTAA64.EFI` and the kernel from the signed image, wires them into the flashing workdir, and launches NVIDIA's flashing script without requiring any additional staging directories or host key material.

The argument to `-s/--signed-sd-image` must be the signed image output directory (for example, the value returned by `nix path-info`), not the `*.img.zst` file itself. The directory is expected to contain `esp.offset`, `esp.size`, `root.offset`, and `root.size`, plus either `sd-image/*.img.zst` or `*.img.zst` at the top level. Passing the image file directly will fail with `Signed image directory not found: ...`.

Running `./result/bin/flash-ghaf-host` without `-s` clears signed-image overrides and flashes the standard unsigned image bundled with the build output.

For ad-hoc debugging you can also point `-s` to a copy of the signed build outside the Nix store (for example, `cp -a $(nix path-info …) /tmp/orin-signed` and then pass `/tmp/orin-signed`), which is useful when the original store path is unavailable on the flashing host.

For CI jobs that still need a deterministic artifact directory (for example when reusing the same signed files across multiple runs), use the extractor helper provided by `jetpack-nixos` and point `ghaf.hardware.nvidia.orin.flashScriptOverrides.signedArtifactsPath` at the staged directory.
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
# Nvidia Orin support for NixOS
jetpack-nixos = {
#url = "github:anduril/jetpack-nixos";
url = "github:tiiuae/jetpack-nixos/march-rebase";
url = "github:vadika/jetpack-nixos/refactor/flash-signed-sd-image";
inputs.nixpkgs.follows = "nixpkgs";
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
imports = [
./partition-template.nix
./jetson-orin.nix
./secureboot.nix
./pci-passthrough-common.nix
./virtualization
./optee/optee.nix
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,20 @@ in
default = "";
};

flashScriptOverrides.signedArtifactsPath = mkOption {
description = ''
Absolute path on the host that contains pre-signed Jetson Orin boot
artifacts.

The flash script expects at least `BOOTAA64.EFI` and `Image` to be
present in this directory. Optional files such as `initrd` or device
trees can be staged as well. The directory can also be provided at
runtime through the `SIGNED_ARTIFACTS_DIR` environment variable.
'';
type = types.nullOr types.str;
default = null;
};

somType = mkOption {
description = "SoM config Type (NX|AGX32|AGX64|Nano)";
type = types.str;
Expand Down Expand Up @@ -300,6 +314,7 @@ in
};

config = mkIf cfg.enable {
ghaf.hardware.nvidia.orin.secureboot.enable = lib.mkDefault true;
hardware.nvidia-jetpack.firmware.eksFile = "${firmwareEkbImage}/eks_t234.img";
hardware.nvidia-jetpack.kernel.version = "${cfg.kernelVersion}";
nixpkgs.hostPlatform.system = "aarch64-linux";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,79 @@ let
mkdir -pv "$WORKDIR/bootloader"
rm -fv "$WORKDIR/bootloader/esp.img"

${lib.optionalString (cfg.flashScriptOverrides.signedArtifactsPath != null) ''
if [ -z "''${SIGNED_ARTIFACTS_DIR:-}" ]; then
SIGNED_ARTIFACTS_DIR=${lib.escapeShellArg cfg.flashScriptOverrides.signedArtifactsPath}
fi
''}

if [ -n "''${SIGNED_ARTIFACTS_DIR:-}" ]; then
echo "Using signed artifacts from $SIGNED_ARTIFACTS_DIR"

for artifact in BOOTAA64.EFI Image; do
if [ ! -f "$SIGNED_ARTIFACTS_DIR/$artifact" ]; then
echo "ERROR: Missing $artifact in $SIGNED_ARTIFACTS_DIR" >&2
exit 1
fi
done

export BOOTAA64_EFI="$SIGNED_ARTIFACTS_DIR/BOOTAA64.EFI"
export KERNEL_IMAGE="$SIGNED_ARTIFACTS_DIR/Image"

if [ -f "$SIGNED_ARTIFACTS_DIR/initrd" ]; then
export INITRD_IMAGE="$SIGNED_ARTIFACTS_DIR/initrd"
fi

if [ -f "$SIGNED_ARTIFACTS_DIR/dtb" ]; then
export DTB_IMAGE="$SIGNED_ARTIFACTS_DIR/dtb"
fi
fi

if [ -n "''${BOOTAA64_EFI:-}" ]; then
if [ ! -f "$BOOTAA64_EFI" ]; then
echo "ERROR: BOOTAA64_EFI not found: $BOOTAA64_EFI" >&2
exit 1
fi
echo "Using external BOOTAA64.EFI: $BOOTAA64_EFI"
cp -f "$BOOTAA64_EFI" "$WORKDIR/bootloader/BOOTAA64.efi"
fi

if [ -n "''${KERNEL_IMAGE:-}" ]; then
if [ ! -f "$KERNEL_IMAGE" ]; then
echo "ERROR: KERNEL_IMAGE not found: $KERNEL_IMAGE" >&2
exit 1
fi
echo "Using external kernel Image: $KERNEL_IMAGE"
mkdir -pv "$WORKDIR/kernel"
cp -f "$KERNEL_IMAGE" "$WORKDIR/kernel/Image"
fi

${lib.optionalString (!cfg.flashScriptOverrides.onlyQSPI) ''
image_source_root=${lib.escapeShellArg (toString images)}

if [ -n "''${SIGNED_SD_IMAGE_DIR:-}" ]; then
if [ ! -d "$SIGNED_SD_IMAGE_DIR" ]; then
echo "ERROR: SIGNED_SD_IMAGE_DIR not found: $SIGNED_SD_IMAGE_DIR" >&2
exit 1
fi
image_source_root="$SIGNED_SD_IMAGE_DIR"
fi

# Read partition offsets and sizes from sdImage metadata
ESP_OFFSET=$(cat "${images}/esp.offset")
ESP_SIZE=$(cat "${images}/esp.size")
ROOT_OFFSET=$(cat "${images}/root.offset")
ROOT_SIZE=$(cat "${images}/root.size")
ESP_OFFSET=$(cat "$image_source_root/esp.offset")
ESP_SIZE=$(cat "$image_source_root/esp.size")
ROOT_OFFSET=$(cat "$image_source_root/root.offset")
ROOT_SIZE=$(cat "$image_source_root/root.size")

img=$(find "$image_source_root" -maxdepth 1 -name '*.img.zst' -print -quit)
if [ -z "$img" ]; then
img=$(find "$image_source_root/sd-image" -maxdepth 1 -name '*.img.zst' -print -quit 2>/dev/null || true)
fi
if [ -z "$img" ]; then
echo "ERROR: No .img.zst found in $image_source_root/sd-image or $image_source_root" >&2
exit 1
fi

img="${images}/sd-image/${config.image.fileName}"
echo "Source image: $img"
echo "ESP: offset=$ESP_OFFSET sectors, size=$ESP_SIZE sectors"
echo "Root: offset=$ROOT_OFFSET sectors, size=$ROOT_SIZE sectors"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# SPDX-FileCopyrightText: 2022-2026 TII (SSRC) and the Ghaf contributors
# SPDX-License-Identifier: Apache-2.0

{
config,
lib,
pkgs,
...
}:
let
cfg = config.ghaf.hardware.nvidia.orin.secureboot;

eslFromCert =
name: cert:
pkgs.runCommand name
{
nativeBuildInputs = [ pkgs.buildPackages.efitools ];
certPath = cert;
}
''
if [ ! -s "$certPath" ]; then
echo "Missing or empty UEFI secure boot certificate: $certPath" >&2
exit 1
fi

${pkgs.buildPackages.efitools}/bin/cert-to-efi-sig-list "$certPath" "$out"

if [ "$(wc -c < "$out")" -le 44 ]; then
echo "Generated ESL ${name} from $certPath is empty" >&2
exit 1
fi
'';

keysDir = cfg.keysSource;

requiredCertFiles = [
(keysDir + "/PK.crt")
(keysDir + "/KEK.crt")
(keysDir + "/db.crt")
];

pkEsl = eslFromCert "PK.esl" (keysDir + "/PK.crt");
kekEsl = eslFromCert "KEK.esl" (keysDir + "/KEK.crt");
dbEsl = eslFromCert "db.esl" (keysDir + "/db.crt");
in
{
options.ghaf.hardware.nvidia.orin.secureboot = {
enable = lib.mkEnableOption "UEFI Secure Boot key enrollment for Jetson Orin";

keysSource = lib.mkOption {
type = lib.types.path;
default = ../../../../secureboot/keys;
description = "Directory containing PK.crt, KEK.crt and db.crt used to generate ESLs.";
};
};

config = lib.mkIf cfg.enable {
assertions = map (certFile: {
assertion = builtins.pathExists certFile;
message = "Missing UEFI secure boot certificate `${toString certFile}`. Set `ghaf.hardware.nvidia.orin.secureboot.keysSource` to a directory containing `PK.crt`, `KEK.crt`, and `db.crt`.";
}) requiredCertFiles;

hardware.nvidia-jetpack.firmware.uefi.secureBoot = {
enrollDefaultKeys = true;
defaultPkEslFile = pkEsl;
defaultKekEslFile = kekEsl;
defaultDbEslFile = dbEsl;
};
};
}
20 changes: 20 additions & 0 deletions modules/secureboot/keys/PK.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDNjCCAh6gAwIBAgIUCVyaeK6BQFa6zWAXk9L3nVY85SgwDQYJKoZIhvcNAQEL
BQAwMzELMAkGA1UEBhMCRkkxDTALBgNVBAoMBEdoYWYxFTATBgNVBAMMDEdoYWYg
VUVGSSBQSzAeFw0yNjAyMTYxMDI4MTZaFw0zNjAyMTQxMDI4MTZaMDMxCzAJBgNV
BAYTAkZJMQ0wCwYDVQQKDARHaGFmMRUwEwYDVQQDDAxHaGFmIFVFRkkgUEswggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDC9o1um1Mc/5/beeDITHdUTiPV
BhAf8hTVEonNBqhrvn6iyZJzrKreYjsan4/7KON25Im+5iNpb3HpuNrOjQr0i6G8
tzNUq0p/FMRJrHObMbI5m6DtgqL0YgtuSdaPFADyvVPc49hbReGJXmmbwpScVDXJ
A6j1uDAymftLLSv7k582fbTvuO57Vq1781A+TrUuCtmmQUH+taFpQmqvPcq/pjoL
8epVy4qhlhPcoobx/0GPyWjyGgOHq79qTkUiF7WOd3BnlWKMXTwuJ6dmZJ7Lg6YL
wOTWdjBvWNaZEfl1K3RTIb6Jd4dqBJyrRWJGXAWlk1YEG0lPa8ioIFf1LjhlAgMB
AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQW
BBTYB3z9cFazEXgEAqbh5aNfsYJWqDANBgkqhkiG9w0BAQsFAAOCAQEAwi8N1fBU
O90XVTqYY/JE6fYFY3monLNYzCOZf9aOF9g6o2HAESdUvRmygqfUGoyEBJyRVvnK
RYJJzCr6CX4lMFl091K8sqa5gXjm+Jub9y7OwxEpu1/QVS2iI5qJM2gjenVMBruv
sC5WBPnVg1jyYXBMxtKa/uQ/fny022Pcaxqxlw7PWkljLZVAT0IJ/PRh2+Soelwt
xPv9cm+n2kbxv69IZayS6iQWAWcXWToupEkrWGx8s8uNQHQXofAq0PNQEhc88kjP
mnAAO0BkEvjRrvGFQC1y3LG6iLD6EKa1afXnbk4NdH2lzavA+XVxoyihFmk8QN/P
Rx3srFWDfvhStA==
-----END CERTIFICATE-----
4 changes: 3 additions & 1 deletion modules/secureboot/keys/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
Generated from `tiiuae/ghaf-infra-pki` `yubi-uefi` artifacts at commit `e1a50f184b8ca7bba34f61f296b38ec870aaf21d`.
Generated from `tiiuae/ghaf-infra-pki` `yubi-uefi` artifacts at commit `56d288c3a65945420086c48449f7eb0331eb1b37`.

Imported files:
- `PK.auth` sha256 `a7dd8d62169ce4a259296cd1e9c619f76310ae5fe56811311cb89437b1335215`
- `KEK.auth` sha256 `6fad4e13e9d71794225dbe9137038d6e153f23a5714cf39b71395c5bee08d713`
- `db.auth` sha256 `f1bb5578afff87b0c45fe27184cb27b77a65b8ac9cee0ac8e713719cab36509f`
- `PK.crt` sha256 `0708ef65cd8b7b03782d047971a65e32f3f3f848594a4ed5e425f80c20305a43`
- `KEK.crt` sha256 `0cdf80e3b8c67a137909b62b0897681c7f37fe94dd8788ad5aa8d0e16966b849`
- `db.crt` sha256 `bc2adf26326de4097d9985351be416197423db4c829152e94d21e13afc579c00`

Certificate fingerprints:
- `PK.crt` sha256 `68:FB:46:A5:2B:03:9A:7D:37:B9:7B:C3:94:13:CD:57:F8:A5:05:FF:2F:84:D2:F8:4A:73:C0:58:98:BC:59:2F`
- `KEK.crt` sha256 `E3:C7:43:B3:06:9A:2B:4B:4B:5F:88:99:BB:C7:CE:9F:84:DF:94:06:E1:28:CD:DA:06:7A:FD:81:EF:34:24:15`
- `db.crt` sha256 `B4:CB:12:37:1F:EF:29:A3:F4:2A:48:80:1C:24:E6:51:49:C5:81:06:0C:F9:42:18:9A:FA:A2:45:FD:6B:C9:3F`
3 changes: 1 addition & 2 deletions targets/nvidia-jetson-orin/flake-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,7 @@ in
builtins.listToAttrs (map (t: lib.nameValuePair t.name t.package) crossTargets)
// builtins.listToAttrs (
map (
t:
lib.nameValuePair "${t.name}-flash-script" t.hostConfiguration.pkgs.nvidia-jetpack.legacyFlashScript
t: lib.nameValuePair "${t.name}-flash-script" t.hostConfiguration.pkgs.nvidia-jetpack.flashScript
) crossTargets
)
// builtins.listToAttrs (
Expand Down
Loading