You are on page 1of 13

Arivendu Bhardwaj

Porting Embedded Linux on ARM Core


Abstract : In the realm of embedded technologies ARM (Advanced RISC
Machine) is very popular. According to Wikipedia around 70% of all 32 bit
embedded CPUs are based on ARM architecture. Its usage is growing in cell
phones, PDAs, GPS devices and RFID systems. The Embedded modules, based on
ARM, can become very complex machines since these are meant to support varied
tasks such as memory management, process management and peripheral
interfaces. For seamless integration of these functional modules an OS has to be
ported on these ARM based CPUs .Traditionally this OS porting is often the
specialized work of third party vendors having expertise in this domain. For every
new CPU architecture, the OS has to be customized, compiled and burnt into the
core .With the coming of age of Linux as an embedded OS all this has changed quite
significantly. Being in Open Source domain, Linux kernel can be freely downloaded
and compiled for any system architecture and this includes ARM based systems
also. This enables the developers to port the OS themselves. This paper describes
the details of porting of embedded Linux on ARM core. The Kernel boot sequences,
where the platform dependent handles are required and the working of boards
Boot Loader are described in detail. Firmware to Kernel transition, early Kernel Init,
IRQ Setup, Core Subsystem initialization and final board Initialization are also
described.

1. Introduction:- In recent years, embedded Linux has been receiving a lot of


attention as a viable, low cost and robust implementation platform for high
performance embedded systems based on popular microcontrollers. Thus Linux
was the natural choice, when we decided to develop a UHF RFID reader based on
ARM7TDMI based AT91SAM7S256 module. This paper brings out our experience
in this development work. From a High level Design perspective, an embedded
system generally consists of the following functional blocks (Fig.1):
The Processor (in the present context its ARM).
The Embedded Operating System (OS) running on the processor, present
context targets Linux.
The peripherals around the processor, e.g. UART, SPI, MMU, Timers etc.
The drivers for the peripherals that support the specific Operating system.
Cross development tools-specific to the hardware as well as the Operating
System-consisting of compiler, debugger, emulator/simulator etc.
Though the Embedded OS is absent in many low end embedded
modules which dont support any or much of peripherals and I/O drivers. Its very
much required in high end systems that have to support a multitude of peripherals
and functional tasks. An embedded OS is often a specialised and customised
version of a general purpose OS like Windows XP, Linux etc. Whereas the general
purpose operating systems are non-deterministic in nature and have a large code
footprint (greater than 500 MB). An embedded OS on the other hand are very
efficient and compact in size (around 10 MB). They excludes the additional
modules of a general purpose OS which are not required in a task specific
embedded system. These embedded OS also often support deterministic and Real
Time behaviour where the OS has to render the services in a known and expected
time frame. The mathematical modelling of these service times doesnt have any
random components which can induce randomly missed deadlines in its services.
Non Real Time embedded OS also provide similar kernel services. The embedded
modules incorporating ARM processors need to support a family of peripherals and
system modules, e.g. the AT91SAM7S series has 32 peripherals, including:
AIC (Advanced Interrupt Controller).
PIO (Parallel Input Output port).
ADC (Analog to Digital Convertor).
SPI (Serial Peripheral Interface).
TWI (Two Wire Interface).
2xUSART(Universal Synchronous Asynchronous Receiver Transmitter).
In order to support them, all these modules have to be configured at start up time.
Doing this at system level requires writing of start up code often in ARM assembly
targeting each of these modules. This task of writing the start up code is quite
complex and time consuming and often leads to buggy code and malfunctioning
peripherals. Moreover, if a peripheral is added or changed in the system a complete
start up code has to be re-written from scratch. Only after this start up code is up
and running can the application software be developed for the module. If there is
an embedded OS running on the processor (ARM) this start up code can be done
away with as then that will be handled by the OS itself. The peripherals have just
to be provided with the required parameters as is done in a general purpose OS
and there is no need to individually program these peripherals. Even for a new
peripheral the task simplifies just to configure it. Starting with ARM9 and above
cores, which incorporate a memory management unit (MMU), the Linux kernel,
which always had modular support for memory management, is very much suited
for ARM core family.
The remaining sections of this paper are organized as follows: Section 2
describes Embedded Linux and the utility tools available for development. Section
3 explains the Linux kernel compilation process. Section 4 explains the Boot loader
details and its configuration. The files involved in kernel customization are dealt
with in Section 5. Section 6 deals with the system initialization process. Section 7
describes the importance of this development work within the overall
developmental framework of our project.
2. Embedded LINUX Embedded Linux uses the same kernel (the core) as used by
the desktop version and there is no need for a special kernel for embedded
applications. In fact the official kernel release [1] can be used to build an
embedded system. The kernel used in an embedded system differs from its desktop
version by its build configuration. Many third party vendors provide kernels that
are optimised and patched to support tools such as kernel debugging. An
embedded Linux system has this customised kernel together with the development
framework, specific to the hardware for which it has to be ported. This framework
includes cross-compilers, debuggers, boot image builders etc. This framework has
to reside on the development host system. Embedded Linux also support special
libraries, executables and configuration files to be used on the target system. With
Linux maturing as an embedded operating system, now the developers can
themselves port it on to a processor including ARM since the source code of the
Linux kernel is freely available. Also Linux is robust, flexible, has a large developer
community and large number of vendors supporting it. These are the major reasons
for choosing Linux as an embedded OS. Device drivers too are available for Linux
and being in open source domain there is no dearth of cross development tools
specific to Linux and ARM. A major online repository of Linux kernel, kernel patches
and cross development tools etc. for ARM platform can be located at [2].
3. The LINUX Kernel This section describes the setting up of Kernel sources, the
required tool chains, environment set up and the kernel compilation. 3.1 Kernel
source tree: On the onset we require a kernel source tree with ARM patches. Linux
follows a versioning system as,Kernel version x.y.z, where:
x: the major revision number
y: the minor revision number.
z: the patch level of the kernel
The ARM kernel tree carries a suffix to the version number i.e. vrsN or rmkN,
where N refers to the patch release number. This refers to the ARM kernel patch
that needs to be applied to the mainline kernel. e.g. 2.4.20 vrs2 np1 kernel is
obtained by patching the mainline 2.4.20 kernel with 2.4.20 vrs2 patch and 2.4.20
vrs2 np1 patch. The patches are hierarchical. So they have to be applied in correct
order. The patch files with more extensions depend on the ones with less extension
e.g. vrs2 np1 patch depends on vrs2 patch, hence vrs2 patch has to be applied
before vrs2 np1 patch.

3.2 Cross development tool chain The cross development tool chains based on
gcc-2.95.3 and gcc-3.0 are available as free downloads [3]. The tool chain needs to
be untared (tar xvjf/tar xvzf) in /usr/local/arm directory and the same has to be
appended to the PATH environment variable.
The files specific to ARM are in:
linux/arch/arm: the code.
linux/include/asm-arm: the header files Linux/arch/arm further contains the
following directories, among others:
kernel: core kernel code.
mm: memory management code.
lib: ARM specific internal library functions.
nwfpe and fastfpe: floating point implementations
boot: has the final compiled kernel.
tools: scripts for auto generating files.
def-configs: default configuration files

3.3 Machine registration The kernel tree identifies each device by a unique
machine ID. We need to get this ID from [4] and if the device is new, it has to be
first registered there in order to get its unique ID. This registration gives a unique
numerical identifier for the device, a configuration variable viz.
CONFIG_MACH_$MACHINE and provides for runtime machine checking. This file,
containing the ID has to be placed at linux/arch/arm /tools/mach-types. The script
linux/arch/arm/tools/gen-mach-types uses this file to generate linux/include /asm-
arch/mach-types.h, which in turn sets the various macros, to be used by the
source.
3.4 Configuration &Compilation For configuring and cross compiling the kernel,
the sequence given in Fig.2 has to be followed.
ARCH and CROSS_COMPILE variables in the top level Makefile have to be
edited with:
ARCH = arm
CROSS_COMPILE = <path_to_cross_compile_toolchain>
A new default config file (named <machinename>) has to be added in
linux/arch/arm/def-configs. Any old .config file should be deleted.
On executing
# make <machinename>_config (for2.6.xx kernel)
And
# make <machinename>_config
# make oldconfig (for 2.4.xx kernel)
The file <machinename> is copied from linux/arch/arm/ def-configs/ to
linux/.config.
To compile, execute:
# make clean
# make dep (optional for 2.6.xx kernel)
# make zImage (generates a compressed image)
#make modules
The compressed kernel image is compiled as arch/arm/boot/zImage [5].
4. The Boot Loader The ARM Linux needs a small amount of machine dependent
code to initialize the system. This code i.e. the bootloader, runs before the main
kernel, and without it the system cannot boot. Its equivalent to the BIOS for an X86
system. The minimum functionality required from the bootloader are: Initialize
the memory system. Initialize at least one serial console Obtain the ARM Linux
machine type. Place the kernel image at the correct memory address. Load the
Initrd (Initial RAM disk) Set up the boot parameters Jump to the kernel with
specific register values. Following changes have to be made to the configuration
files of the boot loader: a) In order to pass the physical memory layout to the
kernel, bootloader uses the ATAG parameters [6].
Fig.3, [6] depicts the ATAG structure as organized in the system memory.
base address
ATAG_CORE increasing

ATAG_MEMORY address

ATAG_NONE

Fig.3. The ATAG structure


b) The boot loader passes the relevant console= option to the kernel, specifying
the port, and serial format options. These options are detailed in
Linux/Documentation/kernel-parameters.txt.
c) The boot loader has to have the correct machine ID, either through hard code,
or through an algorithm. Its the same ID that is generated by the machine
registration process.
d) The compressed kernel image (zImage files) can be either loaded from the flash
or the RAM. If RAM is used the image can be placed anywhere, the recommended
place is 32KB (0x8000) into RAM. TABLE I [7], lists the important fields of the zImage
header.
Table1. Useful Fields In zImage Head Code
Offset into zImage Value Description
0x24 0x016F2818 Magic number (identifies the ARM Linux zImage)
0x28 start address Starting address of the zImage
0x2C end address End address of the zImage ATAG_CORE
ATAG_MEMATAG_NONE Base address increasing
Address.

e) The boot loader places the Initrd image, into the memory at a set location. It uses
the following parameters for this:
ATAG INITRD2:specifies the location of the compressed ramdisk image.
struct atag_initrd2
{
U32 start; /* physical start address */
U32 size; /* size of compressed ramdisk image in bytes */
};
ATAG RAMDISK:ensures that the ramdiskis large enoughfor the decompressed
Initrd image.
struct atag_ramdisk
{ U32 flags; /* bit 0 = load, bit 1 = prompt */
U32 size; /* decompressed ramdisk size in _kilo_ bytes */
U32 start; /* starting block of floppy-based RAM disk image */
}
f) The tagged list passed by the bootloader to the kernel has to conform to the
following constraints: The list has to be placed in RAM, where it cant be
overwritten. The recommended place is, start of RAM + 0x100. It must not extend
past 0x4000 boundary, as after that kernels TLB is created. It has to be word (32
bit) aligned. g) The boot loader calls the kernel image by jumping directly to the
first instruction of the kernel image. For both flash or RAM based, kernel
compressed image, following settings apply:
Processor registers
o r0 = 0
o r1 = machine ID
o r2 = address of the tagged list
Processor mode
o Disabled IRQs and FIQs.
o Processor has to be in supervisor mode (SVC).
In the present context, the GPL universal bootloader, U-Boot is reffered[8].
5. Kernel Customisation The following kernel files have to be edited for the ARM
platform
5.1 Entry Level Files
arch/arm/Makefile
It should have the proper macros to detect the machine name, e.g.
CONFIG_ARCH_xxx; here xxx refers to the specific machine names.
arch/arm/boot/Makefile
This should list the start address from where the kernel image has to be
decompressed, the targeted environment variable is, ZTEXTADDR. Its the same
physical address to where ARM Linux is placed in the bootloader code. Usually its
32KB inside the RAM.
include/asm/arch/uncompress.h
This is used to output the kernel decompression messages to the UART. It provides
following two functions to accomplish this:
1. arch_decomp_setup() : to setup the UART.
2. putstr() : to output a string to UART, it appends every \n with \r
arch/arm/kernel/debug-armv.s
or
arch/arm/kernel/debug.s
This file refers to assembly level debugging, for ARM 32 bit mode. The following
(assembly) functions implemented here communicate to a serial port, independent
of the kernel.
addruart rx: to obtain the address of the serial port in rx
senduart rd,rx: write the character at rd to rx (the serial port)
busyuart rd, rx : the wait function (transmit buffer empty)
waituartrd,rx: wait for handshake signal (clear to send)

5.2 Processor (CPU) Files


arch/arm/mm/proc-<CPUTYPE>
The file contains the table of cpu_mask and cpu_val supported by the kernel.
the cpuid of the target machine is compared against this table. If (cupid &
cpu_mask) = = cpu_val, only then the kernel will run on the target cpu(as specified
by the cpuid).
include/asm-arm/ directory files
Once the cpuid id correctly detected, the set up for the specific CPU is called. The
various functions and routines are coded in this directory. These functions include
CPU init(), idle(), tlbflush() and functions to synchronise both instruction and data
cache. arch/arm/mach-<machinename>/arch.c It includes the macros used for
machine setup, i.e. MACHINE_START and fixup_<machinename>
MACHINE_START(<machinename>, "<machinename>")
MAINTAINER("<vendor_name>.")
BOOT_MEM(<offset>,<peripheral_base_addr_phy>,
<peripheral_base_virtual>)
VIDEO(<start_addr>, <end_addr>)
FIXUP(fixup_<machinename>)
MAPIO(<machinename>_map_io)
INITIRQ(genarch_init_irq)
MACHINE_END
fixup_<machinename> is used for boot time updation of entries. [9]
6. System Initialisation
For Core Subsystem initialization of ARM platform the following files have to be
edited/customised.
6.1 IO Mapping
include/asm/arch/hardware.h
This file has to be edited according to the memory map and IO map of the hardware
architecture. The physical addresses (_START) are mapped on to the virtual
addresses (_BASE).
include/asm/arch/io.h
This lists the following macros:
IO_SPACE_LIMIT(): the upper bound for IO mappings.
arch_getw(<address>): to receive the word from IO <address>.
arch_putw(<data>,<address>): send the word (data) to <address>. All these
macros are hardware specific.
6.2 IRQs for ARM
include/asm/arch/irq.h
It lists the direct mapping of the fixup_irq to a literal. The base addresses of the
various IRQs have to be mentioned here, this is done in conformity to the memory
map of the ARM architecture.
include/asm/arch/irqs.h
This file defines the IRQ numbers, as used by the ARM architecture. These are the
integer values as assigned to the various ARM IRQs.
6.3 Timer Files
include/asm/arch/time.h
The timer interrupt handlers and macros are to be defined here, these are called
after IRQs are initialized. Major ones are:
<machinename>_gettimeoffset:
Returns number of microseconds since last timer tick. In absence of a user defined
function the default handler from arch/arm/kernel/time.c is used.
do_profile(<registers>): Does the kernel profiling, based on the given register
values.
define CLOCK_TICK_RATE : Defines the time period of the hardware clock. [10]
6.4 Low Level Board Initialisation Subsequent to the core subsystem initialization,
a low level Board intialization routine has to be executed. The execution pointer
has to be made to jump into this low level initialization routine. This routine
basically has a single function (which may be nested) e.g. lowlevelinit(). This is very
board specific and includes the <board_name>.h file. Here the configuration and
setup is done for:
Master clock, can be main oscillator, PLL or slow clock.
The Power management controller (PMC).
Advanced Interrupt Controller (AIC).
Peripherals e.g. SPI, I2C, user mode UART etc.
7. Importance of the Development Work The present work is an offshoot of the
project titled Design & Development of UHF RFID reader- under the aegis of the
National RFID Program- which involves the firmware microcontroller using ARM
core. The firmware for the reader is already complex, and needs a majority of time
and development effort. By porting Linux on the ARM based firmware controller,
the peripheral configuration and synchronization tasks, which presently is being
done through system programming, and is more often than not cumbersome, can
be handled by the OS and the reader application shall run via this OS layer. This
will not only save a lot of development time- which can then be utilized for the
application development- but will also result in a much more elegant, efficient and
modular architecture for the firmware. Moreover, as the present project/product
will mature, there shall be a need for added functionality, efficiency and additional
peripherals. This scaling up of the product can again be done much more elegantly
and efficiently if we have embedded Linux ported on to the ARM processor.
Further more, it can always be utilized as a firmware platform for the embedded
modules for future projects.
8. Conclusion The paper discussed a generic environment build of Linux for ARM
core family. This generic build has a limited functionality, as it just enables the
kernel to boot and send debug messages through the configured serial port. For a
full fledged embedded module this is quit elementary without the support for
various peripherals through device drivers which are paramount to the module.
Linux kernel though monolithic has an excellent modular approach which enables
the driver modules to be attached and detached at run time itself. This very much
suits the embedded environment which are always constrained for system
memory. The open source GPL community is again a boon in terms of drivers for
new devices and also to fix issues which would otherwise cramp the development
for new platform ports.
Acknowledgement This paper is the result of research efforts for the development
of a UHF RFID reader, under the National RFID program, sponsored and funded by
the Department of Information Technology, Ministry of communication and IT. The
author expresses his gratitude to the Department of IT and CDAC Management for
giving the opportunity to work on the project. The author also acknowledges Dr.
George Varkey, Exec. Director CDAC NOIDA, for his valuable guidance in completing
the paper, Dr. P.R Gupta, Head School of IT, CDAC NOIDA for her timely feedbacks
and Sh. Sourish Behera, Project Manager, CDAC NOIDA for his support during the
project.
References
[1] http://www.kernel.org [last accessed on 24/12/08]
[2] http://www.arm.linux.org.uk [last accessed on 05/01/09]
[3] http://ftp.arm.linux.org.uk/pub/armlinux/toolchain/ [last accessed on
05/01/09]
[4] http://www.arm.linux.org.uk/developer/machines/[last accesed on 05/01/09]
[5] RusselKingKernelCompilation,http://www.arm.linux.org.uk/docs/,[last
accessed on 06/01/09]
[6]
RusselKing,BootingARMLinux,http://www.arm.linux.org.uk/developer/booting.
ph p, [lastaccessedon 15/01/09]
[7] Vincent Sanders, Booting ARM Linux, rev.1.10, June
2004.http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html [last
accessed on 15/01/09]
[8] http://sourceforge.net/projects/u-boot/ [last accessed on 20/12/08]
[9] Wooking and Tak-Shing, Porting the Linux kernel to a new ARM Platform,
Aleph One, vol. 4, summer 2002.
[10] Deepak Saxena, porting Linux to a new ARM platform, proceedings of Linux
Banglore, 2004.

You might also like