TLDR; You can edit content in “rootfs/
” directly most of the time, and only some “rootfs/boot/
” content is at risk of modification during a flash. Examine “tools/l4t_create_default_user.sh
” to see how QEMU can run Linux commands in that rootfs which will enter the final partition. Do not treat an SD card model with any of this because it isn’t the same (creating SD card content is far simpler/easier, just ask).
You can’t directly partition from the host, at least not in the usual sense (a recovery mode Jetson is not a block device like a USB thumb drive). Are we talking about eMMC models? If so, then an .xml
file can change sizes…but the only one you’d change size of is the rootfs; other partitions (which are only in QSPI if this is an SD card model, which you can’t get to) would only be customized if you’ve changed size requirements of boot content, e.g., you’ve custom built some boot binary and it is now too large for the original partition.
Before I mention any of that, you might find it useful to understand putting that partition onto eMMC (we’re ignoring dev kits with SD card boot because creating an SD card is different from creating boot and rootfs onto eMMC). If this is an SD card model, then you’re greatly complicating your life when something much simpler works.
Any time you flash, a file is generated (on the host PC) at “Linux_for_Tegra/bootloader/
”. The file generated is “Linux_for_Tegra/bootloader/system.img.raw
” (slight inaccuracy for simplification: It is actually system.img
, then this gets moved to system.img.raw
; finally there is a step to create a system.img
from system.img.raw
). This is a 100% exact bit for bit blank file (at least at the start) the size of the rootfs partition. If the rootfs is 28*1024*1024*1024
(28 GiB) bytes, then that is the size the partition must be specified as during flash.
The system.img.raw
is then covered by loopback so commands operating on partitions can treat the file as a partition. That loopback device is formatted as ext4
.
Then, depending on target requirements (such as an Orin kernel being different than an Xavier kernel, or a custom boot sequence differing fromIf one were to flash on command line, and if there is a default one), some boot related content is copied into “Linux_for_Tegra/rootfs/boot/
”. Following this, the entire content of rootfs/
is copied into system.img.raw
. This is the “raw” image and can be used to flash.
A “sparse” image is created from “bootloader/system.img.raw
”, which creates “bootloader/system.img
”. This file, rather than being the size of the partition, might be considered “compressed” (it isn’t really), whereby instead of actually having the blank space it simply marks blank space and doesn’t fill it in. The system.img.raw
and system.img
contain the same information and files, but the sparse image is much smaller (the exception being that as the filesystem fills up, the sparse image grows and approaches the size of the raw image). The only real purpose of this is to save space, although not on the host PC; during flash and creation of rootfs it simply does not require nearly as much time to flash a sparse image compared to a raw image. However, sparse images can’t just be loopback mounted and manipulated, they can more or less only be used for flash. Raw images are much more useful if you don’t care about time to flash. The host PC does not remove the raw image after sparse is created.
So long as there is a system.img
, then that is what gets flashed. If that is the raw or sparse version it won’t matter, the result, other than time to flash, is the same. If we are manually flashing though, and we have a custom partition size which is not the default, then we must name the partition size in the options (or for automated flash, in the correct .xml
file). The .xml
file itself is usually generated from other files depending on the arguments to flash, but examine “bootloader/flash.xml
” to see. Manually flashing is simpler. An example, if our rootfs partition is customized to 16 GiB (16*1024*1024*1024
bytes), and if the target is deIf one were to flash on command line, and if there is fined by “Linux_for_Tegra/jetson-xavier-nx-devkit-emmc.conf
” (target “jetson-xavier-nx-devkit-emmc
”):
# Do something to generate "bootloader/system.img".
# Notice this is the .conf name without .conf suffix:
sudo ./flash.sh -r -S 16GiB jetson-xavier-nx-devkit-emmc mmcblk0p1
Note that mmcblk0p1
is an eMMC target. We can’t equate SD card models with eMMC models so far as rootfs goes. The “-r
” says to "skip building system.img
and use what is there. Even if you didn’t customize, if you flashed before, then this is useful to save time not rebuilding the same image. The “magic” here is the “-S 16GiB
”; this specifies how much space is to be reserved in the partition layout for the system.img
.
There are many ways to build a system.img
. They are like algebra, and have interchangeable quantities. Command line flash will install exactly the right thing if partition sizes are specified correctly. Beware that saving and moving around those images is quite slow since the smallest is probably 2.5GB for a sparse file, and much much larger and slower with a raw file.
You can create a raw and sparse via clone of an eMMC model. You could then flash that over and over. You could loopback mount and examine or modify the raw clone at any time. You can loopback mount and examine the raw generated file as well. By flashing on command line and saying to reuse that file you can be guaranteed your rootfs is an exact copy of that down to the last bit.
Since covering a file with loopback can make it appear to be a block device, you could use an arm64 emulator (e.g., QEMU is already present if you’ve already flashed once) and run commands on it, e.g., a command to add a user name, or update packages. An example to emulate if you want to do this already exists as “Linux_for_Tegra/tools/l4t_create_default_user.sh
”.
What “l4t_create_default_user.sh
” does is to use QEMU and to run commands to create your default user and complete first login directly in “rootfs/
” (which implies any system.img
it creates is also completed). One could use that as an example and complete other commands on the rootfs (though I don’t know about adding networking to the QEMU; I’m unsure if that is present or not, but some commands, e.g., “apt-get
”, might need extra environment setup…you could always work on an example, and if it fails, copy it to the forum and ask about it).
If the content to be edited or changed does not need an arm64
executable, and is just a file copy, then you directly do that. For example, my home directory is already set up, including ssh
, which I did by simple file copies (or rsync
) into the rootfs/
. Various network settings and host alieas are there the instant the flash completes.
Side mention: If you edit “/etc/skel
”, then when you create a new user with defaults, this is where the defaults come from; if you create a lot of users consider poking around there to see how default user home directories are set up.
There are some “gotchas”, whereby if you change the size of one partition it might mean there isn’t room for the other and it’ll happily do the wrong thing. Replacing some content also requires doing so with a signature, but you don’t need to worry about that with a rootfs. Before you start, if you already have a “bootloader/system.img.raw
”, do yourself a favor and record the exact byte size of that before you start; this is what was used last and is likely the default.
Specific use-case examples of what you want to do, combined with the exact model you are working with (third party carrier boards matter greatly, and so does eMMC versus SD) could narrow down an answer to something more specific.