I would like to thank Prof. D.K.Sharma for his invaluable time that he has so generously given to me in past 4 months. I am thankful to Ashrut Ambastha for this support and mentorship throughout the project. I am grateful to Mr. Dave Anders for his constant help and always pointing to the correct sources. Dave has been microsoft free since 1997 and can be reached at : http://www.elinux.org/wiki/prpplague
-- Aneesh Nainani
The motivation behind this project is to boot linux on a ARM board.
The hardware config is as follows -
- We are using a ARM720T based Sharp SoC LH79520
- 16Mb of SDRAM (Sharp - MT48LC2M32)
- 4Mb Flash (LHF00L15) ( not CFI complaint )
All of this was developed by Ashrut Ambastha (IIT-Bombay), if you have doubts regarding the hardware config or want to make a ARM development board yourself you can get in touch with him on elecashrut@yahoo.com.
Though i have mentioned the hardware for the sake of completeness, my job to put in the simplest of words is to boot linux on a LH79520 based board with 16M of RAM ( the FLASH being non-CFI complaint nor uses a standard INTEL command set, hence is of no good exceot to store the boot loader). I would be most happy to see a bash prompt on the minicom window. A output like given below will mark the termination of the project.
$ cat /proc/meminfo
total: used: free: shared: buffers:
cached:
Mem: 6897664 4481024 2416640 0 77824
2781184
Swap: 0 0 0
MemTotal: 6736 kB
MemFree: 2360 kB
MemShared: 0 kB
Buffers: 76 kB
Cached: 2716 kB
SwapCached: 0 kB
Active: 580 kB
Inactive: 2640 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 6736 kB
LowFree: 2360 kB
SwapTotal: 0 kB
SwapFree: 0 kB
$ df -h
Filesystem Size Used Available
Use% Mounted on
/dev/root.old 1.9M 1.4M 509.0k
73% /
$
The fundamental requirement for a GNU/Linux system is a kernel and a filing system for it to execute things from.
The kernel is an autonomous piece of code that doesn't need any other files or libraries to get started, although without a root filing system it will simply stop after initialising the system as it no files to operate on.
The filing system can be loaded from a range of different hardware devices (hard disk, RAM, ROM, CD and NFS mount) and must be in a format that the kernel understands. For embedded systems it is often a RAMdisk. The kernel and RAMdisk both need to be loaded to correct places in memory and the kernel executed. This can be done in various ways, typically by loading over a serial connection or from local flash RAM.
In order to be able to load anything from anywhere some kind of bootloader must be present on the target hardware which knows how to load files and execute them. This can be installed in a physical fashion by inserting a pre-programmed ROM/EEPROM or flash chip, but is more usually installed using a JTAG port, which allows instructions to be executed from an external input, and thus an initial program loaded.
There are many peculiarities involved with booting linux on an embedded system as compared to a PC such as cross compiling, debugging etc which will be taken as they incurre in the following section.
The wisdom that we are presently using is :
Getting the target side of the bootloader installed is normally done with the Jflash utility (both Windows and Linux versions exist). This drives the JTAG interface through the parallel port of the host PC. The Linux version is called JFlash-linux.
Note that this program is different for each target as it depends on knowing the exact hardware of the target.
The included source code was slightly modified for LH79520 by Dave Anders (dav123_aml@yahoo.com). It was then modified to support the flash chip we are using by yours truly.
On Linux, this should be compiled with:
There's a warning about 'gets' being dangerous. If someone could tell me (Dave) what _should_ be used, I'll get rid of it. Dont play around much with the JTAG functions as some linux developers quote "JTAG is a real brain-damaged protocol (it was designed by a committee, need we say more?)".
The executable can only be used by root. Please be sure their arent any application ( check _minicom_ in particular) using your parallel port to avoid conflicts.
Bootloaders are highly system-specific. The two choices we had for a bootloader for LH79520 were Blob and Apex.
Blob is the Boot Loader OBject. Blob started its life as a boot loader for the famous LART, but a patch is available for lh79520. Its able to boot a Linux kernel stored in flash or RAM and provide that kernel with a RAMdisk (again from flash or RAM). The limitation with blob is when downloading the RAMdisk it tries to burn it directly to the flash, since out flash is not supported by the standard CFI / INTEL flash drivers blob isnt able to write to it. Hence we root for APEX which stores the RAMdisk / Kernel received from serial port in RAM.
APEX superceeds Blob . It was written to support specifically the Sharp LH series of SystemOnChip processors though it has been ported to a few other ARM targets.
The source code is archived here: ftp://ftp.buici.com/pub/apex.
Some of its feature which appear nice are :
If the apex installation is sucessful on the Hyperterm ( Baud Rate = 115200 ) you will see something like
APEX Boot Loader 1.2.11 -- Copyright (c) 2004,2005 Marc Singer
APEX comes with ABSOLUTELY NO WARRANTY. It is free software and you
are welcome to redistribute it under certain circumstances.
For details, refer to the file COPYING in the program source.
apex => mem:0x20200000+0x8998 (35224 bytes)
env => nor:128k+64k
Use the 'help .' command to get a list of help topics.
apex>
Please do a help on the apex prompt for xreceive, dump, boot etc commands to get familiar with APEX.
To compile APEX, and also the Kernel later we need a ARM toolchain. The toolchain actually consists of a number of components. The main one is the compiler itself gcc, which can be native to the host or a cross-compiler. This is supported by binutils, a set of tools for manipulating binaries ( e.g linking, objdump etc ) . These components are all you need for compiling the kernel, but almost anything else you compile also needs the C-library (uClibc for embedded ).
As you will realise if you think about it for a moment, cross compiling the compiler etc is not a easy task. If you have a long weekend coming up and got nothing better to do "The GNU Toolchain for ARM targets HOWTO" by Wookey et. al tell step by step how to do so.
Or for a easier way out you can download the crosstool : ftp://ftp.buici.com/pub/arm/crosstool which will make your job easier.
If you are wiser you will just download a precompiled toolchain and start using it right away.
In case you are planning to compile a Linux-2.6 kernel make sure you have gcc version 3.3 or better , and binutils 2.16 or better.
One can choose from a variety of linux kernel (considering that Linus releases a new one every now and then ) to choose from. Patches for LH79520 are available for both linux 2.4 and 2.6. A tested 2.4 patch by Lineo Sharp can be found in the mARMalade distribution ( downloadable from www.earthlcd.com ), linux 2.6 patches can be downloaded from : ftp://ftp.buici.com/pub/arm/.
Despite applying the patch the kernel on building will bail out some errors ( mostly variable UNDEFINED etc) that can be easily fixed looking at the code. BTW, the Kconfig based build for linux 2.6 is much better.
To cross-compile the linux for ARM in the top level Makefile change :
ARCH =
with
ARCH = arm
and
CROSS_COMPILE=
to
CROSS_COMPILE=
eg.
CROSS_COMPILE=arm-linux-
Do a 'make meuconfig' to conveniently configure the kenrel and drivers you need,
followed by 'make dep', and finally 'make Image' to build the kernel (arch/arm/boot/Image). A compressed image can be built by doing a 'make zImage' instead of 'make Image'.
BEFORE compiling make sure to add the cross compiler to $PATH.
Kernel Configuration
Since we are working on a system with a very limited RAM (16M) its better to choose a Kernel with minimum possible config. That is -
- No loadable module support
- Disable most Drivers except just the Serial Ones.
- Stuff like SATA / MTD drivers / Parallel port etc can also be done away with
- File system support - ext2
RAMDISK and initrd support
Make sure this option is selected in the Kernel Config. Also since the RAM size is limited one can change this the default no of RAMdisks from 16 ( only in linux 2.6 ) and the deafault RAMdisk size from 4096K to 2048K.
Default Kernel Command Line
Must be "root=/dev/ram0 mem=8M"
Verbose Kernel Debugging
Though it adds to the kernel size, its worth including specially in the initial development stage.
The RAMdisk is a very useful kernel facility that lets you load the files you need on the system into RAM along with the kernel. It take the form of a compressed filesystem. The kernel automatically allocates RAM for it and uncompresses it, then mounts it as the root filing system.
Again, there are many variations possible on this theme, but the conventional setup is to format the RAMdisk as ext2, the normal Linux disk filesystem. For this to work you need to specify the correct kernel options: RAMdisk support (CONFIG_BLK_DEV_RAM, CONFIG_BLK_DEV_RAM_SIZE, CONFIG_BLK_DEV_INITRD) and support for the filesystem used (normally ext2 - CONFIG_EXT2_FS).
There are many RAMdisk available for popular configs, we could not find "the one" that satisfies our needs, so made one ourselves.
To check an existing RAMdisk
Suppose you downloaded a RAMdisk image from the net (ramdisk.gz) and want to check if its a vaild image and what all it contains. You need to gunzip it and loop-mount it as a filesytem. Your kernel need loop support for this to work, but a desktop kernel will nromally have this (it's really useful).
gunzip ramdisk . gz
mount -o loop ramdisk /mnt/ramdisk
Now you can see the contents by browsing /mnt/ramdisk like any other filesystem. You can even add files by copying them in if there is space left in the RAMdisk. The kernel needs to have support for the filesystem used in the RAMdisk.
To make a RAMdisk
The steps involved are:
So here's an example. The size of the RAMdisk is 2MB, prepared files are in the directory preparedfiles, and we are making a conventional ext2 RAMdisk, rather than something more exotic. /mnt/ramdisk should already be created as a mount point. You will need to be root.
dd if=/dev/zero of=/dev/ram bs=1k count=2048
mke2fs -vm0 /dev/ram 2048
mount -t ext2 /dev/ram /mnt/ramdisk
cp -av preparedfiles /mnt/ramdisk
umount /mnt/ramdisk
dd if=/dev/ram bs=1k count=2048 | gzip -v9 ramdisk . gz
The -m0 option to mke2fs specifies that no extra space for the super-user is reserved again to minimise the size of the filesystem.
Make RAMDISK alternate : buildroot
Buildroot (http://buildroot.busybox.net) can be used to generate the RAMdisk if you want to make a RAMdisk with BusyBox utils. BusyBox (http://busybox.net) combines tiny versions of many common UNIX utilities into a single small executable for ARM. It provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox generally have fewer options than their full-featured GNU cousins.
Famous Error 1
If the bootloader (apex) parameters (e.g for ARCH_LH79520 etc ) are not the same as the Kenrel on booting you will get what Dave calls the Famous Error 1 in the embedded linux world. As the bootloader supplies all these parameters to the kernel on booting.
Silent Kernels
One of the problems in development of a kernel for a new machine type is early kernel hangs. Without feed back from the kernel it is difficult to understand what is going wrong. To combat this you can enable under "kernel hacking" the option for "kernel low-level debugging functions". The will enable the usage of the printascii() function. This writes hard coded information directly to the uarts. In order to make maximum usage of the printascii() function it is necessary to modify the printk() function so that all console messages are sent to the uart. A patch for the same is given below.
Index: kernel/printk.c
===================================================================
RCS file: /home/erik/cvsroot/elinux/kernel/printk.c,v
retrieving revision 1.1.1.94
diff -u -r1.1.1.94 printk.c
--- kernel/printk.c 2001/09/12 02:00:04 1.1.1.94
+++ kernel/printk.c 2001/09/18 16:32:33
@@ -412,6 +412,8 @@
printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
va_end(args);
+ printascii(printk_buf);
+
/*
* Copy the output into log_buf. If the caller didn't provide
* appropriate log level tags, we insert them here
Additionally you may need to modify the arch/arm/kernel/debug-armv.S so that the out is pointed to the correct uart.
#elif defined(CONFIG_ARCH_LH79520)
.macro addruart,rx
mrc p15, 0, \rx, c1, c0
tst \rx, #1 @ MMU enabled?
moveq \rx, #0x80000000 @ physical base address
movne \rx, #0xf8000000 @ virtual address
@add \rx, \rx, #0x00050000 @ Ser3
add \rx, \rx, #0x00010000 @ Ser1
.endm
in this example the printascii() result will be sent to Ser1 and Ser3 is commented out.
Kernel Oops
Unlike linux on the host machine the "ksymoops" program cannot be used to make sense of the dump. The best shot is to binutil "arm-linux-obdump -C vmlinux" and using the PC seach which instruction caused the offence.