This week's installment of the "Exploring the Virtual Platform" series focuses on the Linux kernel that was booted in Part 1 of this series.
In Part 1, when QEMU was invoked to boot Linux there was a -kernel argument: -kernel zImage.integrator
It's probably no secret this is the Linux kernel to run. It was directly downloaded from the QEMU download page. This was fine to show QEMU booting, but to do any actual software development and debugging the kernel must be built from source and run. Building the source code also enables the ability to debug the kernel source code running on the model of the ARM Integrator board. Today, I will demonstrate how to get the kernel, compile it, and run it. Next time we will work on debugging the code and then move on to the contents of the file system.
Before the kernel can be compiled a software tool chain is needed. The Integrator board has the ARM926 CPU so we need a cross-compiler that can build the kernel for the ARM processor. Linux is always compiled with gcc. There are many ways to get gcc, but one way that is very easy for me is to visit CodeSourcery and download binary releases gcc and binutils. Sourcery G++ Lite for ARM has multiple download alternatives on the download page. I took the IA32 GNU/Linux Installer and installed it without any trouble on my openSUSE machine. Once the install is complete and the bin/ directory is added to the PATH then the gcc and binutils executables are available. They are just like the host gcc and binutils except all commands are prefixed by arm-none-linux-gnueabi-. This means gcc is invoked using arm-none-linux-gnueabi-gcc
Unfortunately, I don't know an easy way to verify this compiler is working. CodeSourcery also has another version of gcc for ARM that lists the target OS as EABI (instead of GNU/Linux). This version is targeted at bare metal hardware. Its prefix is arm-none-eabi- and it generates static executables instead of the dynamic executables generated by the Linux tool chain with the arm-none-linux-gnueabi prefix. The executables generated by arm-none-eabi-gcc can be run on the host machine using the QEMU User Space Emulator. Here's a quick demo:
First, create a file hello.c with these 6 lines:
printf("Hello from an ARM cross-compiled program.\n");
jasona@hamlake% arm-none-eabi-gcc -o hello hello.c
Make sure it's an ARM executable:
jasona@hamlake% file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped
jasona@hamlake% qemu-arm hello
Hello from an ARM cross-compiled program.
Now back to the Linux kernel. Before the kernel can be compiled it must be configured for the target hardware, in this case the ARM Integrator board. The kernel supports many different processor types, many flavors of ARM processors, and many ARM boards. The configuration process is a bit overwhelming because there are so many options that can be changed. Linux kernel version 0.01 had 0 configuration options, version 1.0 had 49, version 2.4 had about 1300, and 2.6 has even more (if anybody knows how many please leave a comment). To make things easier we can get the configuration from the kernel that was run on the QEMU Integrator model in Part 1. Start QEMU, login as root, and type:
# uname -a
to see that the kernel version is 2.6.17-rc3. It's not the latest and greatest, but it tells more about what we are dealing with.
There are two ways to extract the kernel configuration so it can be used to configure a new kernel source tree for build.
The first is to get it from a file that stored in the /proc filesystem. Again in QEMU do:
# gunzip < /proc/config.gz
and you will see the kernel configuration file scroll past. Of course, this can be piped into a file, but unfortunately, there is no simple way to get this file out from the target system to the host. The booted file system has no utility like scp or ftp and it's a ramdisk file system.
Another way to get the kernel configuration is using a utility called extract-ikconfig that comes with the Linux kernel source tree. Since I didn't have a kernel tree downloaded yet I found the script on a webpage and copied it to my machine in the directory where my zImage.integrator was located. Looking at the script I saw it uses another utility called binoffset. Again I copied the file binoffset.c to my machine from a webpage and put it in a directory I created named scripts/ where my zImage.integrator is located. If you do this you can run extract-ikconfig to get the kernel configuration file, .config, for the running kernel.
jasona@hamlake:[arm-test]% extract-ikconfig zImage.integrator > .config
Here is a link to another kernel configuration file that is close to this one that we are working with.
Now it's time to get and build a new kernel for the ARM Integrator board. Since this is for educational purposes only, I decided to get a kernel source tree that is about as old as the one that was running. Newer versions can also work, but the configuration will have many new options and it would take some time to get a newer version running on the Integrator board.
The Linux kernel website has all of the versions of the kernel source. I decided to get version 184.108.40.206 to build and run on the Virtual Platform. Download it unzip it:
jasona@hamlake:[kernel]% bunzip < linux-220.127.116.11.tar.bz2 | tar xf -
Now copy the .config file that was extracted from the zImage.integrator and put it in the linux-18.104.22.168 directory, go there, and start the build:
jasona@hamlake:[linux-22.214.171.124]% make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- zImage
If you want to see all of the configuration details you can 'make menuconfig' or 'make xconfig'
Wait for the build to complete (I think it took about 10 minutes) and if all goes well a message is displayed:
Kernel: arch/arm/boot/zImage is ready
This is the new zImage file to boot with QEMU:
jasona@hamlake:[arm-test]% qemu-system-arm -kernel ~/kernel/linux-126.96.36.199/arch/arm/boot/zImage -initrd arm_root.img
Here is the screen shot showing the kernel is now reported at 188.8.131.52 by uname:
Now we have a kernel source tree that is compiled into a kernel image that boots on the Virtual Platform. Even though we are working with QEMU as a reference, all of the concepts shown here apply to any Virtual Platform.
The next step is to debug kernel code with gdb and that's were I will pick up next time. After that we will move to the file system and find out what BusyBox is.
ARM has a Linux Download page, see if you can make sense out of it after what you learned today.
Thanks for reading.