This project is an attempt to build and prepare a minimal Gentoo Linux distribution for the Raspberry Pi computer board.
This setup will assume you have an existing computer with Gentoo installed on it. Since the Raspberry Pi runs off of an SD card and a 700mhz processor, we will be reducing the space and processing requirements by cross-building all of our packages on the host computer.
In this section we'll cover the various utilities you're going to need on the host system before you can begin building for Raspberry Pi.
First we need to make a base working directory. We'll call it ${RASPIGEN}.
# make the working directory mkdir -p "${RASPIGEN}" # change to that directory cd "${RASPIGEN}" # make some other directories mkdir ./root mkdir ./mnt
In order to build Gentoo for Raspberry Pi, we will be using the QEmu emulator to run ARM-compiled libraries. First we need to download and compile QEmu for ARM:
# move to our working directory cd ${RASPIGEN} # clone qemu-1.0 git clone git://git.qemu.org/qemu.git -b stable-1.0 # move to qemu directory cd qemu # configure for static arm-linux-user target with most features disabled ./configure --target-list=arm-linux-user --static --disable-{sdl,vnc,xen,curses,curl,bluez,kvm,vde,attr,smartcard,guest-agent,libiscsi,spice,docs,attr,linux-aio,blobs} # compile make # copy output to root directory mkdir -p ../root/opt/rpi-build/ cp arm-linux-user/qemu-arm ../root/opt/rpi-build/
Now create the following file under ${RASPIGEN}/qemu:
#include <string.h> #include <unistd.h> int main(int argc, char **argv, char **envp) { char *newargv[argc + 3]; newargv[0] = argv[0]; newargv[1] = "-cpu"; newargv[2] = "arm1176"; memcpy(&newargv[3], &argv[1], sizeof(*argv) * (argc - 1)); newargv[argc + 2] = NULL; return execve("/opt/rpi-build/qemu-arm", newargv, envp); }
Then statically build our wrapper and copy it to our root directory:
# build gcc -static qemu-wrapper.c -o qemu-wrapper # move cp qemu-wrapper ../root/opt/rpi-build/
For those of you who aren't aware, there is a magical module for the Linux kernel known as binfmt_misc. What it does is essentially scan any binaries attempting to be ran for a magic string at a specific offset, and if found pass them off to another program to be read.
We will be using this to allow us to run ARM-compiled binaries on our x86 system! Through the magic of binfmt_misc we can have our kernel look for ARM binaries and pass them to qemu-arm for emulation.
You MUST have a kernel with binfmt_misc support. This can be done by either compiling your kernel with CONFIG_BINFMT_MISC=m if building as a module or CONFIG_BINFMT_MISC=y to build it in.
Once you have binfmt_misc, you need to insert the module and mount the proper proc directory. The following commands will do this for you as necessary:
[ -d /proc/sys/fs/binfmt_misc ] || modprobe binfmt_misc [ -f /proc/sys/fs/binfmt_misc/register ] || mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
Lastly we need to register our binary format. The following string is interpreted as :name:type:offset:magic:mask:interpreter:flags. So given the following registration string, we will be registering a magic string interpreter by the name of arm_rpi, and assigning it to pass any ARM binaries it detects to /opt/rpi-build/qemu-wrapper.
echo ':arm_rpi:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/opt/rpi-build/qemu-wrapper:' > /proc/sys/fs/binfmt_misc/register
Now that we have QEmu installed on the host system, it's time to start setting up the base stage that will be ran on our system.
The first step is to download the stage tarball as well as a copy of portage. Go to the Mirror Network and pick mirror.
Download the latest stage3 tarball for armv6j_hardfp under releases/arm/autobuilds/current-stage3-armv6j_hardfp to ${RASPIGEN}.
Also download the latest portage snapshot from under snapshots.
Once we are done downloading these files, extract them:
cd ${RASPIGEN}/root tar -xvjpf ../stage3-armv6j_hardfp-*.tar.bz2 tar -xvjf ../portage-latest.tar.bz2 -C usr/
At this point we have a base stage installed to ${RASPIGEN}/root. Now it's time to chroot into it:
# mount some needed filesystems into our new root mount -t proc proc ${RASPIGEN}/root/proc/ mount -t sysfs sysfs ${RASPIGEN}/root/sys/ mount -o bind /dev ${RASPIGEN}/root/dev/ # copy resolve information for networking cp -L /etc/resolv.conf ${RASPIGEN}/root/etc/ # perform the chroot chroot ${RASPIGEN}/root /bin/bash # update our environment env-update source /etc/profile export PS1="(chroot) $PS1"
In order to start using the stage, we will need to prepare it.
First we need to select a valid profile for our build:
eselect profile set default/linux/arm/10.0
Next edit /etc/fstab to contain the following:
/dev/mmcblk0p1 /boot vfat noauto,noatime 1 2 /dev/mmcblk0p2 / ext4 noatime 0 1
Also overwrite the existing /etc/make.conf file to look like this:
CFLAGS="-Os -pipe -mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard" CXXFLAGS="${CFLAGS}" CHOST="armv6zk-hardfloat-linux-gnueabi"
Since we changed our CHOST value (from armv6j-hardfloat-linux-gnueabi to armv6zk-hardfloat-linux-gnueabi) in make.conf to match the Raspberry Pi, we need to rebuild our system to handle that.
We start by rebuilding our core libraries in order to obtain a working arm6zk toolchain:
First rebuild binutils:
emerge -v1 binutils
Next we build GCC, our compiler. GCC is a bit of a memory hog to build, so in order for it to succeed we need to increase the amount of address space given to Qemu using the QEMU_RESERVED_VA environment variable.
QEMU_RESERVED_VA="0xf7000000" \ emerge -v1 gcc
# specify LD_LIBRARY_PATH to point to our recently built GCC LD_LIBRARY_PATH=/usr/lib/gcc/armv6zk-hardfloat-linux-gnueabi/${GCC_VER} \ gcc-config armv6zk-hardfloat-linux-gnueabi # resource our profile . /etc/profile # re-setup our chroot prompt (gets wiped out by sourcing profile export PS1="(chroot) $PS1"
To be safe, it is generally a good idea to remerge GCC to make sure the toolchain is sound following this change.
Finally build our C library, glibc:
emerge -v1 glibc
At this point we have a new toolchain, so we need to do some sanity checks:
# check gcc-config gcc-config -l # set compiler to the armv6zk one, mine was 4.5.3 gcc-config armv6zk-hardfloat-linux-gnueabi-4.5.3 # check binutils binutils-config -l # set binutils to the armv6zk one, mine was 2.21.1 binutils-config armv6zk-hardfloat-linux-gnueabi-2.21.1 # source and reset source /etc/profile export PS1="(chroot) $PS1"
Now we need to remove references to our old CHOST:
# look for files with references to the old CHOST # this should produce some files grep -R armv6j /etc/env.d # double-check to makes sure we have references to the new ones # before deleting the old grep -R armv6zk /etc/env.d # if all looks good, delete the old files rm -i $(grep -R -l armv6j /etc/env.d) # regen environment and reset env-update source /etc/profile export PS1="(chroot) $PS1"
Now we need to rebuild libtool and fix some of its archives:
emerge -v1 libtool # the GCC version I merged was 4.5.3, be sure to use the right one /usr/share/gcc-data/armv6zk-hardfloat-linux-gnueabi/4.5.3/fix_libtool_files.sh 4.5.3 --oldarch armv6j-hardfloat-linux-gnueabi
The final step in the process is to recompile the world using the new build toolchain.
QEMU_RESERVED_VA="0xf7000000" \ emerge -ev world
When this is done, we have a functional system completely rebuilt specifically for the Raspberry Pi's hardware. The next step is to start installing the tools and utilities specific to the Raspberry Pi.
Now that we have a working stage 3, it is time to start installing the parts of the system specific to the Raspberry Pi.
I have set up an overlay that includes the R-Pi kernel patches to make things easier. First install layman:
# set up a use flag for GIT (if not in the global make.conf) mkdir -p /etc/portage/package.use echo "app-portage/layman git" >> /etc/portage/package.use/layman # install emerge -v layman
Next we need to edit the layman configuration file to include my overlay. Edit the overlays line of the /etc/layman/layman.cfg file to include the cvpcs repo:
overlays : http://www.gentoo.org/proj/en/overlays/repositories.xml https://raw.github.com/cvpcs/gentoo-overlay/master/profiles/repo.xml
Then we need to install layman into our make.conf file:
# sync the layman tree layman -L # add the cvpcs overlay layman -a cvpcs # hook layman into portage echo "source /var/lib/layman/make.conf" >> /etc/make.conf
In order to boot the Raspberry Pi system, we need to have a kernel. The cvpcs overlay contains a copy of the Raspberry Pi patchset as rpi-sources. In this section we will install, configure, and cross-compile those sources.
Install the sources onto your stage root from the cvpcs overlay.
emerge -v rpi-sources
The sources contain a default configuration specifically tailored to the Raspberry Pi device.
# change to the kernel source directory cd /usr/src/linux # set the default configuration for rpi make bcmrpi_cutdown_defconfig
Next open .config and search for the line starting with CONFIG_CMDLINE, change the end of the line from rootfstype=ext3 to rootfstype=ext4 and save the file.
Finally it is time to compile the kernel and modules:
# compile the kernel and modules make # install the modules into the appropriate location make modules_install
stuff needs to be fixed here
The next step is to plug in the SD card you plan to use and prepare it for your Raspberry Pi.
I will assume your SD card was recognized as /dev/mmcblk0. This may not be the case depending on your kernel setup. If you connected your SD card via a USB SD card reader it may appear as /dev/sdX (where X is some letter).
The first step is setting the geometry of our SD card.
Open fdisk:
fdisk /dev/mmcblk0
Wipe the contents by typeing o, ENTER.
Command (m for help): o Building a new DOS disklabel with disk identifier 0x82af8ca7. Changes will remain in memory only, until you decide to write them. After that, of course, the previous content won't be recoverable. Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)
Print the card info by pressing p, ENTER.
Command (m for help): p Disk /dev/mmcblk0: 15.9 GB, 15931539456 bytes 256 heads, 63 sectors/track, 1929 cylinders, total 31116288 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x82af8ca7 Device Boot Start End Blocks Id System
Next we enter expert mode by typing x, ENTER. And then set the geometry to 255 heads and 63 sectors by typing h, ENTER, 255, ENTER and s, ENTER, 63, ENTER, respectively.
Command (m for help): x Expert command (m for help): h Number of heads (1-256, default 256): 255 Expert command (m for help): s Number of sectors (1-63, default 63): 63
Now we need to calculate the number of cylinders. The calculation is done based on the following formula:
$$ \text{cylinders} = \left \lfloor \frac{\text{total_size_in_bytes}}{\text{heads} \cdot \text{sectors} \cdot 512} \right \rfloor $$
Hence in our calculation looks as such:
$$ \text{cylinders} = \left \lfloor \frac{15931539456}{255 \cdot 63 \cdot 512} \right \rfloor = \lfloor 1936.899346405 \rfloor = 1936 $$
Enter this number as the cylinder count by typing c, 1936 (or whatever number you came up with), ENTER. Then leave expert mode by typing r, ENTER.
Expert command (m for help): c Number of cylinders (1-1048576, default 1929): 1936 Expert command (m for help): r
Now we have to partition the SD card. At this point you should be in the basic command prompt for fdisk. We will be creating two partitions: a boot and root partition.
First make the boot partition. This must be a FAT32 partition and bootable. Type the following to create a 64MB boot partition: c, ENTER, ENTER, ENTER, ENTER, +64M, ENTER, t, ENTER, c, ENTER, a, ENTER, 1, ENTER.
Command (m for help): n Partition type: p primary (0 primary, 0 extended, 4 free) e extended Select (default p): p Partition number (1-4, default 1): Using default value 1 First sector (2048-31116287, default 2048): Using default value 2048 Last sector, +sectors or +size{K,M,G} (2048-31116287, default 31116287): +64M Command (m for help): t Selected partition 1 Hex code (type L to list codes): c Changed system type of partition 1 to c (W95 FAT32 (LBA)) Command (m for help): a Partition number (1-4): 1
Next we fill the rest of the disk with our Linux root partition by typing n, ENTER, ENTER, ENTER, ENTER, ENTER.
Command (m for help): n Partition type: p primary (1 primary, 0 extended, 3 free) e extended Select (default p): Using default response p Partition number (1-4, default 2): Using default value 2 First sector (133120-31116287, default 133120): Using default value 133120 Last sector, +sectors or +size{K,M,G} (133120-31116287, default 31116287): Using default value 31116287
Finally, write the changes to the card by typing w, ENTER. This will exit fdisk.
Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. WARNING: If you have created or modified any DOS 6.x partitions, please see the fdisk manual page for additional information. Syncing disks.
Lastly you need to create the filesystems on your new partitions. This can be done using the following steps (taking note to use the proper partition devices):
mkfs.vfat -F 32 /dev/mmcblk0p1 -n RPI_BOOT mkfs.ext4 /dev/mmcblk0p2 -L RPI_ROOT
Now we need to prepare the boot partition.
Then we install the boot firmware and utilities package:
# install bootloader and tools xmerge -v rpi-boot xmerge -v rpi-tools
Then we package up the kernel we just built and throw it on the boot partition.
# create the kernel bootable image "${RASPIGEN}"/root/usr/bin/rpi-mkimage \ "${RASPIGEN}"/root/usr/src/linux/arch/arm/boot/Image \ "${RASPIGEN}"/root/boot/kernel.img
need to set the root password in some way, below is a hash that can be thrown in the shadow file to set the password to “root”, but there needs to be a better way than this
$6$wwRnB8p6$5a1hOb.b75EwAnv2TbQBKLcjzxA15gcF53Ul2RYOVySZodvgjR0AAiIfp1VUE.gbQXEYo56O4vFkFJ8Ownb6A0
This isn't true, we don't have them copy to the SD card yet
At this point your boot card should be good to go! Unmount it and try it out!
cd "${RASPIGEN}" umount "${RASPIGEN}"/root/boot umount "${RASPIGEN}"/root
Specify common things to do after installation completes such as changing CHOST and setting up SSH+Networking