2009-08-04

How to ARM Linux

During last few years I've never found a good excessive topic covering most of the areas to get Linux runnnig from scratch. I remember myself doing a lot of investigation about correct building of a crosscompiler, studying bootloader internals, and so on... I'm definitely sure - if I had a person that could point me in right direction, then all the development should finish much earlier.

Consider this article as my personal note and feel free using it your own way.

1. Boot Sequence


It is important to understand how the board is able to load Linux. Entire start-up process can be divided into 3 parts.

1.1. 1-Stage Bootloader


Right after board start-up all the peripherals are uninitialized. The purpose of 1-stage bootloader is to initialize most vital peripherals which are required at the moment - RAM, flash, and console (serial), then transfer 2-stage bootloader from flash to RAM, and execute it.

I will not mention 1-stage bootloader in the rest of article, as this is specific to the CPU you are using. You should have it already on your resource CD from your development board, including sources. Please note, that in some circumstances you may need to change 1-stage bootloader, so that it can download, for example, 2-stage loader from different resource, which is unsupported by your original 1-stage loader.

1.2. 2-Stage Bootloader


This stage is required to initialize remaining peripherals and finally load kernel and execute it.

Now, you may wonder, why 2-stage bootloader is actually required if everything can be done by 1-stage bootloader?
Answer is simple: after the start-up memory is uninitialized and CPU can load code, limited to the amount of CPU's SRAM memory. Definitely, this size is not enough for a good bootloader that has all the possible goodies you may need.

In our example we will use most popular bootloader at the moment, U-Boot. Here is a good intro.

With a little effort of patching, if this is not already done, it is possible to have a single bootloader, which will serve as 1-stage and 2-stage bootloader at the same time. True, this is the case only if U-Boot is located in parallel flash. Check U-Boot documentation regarding relocation feature.

1.3. Kernel


Well, this is our task, to get kernel running.
Believe me or not, but this stage is easiest if we talk about debugging. For 1-stage and 2-stage bootloaders sometimes the only possibility you may have is a hardware JTAG.

2. Crosscompiler


This is a very first thing to start with. Before doing actual development you may want to play a little with your development kit and get to work at least 'hello world'.

Now, stop surfing the contents of a CD that you get with your development kit. Personally, I own bunch of developer kits, and all of the compilers I had received was outdated.

There is a bunch of pre-made crosscompiler packages out there that you may use out of the box. Check out this list.

Personally, I use crosstool-ng - a tool for building your own toolchain. This will give you ability to build everything you need: crosscompiler and libraries for your future filesystem. Here is my crosstool-ng (version 1.2.2) configuration file, in case if you are lost in configuration options.

After crosstool-ng will finish building your toolchain there is a good practice to modify your bash profile so you can switch to crosscompiler only when you want to:

.bashrc
# Toolchain related functions
toolchain-arm () {
    export ARCH=arm
    export TARGET=arm-linux-gnu
    export CROSS_COMPILE=${TARGET}-
}
    toolchain-none () {
    unset ARCH
    unset TARGET
    unset CROSS_COMPILE
}
Remember to update your PATH, so it will point to your new crosscompiler binaries:

.bash_profile
PATH=$PATH:$HOME/x-tools/arm-linux-gnu/bin
export PATH

Now, as example that everything is right, build a 'hello world' application:

test.cpp
int main(int argc, char **argv)
{
    std::cout << "Hello world!\n"; return 0;
}
arm-linux-gnu-g++ test.cpp -o test
This should successfully build you the test application for your ARM board. Just to be sure you can check it's ELF header:
arm-linux-gnu-readelf -h test
Which will give you something like:
ELF Header:
Magic:                             7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00
Class:                             ELF32
Data:                              2's complement, little endian
Version:                           1 (current)
OS/ABI:                            ARM
ABI Version:                       0
Type:                              EXEC (Executable file)
Machine:                           ARM
Version:                           0x1
Entry point address:               0x84e0
Start of program headers:          52 (bytes into file)
Start of section headers:          3732 (bytes into file)
Flags:                             0x202, has entry point, GNU EABI, software FP
Size of this header:               52 (bytes)
Size of program headers:           32 (bytes)
Number of program headers:         6
Size of section headers:           40 (bytes)
Number of section headers:         33
Section header string table index: 30
That's right, you just have built your first ARM binary using your vanilla gcc crosscompiler. While you are playing with getting sample applications to work on pre-installed board, you may face some errors with missing libraries. This is absolutely normal, because your pre-installed linux was build using toolchain with different versions of system libraries, and your application needs one or more libraries that is missing at the moment. Solution is easy - you need to transfer these libraries from the toolchain you've just build to your board. List of the libraries required by your application may be obrained using following command:
arm-linux-gnu-readelf -d test
Which will tell you which libraries the application needs:
0x00000001 (NEEDED)                     Shared library: [libstdc++.so.6]
0x00000001 (NEEDED)                     Shared library: [libm.so.6]
0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED)                     Shared library: [libc.so.6]
Now, just copy the missing libraries from the sys-root directory of your toolchain (for me, they are located in ~/x-tools/arm-linux-gnu/arm-linux-gnu/sys-root) to your board's /lib directory and the application will work.

This article is a draft... Please come back later.

No comments:

Post a Comment