Flash Custom OS on Jetson nano eMMC from Ubuntu Host

Hi there, Is there any way we can flash the custom OS on Jetson Nano eMMC from a Ubuntu Machine?

Please use the flash.sh helper script, see NVIDIA Jetson Linux Driver Package Software Features : Flashing and Booting the Target Device | NVIDIA Docs

Where can I find the flash.sh script?

I found this issue and it says

"This file should be in this folder (for AGX Xavier - JetPack 4.5.1):

The other device and different JetPack’s located at:

But in my case, the host is not an Nvidia device and I am not using Jetpack. It’s Ubuntu 18.04.


This folder is on x86 host. Not jetson. After you install sdkmanager once, sdkmanger will install this folder to your host.

The tool inside this folder can flash your custom OS.

Hope this clarify your problem.

Some added information you might find useful: JetPack/SDK Manager is just a front end to the actual flashing software and flash content. Most of the content in the “Linux_for_Tegra/” subdirectory is from the “driver package”, which can be downloaded separately from the particular L4T release page. In recovery mode the Jetson becomes a custom USB device which is only understood by a custom driver…the “driver package”. The second step of manual install of flash software is to unpack the sample rootfs (which is just pure Ubuntu 18.04) into the “Linux_for_Tegra/rootfs/” subdirectory using sudo and making sure the filesystem type is ext4 on the host PC. The last step is to add the NVIDIA drivers with “sudo ./apply_binaries.sh” (from the “Linux_for_Tegra/” subdirectory). All of this is done automatically if you’ve used JetPack/SDKM.

During actual flash some content is written to boot-related items in “rootfs/”, but otherwise this is a verbatim copy of what gets flashed to the rootfs. Other partitions for eMMC models (or content in QSPI memory for SD card dev models) are pretty much all binary content which is not part of the running system, but which is needed for either the equivalent of BIOS function plus boot content.

By default boot software looks for the “/boot/extlinux/extlinux.conf” file for configuration (“Linux_for_Tegra/rootfs/boot/extlinux/extlinux.conf” on the host PC), but depending on case, might have fallbacks. This normally gets added or updated in “rootfs/” during flash based on the target hardware. For example, you might have different content if the target is jetson-nano-qspi-sd versus jetson-nano-emmc. This is what is edited immediately before the image to flash is generated, and then the “Linux_for_Tegra/bootloader/system.img” is created based on this for the exact rootfs partition content.

That target refers to a configuration file. If at “Linux_for_Tegra/” you run the command “ls -l *.conf”, then you will find files named after module and carrier board specifications, plus symbolic links with the common name as an alias to some of the specific conf files. The flash target is just one of those conf files after removing the “.conf” suffix. You could create your own target with a copy of one for your particular board, and then editing.

Btw, I see mention of Xavier, but this is the Nano forum, so I’m going to assume Nano.

You could log a command line flash of normal L4T (Linux for Tegra is what gets flashed), and use the log to find out exactly which content is used. There is a general theme of copying a reference file to the staging area, and then either flashing the reference (if it is a partition image) to a staging area before either directly flashing the content or copying that content into “rootfs/” (the kernel Image and device tree and extlinux.conf would be typical of copy content). Then, using your specification, substitute different content, e.g., kernel Image (I assume Linux since other o/s would be rather difficult to work with).

Note that if you were to clone an eMMC model’s rootfs, and replace any existing “bootloader/system.img” with your rootfs binary partition content, and then flash with the “-r” option to “flash.sh”, then it would simply use that rootfs and not even edit or work with “rootfs/” content (this content is for generating a default partition, but if you reuse an existing partition, then there is no need to generate the partition a second time).

On Xavier and before raw partition sizes must be an exact multiple of MiB (10241024 bytes) or GiB (102410241024 bytes). If that partition is not the exact size of the default partition size (which you can probably find going through the chain of .conf files or .xml files), then you must specify. For example, this flashes a partition using a preexisting system.img of size of 30064771072 bytes (10241024102428 is GiB):
sudo ./flash.sh -r -S 28GiB jetson-nano-emmc
(and if you want to log that: “sudo ./flash.sh -r -S 28GiB jetson-nano-emmc 2>&1 | tee log_flash.txt”)

Note that when flash.sh generates a partition it will generate both “bootloader/system.img” (a sparse file) and “bootloader/system.img.raw” (a raw file). The raw file or the sparse file will work if named “system.img”, but the raw file is full partition size (28 GiB in the example case), while the sparse file is smaller (usually about 3 GB for default files…as the partition fills up the sparse file approaches the size of the raw file). The raw file can be loopback mounted and edited or examined, but the sparse file is good only for flashing. One can use NULL filler bytes along with the mksparse utility to create a sparse file from a raw file, but this is only useful if you plan to save flash time at the expense of time to create the sparse file.

If you create your own exact partition, and populate it with your content, and put this on an ext4-formatted raw loopback file (or appropriately edit “Linux_for_Tegra/rootfs/”, being careful of what gets edited with the flash target), then what gets installed for rootfs is what you’ve created. Note that you could create your own directory and use the “-R <rootfs dir>” option to specify an alternate “rootfs/” subdirectory and leave the original alone.

If you wanted to manually create a loopback mountable ext4 file which is 30064771072 bytes in size (28 GiB), then you could do this:

# Be VERY CAREFUL that your host PC has enough disk space. For example, "`df -H .`".

# Create an empty file; "bs" (block size) is normally just to increase performance, it has no effect.
# However, we are going to name a multiple of blocks for size, thus it can change how big the
# loopback file gets. Using a convenient block size of 1024*1024 == 1048576 bytes, so we need
# 28672 copies of 1 MiB blocks to get 28 GiB...implies flash with "-S 28GiB":
dd if=/dev/null of=custom.img bs=1048576
# Cover this with a loop device:
sudo losetup --find --show ./custom.img
# Note that the losetup will name the loop device. For example, it might be "/dev/loop0";
# adjust for your case. There are a limited number of loopback devices, so don't randomly
# generate these unless you are going to delete them.
# Now format this, assuming "/dev/loop0":
sudo mkfs.ext4 /dev/loop0
# Now you can detach the loop device:
sudo losetup -d /dev/loop0
# To automatically deal with loopback and mounting use the "-o loop" option to "mount":
sudo mount -o loop custom.img /mnt
# Now you can examine or edit that content:
df -H -T /mnt
ls /mnt/
# You're free to edit content, e.g., add your custom operating system to the mount point.
# Then unmount with:
sudo umount /mnt
# Should you want to flash this enormous file (very very slow to copy or move), then you
# can do this if in "Linux_for_Tegra/" (sudo is sometimes required):
sudo cp custom.img ./bootloader/system.img
# Make sure the Jetson is connected and in recovery mode. Then:
sudo ./flash.sh -r -S 28GiB jetson-nano-emmc

As far as copying content into the loopback mounted custom.img goes you can use anything which preserves permissions and user/group IDs. You could unpack a tar file there. You could also use rsync to copy an existing Linux system, or a subdirectory of a Linux system which contains the files (be certain your rsync preserves numeric user and group IDs, and does not descend into pseudo filesystems). For example, you could make this an exact duplicate of the “rootfs/” with rsync. If you are interested in rsync commands just ask.

Note that when you develop on a Jetson it is a good idea to have a backup. Much of this applies to backups as well. You could start with a raw clone (delete the sparse clone). Then you could on occasion use rsync to update the loopback mounted clone.

Note: On Orins I think the size multiple of a partition has to be a multiple of 4096 instead of 1024 (MiB and GiB are multiples of 1024).

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