Maintaining the Ubuntu PS3 Kernel: Cross Compiling

The easiest way to build the kernel for PS3 is to do so on the PS3 itself. However even with great tools like ccache to speed up repeated builds it can take a long time on PS3 most probably because of its relative small amount of RAM (256MB). So I made a point of working out a way to cross build the Ubuntu packaged version of the kernel on my x86 machine. Here are my notes on how to do that.

Prerequisites

I’m going to presume the reader already has a git checkout of the ubuntu ports source tree and knows how to build the Ubuntu kernel packages on the target architecture. Notes on this can be found in the “Performing Builds” section of the KernelMaintenance wiki page.

I recommend the reader either runs Hardy or Intrepid on their x86 system so that toolchain packages from the PS3 Dev Team PPA will install easily.

The Problem

Cross compiling the Linux Kernel directly is well supported. You need only set 2 environment variables so the build scripts know the target architecture and where to find the cross build tool chain. E.g. if you’re targeting PowerPC and your build toolchain (binutils, gcc etc) binaries are named as powerpc-linux-gnu-gcc, powerpc-linux-gnu-ld etc then you’d run something like this to build the kernel:

make ARCH=powerpc CROSS_COMPILE=powerpc-linux-gnu-

But what I wanted was a valid installable Ubuntu package at the end. Usually to avoid any chance of missing build platform related build errors Ubuntu/Debian packages are always built on the target architecture. However there’s quite a bit of work happening in Debian to help support cross building for low resource embedded platforms (e.g. mobile, set top boxes etc) so with a little hunting I was able to find out a very simple way of achieving my goal.

The Toolchain

First problem is we need to get a toolchain built for x86 but compiled to build for our target architecture which is powerpc64 with specific knowledge of the Cell CPU.

Luckily, the old Cell-SDK had already been packaged for Ubuntu by Matthias Klose, and is built for both powerpc and x86 architectures. However there was a bug which prevented kernel compilation. So I updated these packages to the latest from the Cell-SDK site. To get my updated packages add the PS3 Dev Team’s PPA to your APT sources list (basic instructions on that last page, make sure you choose ‘hardy’ or ‘intrepid’ ) and run:

sudo apt-get install ppu-gcc

Now your cross toolchain should be available as ppu-gcc and ppu-ld etc.

While the toolchain patches for the Cell were being developed they were created against GCC 4.1 and Binutils 2.17 and sources and binaries were (and still are) hosted at Barcelona Supercomputing Centre and known as the Cell-SDK. These patches have now been absorbed into the current versions of GCC and Binutils (GCC 4.3 and Binutils 2.18 I believe) so in theory it’s no longer necessary to use the Cell-SDK.

Intrepid’s toolchain is based on these newer versions and despite there being notes on a way to create Debian packaged cross toolchains using them I wasn’t able to get them both to compile. See debian/README.cross in the binutils and gcc package sources for more info. I’m sure the problem can be easily resolved but for now the Cell-SDK has got me up and running quickly.

Setting The Target Architecture For The Package Scripts

Now we have a toolchain the next problem was how to force the Ubuntu packaging scripts to use it. After much scanning of source code this turned out to be very simple indeed. There are whole a series of environment variables which need to be set. Luckily the utility dpkg-architecture makes it very simple to set these. You just need to pass it your desired architecture and evaluate it’s output in your shell, thus:

eval $(dpkg-architecture -apowerpc -s)

I’ll leave it to the reader to look at the man page for dpkg-architecture to understand more about this utility and the variables which get set.

Putting It All Together

So putting all these things together, this is roughly the script I use to cross build the kernel.

#!/bin/sh

# Decide where we're going to log to this time
LOG=$(pwd)/../$(basename $(pwd))-$(date +%F-%H%M).log

# Make sure ccache is used for HOST_CC
export PATH=/usr/lib/ccache:$PATH

# Setup the deb arch variables for cross building to target powerpc
eval $(dpkg-architecture -apowerpc -s)

# Run the build
CROSS_COMPILE="ccache ppu-" \
  fakeroot debian/rules binary-powerpc64-smp 2>&1 | tee "$LOG"

# Let you know it's finished
aplay /usr/share/sounds/phone.wav

This script should be run from the top of the Ubuntu kernel source. It logs all the build output to a file in the directory above but also outputs everything to the shell.

Note CROSS_COMPILE is set before running the build command. Although this variable is not touched directly by debian/rules it is propogated to the kernel build scripts.

ccache can be used for cross building. For me it halved the build time to 25 mins on second run so it’s well worth using it. Caching for the host architecture is supported on Ubuntu by pre-pending the /usr/lib/ccache directory to $PATH. I’ll leave it to the reader to find out how this works. In order to support caching for the foreign architecture we can simply modify the CROSS_COMPILE variable to prepend a call to ccache.

I also like to use the screen utility so I can walk away/logout etc and return later. See my previous post on how to use that. Finally the script runs a WAV file so I know when it’s finished.

UPDATES:

30 Aug 08: Updated notes on using ccache for cross compilation. Updated the build script to use ccache for the target architecture.