Once we have understood how our device boots up, we will then cover the technical aspects of how to flash our device. As in the boot sequence, the 3 main components that need to be transferred -”flashed”- to the board, are: The Bootloader, the Kernel and the OS (Android). We cover them in different posts, continuing here with The Kernel.
As explained in the post explaining the boot sequence (here), the kernel is the main component of most OS, acting as a bridge between the software (applications) and the hardware. This makes the kernel being the lowest-level abstraction level of the hardware and therefore platform specific. From the most traditional point of view (this still remains today) the kernel is in charge of handling resources and manage how other software (programs, applications, etc.) use this resources. We can group resources in three classes, matching the 3 basic abstractions of a computer :
- The CPU (processing): The Central Processing Unit is the brain of a computer system, in charge of executing instructions from its instruction set. A set of instructions in a logic order is what is commonly known as a program. The kernel is responsible for deciding which process get the CPU for executing its instructions. Since one CPU (one core) can only execute instructions from one process at a time, the kernel must assign the CPU to each process for a controlled amount of time. This task is known as scheduling.
- Memory (memory): The main memory is the place where both instructions and data are allocated before a program can be executed. Since the memory is -once again- a limited resource, the kernel is in charge of allocating and deallocating processes from it so that a given process can execute correctly in a given moment in time. The kernel is also in charge of applying certain strategies to optimize the use of the memory and to determine what should be done in case there is not enough memory for a process to be executed.
- I/O devices (storage – among others): Devices connected to our machine which carry out a specific task. In the case of embedded systems, the most relevant devices in this matter are the ones related to permanent storage, normally -in this context- a flash device. If we widen our view of an I/O device, we can add almost anything that is not considered CPU or Memory: Display, mouse, keyboard, printer, network adapters, etc. The kernel is in charge of handling requests from applications to perform I/Os to an appropriate device and providing convenient methods for using that device.
The Kernel, mostly associated to Linux and Unix-like OS is released under the GNU General Public License, and thereby free software that can be used and modified for non-commercial purposes.
In a Linux based OS, the kernel can be changed -under some constrains (e.g. Android 4.1.c Jelly Bean is based on the Linux kernel 3.1.10)- and also re-compiled in order to add new features. As explained before, the kernel is the closest piece of software to the hardware, implying that it has to “know” every piece of hardware in the device it is running on. Where we want to get here is: The kernel must be compiled specifically for the architecture and device it will be running on top of. Since we are dealing with devices such as tablets, phones, boards,etc. we need to cross-compile the kernel in a host machine and transfer it to the device in question afterwards. Here we are going to comment the technical aspects of this process, taking as a example the hardkernel’s ODROID-PC (Samsung’s Exynos 4) developer board.
Cross-Compile the Kernel
The very first step is counting on a cross-compiler for the platform we want the kernel to run in (target platform). Please refer to this post, in order to set up your system for cross-compiling for different platforms (and developing for the Android system too). Once this is done, we need to set the cross-compiler we need, depending on the target platform (remember that we can have more than one cross-compiler installed in our machine). In our case:
- export CROSS_COMPILE=arm-none-eabi-
- export ARCH=arm
- export PATH=$PATH:$CROSS_COMPILERDIR/bin/ (e.g. PATH=$PATH:/home/javigon/CodeSourcery/Sourcery_G++_Lite/bin)
After this, we are ready to actually compile (cross-compile) the kernel.
First we initialize object, config, etc. and clean previous attempts of compilation.
- cd $KERNELDIR
- make distclean
Now, we need to create the .config kernel configuration file. This is mainly a board specific configuration file that sets some variables to different values, depending on the capabilities of the board. Configuration files can be found at $KERNELDIR/arch/$PLATFORM/configs/. For example, the Marvell Dove MX51 configuration file – which runs with an ARM processor and can be thereby found at $KERNELDIR/arch/arm/dove_defconfig– looks like:
In order to create the .config configuration file we type:
- make $BOARD_decfonfig (e.g. in the case of the MX51 we would type make dove_defconfig)
Once the the .config file is created, we can compile the kernel. Please note that this process can take some time (up to several minutes) depending on your hardware.
- make -j8
The -j option refer to the number of jobs (commands) that can run simultaneously (in parallel). Some say this number should twice the number of CPUs in your system. Actually, N refers to the number of tasks to be carried out simultaneously, and it should be between 1 and 2 times the number of hardware threads on the computer being used for the build (e.g. on a dual-E5520 machine (2 CPUs, 4 cores per CPU, 2 threads per core), the fastest builds are made with commands between
make -j16 and
make -j32.)  Since the appearance of multi-core processors (1 CPU but several cores) this rule of thumb can also be applied to the number of cores. In order to find out how many processors do we count on: (For more information concerning the make command, please consult the make manual page)
- cat /proc/cpuinfo | grep processor | wc -l (Only with cat /proc/cpuinfo we will get detailed information regarding our processors)
Once the kernel is done compiling, the zImage of the kernel is ready and we can flash it in our device. For this, we are going to make use of fastboot, a command that will help us in the process. Refer to this post about fastboot if you are not familiar with it. (Please note that you should count on a device with a boot loader flashed on it in order to continue from this moment on. Refer to this tutorial to build and flash a boot loader in your board).
We connect our device to our host machine and open minicom -or any other serial terminal such us cutecom, screen, etc- (how to configure a serial terminal here – coming soon), turn on our device and press any key to interrupt the boot sequence. We will get a command terminal we can use to carry out simple -but very useful- actions. Type fastboot and connect your OTG cable to both your host machine and your device.
From our host machine, we can check that the OTG cable is connected.
From our host machine also, we flash the kernel into our device:
- cd $KERNELDIR
- fastboot flash kernel arch/$ARCH/boot/zImage (e.g. In the case of the the MX51 mentioned above: fastboot flash kernel arch/arm/boot/zImage)
In out serial terminal we wil read partition ‘kernel’ flashed. The kernel is then flashed and ready to use in our device. Type fastboot reboot in your host machine to reboot the device. It will start but if we do not have a SO flashed in our device, it will return a kernel panic since it will be impossible for it to find a root file system. This will be our next objective, build and flash a SO – Android in our case. (here – coming soon)
 Android Open Source Project (here)