Xavier NX Custom Kernel / Device Tree Deployment


I’ve been customising the kernel and device tree supplied with Jetson Linux 35.1 for use with a Xavier NX.

With the help of the documentation this has gone well. I was able to test my updated kernel and device tree by replacing the /boot/Image and /boot/dtb/kernel_tegra194-p3668-0001-p3509-0000.dtb with my new files.

I’m now looking for the proper way of providing these to my customer for testing. So far I’ve provided them with a .tar.gz which simply replaces the above two files with my test ones which feels like a rather dirty way of providing an updated kernel. Especially when it appears identical to the stock kernel.

I discovered the notes on the nvdebrepack.sh tool and repackaged the kernel, dts, headers and display additions, with my new kernel, device tree and appropriate changelogs.

I was hoping to replace the Debian packages in the “Linux_for_Tegra/kernel”, allowing a fresh unit to be flashed with the updated kernel and device tree.

I tried this but the kernel appears unchanged, with the stock kernel booting after flashing. I suspect this is due to there being at least two further copies of the kernel in the Linux_for_Tegra package, with Image and Image.gz appearing in the kernel directory alongside the debian packages. Along with a dts directory full of device tree components.

Next I thought I may be able to flash a stock release and install my customised kernel debian packages on the Xavier NX directly, however the packages fail to install with an error related to the kernel’s post-install script.

What would be the proper way of providing a set of kernel binaries which could be either flashed into a device using the normal sudo ./flash.sh jetson-xavier-nx-devkit-emmc mmcblk0p1 arrangement?

Or installed into the device after, with some traceability. i.e a means of telling the updated kernel has been installed (for example via Debian package version changing)?



FYI, for command line flashing, you can log this via:

./flash.sh jetson-xavier-nx-devkit-emmc mmcblk0p1 2>&1 | tee log_flash.txt

That log will tell you exactly where files and partitions have their source for most things. For example, you could search (case-sensitive) for “Image” in the log. Partitions are also mentioned and the binary image the source is from (and the signing step; default is that the signing is a NULL key, but it is still signed).

If the kernel and device tree are mentioned in the supplied extlinux.conf (search for that in the flash command log), then this is where kernel and device tree are loaded from. If not there, then they come from the signed partition. If this is a model with security fuses, then only the signed partition can be used, regardless of whether or not extlinux.conf mentions them.

Any time you flash a target that target name will have a related “.conf” file. For example, “jetson-xavier-nx-devkit-emmc” refers to file “jetson-xavier-nx-devkit-emmc.conf”. That file is a human-readable script. It may include other similar files which are also human-readable scripts. Consider making a custom target, including a copy of the original(s), but renamed. If you find you want to change a source in the log, then you could change it in the .conf.

Something you might want to understand which goes a bit beyond this is how the partition image for rootfs is generated…

The JetPack/SDKM software is just a GUI frontend to the actual flash software. The software being flashed is L4t (a sample rootfs which is purely Ubuntu, plus NVIDIA drivers). That software actually performing the flash is the “driver package” (you can find it via either the JetPack or L4T downloads; the two are tied in their release versions). If you download and unpack the driver package separately (which is one of the conveniences from the JetPack/SDKM software), then unpacking the driver package is what creates the “Linux_for_Tegra/” directory, along with the flash.sh script. Note that the “rootfs/” subdirectory would be empty.

For a manual install of the flash content on the host PC, after unpacking the driver package as a regular user, you would then unpack the “sample rootfs” using sudo into the “rootfs/” subdirectory. This is pure Ubuntu.

To turn the “rootfs/” into L4T you would then run “sudo ./apply_binaries.sh” from the “Linux_for_Tegra/” directory. This installs the NVIDIA content to the “rootfs/”. Any packages being installed use the QEMU emulator since you are executing arm64/aarch64 packages to a non-desktop architecture. Note that this is only performed once, and from then on, every flash simply uses that content when creating the rootfs partition flash image (“mostly”).

If you were to log the apply_binaries.sh step, then you could in theory modify what creates the partition image. I’ll get back to that, but it is important to note that when the partition image is created (the “APP” partition label), that the target name (jetson-xavier-nx-devkit-emmc in your case) can modify the boot content, e.g., content in “rootfs/boot/” and the device tree content. So whatever is added in “apply_binaries.sh” might not be the end of the story when creating an image (at least not if you consider “/boot”…other content will be verbatim).

Back to apply_binaries.sh. If you want the source of generating images to include a substitute kernel via a .deb package, then this is where you would add it should you want your original image creation mechanism to add this. Should you want to modify the Image file, or device tree, consider changing the .conf files (incidentally, the sub-files included would be named after the carrier board or module). Should you want to provide alternative boot entries which can be reached with serial console, then you want to modify the extlinux.conf (which is probably something you would modify in the .conf files).

If you want testing to be low risk, then your customer must have a serial console set up, and understand how to interrupt boot to get to the section where you can pick among multiple extlinux.conf boot entries. You would leave the original (or known working Image) in place, and add a second entry which names the test. If desired, and you think it will work, you could make your test entry the first entry and set it to default. However, if something went wrong, then manually interrupting boot at the kernel picking step would allow continuing. It is easy to swap entries, either manually, or to create a script (or to simply provide both extlinux.conf and once booted have him copy the other in place).

If you are actually booted into Ubuntu, then you can find the package owning a particular file if you are searching for what is already there. For example:

# Find which "bash" is in the search path:
which bash
# Find the package owning it:
dpkg -S /bin/bash

Some notes:

  • Make sure your updated Image file has a slightly different name, e.g., Image-custom.
  • Similar, make sure your device tree has a new name, e.g., Original-modified.dtb.
  • Use those modified names in extlinux.conf, and leave the originals in place unless you are certain they can be removed. Keep an original setup entry for serial console backup access.
  • Your modified kernel, if it modifies an entry with “=y” (versus “=m”), should have a new “uname -r” version and all new modules installed. If you’ve only installed a new module, then it is ok to simply reuse the old “uname -r” and reuse the original kernel modules. Remember the CONFIG_LOCALVERSION by default is “-tegra”, and this is what determines the suffix of “uname -r”.
  • Don’t forget you might need to (at times) also flash partitions. It isn’t a bad practice to test your modifications with extlinux.conf missing the “FDT” (device tree) and “LINUX” (kernel) entries. If you want to save a copy of the signed version of those partitions, then you might need to run the “sudo ./flash.sh --no-flash ...” command to keep the signed partitions after the “just pretend to flash” command. The signed partitions, if valid on that module, can in fact be put in place with the dd command on eMMC models (SD card models, dev kits, use QSPI memory for this instead of eMMC partitions).
  • If you examine the contents of “/proc/config.gz” after boot, then you could test if feature changes are showing up for the “=y” kernel features.
  • If you change the “uname -r” via the kernel config CONFIG_LOCALVERSION, then this too can be a good proof that you have the new kernel in place. Just remember that if the “uname -r” changes (such as from CONFIG_LOCALVERSION changing), that you must also rebuild and install all of the kernel modules.
  • For the most part you can check your device tree via the content in “/proc/device-tree/”. If you want to create a “.dts” file:
    dtc -I fs -O dts -o extracted.dts /proc/device-tree
    (however, it is possible for early boot stages to actually edit some parts of the device tree before passing it on to the Linux kernel; absolute match to what you provided is not a guarantee, but mostly this will check whatever it is you wanted to modify)
  • Most device tree entries can be treated as arguments passed to drivers at the time of driver load. The “compatible” part is how drivers know they should look at the tree entry. I will only recommend this if other methods are insufficient for verification, but you might find you can create a “dummy” entry not compatible with any driver and not of a format any driver would currently look at, and search for the dummy entry for verification.
  • The arguments passed to the kernel at boot can be viewed with “cat /proc/cmdline”. Part of this is from the “extlinux.conf” “APPEND” key/value parameter, but note that “${cbootargs}” is inherited as part of the environment, and that this in turn is created via the device tree’s “chosen->bootargs” entry. This too will be a set of arguments passed to drivers, and if the argument is not such that a driver is interested in the format of the entry, then the entry will be ignored. One could add an argument to kernel command line via either APPEND or “chosen->bootargs” and see if it shows up in “/proc/cmdline” to know if device tree or extlinux.conf comes from what you expect (you could use a different “fake/dummy” argument in APPEND and bootargs).
  • Note that if you have multiple optional boot entries in extlinux.conf, then although you could add custom APPEND arguments to distinguish them, that the “MENU LABEL” would be sufficient should you just want to know which is which at serial console boot interrupt.
  • I’m not certain of all of the differences in boot interrupt for the UEFI releases compared to the CBoot releases. JetPack 5.x+ is all UEFI (L4T R34.x+). JetPack 4.x and earlier is all CBoot (L4T R32.x and older). You can find the L4T release version with “head -n 1 /etc/nv_tegra_release”). The above will cover anything in R32.x or older, and most of R34.x+, but there might be some differences in the boot menu part and serial console for these newer releases compared to serial console of older releases.
  • Note that .deb packages are a standard mechanism of Ubuntu. A standard mechanism, such as this will hold a release from update:
    sudo apt-mark hold <package name>
    (this Google search shows a lot on the topic)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.