Sunday, February 25, 2007

Kernel (almost) correctly recompiled for Tungsten E2 !!!

Good news ! I was able to boot a home compiled kernel for Palm Tungsten E2 today. :-) This time, I downloaded an older kernel revision from hackndev svn (revision 382, I think, from April 2006). It's a modified 2.6.16 and it's probably the revision nerdfeliz used to compile his kernel. The adjustments were simple, and after some trial and error, I was able to setup an USB serial port. Here are the steps to build the TE2 kernel:
  • download hackndev kernel revision 382
  • in the main Makefile, change the CROSS_COMPILE to reflect the prefix name of your cross toolchain (mine was "arm-linux-")
  • in arch/arm/mach-pxa/palmte2/palmte2_buttons.c, remove the __exit macro from palmte2_buttons_remove
  • do a make menuconfig and load nerdfeliz's configuration 20060406.
  • add usb gadget support, pxa usb support and serial gadget with CDC ADM support
  • in boot options, add "g_serial.use_acm=1" option to change usb serial behavior
  • make and cp
That's it. This kernel should boot. :-) To activate USB, we need the following trick:
  • mknod /dev/usb_serial c 127 0 (in TE2 /dev directory, not in your PC ;-) )
  • add a "getty -L 38400 usb_serial" line in TE2 /etc/inittab
  • load Garux and set the "Open Cradle USB" option
  • boot with the device connected to the PC
  • after the many usb error messages disconnect the USB cable, wait your desktop flush /dev (wait 5 seconds) and plug the USB connector again :-D
  • open gtkterm or minicom and open /dev/ttyACM0 in your desktop (or ACMx if you have other ACM devices)
All right, then ! Now, we have a working ARM linux system and a really expensive dumb terminal linked to it !!! Not quite. :-( I was unable to log in the remote system using gtkterm or minicom, either with "root" user or "default" (buildroot default user), even after playing with securetty and passwd/shadow configurations. Also, there's another sad bug: there's some kind of problem with the SD card driver, it keeps popping error messages. More to come...

Tuesday, February 20, 2007

Getting the vsyscall address from the ELF auxiliary vector [ref]

In a previous post I have showed how to make new style system calls in linux x86. I've also remarked that assuming that the __kernel_vsyscall address was always at a fixed address was wrong. The proper way to get the function address would be declare it as "extern", and link it with with linux-gate.so.1. However, I don't know how to do that, because linux gate is virtual and ld can't find it. I'll post here another way, that gets the information passed to the running software by the ELF loader. It retrieves an auxiliary ELF structure, that is present in the stack, after the usual argc, argv and envp pointers.

First, let's modify our previous hello.asm to call a new function "get_kernel_vsyscall", and set the address returned as the system call address:

; hello.asm
bits 32
global _start
extern get_kernel_vsyscall ; new function in vsyscall.asm
section .data
     hello: db 'Hello World !', 10
     hello_len: equ $-hello
section .bss
     linux_gate: resd 1 ; now, linux gate is variable
section .text
_start:
     ; New code to save the vsyscall address
     mov eax, esp
     call get_kernel_vsyscall
     mov [linux_gate], eax

     mov eax, 4 ; write()
     mov ebx, 1
     mov ecx, hello
     mov edx, hello_len
     call [linux_gate] ; call with dereference

     mov eax, 1 ; _exit()
     mov ebx, 42
     call [linux_gate] ; call with dereference


So, we changed "linux_gate" into a variable and filled it with the address returned by "get_kernel_vsyscall", basically. This function loops in the stack, beginning with argc, until it finds the correct (key, value) pair in the ELF header:

; vsyscall.asm
bits 32
global get_kernel_vsyscall
%define ELF_VSYSCALL_ID 32 ; defined in elf.h as AT_SYSINFO
%define ELF_EOF_ID 0 ; defined in elf.h as AT_NULL

section .text
get_kernel_vsyscall:
; Returns the kernel vsyscall address from the
; ELF aux vector. The vsyscall address is the
; subroutine used for the new style system call.
; in:
;     eax: the address, in the stack, of "argc"
; out:
;     eax: the kernel vsyscall address, or zero
;     on error

     push ebx

     ; First we skip argc and argv
     mov ebx, [eax]
     inc ebx
     inc ebx
     shl ebx, 2
     add eax, ebx

     ; For envp, we need a loop until NULL is found
envp_loop:
     mov ebx, [eax]
     add eax, 4
     cmp ebx, 0
     jne envp_loop

     ; Now we are in the ELF aux vector. Look for
     ; the correct (key, value) pair
     mov ebx, [eax]
     cmp ebx, ELF_VSYSCALL_ID
     je vsyscall_id_found
elf_vector_loop:
     add eax, 8
     mov ebx, [eax]
     cmp ebx, ELF_VSYSCALL_ID
     je vsyscall_id_found
     cmp ebx, ELF_EOF_ID
     jne elf_vector_loop
vsyscall_id_found:
     add eax, 4
     mov eax, [eax]

     pop ebx
     ret

Weird, eh ? We needed to look for the AT_SYSINFO field (ELF_VSYSCALL_ID in the code) in some extra vector provided by the ELF loader. Other executable formats would give those parameters some other way. We have two options, now: use a hardcoded vsyscall address or retrieve it from the ELF vector. Compile and link with "nasm -f elf hello.asm; nasm -f elf vsyscall.asm; ld -o hello hello.o vsyscall.o".

Hackndev custom linux kernel for PDAs [ref]

I've downloaded hackndev modified linux kernel. It's a heavily modified kernel, with support for a lot of ARM PDAs. After reading TE2 mailing lists and some fixups I was able to compile a Tungsten E2 specific kernel. I couldn't boot it, however, the screen just faded after booting and nothing else. I tried two different kernel revisions and two different "config" files, with no success. The kernel image that is correctly booting in my Palm is from April 2006. I'll continue my research to a proper kernel in hackndev svn around this date.

Sunday, February 18, 2007

New style system call in linux x86 [ref]

We all know that the x86 architecture is bizarre. An interesting event that has happened in the last 10 years was the change of the "system call" instruction. We all remember from DOS time that we issued "int 0x21" to make a DOS system call, and "int 0x10" to make a ROM BIOS system call. Some of us has even played with the linux system call, "int 0x80". However, five years ago, when using a Pentium IV, someone has found out that the "int" instruction could be half as fast in a Pentium IV than in a Pentium III. Why was that ?

Intel introduced a new system call instruction in Pentium Pro/Pentium II, called "sysenter" that was supposed to be a "Fast System Call" facility. This meant that Pentium processors had two system call instructions: one that was the fast system call and the other, the slow system call (currently, there are three system calls: "int", "sysenter" and "syscall" !!!). Linux has since migrated to new style system calls. Instead of changing the "int" logic to the "sysenter" logic, another approach was taken. A new virtual shared library was created, called "linux gate". This library is virtual because it's like a standard .so library, but only exists in memory (do a "ldd /bin/ls" and see if your system is using "linux-gate.so.1"). Applications (like libc or other system call front-ends) may then call a simple function exported from this library, __kernel_vsyscall, and this function will "systenter", "int" or issue whatever instruction is necessary for completing the system call. So here we have quick dirty "hello world" written in NASM assembly for linux (should work on linux 2.6 for x86). The code assumes that __kernel_vsyscall is always mapped at 0xffffe400 in memory (which is wrong ;-)).

; hello.asm
bits 32
global _start
section .data
   linux_gate: equ 0xffffe400
   hello: db 'Hello World !', 10
   hello_len: equ $-hello
section .text
_start:
   mov eax, 4 ; write()
   mov ebx, 1
   mov ecx, hello
   mov edx, hello_len
   call linux_gate

   mov eax, 1 ; _exit()
   mov ebx, 42
   call linux_gate

Notice that we are making a procedure call, not a system call. Compile it with "nasm -f elf hello.asm; ld -o hello hello.o" . :-)

Saturday, February 17, 2007

Pythonic C++

Pythonic C++ may not be possible. :-) However, this simple trick will allow you to better follow PEP 8 without recurring to pre/post-fixed notations when accessing variable or function members:


Car::Car(unsigned engines) :
  
Vehicle(4, engines)
{
  // use reference instead of pointer ;-)
  Car &self = *this;

  self.position = 5;
  self.movement = 10;
}

Amazing, isn't it ? :-D With just a simple trick we have boosted code readability by one million. PS: how do I highlight "self" ? :-)

Sunday, February 11, 2007

Back to buildroot, successful root system deployed

I was unable to compile glibc with the target architecture set to xscale-linux-elf. I think I should have used arm-linux at this moment, which is the same that buildroot uses. Back to buildroot, I've removed the software that was giving compile errors (jffs2/mtd utils) and was able to create a basic root filesystem. I was unable to use the system, however, because I don't have an installed keyboard and there was no virtual keyboard installed. I was unable to boot a newly compiled ARM stock kernel, with USB gadget support for telneting into it. Now I'll rebuild buildroot with Tiny X and Qtopia while I download hackndev's modified kernel and try to enable USB support.

Saturday, February 10, 2007

Cross compiling binutils [ref]

After being unable to automate a Xscale toolchain cross-build for my Palm TE2 with buildroot, I'll use the MIPS Cross Linux From Scratch to derive a native toolchain for TE2. The binutils cross-build worked with CLFS_TARGET=xscale-linux-elf.

Linux on Palm Tungsten E2 How-to [ref]

Meet Garux, a linux bootloader for Palm OS. There's a specific Palm Tungsten E2 Garux build in that page, along with instructions on how to install Opie on it.