Arivendu Bhardwaj Porting Embedded Linux on ARM Core Arivendu Bhardwaj Abstract : In the realm of embedded technologies ARM (Advanced RISC Machine) is very popular. According to Wikipedia around 70% of all 32 bit embedded CPU’s are based on ARM architecture. Its usage is growing in cell phones, PDA’s, 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 board’s 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. Key Words: ARM, Operating System, Linux, Kernel. 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 it’s 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. Proceedings of ASCNT – 2009, CDAC, Noida, India, pp. 184 –193 184 Porting Embedded Linux on ARM core Device drivers Operating System Peripheral Peripheral Processor (ARM) Fig.1. Organization of an Embedded Module Though the Embedded OS is absent in many low end embedded modules which don’t support any or much of peripherals and I/O drivers. It’s 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 doesn’t 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. 185 Arivendu Bhardwaj 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. 186 Porting Embedded Linux on ARM core 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 = A new default ‘config’ file (named ) has to be added in linux/arch/arm/def-configs. Any old “.config” file should be deleted. On executing # make _config (for2.6.xx kernel) And # make _config # make oldconfig (for 2.4.xx kernel) The file 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 Fig.2. The kernel configuration & compilation sequence 187 Arivendu Bhardwaj 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. ATAG_CORE Base address ATAG_MEM ATAG_NONE Fig.3. The ATAG structure Increasing address 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. It’s 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 0x28 0x2C 0x016F2818 start address end address Magic number (identifies the ARM Linux zImage) Starting address of the zImage End address of the zImage 188 Porting Embedded Linux on ARM core 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 can’t be overwritten. The recommended place is, start of RAM + 0x100. It must not extend past 0x4000 boundary, as after that kernel’s 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 referred [8]. e) 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 189 Arivendu Bhardwaj This should list the start address from where the kernel image has to be decompressed, the targeted environment variable is, ‘ZTEXTADDR’. It’s the same physical address to where ARM Linux is placed in the bootloader code. Usually it’s 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- 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-/arch.c It includes the macros used for machine setup, i.e. MACHINE_START and fixup_ MACHINE_START(, "") MAINTAINER(".") BOOT_MEM(,, ) VIDEO(, ) FIXUP(fixup_) MAPIO(_map_io) INITIRQ(genarch_init_irq) MACHINE_END fixup_ is used for boot time updation of entries. [9] 190 Porting Embedded Linux on ARM core 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(): to receive the ‘word’ from IO ‘’. arch_putw(,): send the ‘word’ (data) to ‘’. 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: _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(): 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 .h file. Here the configuration and setup is done for: Master clock, can be main oscillator, PLL or slow clock. 191 Arivendu Bhardwaj 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. 192 Porting Embedded Linux on ARM core References [1] [2] [3] [4] [5] [6] [7] http://www.kernel.org [last accessed on 24/12/08] http://www.arm.linux.org.uk [last accessed on 05/01/09] http://ftp.arm.linux.org.uk/pub/armlinux/toolchain/ [last accessed on 05/01/09] http://www.arm.linux.org.uk/developer/machines/[last accesed on 05/01/09] RusselKing“KernelCompilation”,http://www.arm.linux.org.uk/docs/,[last accessed on 06/01/09] RusselKing,“BootingARMLinux”,http://www.arm.linux.org.uk/developer/booting.ph p, [lastaccessedon 15/01/09] 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] http://sourceforge.net/projects/u-boot/ [last accessed on 20/12/08] Wooking and Tak-Shing, “Porting the Linux kernel to a new ARM Platform”, Aleph One, vol. 4, summer 2002. Deepak Saxena, “porting Linux to a new ARM platform”, proceedings of “Linux Banglore”, 2004. [8] [9] [10] About Author Mr. Arivendu Bhardwaj received his Bachelor’s degree in Electronics and Communication, from Kurukshetra University, Kurukshetra, and P.G. Diploma in Embedded Systems and VLSI Design, from CDAC NOIDA. He joined CDAC NOIDA in 2005, and is presently working here as a Project Engineer, in Embedded Systems Lab. He has worked on system programming for 8/16 bit microcontrollers, GSM/GPRS engine, IP Set top Box and Linux internals. He is presently working in the UHF RFID domain, involving single chip RFID Radio and ARM firmware controller. His areas of interest include ARM architecture, Linux kernel internals and inter chip communication protocols. 193