Most of what you find on other Linux documentation for kernel build is still valid on a Jetson, but if you cross-compile then extra information is needed. Actual install differs from a PC due to using a different boot mechanism (PCs use GRUB, Jetsons use U-Boot).
The official compile information is available with the Documentation download of your version of L4T. For example, look here (documentation will change for some details depending on L4T version…so get Documentation for your version):
https://developer.nvidia.com/embedded/linux-tegra
Historic version listings:
https://developer.nvidia.com/embedded/linux-tegra-archive
A useful part of the official documentation is information on cross-compile tools. This information is not needed for native compile on the Jetson itself.
Preparation:
Consider using the driver package’s “source_sync.sh” script to get the correct kernel source. If you’ve flashed the most recent JetPack3.3 (a front end to command line tools, plus package management), then your L4T version will be R28.2.1 (see “head -n 1 /etc/nv_tegra_release”). With R28.2.1 as an example, using source_sync.sh from the driver package (which JetPack would have downloaded for you if you used that to flash…it produces the “Linux_for_Tegra/” subdirectory on your host), this would download kernel source (and some related subdirectories for firmware and device tree):
./source_sync.sh -k tegra-l4t-r28.2.1
(you can use this script anywhere…you can copy it to a temporary location on the Jetson itself if you want to compile the kernel natively on the Jetson…other documentation will suggest cross-compile from an Ubuntu PC host…native compile is simpler, but not everyone wants to use the disk space on the Jetson for building)
I can’t help with what changes other carrier boards might require, but if you have a running system now which you’ve flashed to a version you want, then save a copy of “/proc/config.gz” somewhere safe and use a copy of that for any starting config. Also take note of the current output of “uname -r”. On R28.2.1 with a TX2 it is “4.4.38-tegra” (likely the 4.4.38 will be the same on your carrier board, but the “-tegra” probably differs). Note that when someone mentions setting up kernel config via “make something_defconfig” that this is to produce a basic verison of this config file named “.config” in the kernel build area; however, a copy of what is actually running is probably superior. If you have copied a version of config.gz via gunzip and rename to “.config”, then don’t run any of the “make something_defconfig” commands (for a TX2 it is “make t186_defconfig”…I don’t know if TX2i differs…Orbitty probably needs edits to defconfig, but “/proc/config.gz” does not need edits).
FYI, I don’t have a TX2i (and I only have the dev carrier board), so I’m going by the TX2 dev kit for information (for the most part nothing will differ between TX2/TX2i or dev board/Orbitty board which the “/proc/config.gz” sample doesn’t already take into account…device tree is a different story, but you don’t need to know anything about that for a simple kernel build (device tree is a separate topic, but it ships with the kernel source because they are related).
The “/proc/config.gz” file is a gzip compressed copy of the running system’s idea of what the kernel config is right at that moment (it isn’t a real file, this exists only in RAM). With the exception of CONFIG_LOCALVERSION this is an exact match to what you have now. Always copy this somewhere safe before working on a kernel…once you’ve changed your kernel this is lost. You will see kernel compile steps about building a “defconfig” (e.g., “make t186_defconfig”), but the actual starting config is a far better starting point. If your carrier board had to change something, then the “/proc” version of config will reflect that…you won’t even have to know what that difference is (so far as kernel config goes…there may still be other details changing).
When a kernel searches for modules (perhaps you will build your driver as a module instead of integrated into the kernel) it searches at “/lib/modules/$(uname -r)/”. CONFIG_LOCALVERSION is made up of the kernel source base version number (“4.4.38” in most recent Jetsons) plus CONFIG_LOCALVERSION. If you were to set CONFIG_LOCALVERSION to “-tegra” on a “4.4.38” kernel source before building the kernel, then running that kernel would result in “uname -r” of “4.4.38-tegra”, and modules would be searched for in “/lib/modules/4.4.38-tegra/”. Should your modules be located in another directory, then there will be a failure to find those modules (and perhaps this would imply system failure or just some missing feature). Note that you can directly use an editor on the “.config” for CONFIG_LOCALVERSION, or the menu-based editors can do this.
Before you start building you might want to install a package used for some of the command line config editors (this is used for some utilities which work in console even when there is no GUI):
sudo apt-get install libncurses5-dev
Once have your kernel source and get an exact match to your existing kernel config (with file name “.config”) you’ll be ready to edit the configuration for any change you want to make. The “make menuconfig” or “make xconfig” or “make nconfig” arguments in the kernel build location will run an editor (I use “make nconfig”…xconfig is GUI only, but nconfig is what I recommend even if you have a GUI…nconfig has the best “search” function). If you have copied your “/proc/config.gz” to the kernel source parent directory, unzipped it with “gunzip”, and edited CONFIG_LOCALVERSION, then the correct starting config will be automatically loaded when you start that edit. Basically:
cp /proc/config.gz /where/ever/it/is/kernel
cd /where/ever/it/is/kernel
gunzip config.gz
mv config .config
# Note "uname -r", edit .config for correct CONFIG_LOCALVERSION
Build Notes:
You’ll find it useful to know that the basic order is to configure, followed by “make Image” (we don’t bother with zImage or uImage…these wouldn’t hurt, but are not used on a TX2…you’d still get an Image file) to build a kernel, then “make modules” to build any modules. If you were to skip “make Image”, then you’d need to run “make modules_prepare” prior to building modules (building Image does the equivalent of “make modules_prepare”). I suggest always building your config at least once as “make Image” even if you don’t need the Image…it is a sanity check.
Environment variables change how the build is done. The environment which is most useful (regardless of cross-compile or native compile) is to put your work in a directory other than the kernel source itself. Keeping the kernel source clean is an excellent choice. So this contrasts building a kernel and modules in a bad way (polluting the kernel source itself…it isn’t really bad because you can “make mrproper” to get rid of old files, but it is ugly compared to not putting intermediate files there in the first place):
# All methods (for the sake of convenience):
export SRC=/where/ever/your/kernel/source/is/
# Ugly
cd $SRC
# ...configure...results in ".config" being here.
make Image
make modules
# ...Image and modules will be somewhere in the current tree...
# Nice
cd $SRC
mkdir /some/other/empty/temp/location/build
export TEGRA_KERNEL_OUT=/some/other/empty/temp/location/build
mkdir /some/other/empty/temp/location/modules
export TEGRA_MODULES_OUT=/some/other/empty/temp/location/modules
# ...configure...put your ".config" in TEGRA_KERNEL_OUT...
make -j4 O=$TEGRA_KERNEL_OUT Image
# ...Image will be somewhere in $TEGRA_KERNEL_OUT
make -j4 O=$TEGRA_KERNEL_OUT modules
make O=$TEGRA_KERNEL_OUT modules_install INSTALL_MOD_PATH=$TEGRA_MODULES_OUT
# ...modules will be in $TEGRA_MODULES_OUT
If you were to cross-compile from the PC the above would still be true, but you would have to additionally tell the compiler this is for cross-compile:
# This assumes you have cross-compiler "/where/ever/your/cross/tool/is/bin/aarch64-unknown-linux-gnu-<b>gcc</b>
export CROSS_COMPILE=/where/ever/your/cross/tool/is/bin/aarch64-unknown-linux-gnu-
export ARCH=arm64
The CROSS_COMPILE plus ARCH would be done prior to any “make” for a cross-compile. The “.config” would go directly in SRC if doing it the ugly way, or in TEGRA_KERNEL_OUT for the clean way.
Tips:
- If you run "make mrproper", then you will make the kernel source completely pristine. You will remove any ".config". If you run "make O=$TEGRA_KERNEL_OUT mrproper", then you will completely clean your alternate "nice" way of doing it directory...but there is a catch on Jetsons. There are some other directories related to device tree and firmware which "mrproper" won't clean out for "make O=$TEGRA_KERNEL_OUT mrproper"...you might need to "rm -Rf $TEGRA_KERNEL_OUT/*". If you did everything as a non-root user you will be far safer than if you do this as root...be very careful about recursive rm. Check what an environment variable is with "echo" prior to using it (e.g., "echo $TEGRA_KERNEL_OUT" or "echo $TEGRA_MODULES_OUT").
- Never use "make modules_install" except with an alternate location specified via the "INSTALL_MOD_PATH=$TEGRA_MODULES_OUT" unless you are absolutely certain you want the results to go into the running machine.
- Modules, after modules_install, will be in a directory tree matching the subdirectory of the kernel source which you just built. The "/lib/modules/$(uname -r)/" directory will follow this same pattern and can more or less be recursively copied into the correct "/lib/modules/$(uname -r)/" location.
- The original "$TEGRA_MODULES_OUT" location will contain a symbolic link ("build"). You don't want to copy the content the link point to; it copies the entire kernel build source. This won't hurt, but it sure will eat up disk space. You can safely "rm" the link before a recursive copy.
- See: "find $TEGRA_MODULES_OUT -type l" to locate the sym link.
- The Documentation downloadable from one of the above URLs gives information on using environment variables for cross-compile, along with information about cross-compile tools.
About Installing:
If you build a feature as integrated (you used ‘y’ in the menu editor to enable the feature), then the change will be in the Image file of “$TEGRA_KERNEL_OUT”. Install will consist of copying Image to the right location/name of “/boot” (preferably with a name change to preserve the original Image). This may have some things to watch out for.
If you build a feature as a module (you used ‘m’ in the menu editor to enable the feature), then install will go to the right location of “/lib/modules/$(uname -r)/”. Reboot or “sudo depmod -a” will inform the system to look for changes to modules.
If you build a module, then it will be in a subdirectory of “$TEGRA_MODULES_OUT”. When installed it will go in a matching directory tree of “/lib/modules/$(uname -r)/kernel/”. For example, if the module is at “$TEGRA_MODULES_OUT/drivers/foo/bar.ko”, then you’d copy the “bar.ko” file to “/lib/modules/$(uname -r)/kernel/drivers/foo/bar.ko”. You’d need to verify after reboot that your “uname -r” is what you expected.
Sometimes you will see an added “+” in your “uname -r” which you didn’t expect. You might want to recursively copy your previous modules to this location, and then install the new modules over the top of this. Example assuming you had “/lib/modules/4.4.38-tegra/”, and now you have “uname -r” of “4.4.38-tegra+”:
sudo -s
cd /lib/modules
cp -R 4.4.38-tegra 4.4.38-tegra+
cd 4.4.38-tegra<b>+</b>
# You have previously removed the symbolic link in $TEGRA_MODULES_OUT
cp -R $TEGRA_MODULES_OUT .
# depmod -a OR reboot
If you build a feature purely as a module with no other changes (other than adding modules), then the original Image will be guaranteed as compatible with your module. If you change a feature integrated into the Image file, then you are no longer guaranteed that the module is compatible. In many cases you will have good reason to believe there is not a compatibility issue, and thus recursively copying the old modules into the directory with a new “uname -r” will work. For cases where something significant changed in the Image, then you want to purposely use a different CONFIG_LOCALVERSION to get a completely new and separate “uname -r”…in which case you would use only newly compiled modules and not recursively copy anything from the old module directory.
U-boot looks for the Image file via the “/boot/extlinux/extlinux.conf” config. The typical extlinux.conf has an entry like this, and the “LINUX” key/value pair is the location of Image:
LABEL primary
MENU LABEL primary kernel
LINUX /boot/Image
APPEND ${cbootargs} root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4
If you want to add a separate entry while preserving the original you might add this (FYI “LABEL” is what U-Boot looks for, “MENU LABEL” is what a boot menu will show the user during boot):
LABEL <b>custom</b>
MENU LABEL <b>custom kernel</b>
LINUX /boot/Image-4.4.38_custom
APPEND ${cbootargs} root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4
…if your new Image file were then copied to name “/boot/Image-4.4.38_custom” (CONFIG_LOCALVERSION would be “_custom”), then this second entry would boot to that entry and leave the original available for rescue or comparison. You need a serial console to pick this at boot time (there is a short moment during early boot you can hit a key stroke and interrupt U-Boot to pick an entry). See:
http://www.jetsonhacks.com/2017/03/24/serial-console-nvidia-jetson-tx2/
If you don’t have a serial console, then you would probably still be advised to use an alternate entry…but you’d have to set the “DEFAULT” key/value pair at the top of extlinux.conf to name the new “LABEL” (in this example it is “custom”). In the above “DEFAULT custom” would boot the custom entry if nobody manually selects something different at boot. Should this fail, then you’d probably need a serial console to get back in (unless you are willing to spend a lot of time cloning and restoring).
About Your Particular Edits to .config:
Sorry, I don’t know anything about the particular hardware drivers you are wanting to add. If you use a text editor to look at “.config”, then you’ll see a lot of “CONFIG_…something…” lines. Some are integrated already (“=y”), some are inactive and commented out, others are as a module (“=m”). In “make nconfig” you’ll see at the bottom a hot key is available for searching. You can search for symbols, but do not need to use “CONFIG_” in the search (it’ll look for the part after “CONFIG_”). The search also is case insensitive so you don’t have to use capital letters. You will have to research what drivers your hardware uses, find this in the “make nconfig”, and enable it. Usually a driver works for a chipset series and may not be specific to your exact device (other manufacturers may use the same driver so long as their hardware all uses the same chipset).
Someone with your particular hardware may be able to tell you what driver(s) is required.