Next Previous Contents

6. The porting process.

This section will explain how we attacked the problem of porting Minix v2.0.2 (i386) to an iPAQ (StrongARM CPU). Its quite technical and many hours of sweat have been poured over our stupid mistakes, so don't dare to e-mail us any corrections/ideas. We will silence you :p

6.1 Get to know thyself

Actually, its should be Get to know the compiler. Understand from the previos section how the compiler works, how to cross-link assembler and C source code. How to deal with text-location and working around no support from libc.

If you think you got a grasp on that, then you are good to go.

6.2 Some thoughts

Modularize modularize and modularize everything you can. Do little test/suites thing to make sure that your code works.

Do daily backups of your data.

Don't drink too much coffee. You will end up spilling it on the keyboard.

6.3 Providing Input to the Ipaq

As mentioned in an earlier section, input to the Ipaq can be achieved in 2 ways: via its touch screen or serial port. It is plainly obvious that the latter is the only feasible method of providing input to the Ipaq in this case because the touch screen requires some sort of windowing system and touch screen driver to be useable. Therefore, the Ipaq had to be connected to the host PC (running Linux) via a serial cable. The terminal program, Minicom, was used to transfer data (such as files and keystrokes from the host PC) to the Ipaq. Output from the Ipaq was returned to Minicom via the same serial cable. Any terminal program can be used, provided the following settings are maintained: 115,200bps, 8 data bits, no parity bits, and 1 stop bit. Flow control must also be disabled in order for this to work.

For screenshots of our accomplishment visit this iPAQ Linux success! url.

6.4 Bootloader installation

Instructions for installing the bootloader can be found at the Handhelds.org website.

The bootloader was exactly what we wanted. After we looked at the source code, it became evident that we have multiple ways of loading our kernel. We could either load in the memory (load ram), which would mean that our starting position would become 0xc0000000. That is of course the flag that must be submitted to the linker. Otherwise you are screwed.

6.5 Printk and Standard Output

It is obviously essential to have the ability to print to standard output in order to be able to print messages and other information when testing and debugging the kernel. On the PC, output to the screen is achieved by simply writing data to the frame buffer of the video card. Due to the Ipaq's radically different architecture, it is not possible to do this in a manner similar to the PC. Therefore, the simplest means of outputting information from the Ipaq was to write data to its serial port. The information will then show up in the window of the terminal program (Minicom) running on the host PC. The following snippet of ARM assembler code does this:

boo.s



 .text

                .global putc            @ void putc (char x)
                .global uart_init       @ void uart_init (void)

#define UTCR0   0x00
#define UTCR1   0x04 
#define UTCR2   0x08
#define UTCR3   0x0c
#define UTDR    0x14
#define UTSR0   0x1c
#define UTSR1   0x20

#define BAUDRATE        115200
#define BAUD_DIV        ((230400/BAUDRATE)-1)

                .align

test_putc:
                mov     r0,#'M'
                bl      putc    
                mov     pc,lr   
                
putc:
                ldr     r3, UART_BASE
                mov     r1, r0
                mov     r0, #0
                b       1f
                mov     pc,lr

uart_init:
                ldr     r3, UART_BASE
                mov     r1, #0
                str     r1, [r3, #UTCR3]
                mov     r1, #0x08               @ 8N1
                str     r1, [r3, #UTCR0]
                mov     r1, #BAUD_DIV
                str     r1, [r3, #UTCR2]
                mov     r1, r1, lsr #8
                str     r1, [r3, #UTCR1]
                mov     r1, #0x03               @ RXE + TXE
                str     r1, [r3, #UTCR3]
                mov     r1, #0xff               @ flush status reg
                str     r1, [r3, #UTSR0]
                mov     pc, lr

UART_BASE:      .long   0x80050000              @ UART3

This code outputs the character 'M' to standard output, which in this case, is the serial port of the Ipaq. At the host PC, the character 'M' will appear in the terminal program's window. The function uart_init is responsible for initializing the serial port on the Ipaq and putc writes a single character to the serial port. Modifying this code to print a sequence of characters i.e. a string is relatively simple. Since the putc function has been declared global, it can be accessed from a C program. Then, within the C program, a loop that repeatedly calls the putc function by passing it pointers to characters can execute in order to print out a character sequence. Alternately, a puts function that does the same thing can be coded in assembler. The latter method is probably more efficient.

However, this code is not entirely sufficient for our purposes. For example, suppose we want to print formatted output such as decimals and hexadecimals. Since we are not using any libc, we cannot use functions such as printf. However, the Minix kernel has a function called printk, which is short for kernel print. Kernel print uses a put character function (which we already have -- see above) to print formatted output. This function is written in C, so it can be linked with the assembler code above. Please refer to the Minix v2.0.2 source for a full listing of this function.

6.6 Assembler Libraries (klib and mpx)

here are two main libraries used in the Minix i386 kernel. These are klib386.s and mpx.s. Hence, the first task in porting the Minix kernel to the Ipaq is to convert these libraries to run on the StrongARM CPU. Due to the vast differences in hardware between an Ipaq and an IBM PC, some functions required on the PC side may not be required on the Ipaq and vice-versa.

The klib contains a number of assembly code utility routines required by the kernel. In the klib386.s for the IBM PC, there are many routines that are PC-specific such as functions to copy and move data to the frame buffer of the video card. To get a minimalist Minix kernel running on the Ipaq, the following functions from klib386.s were ported to the StrongARM CPU to form a new library called klibsa1110.s:


void phys_copy(phys_bytes source,phys_bytes destination,
               phys_byes bytecount);

void lock();

void unlock();

void exit();

The phys_copy function simply copies a block of physical memory. The source, destination, and bytecount are represented as unsigned longs. The implementation for the StrongARM CPU loads 4 words at a time and stores them on a memory stack. The lock() and unlock() functions disable and enable CPU interrupts respectively. The exit() function is provided merely as a convenience for library routines that use exit. Since no calls to exit can actually occur within the kernel, a dummy version that simply returns to the caller is implemented.

The mpx library handles process switching and message handling. All transitions to the kernel go through this library. Interrupts (software and hardware) and sending/receiving message calls can cause transitions to the kernel. The most important function in this library is the system call (s_call) function. The system call function handles interrupts caused by system calls (software interrupts / SWI's). This was the only function ported to the StrongARM from the mpx.s library.

6.7 Setting up a stack for the C Environment

The kernel must be allocated a stack in main memory for it to operate in. The following snippet of assembler code does this:

head.s


 .text
                .align 2
                .global boo

                .text
boo:
                ldr sp,STACK
                bl _start
                
STACK:          .long 0xc0080000



Next Previous Contents