Booting Linux with ramdisk
In this section, the focus is on booting Linux. To make things simpler, the root file system called “ramdisk” will be used.
Preparation
To boot Linux OS, 4 things listed below will be needed;
- Boot image (includes FSBL, Hardware bitstream and U-Boot)
- Linux kernel image (uImage or zImage)
- Flattened Device Tree
- Root file system image
Boot image is already prepared in previous section. The latter will be explained in this section.
Preparing Linux kernel image
Get the Linux kernel source code for Zynq SoC from here;
To cross compile this source code for ARM, export environment variables.
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
Also, to make Linue kernel image for U-Boot, the path to the “tools” folder in U-Boot source tree should be added before compiling.
export PATH=${PATH}:/<U-Boot path>/tools
Configure the kernel with default configuration. Configuration files for ARM processors are in /<Kernel Source Directory>/arch/arm/configs/. Configure kernel with “xilinx_zynq_defconfig”.
make distclean
make xilinx_zynq_defconfig
If you want to change the kernel configuration further, launch menuconfig.
make menuconfig
Compile the kernel with the command like below;
make -j9 uImage LOADADDR=0x8000
If the build process succeeded, then Linux kernel image zImage and uImage will be created in /<Linux Kernel Source Dir>/arch/arm/boot/.
You can also refer to Xilinx’s Wiki.
Generating and Compiling device tree
Device tree is a description of the peripherals (in SoC/on board). This is given to the Linux kernel during boot. Before ARM processor became popular, the resource description (Memory Mapped I/O addresses and interrupt resources) of peripherals is hardcoded. But, since the ARM processor is embedded into various SoCs and boards, it’s quite difficult to keep it hardcoded. Device tree describes addresses/interrupt resources of peripherals (it is called platform devices in Linux), so that those devices will be recognized by Linux kernel, and then the device drivers (platform device drivers) will be loaded correctly. If the hardware is changed, device tree has to be updated.
To generate the device tree for our own devices in FPGA, Xilinx provides the device tree generator.
Device tree generator works with Xilinx SDK. In this example, command line interface will be used to generate device tree. Type “hsi”, then the command prompt shows up.
%hsi open_hw_design <hardware design hand-off file>.hdf
%hsi set_repo_path <path to device-tree-xlnx repository>
%hsi create_sw_design device-tree -os device_tree -proc ps7_cortexa9_0
%hsi generate_target -dir <output directory>
If there’s no error, then device tree source files are generated in target directory. The extension *.dts is device tree source file. It has to be compiled into *.dtb (Device Tree Blob) file.
IMPORTANT: Write protect for mmc devices must be disabled. Open generated dts file “zynq-7000.dtsi” and add “disable-wp;” for each mmc devices.
sdhci0: mmc@e0100000 {
compatible = "arasan,sdhci-8.9a";
status = "disabled";
clock-names = "clk_xin", "clk_ahb";
clocks = <&clkc 21>, <&clkc 32>;
interrupt-parent = <&intc>;
interrupts = <0 24 4>;
reg = <0xe0100000 0x1000>;
disable-wp;
};
sdhci1: mmc@e0101000 {
compatible = "arasan,sdhci-8.9a";
status = "disabled";
clock-names = "clk_xin", "clk_ahb";
clocks = <&clkc 22>, <&clkc 33>;
interrupt-parent = <&intc>;
interrupts = <0 47 4>;
reg = <0xe0101000 0x1000>;
disable-wp;
};
To compile generated device tree, use Device Tree Compiler generated in Linux kernel source directory.
export PATH=${PATH}:/<Linux Kernel Source Dir>/scripts/dtc
dtc -I dts -O dtb -o zynq_zed.dtb system-top.dts
You can refer to the Wiki here. Generated “zynq_zed.dtb” will be used as flattened device tree.
Root file system image
Linux kernel alone is not actually useful since it doesn’t even have user interface. Shell, or all the tools necessary to make Linux kernel useful is called “distribution”. Shell sits in root file system, and gets executed when the root file system is mounted.
To make it simple, we will use ramdisk (initrd) as root file system. initrd gets loaded into DRAM, and then mounted as a root file system. Therefore, it will be lost when the device is rebooted. Download “arm_ramdisk.image.gz” from Xilinx wiki.
This has to be converted to U-Boot image file (wrapper is added to make it recognizable by U-Boot). To do so, use “mkimage” utility in U-Boot source tree.
export PATH=${PATH}:/<U-Boot source dir>/tools
mkimage -A arm -T ramdisk -C gzip -d arm_ramdisk.image.gz uramdisk.image.gz
Then, ramdisk image for U-Boot “uramdisk.image.gz” will be generated.
Booting Linux
So now, all the necessary files are prepared and ready to boot Linux. Copy all those files (BOOT.bin, uImage, zynq_zed.dtb and uramdisk.image.gz) into boot partition in SD card. Insert SD card and turn on Zedboard, connect serial console to USB-UART on Zedboard.
When U-Boot is booted, what we will do is;
- load those 3 files (kernel image, device tree and ramdisk) into memory
- boot Linux from memory
To load those files, type commands in U-Boot prompt;
load mmc 0:1 0x03000000 uImage
load mmc 0:1 0x02a00000 zynq_zed.dtb
load mmc 0:1 0x02000000 uramdisk.image.gz
So now, those 3 files necessary to boot Linux are loaded into DRAM. “bootm” command will boot Linux kernel from the memory.
bootm <Linux kernel address> <ramdisk address> <device tree address>
So now, boot Linux by typing this.
bootm 0x03000000 0x02000000 0x02a00000
If the Linux kernel is booted, it will show Linux shell. If there are some errors, then either U-Boot or Linux kernel will tell you what’s went wrong.