Installing Gentoo on a MacBookAir3,1

The new MacBookAir3,1 (aka October 2010 11”) is a bit of a challenge. The usual methods don’t seem to apply here… or at least I couldn’t get them to work. In the past we’re used to using BIOS emulation mode to get things working, at least easier than using “native” EFI mode. However, with the new Airs (at least with mine), BIOS mode is buggy and refuses to initialize some hardware properly. As a result, the SATA controller shows up as a slow-ass generic PATA controller in some kind of emulation mode. I do hear of other people getting BIOS mode to work, so YMMV.

EFI mode is no picnic either. I hear reports that the nvidia proprietary driver works in BIOS mode, but I can’t get it to work in EFI mode. The usually-awesome nouveau has issues doing… something… with the internal eDP connector and so isn’t able to figure out how to drive the internal LCD. So I’m stuck with the crappy unaccelerated fbdev driver (forget about full-screen video).

I want to somewhat do a step-by-step here, but things have actually changed quite a bit in the past couple weeks, so my original experience is already out of date. So I’ll try to outline what I’ve done here and how to get things started based on what works right now.

For starters, all files I reference here that aren’t really available elsewhere can be grabbed here.

Stuff You Need

Yes, that’s actually it. A couple weeks ago I would’ve added “a USB flash drive” to that list, but I’ve worked out how to do your first boot without that, even. No, you don’t need a USB CD/DVD drive, and no, you don’t need a USB ethernet dongle. And you really only need the other Linux machine for two things: 1) to build EFI GRUB2, because I couldn’t get it to configure properly (let alone compile) on MacOS X, and 2) to build your first-boot kernel, because most LiveCD kernels just aren’t going to cut it (at least not yet). I imagine you could actually do these two things by building an x86_64-pc-linux-gnu cross compiler for OSX, but doing so is left as an exercise to the (masochistic) reader.

So let’s get to it. For simplicity I’m assuming you’re going to unpack all tarballs and clone all source repositories into subdirectories of $HOME. Feel free to do something else, but modify paths accordingly.

Partition Resizing

Fire up Disk Utility in MacOS X and scale down your main HFS+ partition. I left it at a little over 20GB, but if you don’t install anything on your Mac partition and aggressively delete stuff, you can probably get it smaller. Create 2 partitions after it: one for Linux and a swap partition. If you prefer to use swap files instead, don’t create the swap partition, but then (I think) hibernate won’t work. I did around 95GB for Linux and 4GB for swap. If you prefer to create more partitions to keep /home or whatever on it’s own, feel free. I choose to keep it simple. Note that most of the time I would advocate creating a separate /boot partition, but our EFI-only system will actually not need a /boot partition at all (the directory is empty for me right now).

Since Disk Utility is awesome, it won’t make you reboot to resize and mess around with the partitions.

rEFIt

Go to the rEFIt website, download it, and install it. Seriously. It makes things a lot easier. Might want to reboot and verify that rEFIt comes up properly and lets you boot in to MacOS. If not, read some of the rEFIt troubleshooting steps on their website and figure it out.

One thing I noticed is that occasionally the keyboard doesn’t work on the rEFIt screen. Just hit the power button to turn the laptop off, and then back on again and that should clear it up.

EFI GRUB2

On my extra Linux box, I grabbed the current tip of development using bzr:

1cd $HOME
2bzr clone http://bzr.savannah.gnu.org/r/grub/trunk/grub``</pre>

Hopefully the state of the code when you clone it is good enough to work properly. Next you want to go and read this page in its entirety. Build yourself a 64-bit EFI GRUB2 image as instructed:

1./autogen.sh
2./configure --with-platform=efi --target=x86_64 --program-prefix=
3make
4cd grub-core
5../grub-mkimage -O x86_64-efi -d . -o grub.efi -p "" part_gpt hfsplus fat ext2 normal chain boot configfile linux multiboot

Install GRUB2

The installation is kinda manual at this point. Your stock MacOS install has a relatively small FAT partition before the main HFS+ partition. Since both MacOS and Linux can easily read and write to FAT partitions, we’re going to use that little partition to store both our GRUB2 and Linux kernel images.

Use some method (wireless, USB, magic) to copy the grub.efi, *.mod, and *.lst files in the current grub source tree directory over to your Air. On the Air, mount /dev/disk0s1 to somewhere. From inside the root of that mount point, create a directory called “grub2” inside the existing “EFI” directory (if the “EFI” directory isn’t there, create it… and make sure it is in all capitals, or stuff won’t work later). Copy grub.efi, and all the .mod and .lst files into this new grub2 directory. Congrats, GRUB2 is now officially “installed,” though it’s not entirely useful yet.

Next we’ll create a grub.cfg file in the grub2 so we can actually boot something. Try this to start:

 1set timeout=5
 2
 3menuentry "Installer Bootstrap" {
 4    insmod efi_gop
 5    search --set -f /boot/vmlinuz-2.6.37-rc4+
 6    loadbios /boot/vbios.bin /boot/int10.bin
 7    linux /boot/vmlinuz-2.6.37-rc4+ video=efifb real_root=/dev/sda3 initrd=/boot/initrd-2.6.37-rc4+.gz rw
 8    initrd /boot/initrd-2.6.37-rc4+.gz
 9}
10
11menuentry "Gentoo Linux (2.6.37-rc4+)" {
12    insmod efi_gop
13    search --set -f /boot/vmlinuz-2.6.37-rc4+
14    loadbios /boot/vbios.bin /boot/int10.bin
15    linux /boot/vmlinuz-2.6.37-rc4+ video=efifb root=/dev/sda3 ro nocoldplug
16}

The first config entry is gonna boot us into our yet-to-be-created initramfs where we’ll bootstrap the install. You can think of this as sort of your LiveCD, though it’ll be way more minimal than even the LiveCD image.

(Sidebar: actually, now that I got a patch into the kernel that makes USB work properly once Linux boots, you could actually create a LiveUSB with the contents of a ‘real’ Gentoo LiveCD. But I don’t feel like walking through that right now.)

The second config entry is what’s actually gonna boot our real final system once it’s installed.

Note: if you’re doing this a while later, when newer kernels are released, obviously you’ll want to put different versions in the file names and menu entry.

Kernel

Back to the Linux box.

Right now I’m using 2.6.37-rc4+, pulled from a combination of Linus’ official tree and the nouveau DRM tree. The latter is currently unnecessary as nouveau isn’t working yet on the new Air, but once it is, you’ll probably want it. The nouveau tree usually lags a little bit behind Linus’ tree (though usually not far behind), so you may need to merge Linus’ tree on top of the nouveau tree. Anyhow:

1cd $HOME
2git clone git://anongit.freedesktop.org/nouveau/linux-2.6
3cd linux-2.6
4git remote add linus git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
5git fetch linus
6git merge linus/master

Hopefully that last command doesn’t cause any merge conflicts. If it does, you’ll need to resolve them. If you aren’t able to do that, kill the merge and just use a stock copy of Linus’ tree:

git reset –hard origin/master git checkout -b linus-master linus/master

Next there are some patches that need to be applied. Go grab them from my files directory. There are two that should already be in mainline: a patch that adds support for the sound hardware, and a patch that fixes problems with the USB controller. Hopefully as time goes by more patches will get upstreamed, but they’re not there yet. I generated each patch using “git format-patch”, so you should be able to apply them using “git am”.

Next grab my kernel config (the “kernel-config” file in my files directory) and save it as “.config” in the root of the kernel tree. Then build the kernel:

1make oldconfig
2make

Install it by copying arch/x86/boot/bzImage to the Air, and then to the FAT partition you mounted before. First create a directory called “boot” in the root of the FAT partition, and then copy the bzImage file there (I renamed it to vmlinuz-2.6.37-rc4+; rename it to match whatever’s in your grub.cfg file). You’ll also probably want to stick a suitably-renamed System.map there as well.

Kernel Initramfs

To bootstrap the install, we need a Linux “install” to boot into. For this we’ll use a minimal initramfs with some extra goodies thrown in. The “genkernel” app can make you a nice initramfs that will get you to a shell. Just emerge it on your existing Linux machine and run:

1genkernel --no-menuconfig --no-clean --no-mrproper --no-splash --no-install --kerneldir=$HOME/linux-2.6 --kernel-config=#HOME/linux-2.6/.config --disklabel ramdisk

Unfortunately the initramfs that genkernel creates isn’t quite enough for us. We’re going to need some extra stuff on it. So we need to unpack it, add some files, and then repack it:

1mkdir ~/initramfs
2cd ~/initramfs
3zcat /var/tmp/genkernel/initramfs-2.6.37-rc4+ | sudo cpio -i

That’ll unpack your initramfs to the current directory. Next copy in mkfs.ext4, wpa_supplicant, iwconfig, and dhcpcd (or dhclient) from your host Linux system. You’ll also need to copy in any shared libraries that these guys depend on. I ended up building a super-minimal wpa_supplicant that required fewer shared libraries, but that’s not strictly necessary (unless your initramfs gets larger than a certain size that makes it not boot).

Next install kernel modules into the initramfs:

1cd ~/linux-2.6
2sudo make modules_install INSTALL_MOD_PATH=$HOME/initramfs

WiFi Driver

Currently the open source b43 driver does not work with the WiFi chipset in the Air. The recently open-sourced brcm80211 driver included in the kernel’s staging tree does, but it’s a bit flaky. Your best bet is unfortunately still the proprietary Broadcom “wl” driver. Grab the latest source (either by using emerge –fetchonly on the Gentoo system, or by grabbing it directly from broadcom.com), build it for your new kernel, and install the resulting module into your new initramfs:

1mkdir ~/broadcom-sta
2cd ~/broadcom-sta
3tar xvzf /path/to/hybrid_portsrc_x86-64_v*.tar.gz
4make -C$HOME/linux-2.6 M=$(pwd)
5sudo make -C$HOME/linux-2.6 M=$(pwd) INSTALL_MOD_PATH=$HOME/initramfs modules_install

Next you want to create etc/wpa_supplicant/wpa_supplicant.conf in the initramfs and put something useful in it. Something simple (for a WPA/WPA2 network) like this should do:

1network={
2    ssid="MyNetworkName"
3    psk="mysupersecretpassphrase"
4}

Note that the double quotes around the SSID and PSK are not optional.

Repack the Initramfs

Make sure module dependencies are set up right in the initramfs and then repack it:

1cd ~/initramfs
2sudo depmod -a -b $(pwd) -F $HOME/linux-2.6/System.map 2.6.37-rc4+
3find . | cpio -o -H newc | gzip -9c > ../initramfs-2.6.37-rc4+.gz

Then ship the initramfs-2.6.37-rc4+.gz file over to the Air and stash it in your “boot” directory on the FAT partition (suitably renamed to match your grub.cfg).

While you’re here, also grab the vbios.bin and int10.bin files from my files directory and also stick them in the “boot” directory. It’s not clear to me yet if they’re needed (they certainly aren’t now, but may be once we figure out how to get nvidia or nouveau working), but can’t hurt.

First Boot

Whew. Ok, in theory, we can actually boot Linux now. Reboot the Air, and you should see a new icon pointing to GRUB. Boot it. Pick the “Installer Bootstrap” option, and you should soon see your kernel boot. You’ll probably get an error about not being able to find a root partition. That’s expected; just type “shell” to get a shell.

And then get WiFi working and connected:

1modprobe wl
2wpa_supplicant -ieth0 -c/etc/wpa_supplicant/wpa_supplicant.conf -Dwext -d &
3# wait for the wpa_supplicant output to show it's connected
4dhcpcd eth0 &

Now, I had trouble with DNS resolution. Even with /etc/resolv.conf set up properly, Busybox’s wget was still unable to resolve DNS names. Perhaps resolver support just wasn’t built into Busybox. In my case I have Apache running on a Linux box at home, so I fetched a Gentoo stage3 tarball and stuck it on my local webserver, and then used wget and the server’s IP address to pull it over to the MacBook Air.

From here you can more or less follow the Gentoo Install Guide instructions. Create a mount point, use mkfs.ext4 to create a filesystem, mount it on your mount point, unpack the stage3 tarball, and then chroot into it. The one issue I found here was that Busybox’s mount command doesn’t have the –bind option. In this case it’s not a big deal, as you can remount /proc and /sys in the chroot without using –bind (just mount it as if you were mounting it for the first time).

Obviously you don’t want to install old GRUB or re-build the kernel, but otherwise the Install Guide steps should be the way to go.

Regarding the kernel, what I ended up doing was using rsync to pull the already-built kernel tree from my other Linux machine to /usr/src in the new install. This takes a while but can be done unattended in the background and saves you from having to re-pull kernel sources and repatch and all that un-fun stuff.

Do make sure you don’t forget to emerge broadcom-sta and wpa_supplicant, or when you reboot you’ll find you have no wireless and will have to use USB or something to get a wireless driver onto your Air.

Post Install

After your reboot, you can choose the other GRUB2 menu option that boots the system normally. Cross your fingers and hope everything boots ok. Currently udev’s coldplugging causes a kernel panic (or at least it did when I started on 2.6.37-rc2 with a slightly older udev version; should check and see if that’s still the case), hence the “nocoldplug” option on the kernel command line.

After you get X installed, you’ll need a minimal xorg.conf like this:

1Section "Device"
2    Identifier "Card0"
3    Driver "fbdev"
4EndSection

Unfortunately fbdev is all that works for me right now.

Caveats

Please let me know via email if anything here seems wrong or you need to do anything else to get things working. I wrote all of this from memory, and this isn’t the actual process I used because things have changed (for the better) since I did my install. Please do not email me if you are asking for help with how to do any of the things outlined here. Many of these steps assume some familiarity with boot loaders, kernels, and kernel patching and building, and I frankly don’t have the time or inclination to walk people through these topics.

Having said that, good luck!