[Galaxy S I9000 Android Development] Hexabit:
The idea of this exercise is (at least) to get a stable starting point for kernel development. The thing which is currently missing is a proper working .config. I have reconstructed it using differential analysis and in the process hoped to find which components have actually been activated and to uncover changes (or Easter eggs) in the sources which have not been advertised. Having a working and identical I9000XWJP6 kernel also means that open development can continue from the current official public release. From there the things possible are only limited by your imagination.

The following is a walk-through on how to build the kernel, description of pitfalls that will cause changes in .config to break, and some annotations on discoveries made in the process.

The things you need are:

Mandatory:

- I9000XWJP6 zImage : from your favorite location

- Source tree : opensource•samsung•com the GT-I9000 OpenSource Froyo Update JPM.zip

- Sourcery G++: www•codesourcery•com/sgpp/lite/arm/portal/release1039

- Tweak-Kit : <attached>

Optional:

- Arm enabled GCC and binutils, including a development libbfd.

- Lots of your favorite beverage

The md5sum of the zImage should be: 26e9d5d206baf1515144c6b8de6f10d2

It is critical that the Sourcery G++ version is 2009q3-67.

The Tweak-Kit contains the following components:

Readme.txt - You're reading it

mkvmlinux.cc - convert zImage to vmlinux and extract the init ramdisk image

I9000XWJP6_defconfig - default .config

stamp.patch - set date/time and such to original

style.patch - fix style related warnings

prototype.patch - fix prototype related warnings

error.patch - Recoverable errors

houston.patch - Unrecoverable errors

shadow.patch - Fixate data structures

I9000XWJP6.h - Fixated macros

I9000XWJP6.c - Entry point stubs

SOME THINGS I ENCOUNTERED IN THE PROCESS:

a) What I absolutely did not expect was that I found two different encodings of the build timestamp. I could deduce that the timezone was central Europe. I had the assumption it would be Asia or America.

b) What was to be expected is that the source tree is incomplete. The directories drivers/fsr and fs/rfs are missing. You can still compile the kernel as the missing files are used to build modules. Problems start when you change the config. Doing so will change entry points and data structures and your kernel might die a horrible death when it loads modules who are unaware on these changes. There is a workaround which I will explain later.

c) The weirdest thing I encountered were the functions enable_hlt() and disable_hlt(). The are located deep in the unwind tables, a section not intended for code. I spent many hours trying to figure out how they got there or why but I still have no clue. I found exactly only one way to reproduce this behaviour and it is certainly not due to a typo, accident or ignorance.

d) The kernel is not a production but a debug version. It has nearly all tracking/tracing/debug bells and whistles switched on. If the energy required to maintain the statistics where to emit light, you could use your Galaxy as a Christmas tree. Function profiling is enabled and has a considerable negative effect on performance, code is not optimized for size but speed, and unwind tables have been enabled which are not used. These have a really bad impact on footprint size. I really hope that the same compiler and settings are not used for the Android layer. Changing the config into a production version will not work (and crash) as the non-native modules expect the debugging hooks which will no longer exist. But the same workaround as above can be used.

e) The functionality of the power management domains have been optimized to oblivion due to the excessive placing of code disabling comments in large parts of the clock, power management and mach-aries.c. Maybe because the Galaxy hardware is too different than the evaluation boards, or the hardware is buggy and disabling the code makes it less unstable, or there was just not enough time to get the code working. Anyway, at this moment I have no oversight into what degree the absence of power domains influence battery usage.

f) When I started examining the binary code I was puzzled by snippets of code I could not reproduce. Even worse, I encountered snippets that were just questionable. Unusual instruction sequences, and resister usage. Thinking I bumped into a GCC bug, I started debugging the compiler and even tweaked instruction scheduling weights but with no satisfying outcome. I know that GCC is very stubborn with regard to saving and clobbering registers in/across function calls and the code I saw was just incorrect. I knew a different compiler was used and I suddenly realized that it may be more different than what first meets the eye. The culprit turned out to be Sourcery G++. It is a private maintained branch of GCC for reasons I have not investigated. Even the Sourcery assembler is tainted as it played a nasty trick on me with the enable_hlt/disable_hlt thing. I do not like the code I see and I am aiming into getting the sources stock GCC friendly with a working kernel. However, GCC and Sourcery generate code which seem difficult to mix, but I'm getting closer.

g) Compiler warnings. Many of the Samsung sources generate warnings, something I really dislike. In my opinion a warning is emitted for a piece of code which can be interpreted in several ways, leaving the compiler to choose which. Usually it will choose the wrong one. Most warnings were related to coding style shortcuts, a couple of incorrect function prototype resulting in functions that should return int to return random or falsely ignoring return values. There were also a couple of nasties like deference of uninitialized pointers, accessing out-of-bound data and mixing clock data-structures of different types. Included are a number of patches to fix them.

h) I looked deeper into why GCC and Sourcery won't mix and discovered that they have different implementations with regard to constant definition within enum declarations. Google points to the staring point "GCC bug 30260" where is written that the behaviour of enumeration constants has changed to becoming signed int. I have noticed that even explicit unsigned values will change to signed.

Here is an example of what is going wrong:

Take following declaration

enum rt_class_t { RT_TABLE_MAX=0xFFFFFFFF }

. GCC will consider RT_TABLE_MAX to be -1, and Sourcery will consider it 4294967295. Now, in net/ipv4/fib_rules.c there is this code snippet

for (u32 id = 1; id <= RT_TABLE_MAX; id++)

GCC will skip the loop, and Sourcery will have a hard time doing nothing.

There are more examples like calculating the location of physical memory or signed/unsigned comparisons. The compiler switches -fwrapv and -fstrict-overflow might influence things, but it general the behaviour is hardcoded and both compilers have a different flavour. I think it would be wiser to choose the GCC flavour as it is more widespread and thus better tested (and fixed).

i) GCC. I noticed that early versions of kernels compiled with GCC would not start. At first I thought it was because of Sourcery /GCC code generating differences. After a number of buxfixes (in error.patch) I suddenly noticed that the GCC kernel is working. My phone is running a GCC compiled production configured kernel for nearly a week.

j) "Houston, we've had a problem" with the light sensor. One of the compiler warnings brought me to the file drivers/sensor/optical/gp2a.c. There within are located two routines which read the light and proximity sensor. They seem copy-pasted identical, however the sensor value types are different as the proximity value is a char and the light intensity a double. What is more convenient than to simply change the data type of the supplied buffer in the function prototype. Now headache starts as the semantics of the read (and write) call say that the unit size is byte. So returning "1" indicates that only the first byte of the sensor value is copied. Also, there is no bounds/access checking so supplying an invalid pointer to the call will crash the kernel. So, assuming this is all one big mistake, I redesigned the function to do better (see houston.patch) and built a new kernel with it. To my utter surprise my battery charge extended from <24 hours to 2 days and 20 hours.

However... I also noticed that my backlight intensity level was constant at it's lowest although the setting was set to auto. I needed to know how the caller invokes the call, but after an extensive search of internet and android sources it is still something I have not found. Heuristics show that the reading the light sensor is called with a buffer length of 1, and the returned value is only accepted when returning a 1 and that the sensor value type is a double (8 bytes). This is wrong: read() semantics require that you supply a length of 8, and expect a return value of 8. This may be the base of many light sensor issues I found when Googling.

Anyway, I returned the code to it's original faulty behaviour, and being illuminated I disabled the auto backlight intensity and changed it to it's lowest setting to enjoy a longer life between battery charges.

TO CREATE YOUR KERNEL:

1) Prepare a working environment

1a) Unpack Sourcery G++. No installation needed, unpacking is sufficient

1b) Unpack the Samsung sources and cd to the location of the top-level Makefile.

1c) Unpack the zImage and the contents of the Tweak-Kit to the same location

1d) Make sure the zImage is called zImage.I9000XWJP6

2) The ramdisk image is required and can be extracted from zImage.I9000XWJP6

2a) Create an uncompressed image Image.I9000XWJP6

gcc scripts/binoffset.c -o scripts/binoffset
ofs=`scripts/binoffset zImage.I9000XWJP6 0x1f 0x8b 0x08 0x00 2>/dev/null`
dd ibs=$ofs skip=1 <zImage.I9000XWJP6 | gzip -c -d >Image.I9000XWJP6

2b) The Tweak-Kit includes mkvmlinux which converts the uncompressed binary image into a bfd object. You need an Arm enabled libbfd to get it working. This does not get installed by default so you need to deeplink into binutils. mkvmlinux locates and decodes the kallsym data and econstructs the symbol table. It then uses the values of __initramfs_start/end to extract the initramfs. If you are not bothered with the hassle, just use dd with hardcoded values.

g++ mkvmlinux.cc -o mkvmlinux -lbfd -liberty -lz [-I and -L that deeplink into binutils]
./mkvmlinux Image.I9000XWJP6 vmlinux.I9000XWJP6 -r initramfs.cpio

or

dd if=Image.I9000XWJP6 of=initramfs.cpio bs=1 count=2739712 skip=165568

3) Patch date/time and other environmental issues to the moment of original creation

patch -p1 <stamp.patch

4) Make your computer happy

# edit Makefile line 184 and update the macro CROSS_COMPILE=

cp I9000XWJP6_defconfig arch/arm/configs/I9000XWJP6_defconfig
make I9000XWJP6_defconfig
make

5) Verify that the kernel is identical

diff zImage.I9000XWJP6 arch/arm/boot/zImage

AND NOW FOR SOMETHING COMPLETELY DIFFERENT...

Tweaking the configuration will build you a new kernel but when your Galaxy powers on it will either die silently (hang) or experience a horrible death (reboot). The problem is that there are modules built from sources located in the removed directories drivers/fsr and fs/rfs. These modules were compiled with a specific data structure layout and entry points. These will surely change when re-configuring. The way to keep the non-native modules happy is to keep the structures and entry points intact.

The structure layout is influenced by the CONFIG_ macros. The structures can be fixed to reflect the state of the I9000XWJP6 kernel by replacing the CONFIG_ macro's by something that does not change after reconfiguration. For that I use a collection of 'shadow' macro's which have SHADOW_ as prefix. Because the data structures cannot expand, you cannot (easily) enable configure functionality which require extra fields in the data structures. Reducing functionality is highly seldom a problem.

If changing kernel functionality results in removal of entry points, then stubs are required for those entry points needed by the non-native modules

There are automated methods to verify that a new kernel abides to the above constraints. For the data structures the compiler must generate gstabs debug information. This is human readable and includes detailed structure descriptions. This information should be identical across re-configuration. However, the scripts get confused by anonymous structs which are by product of "typedef struct {" constructions. These need to be named, something shadow.patch also does.

The kernel modules have easily-readable symbol tables containing needed kernel entry points. These should all be present in all re-configured kernels. Validation tests that fail emit enough information to further fix data structures and entry points. The Tweak-Kit contains two files: I9000XWJP6.h containing the SHADOW_ macro's and I9000XWJP6.c for the stubs. Both were constructed in an on-demand basis for the reconfiguration I am currently using and both serve as good examples on what to do when validation fails.

Before reconfiguring, rebuild the kernel for usage as a validation checkpoint.

1) Undo the timestamp patches

patch -R -p1 <stamp.patch

2) Fix the warnings

patch -p1 <style.patch # style related issues
patch -p1 <prototype.patch # prototype related issues
patch -p1 <error.patch # bug fixing

3) Apply datastructure fixation, entrypoint stubbing and Makefile tweaking

patch -p1 <shadow.patch
cp I9000XWJP6.c arch/arm/plat-samsung

4) Before recompiling everything, you need to issue "make clean" first. However, the missing directories will now pose a problem as "make clean" will include their Makefiles and will fail if it can't. Just create empties to keep the build happy.

mkdir -p drivers/fsr fs/rfs
touch drivers/fsr/Makefile fs/rfs/Makefile

5) Optionally change the Makefile to point to your favorite compiler/toolchain. Please note that I am using GCC 4.4.5. GCC 4.5.1 is bumping into problems I haven't looked into yet.

# edit Makefile line 184 and update the macro CROSS_COMPILE

6) This build will generate gstab debug information. Unexpectingly, this might bite when combined with function profiling, so disable that. But do not CONFIG_FUNCTION_TRACER yet as that does more.

# edit Makefile line 553, disable the line containing KBUILD_CFLAGS += -pg

7) Unpack the initramfs image. The directory /lib/modules needs to be examined/updated

mkdir initramfs.dir
cd initramfs.dir
cpio -i --make-directories --preserve-modification-time --no-absolute-filenames <../initramfs.cpio
cd ..

8) Repack initfs as a tarball, as make clean will erase all the modules

tar cf initramfs.tar initramfs.dir

9) The initramfs image will contain new kernel modules. Make sure a new version will get generated.

# in .config line 80 point to the unpacked initram location
CONFIG_INITRAMFS_SOURCE="initramfs.dir"

# in .config lines 86-89, select your favourite compression
CONFIG_INITRAMFS_COMPRESSION_NONE=N
CONFIG_INITRAMFS_COMPRESSION_GZIP=Y

10) Build a new kernel

# not cleaning will confuse the verification
make clean
make CONFIG_DEBUG_INFO=y
# install the modules
tar xf initramfs.tar
cp `find drivers -name '*.ko'` initramfs.dir/lib/modules
# rebuild with a fresh new ramdisk image
rm usr/initramfs_data.cpio*
make CONFIG_DEBUG_INFO=y

11) Checkpoint structure/entrypoint information. This is architecture independent.

# extract structures. They are the entries with :T
objdump -G vmlinux | awk '{ print $7 }' | grep :T | sed 's/([^)]*)/()/g' | sed 's/=\*()//g' | sort -u > gstabs.ckp
# extract the entrypoints
nm vmlinux | grep 'r __ksymtab_' | awk '{ print $3 }' | sort > ksymtab.ckp

12) Do a test-run. Pack zImage and flash with Odin. If your Galaxy is up and running, I strongly suggest you make a backup of your environment. If you later change something and it breaks, then this is the best place to restart.

cp arch/arm/boot/zImage .
tar cf I9000XWJP6-2.6.32.9-test.tar zImage

13) Make your re-configuration. I really suggest you do not make too many changes in one go because it gives more work when the structure/entrypoint verification fails.

# re-configure. For this exercise, change the kernel to a more production version

CONFIG_CC_OPTIMIZE_FOR_SIZE=Y
CONFIG_DM_DEBUG=N
CONFIG_S3C_KEYPAD_DEBUG=N
CONFIG_DEBUG_FS=N
CONFIG_DEBUG_KERNEL=N
CONFIG_LATENCYTOP=N
CONFIG_FTRACE=N
CONFIG_ARM_UNWIND=N
CONFIG_DEBUG_USER=N

14) Build a new kernel

# not cleaning will confuse the verification
make clean
make CONFIG_DEBUG_INFO=y
# install the modules
tar xf initramfs.tar
cp `find drivers -name '*.ko'` initramfs.dir/lib/modules
# rebuild with a fresh new ramdisk image
rm usr/initramfs_data.cpio*
make CONFIG_DEBUG_INFO=y

You get "struct has no member named" errors if you have enabled subsystems that require data structures to change which are incompatible with the non-native modules.

15) Verify structure/entrypoint checkpoint

# extract/verify structures
objdump -G vmlinux | awk '{ print $7 }' | grep :T | sed 's/([^)]*)/()/g' | sed 's/=\*()//g' | sort -u > gstabs.t
# new/changed structures are tagged with '+'. Display only the changed ones
diff -U0 gstabs.ckp gstabs.t | grep '+' | grep ':T' | sed 's/+//' | sed 's/:T.*/:T/' | while read s; do
grep -q "$s" gstabs.ckp
if [ $? -eq 0 ]; then
echo $s;
fi
done

# extract/verify entrypoints
nm vmlinux | grep 'r __ksymtab_' | awk '{ print $3 }' | sort >ksymtab.t
# extract all entrypoints needed by the modules
nm `find initramfs.dir/ -name '*.ko'` >allkosym
# some symbols are referenced in other modules. Cross-reference and remove from list
grep ' U ' allkosym | sort -u | awk '{ print $2 }' | while read s; do
if ! egrep -q "^[0-9a-f]* . $s\$" allkosym; then
echo $s;
fi
done > allusym
# check that kernel has entrypoints for all final undefined symbols
cat allusym | while read s; do
if ! egrep -q "^__ksymtab_$s\$" ksymtab.t; then
echo $s;
fi
done

Both scripts will generate output if you have enabled subsystems that require datastructures to change. These do not necessarily have to be data structures needed by non-native modules. However, missing entrypoints are those used by the modules. If it's about datastructures, your best chance is to lookup the data type and see if there any #ifdef CONFIG_ macros that need to be changed into #ifdef SHADOW_. If it's a missing entry point, you need to add a stub in I9000XWJP6.c.

16) Do a test-run. Pack zImage and flash with Odin.

cp arch/arm/boot/zImage .
tar cf I9000XWJP6-2.6.32.9-test.tar zImage

17) If you want more, jump to step 13

18) When you are really done, rebuild a final and fresh kernel and initramfs image with debugging stuff removed. The -gstabs compiler switch slightly influences code generation.

# not cleaning will confuse the verification
make clean
make
# install the modules
tar xf initramfs.tar
cp `find drivers -name '*.ko'` initramfs.dir/lib/modules
# rebuild with a fresh new ramdisk image
rm usr/initramfs_data.cpio*
make

My uncompressed image has now shrunk from 14700623 to 11822559 bytes.

Happy Hacking...

[...and now to find a better workaround for those non-native modules.]