Wednesday, January 23, 2008

Friday, May 4, 2007

Welcome to the world of Embedded

Hi There!!
It's a tough battle that you have got in to. Not only is it difficult to find resources in the internet about embedded solutions, its almost impossible to get information at one location.

Make kernel porting easier on your own SDK:-))

Before port a kernel with root file system in your SDK you need some basic ideas.
So first I am trying to put some basic definition .
What is Embedded linux.?
It is based embedded operating system used in PDA, mobile phone , media player handsets and other consumer electronics devices. Its Combining the Linux kernel with a small set of free s/w utilities.

Inside total document I am going to discuss about
1. Toolchain.
2. Boot loader.
3. Linux kernel.
4. Root file system.

To port a kernel at ARM processor, we need
Tools:
1. SDK with all require information about the hardware.(Target)
2. A PC(Host)
3. Toolchain
4. Boot loader with information.
5. A debugger.(Ex. JTAG)
In embedded system SDK is known as target where you want to port your kernel. Your personal computer is known as Host machine.

Now your host machine and target has contain two different processor. You have to build a kernel/other utilities binary at your host machine, and your host machine binaries is not going to work at your target machine.
So in that case the solution is Toolchain. Using toolchain you will able to do the cross build.
My main aim is to port the kernel so am not going to discuss about toolchain in details. Nowadays toolchain problem has been solved by Dan kegel. His web site www.kegel.com will provide you more information.
But one thing i want to share, that is, the arm-linux-elf toolchain could not be build from kegel.com.
For that you can visit this web site http://www.gnuarm.org/.

Boot loader.
The SDK hardware alone cannot perform complex actions such as loading a program from disk, so an apparent paradox exists: to load the operating system into memory, one appears to need to have an operating system already loaded. The solution is to use a special small program, called boot loader, bootstrap. This program's only job is to load other software for the operating system to start. Often, multiple-stage boot loaders are used, but it very much depends on the Hardware maker, that is SDK maker. For example I ported linux kernel on LH71404 ARM cpu, here the boot loader was some special bootloaded that is IPL kicker. And the documentation of boot loader has been provider by the SDK supplier. Its a single stage boot loader.
On the other hand At91sam9260-ek evalition board contain the ARM926EJ-S processor. It is using two stage boot loader.
Its not our concern that how to port the boot loader, rather we have to know how boot process is working, how kernel is depend on boot loader.

Over view of boot Process
1st step -->Power on/ reset
2nd step-->Stage1 boot loader
3rd step-->Stage 2 bootloader
4th step-->Linux kernel
5th step-->init (user space)

When a system is first booted, or is reset, the processor executes code at a well-known location. The CPU in an embedded system invokes the reset vector to start a program at a known address in flash/ROM.
The system startup stage depends on the hardware that Linux is being booted on. On an embedded platform, a bootstrap environment is used when the system is powered on, or reset. Examples include U-Boot, RedBoot, Embedded platforms are commonly shipped with a boot monitor. These programs reside in special region of flash memory on the target hardware and provide the means to download a Linux kernel image into flash memory and subsequently execute it. In addition to having the ability to store and boot a Linux image, these boot monitors perform some level of system test and hardware initialization. In an embedded target, these boot monitors commonly cover both the first- and second-stage boot loaders. What are those devices that have been initialize by the boot loader, should be written at boot loader document. And it is very much depend on the SDK .

Now I am going to discuss about the 4th step, that is Linux kernel.

First should getting familyer with kernel directory structure. So please download the linux kernel image from from www.kernel.org. Inside kernel top directory these are the some common sub directory.
arch-This subdirectory contains all of the architecture-specific code. For each supported architecture ( ARM, MIPS and so on), there is a subdirectory under "arch". Each supported architecture subdirectory has four major subdirectories:
1.kernel, which contains the architecture-specific kernel code
2.mm, which contains the architecture-specific memory management code
3.lib, which contains architecture specific library code (vsprintf and so on)
4.MY_PLATFORM (target platform directory, ex: mach-at91rm9260), which contains platform-specific code.

Documentation-This subdirectory contains the documentation for the kernel

drivers-This subdirectory contains code for the device drivers. Each type of device has further subdirectories, such as char, block, net, and so on.

include-The include subdirectory contains the include files for the kernel. It has further subdirectories for common include files (for all architectures), one for every architecture supported, and a couple of other subdirectories.

init-This directory contains the initialization code for the kernel.

kernel-This directory contains the main kernel code.

lib-This directory contains the library code of the kernel.

mm-This directory contains the memory management code.

At 4th boot process step the kernel stage is beginning. The kernel image isn't so much an executable kernel, but a compressed kernel image. Typically this is a zImage (compressed image, less than 512KB) that has been previously compressed with zlib. At the head of this kernel image is a routine that does some minimal amount of hardware setup and then decompresses the kernel contained within the kernel image and places it into high memory. If an initial RAM disk image(rootfs) is present, this routine moves it into memory and notes it for later use. The routine then calls the kernel and the kernel boot begins.

The major function flow when a compressed linux kernel is booting.
Function name Location of the function
decompress_kernel()-------->/linux($ver)/arch/arm/boot/compressed/misc.c
|
|
startup_32()----------------->/linux($ver)/arch/arm/kernel/head.S(this is the uncompress kernel entry point, you can customize this file if any minimum boot loader information for kernel is missing like reset vector register R0 value is missing,or machine type R1 Value is missing.)
|
|
start_kernel()---------------->/linux($ver)/init/main.c
|
|
cpu_idel()------------------>/linux($ver)/init/main.c
The function decompress_kernel is calling from /linux($ver)/arch/arm/boot/compressed/head.S. This function is used to decompressed the kernel.
Then new startup_32 function (also called the swapper or process 0), the page tables are initialized and memory paging is enabled. The type of CPU is detected (machine type)along with any optional floating-point unit (FPU) and stored away for later use. The start_kernel function is then invoked (init/main.c), which takes you to the non-architecture specific Linux kernel. This is, in essence, the main function for the Linux kernel.
With the call to start_kernel, a long list of initialization functions are called to set up interrupts, perform further memory configuration, and load the initial RAM disk. In the end, a call is made to kernel_thread (in arch/arm/kernel/process.c) to start the init function, which is the first user-space process. Finally, the idle task is started and the scheduler can now take control (after the call to cpu_idle). With interrupts enabled, the pre-emptive scheduler periodically takes control to provide multitasking.

After the kernel is booted and initialized, the kernel starts the first user-space application. This is the first program invoked that is compiled with the standard C library. Prior to this point in the process, no standard C applications have been executed.
In a Linux system, the first application started is commonly /sbin/init. But it need not be. Rarely do embedded systems require the extensive initialization provided by init (as configured through /etc/inittab). In many cases, you can invoke a simple shell script that starts the necessary embedded applications.

Building the kernel image.
Minimum system requirement.

Linux kernel sources will use anywhere from 40Mb to 80Mb of file system space on the host development platform. On the target platform, the binary kernel image and included modules should require in the order of 2Mb, depending on functionality selected when configuring the kernel.
BSP Patch.
Some vendors that sell chips with embedded ARM cores have associated patches available for specific versions of the Linux kernel. Typically you apply these by unpacking the kernel, changing directory into the unpacked kernel and executing something like
patch -p1 patchfile.patch’

Configure the kernel.

The configuration process allows users to select features to be included in the kernel image. This usually requires knowledge of the hardware platform and environment you will use to run Linux.
We have to make a kernel image which will be minimum in size. Only the useful option will be
use add by make xconfig or make manuconfig.
The steps to configure the kernel are;
Enter into the Makefile at the top directory or Linux kernel. Inside Makefile you will find
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?=
change this two line with
ARCH ?=arm
CROSS_COMPILE=/arm- linux-
After setting the Makefile , Run these command.
$make allnoconfig
$make ARCH=arm xconfig

It will alllow you to see a graphical view to configure the kernel.
I am explaining about minimum configuration to port the kernel.

|------- General Setup------->Optimize for size
|
|------- System Type---------->( your system type, that will support by your processor)
| | |
| | |
| | Processor type
| | |
| | |_ _(select your appropriate processor ex:Support
| | ARM926T processor)
| |
| |
| Select Board Type------->(Select Board type that will is very match with your SDK
| ex:AT91SAM9260>
|-------- Boot option--------------->set (ZBOOT_ROM_TEXT)
| |
| |-------------------->set (ZBOOT_ROM_BSS)
| |
| |--------------------->set (CMDLINE)
|
|-----------Floating point emulation----(at least one has to be select)
|
|----------User space binary formats--------(select one)
|
|----------Character devices-------------(Select proper Serial deriver)
|
|----------Kernel hacking---------------(Debugging kernel)

What ever has been explain above all are very important to configure a kernel for some specific
architecture.
Please take care about Boot option. If you want to see your kernel message at debug terminal, then set the CMDLINE option properly. For example “console=ttyS0,115200 mem=64M”. This is the command line option for AT91SAM9260-ek board to see the kernel message at serial debugger port. This CMDLINE is very important, and to know more about what are those parameter that can be passed to the kernel boot option you have to go through the /linux/Documentation/kernel-parameters.txt .

To see kernel message at debug terminal another option should choose carefully, that is Character device.
Now your kernel configuration has finished. Now run this command and relax :-)

$make ARCH=arm CROSS_COMPILE=(your cross compiler path)/arm-linux-
After successfully compiling the kernel you will get the compressed from of kernel image at
/linux($ver)/arch/arm/boot/zImage or Image or at top dotectory or kernel you will get the uncompressed vmlinux.

Port the kernel.
How to port the kernel at flash memory that is depend on SDK supplyer. So they will provide you all require information. But at first stage should be the debugging stage, and you can use ICE/JTAG. Now using ICE/JTAG download the vmlinux at SDRAM or your SDK. (Before download you must have to set the JTAG for your SDK).
Now run/execute the kernel.
If every thing is ok then you will able to see the kernel message(using printk) at your serial terminal.
If no message is coming then look at the minimum requirement for the booting the kernel successfully.

Minimum requirement to boot the kernel.
The kernel start booting when boot loader has performed all the initial task. The bootloader should initialize and enable one serial port on the target. This includes enabling any hardware power management etc, to use the port. This allows the kernel serial driver to automatically detect which serial port it should use for the kernel console (generally used for debugging purposes, or communication with the target.)The bootloader must pass parameters to the kernel to describe the setup it has performed, the size and shape of memory in the system and, optionally, numerous other values. Once the bootloader has performed all the other steps it must start execution of the kernel with the correct values in the CPU registers.

The kernel entry requirements are:
The CPU must be in SVC (supervisor) mode with both IRQ and FIQ interrupts disabled.
The MMU must be off, so code running from physical RAM with no translated addressing.
Data cache must be off.
Instruction cache may be either on or off.
CPU register 0(R0) must be 0.
CPU register 1(R1) must be the ARM Linux machine type.
CPU register 2 (R2)must be the physical address of the parameter list.
The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image.

So if the kernel debugging message is not coming at serial terminal, then please check all the above points.

Generally all embedded linux boot loader (like u-boot, redboot)provide the minimum require information including machine type(register R1). But some time we are not so much lucky. So better first check all the above minimum requirement has been full filled or not.

You can check and set register value by the JTAG. Or to set the register value at kernel source code you have to customization at /linux($ver)/arch/arm/kernel/head.S.

A machine type should be obtained as early in a projects life as possible, it has a number of ramifications for the kernel port itself (machine definitions etc.) . These values are represented by a list of defines within the kernel source (linux($var)/arch/arm/tools/mach-types).

Now if you able to get the kernel debug message at the serial terminal, at the end of message you will see some kernel panic that no rootfs has found. So next job is to mount the root file system.

Mount Root File System(initramfs):

After porting kernel , next to mount the linux rootfs. Initramfs is the most easy way to mount to root fs at the begining.
The successor of initrd is called initramfs. initramfs, a Linux 2.6 feature that enables an initial root file system and init program to reside in kernel memory cache, rather than on a ramdisk, as with initrd files systems. Compared to initrd, intramfs can increase boot-time flexibility, memory efficiency, and simplicity.

Key feature of initramfs
1.It is single file, CPIO archive
2.Contains a list of files, device nods, directories
3.Contains an init binary file, which is executed early in the boot process (hence the name, “early-userspace”).
To get more information please read /linux($ver)/Documentation/early-userspace/README
To create a initramfs archive, you need to set the CONFIG_INITRAMFS_SOURCE option in the kernel’s configuration to point to a text file, which describes the content of the initramfs archive. Also have to set BLK_DEV_INITRD is yes at kernel configuration option.
Initramfs text file

First i am putting a sample initramfs.txt file that i had use for rootfs mounting on some ARM9(lh7a404).
#Develop by: Deb
#Project Name :**********
#Date:19/04/07
########################
# /dev directory
dir /dev 0755 0 0
nod /dev/tty0 0666 0 0 c 4 0
nod /dev/tty1 0666 0 0 c 4 1
nod /dev/tty2 0666 0 0 c 4 2
nod /dev/tty3 0666 0 0 c 4 3
nod /dev/ttyp0 0666 0 0 c 3 0
nod /dev/ttyp1 0666 0 0 c 3 1
nod /dev/ttyp2 0666 0 0 c 3 2
nod /dev/ttyp3 0666 0 0 c 3 3
nod /dev/ttyAM0 0666 0 0 c 204 16
nod /dev/ttyAM1 0666 0 0 c 204 17
nod /dev/console 0666 0 0 c 5 1
slink /dev/console /dev/ttyAM1 0666 0 0

# /etc directory
dir /etc 0755 0 0
dir /etc/init.d 0755 0 0
file /etc/inittab /root/Desktop/initramfs/inittab 0755 0 0
file /etc/init.d/rcS /root/Desktop/initramfs/rcS 0755 0 0

# misc. directories
dir /root 0700 0 0
dir /proc 0755 0 0
dir /sys 0755 0 0

# bin/sbin directories
dir /sbin 0755 0 0
dir /bin 0755 0 0

file /bin/busybox /root/Desktop/busybox-1.2.1/busybox 755 0 0

slink /bin/ash /bin/busybox 0755 0 0
slink /bin/sh /bin/ash 0755 0 0
slink /bin/echo /bin/busybox 0755 0 0
slink /bin/mount /bin/busybox 0755 0 0
slink /bin/getty /bin/busybox 0755 0 0
slink /bin/ls /bin/busybox 0755 0 0
# 'init' stuff
slink /init /bin/busybox 0755 0 0
slink /linuxrc /bin/busybox 0755 0 0
slink /sbin/init /bin/busybox 0755 0 0
#file /hello /root/Desktop/initramfs/hello 755 0 0
##############END OF initramfs.txt FILE ##############################
Please look initramfs.txt file has two external dependency. Busybox(Busybox binary) and rcS(script).
So to build initramfs, you need:
busubox execuatable.(download busy box and build it yourself, same like kernel build)
Ender into busy box source directory, Change the Makefile same as you did when compile the linux kernel and run these command

$make allnoconfig
$make ARCH=arm menuconfig
$make ARCH=arm CROSS_COMPILE=//arm-linux-
The main feature of initramfs.txt file is
/dev directory with some device nodes (tty, console)
/bin/busybox executable
/bin directory with many softlinks to the busybox binary.
/etc/init.d directory
/etc/init.d/inittab script, gets executed by busybox on start-up.
/init softlink to the busybox binary.

######Sample inittab#####
::sysinit:/etc/init.d/rcS
::respawn:-/bin/ash
a1:12345:respawn:/sbin/getty 115200 ttyAM1 vt100
######################
In case of initramfs , the kernel boot option(ie CMDLINE) is rdinit=/init, to do the test for initramfs you should first try with hello world. Write hello world program in c, cross compile it and keep the hello binary at a suitable path. Just add this line at the txt file
file /hello /root/Desktop/initramfs/hello 755 0 0
and then edit kernel CMDLINE with rdinit=/hello.

NOTE: cross compile hello world should be statically compile, using this command
$arm-linux-gcc -static -o hello hello.c.
Check what kind of binary is this(hello)
$file hello
check initramfs.
$ mkdir -p check_rootfs
$ cd check_rootfs
$ zcat ../usr/initramfs_data.cpio.gz > initramfs_data.cpio
$ cpio -i --make-directories <>
$ rm -fr *
$ cd ..
Now you can take a copy of this txt file and edit the path and console and nod's major and minor number's according to your board. initramfs is not a permanent rootfs, but a first stage solution of rootfs. After getting success on initramfs you can move on to NFS/ramdisk rootfs. Still now i am trying to give a theoritacal knowledge about kernel porting. Now its time to take a live and hand experience . So all the best and if you have any problem then just drop me an email.

Comming soon
Porting X server.
Writting Device Driver for embedded devices.