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
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.
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.
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.
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.
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:
.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.
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.
The kernel must be allocated a stack in main memory for it to operate in. The following snippet of assembler code does this:
.text .align 2 .global boo .text boo: ldr sp,STACK bl _start STACK: .long 0xc0080000