This shows you the differences between two versions of the page.
ass:laboratoare:02:tasks:01 [2023/07/18 23:22] florin.stancu |
ass:laboratoare:02:tasks:01 [2024/08/06 15:05] (current) florin.stancu [01. Preparing the Linux μImage] |
||
---|---|---|---|
Line 12: | Line 12: | ||
=== Task A - Linux kernel === | === Task A - Linux kernel === | ||
- | Clone the kernel from the official github repo. Since you've built U-Boot previously, you should be somewhat familiar with the Kbuild system. Generate the default ARM configuration file and compile the kernel. | + | Clone the kernel from the [[https://github.com/torvalds/linux/|official GitHub repo]], using the ''v6.6'' tag. |
+ | We recommend you to use [[https://stackoverflow.com/questions/1778088/how-do-i-clone-a-single-branch-in-git|this git cloning technique]] to avoid fetching the entire git history (requiring several GBs of disk space). | ||
- | Note that the kernel image you will be including in the FIP is called **Image**. Unlike **vmlinux** which is an ELF file (i.e.: contains useless sections, including //.debuginfo// if you want to debug the kernel), **Image** is a boot executable image, made specifically for you to jump straight in and start executing. After the build process finalizes, **find** the **Image** file within the Linux repo. | + | Since you've built U-Boot previously, you should be somewhat familiar with the Kbuild system. Start by generating the build configuration from its [[https://github.com/torvalds/linux/tree/v6.6/arch/arm64/configs|default configuration template]]. Optionally, run a ''menuconfig'' to inspect the Linux kernel options available. |
<note warning> | <note warning> | ||
You must set the ''ARCH'' argument to the appropriate architecture **AT ALL TIMES** when invoking linux's ''make''! \\ | You must set the ''ARCH'' argument to the appropriate architecture **AT ALL TIMES** when invoking linux's ''make''! \\ | ||
Check out the subdirectories in ''linux/arch/'' for possible values. \\ | Check out the subdirectories in ''linux/arch/'' for possible values. \\ | ||
- | Also, do not forget about the ''CROSS_COMPILE'' argument. | + | Also, do not forget about the ''CROSS_COMPILE'' environment variable (export it inside your Makefile & terminal). |
</note> | </note> | ||
+ | |||
+ | In order to build the kernel image, simply ''make'' with the required variables (see the warning above!). | ||
<note tip> | <note tip> | ||
- | Paralellize the build process using ''make -j'', unless you want to waste an hour for Linux to build. \\ | + | Paralellize the build process using ''make -j <num_CPUs>'', unless you want to waste an hour for Linux to build. \\ |
</note> | </note> | ||
+ | |||
+ | Note that the kernel image you will be including in the FIP is called **Image**. Unlike **vmlinux** which is an ELF file (i.e.: contains useless sections, including //.debuginfo// if you want to debug the kernel), **Image** is a boot executable image, made specifically for you to jump straight in and start executing. After the build process finalizes, find the **Image** file within the ''<linux-kernel-src>/arch/arm64/boot/'' output directory. | ||
+ | |||
+ | While waiting for the build, explore the Linux source using your favorite code editor (especially [[https://github.com/torvalds/linux/tree/v6.6/arch/arm64/boot/dts/freescale|the device trees]])! | ||
<note tip> | <note tip> | ||
Line 34: | Line 41: | ||
</code> | </code> | ||
- | This will generate a ''compile_commands.json'' file that contains the **gcc** invocation cmdline. Any half decent language server will be able to (automatically) parse this file and deduce the include paths and definitions used. This will enable features like [[https://neovim.io/doc/user/lsp.html#vim.lsp.buf.definition()|go-to-definition]] between different source files and much more. | + | This will generate a ''compile_commands.json'' file that contains the **gcc** invocation cmdline. Any half decent LSP-based IDE (VSCode, [[http://lunarvim.org/|LunarVim]]) will be able to (automatically) parse this file and deduce the include paths and definitions used. This will enable features like [[https://neovim.io/doc/user/lsp.html#vim.lsp.buf.definition()|go-to-definition]] between different source files and much more. |
Note that this script only works on the current project. For a more generic tool, try [[https://github.com/rizsotto/Bear|bear]]. Be warned though that its LD_PRELOAD hooking of ''exec()'' calls (needed to extract the cmdargs) interferes with the [[https://www.gnu.org/software/automake/|Automake]] configuration stage. | Note that this script only works on the current project. For a more generic tool, try [[https://github.com/rizsotto/Bear|bear]]. Be warned though that its LD_PRELOAD hooking of ''exec()'' calls (needed to extract the cmdargs) interferes with the [[https://www.gnu.org/software/automake/|Automake]] configuration stage. | ||
Line 41: | Line 48: | ||
=== Task B - Flattened Device Tree === | === Task B - Flattened Device Tree === | ||
- | Luckily, the FDT for our platform is also included in Linux. It's name should be **imx8mq-pico-pi.dtb**. | + | Luckily, the FDT for our platform is also included in Linux. It's name should be ''imx8mq-pico-pi.dtb'' (generated from its source counterpart, [[https://github.com/torvalds/linux/blob/v6.6/arch/arm64/boot/dts/freescale/imx8mq-pico-pi.dts|imx8mq-pico-pi.dts]]). |
If for some reason it wasn't built alongside the kernel Image, check out the ''dtbs'' make target. | If for some reason it wasn't built alongside the kernel Image, check out the ''dtbs'' make target. | ||
Line 53: | Line 60: | ||
Buildroot on the other hand is geared towards simplicity and ease of use. Being based on Kbuild and Makefile (same as Linux and U-Boot) makes it instantly familiar to most developers. And even if you are new to this, while Yocto requires you to read entire [[https://www.goodreads.com/search?q=yocto|books]] in order to utilize it properly, Buildroot can be summed up in a 1k LoC Makefile. | Buildroot on the other hand is geared towards simplicity and ease of use. Being based on Kbuild and Makefile (same as Linux and U-Boot) makes it instantly familiar to most developers. And even if you are new to this, while Yocto requires you to read entire [[https://www.goodreads.com/search?q=yocto|books]] in order to utilize it properly, Buildroot can be summed up in a 1k LoC Makefile. | ||
- | == Step 1: Download Buildroot == | + | == Download Buildroot == |
Clone the official [[https://github.com/buildroot/buildroot.git|Buildroot repo]]. | Clone the official [[https://github.com/buildroot/buildroot.git|Buildroot repo]]. | ||
- | == Step 2: Create the configuration == | + | == Create the configuration == |
Find an existing defconfig for our platform (the **imx8mq**) and take a look at what it contains. Notice how the following config options are enabled: | Find an existing defconfig for our platform (the **imx8mq**) and take a look at what it contains. Notice how the following config options are enabled: | ||
Line 68: | Line 75: | ||
Yes, Buildroot also integrates the bootloaders into its build system. However, keeping the components separate makes it easier to appply patches and debug problems. What we want from Buildroot is a minimal root filesystem and nothing more. So, after generating ''.config'', enter ''menuconfig'' and make a few changes: | Yes, Buildroot also integrates the bootloaders into its build system. However, keeping the components separate makes it easier to appply patches and debug problems. What we want from Buildroot is a minimal root filesystem and nothing more. So, after generating ''.config'', enter ''menuconfig'' and make a few changes: | ||
+ | |||
* Disable all components that we've already integrated in our build environment (starting with the ones above). | * Disable all components that we've already integrated in our build environment (starting with the ones above). | ||
- | * Use an **external, custom, pre-installed toolchain**. | + | * Use an **external, custom, pre-installed toolchain** (remember ''CROSS_COMPILE''? check the menu for it!). |
* Use **systemd** as the init system instead of **BusyBox**. | * Use **systemd** as the init system instead of **BusyBox**. | ||
- | * Use **bash** as the default shell instead of **sh**. | + | * Make sure **systemd-logind** is included in the build, or you may have [[https://lore.kernel.org/all/b49c7676-1864-d838-fcf7-7a1208d6ba78@benettiengineering.com/T/|this problem]]. |
- | * Set //"root"// as password for the root user. | + | * Choose **bash** as the default shell instead of **sh**. |
- | * Include a text editor package (e.g.: **vim** or **nano**). | + | * Enable password login for the //root// user, then set a password. |
+ | * Include a text editor PACKAGE (e.g.: **vim** or **nano**). | ||
+ | * Include the **coreutils** PACKAGE. | ||
* Generate an **uncompressed CPIO** image from the output file system (see the Note below). | * Generate an **uncompressed CPIO** image from the output file system (see the Note below). | ||
+ | |||
+ | <note> | ||
+ | Note how the the configuration variables are prefixed with ''BR2_'', packages with ''BR2_PACKAGE_'' and so on! | ||
+ | </note> | ||
<note> | <note> | ||
Line 154: | Line 168: | ||
</solution> | </solution> | ||
- | == Step 3: Build it! == | + | == Build & inspect the rootfs == |
Not much to it, really. Once everything's done, check out the ''output/'' directory. What does it contain? Where is your CPIO archive? | Not much to it, really. Once everything's done, check out the ''output/'' directory. What does it contain? Where is your CPIO archive? | ||
Line 166: | Line 180: | ||
Create a staging directory and copy everything that we've obtained from the previous three tasks. Then, create an Image Tree Source (ITS) file. We'll be referring to it as ''linux.its'' but the name doesn't really matter. What matters is the content: | Create a staging directory and copy everything that we've obtained from the previous three tasks. Then, create an Image Tree Source (ITS) file. We'll be referring to it as ''linux.its'' but the name doesn't really matter. What matters is the content: | ||
- | <spoiler linux.its> <code> | + | <spoiler linux.its> |
+ | <code> | ||
/dts-v1/; | /dts-v1/; | ||
Line 211: | Line 226: | ||
fdt = "fdt"; | fdt = "fdt"; | ||
ramdisk = "initrd"; | ramdisk = "initrd"; | ||
- | loadables = "fdt", "ramdisk"; | ||
}; | }; | ||
}; | }; | ||
}; | }; | ||
- | </code> </spoiler> | + | </code> |
+ | </spoiler> | ||
+ | \\ | ||
Let's unpack this: | Let's unpack this: | ||
- | * The file starts with ''/dts-v1/;'', identifying it as a Device Tree Source. | + | * The file starts with ''/dts-v1/;'', identifying it as a Device Tree Source. |
- | * Next, we have a root ''/'' node with two child nodes: ''images'' and ''configurations''. | + | * Next, we have a root ''/'' node with two child nodes: ''images'' and ''configurations''. |
- | * ''images'' contains the description of each binary that will need to be loaded by U-Boot when processing the FIT. | + | * ''images'' contains the description of each binary that will need to be loaded by U-Boot when processing the FIT. |
- | * Each image entry contains an [[https://git.kernel.org/pub/scm/utils/dtc/dtc.git/commit/?id=e37ec7d5889fa04047daaa7a4ff55150ed7954d4|incbin]] directive that tells **mkimage** to copy paste the contents of the specified file into the output DTB. | + | * Each image entry contains an [[https://git.kernel.org/pub/scm/utils/dtc/dtc.git/commit/?id=e37ec7d5889fa04047daaa7a4ff55150ed7954d4|incbin]] directive that tells **mkimage** to copy paste the contents of the specified file into the output DTB. |
- | * Each image also contains a ''load'' property, specifying the address where the ''data'' will be placed. | + | * Each image also contains a ''load'' property, specifying the address where the ''data'' will be placed. |
- | * In addition to ''load'', the kernel image also has an ''entry'' property specifying where U-Boot will jump when relinquishing control to the Linux kernel. | + | * In addition to ''load'', the kernel image also has an ''entry'' property specifying where U-Boot will jump when relinquishing control to the Linux kernel. |
- | * ''configurations'' contains sets of image configurations that can be chained. | + | * ''configurations'' contains sets of image configurations that can be chained. |
- | * The only configuration we have is also the ''default'': ''normal-boot''. | + | * The only configuration we have is also the ''default'': ''normal-boot''. |
- | * Notice how it has pre-defined attributes for ''kernel'', ''fdt'', ''ramdisk''. These are not simple binary blobs; instead, they each have a role to play in the boot sequence. E.g., U-Boot will know to take the ''load'' address of the ''fdt'' image and pass it via a certain register (decided by convention) to the ''kernel'', informing it where in memory to look for the FDT. | + | * Notice how it has pre-defined attributes for ''kernel'', ''fdt'', ''ramdisk''. These are not simple binary blobs; instead, they each have a role to play in the boot sequence. E.g., U-Boot will know to take the ''load'' address of the ''fdt'' image and pass it via a certain register (decided by convention) to the ''kernel'', informing it where in memory to look for the FDT. |
Note how we replaced the ''load'' and ''entry'' addresses with placeholders such as //XXX//. Replace these with whatever addresses you want such as the binaries do not overlap once U-Boot starts loading them. | Note how we replaced the ''load'' and ''entry'' addresses with placeholders such as //XXX//. Replace these with whatever addresses you want such as the binaries do not overlap once U-Boot starts loading them. | ||
Line 246: | Line 262: | ||
$ mkimage -f linux.its linux.itb | $ mkimage -f linux.its linux.itb | ||
</code> | </code> | ||
+ |