Skip to content

Commit

Permalink
Don't require using systemd-boot to get automated firmware updates
Browse files Browse the repository at this point in the history
Using systemd-boot is not a requirement for applying capsule updates. We
can make the firmware update process a systemd service, which gives us
the behavior of running on every `switch-to-configuration` when the
capsule update file changes. We do not apply the firmware update if we
notice that the currently active nixos system (represented by
/run/current-system) is not the same as the system profile that is
persisted to /nix/var/nix/profiles/system. This allows us to retain the
behavior from before this change where a user running `nixos-rebuild
test` or similar will not have a capsule update applied.
  • Loading branch information
jmbaur committed Apr 23, 2024
1 parent 0739781 commit bb03752
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 36 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Weston and sway have been tested working on Orin devices, but do not work on Xav
### Updating firmware from device
Recent versions of Jetpack (>=5.1) support updating the device firmware from the device using the UEFI Capsule update mechanism.
This can be done as a more convenient alternative to physically attaching to the device and re-running the flash script.
These updates can be performed automatically after a `nixos-rebuild boot` if the `hardware.nvidia-jetpack.bootloader.autoUpdate` setting is set to true and systemd-boot is used.
These updates can be performed automatically after a `nixos-rebuild boot` if the `hardware.nvidia-jetpack.bootloader.autoUpdate` setting is set to true.
Otherwise, the instructions to apply the update manually are below.

To determine if the currently running firmware matches the software, run, `ota-check-firmware`:
Expand Down
95 changes: 60 additions & 35 deletions modules/flash-script.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{ config, pkgs, lib, ... }:
{ config, pkgs, lib, utils, ... }:

# Convenience package that allows you to set options for the flash script using the NixOS module system.
# You could do the overrides yourself if you'd prefer.
Expand Down Expand Up @@ -403,42 +403,67 @@ in
}.${cfg.som}
)) else lib.mkOptionDefault [ ];

systemd.services = lib.mkIf (cfg.flashScriptOverrides.targetBoard != null) {
setup-jetson-efi-variables = {
enable = true;
description = "Setup Jetson OTA UEFI variables";
wantedBy = [ "multi-user.target" ];
after = [ "opt-nvidia-esp.mount" ];
serviceConfig.Type = "oneshot";
serviceConfig.ExecStart = "${pkgs.nvidia-jetpack.otaUtils}/bin/ota-setup-efivars ${cfg.flashScriptOverrides.targetBoard}";
};
systemd.services.setup-jetson-efi-variables = lib.mkIf (cfg.flashScriptOverrides.targetBoard != null) {
description = "Setup Jetson OTA UEFI variables";
wantedBy = [ "multi-user.target" ];
after = [ "opt-nvidia-esp.mount" ];
serviceConfig.Type = "oneshot";
serviceConfig.ExecStart = "${pkgs.nvidia-jetpack.otaUtils}/bin/ota-setup-efivars ${cfg.flashScriptOverrides.targetBoard}";
};

boot.loader.systemd-boot.extraInstallCommands = lib.mkIf (cfg.firmware.autoUpdate && cfg.som != null && cfg.flashScriptOverrides.targetBoard != null) ''
# Jetpack 5.0 didn't expose this DMI variable,
if [[ ! -f /sys/devices/virtual/dmi/id/bios_version ]]; then
echo "Unable to determine current Jetson firmware version."
echo "You should reflash the firmware with the new version to ensure compatibility"
else
CUR_VER=$(cat /sys/devices/virtual/dmi/id/bios_version)
NEW_VER=${pkgs.nvidia-jetpack.l4tVersion}
if [[ "$CUR_VER" != "$NEW_VER" ]]; then
echo "Current Jetson firmware version is: $CUR_VER"
echo "New Jetson firmware version is: $NEW_VER"
echo
# Set efi vars here as well as in systemd service, in case we're
# upgrading from an older nixos generation that doesn't have the
# systemd service. Plus, this ota-setup-efivars will be from the
# generation we're switching to, which can contain additional
# fixes/improvements.
${pkgs.nvidia-jetpack.otaUtils}/bin/ota-setup-efivars ${cfg.flashScriptOverrides.targetBoard}
${pkgs.nvidia-jetpack.otaUtils}/bin/ota-apply-capsule-update ${config.system.build.jetsonDevicePkgs.uefiCapsuleUpdate}
fi
fi
'';
systemd.services.firmware-update = lib.mkIf (cfg.firmware.autoUpdate && cfg.som != null && cfg.flashScriptOverrides.targetBoard != null) {
wantedBy = [ "multi-user.target" ];
path = [ pkgs.nvidia-jetpack.otaUtils ];
after = [
"${utils.escapeSystemdPath config.boot.loader.efi.efiSysMountPoint}.mount"
"opt-nvidia-esp.mount"
];
unitConfig = {
ConditionPathExists = "/sys/devices/virtual/dmi/id/bios_version";
# This directory is populated by ota-apply-capsule-update, don't run
# if we already have a capsule update present on the ESP.
ConditionDirectoryNotEmpty = "!${config.boot.loader.efi.efiSysMountPoint}/EFI/UpdateCapsule";
};
script =
# NOTE: Our intention is to not apply any capsule update if the
# user's intention is to "test" a new nixos config without having it
# persist across reboots. "nixos-rebuild test" does not append a new
# generation to /nix/var/nix/profiles for the system profile, so we
# can compare that symlink to /run/current-system to see if our
# current active config has been persisted as a generation. Note that
# this check _may_ break down if not using nixos-rebuild and using
# switch-to-configuration directly, however it is well-documented
# that a user would need to self-manage their system profile's
# generations if switching a system in that manner.
lib.optionalString config.system.switch.enable ''
if [[ -f /nix/var/nix/profiles/system ]]; then
latest_generation=$(readlink -f /nix/var/nix/profiles/system)
current_system=$(readlink -f /run/current-system)
if [[ $latest_generation == /nix/store* ]] && [[ $latest_generation != "$current_system" ]]; then
echo "Skipping capsule update, current active system not persisted to /nix/var/nix/profiles/system"
exit 0
fi
fi
'' + ''
CUR_VER=$(cat /sys/devices/virtual/dmi/id/bios_version)
NEW_VER=${pkgs.nvidia-jetpack.l4tVersion}
if [[ "$CUR_VER" != "$NEW_VER" ]]; then
echo "Current Jetson firmware version is: $CUR_VER"
echo "New Jetson firmware version is: $NEW_VER"
echo
# Set efi vars here as well as in systemd service, in case we're
# upgrading from an older nixos generation that doesn't have the
# systemd service. Plus, this ota-setup-efivars will be from the
# generation we're switching to, which can contain additional
# fixes/improvements.
ota-setup-efivars ${cfg.flashScriptOverrides.targetBoard}
ota-apply-capsule-update ${config.system.build.jetsonDevicePkgs.uefiCapsuleUpdate}
fi
'';
};

environment.systemPackages = lib.mkIf (cfg.firmware.autoUpdate && cfg.som != null && cfg.flashScriptOverrides.targetBoard != null) [
(pkgs.writeShellScriptBin "ota-apply-capsule-update-included" ''
Expand Down

0 comments on commit bb03752

Please sign in to comment.