Directly boot a 64bit kernel on a 32bit EFI Macbook

G'day! This write up is about 6 months late. Mostly because I just keep doing other cool things instead of writing a boring article.

Early last year I purchased a Macbook 4,1 for $90 AUD, played around with it for a bit, then gave up. The performance was abysmal, and my health was too poor to do much outside of rest all day, so didn't bother sorting it out. Fast forward about 5 months to about July, my health had improved drastically and I wanted to start playing around with this cool "new" toy! 😂

Getting a GNU/Linux distro too boot was simple enough with the Debian multi-arch ISO, which will boot using a 64bit kernel, and will install GNU GRUB correctly. Being a former Arch Linux user, I changed my sources to Sid so that I could survive, and started figuring out issues like high system load, frame buffer crashes, and gpe17 going crazy.

But I'm a tinkerer are heart and wanted more control over my system, so I installed Gentoo! This got me thinking about better ways to boot my system, and I found efibootmgr. At first I couldn't get any method to work because Apple doesn't exactly follow the EFI spec sheet to the letter.

This is my Macbook version:

$ dmesg | grep DMI
[    0.000000] DMI: Apple Inc. MacBook4,1/Mac-F22788A9, BIOS     MB41.88Z.00C1.B00.0802091535 02/09/08

It's almost 11 years old, and it boots in 20 seconds: about 15 seconds for the slow EFI and 5 for the kernel+userland. This would be an extra 30 seconds if I was booting in BIOS mode.

I've removed the Bluetooth antenna and the DVD drive to make it lighter (only removed the Bluetooth antenna because I felt like it), and upgraded to 4GB of RAM and installed a 112GB Samsung SSD from late 2013.

Preparation

You need to enable the mixed EFI mode in the kernel, and you'll want to build your boot arguments into the kernel. While technically you should be able to set them with efibootmgr, I haven't had any success doing it that way.

The needed EFI config is:

Processor type and features  --->
   [*] EFI runtime service support
   [*]   EFI stub support
   [*]     EFI mixed-mode support

Firmware Drivers  --->
   EFI (Extensible Firmware Interface) Support  --->
       <*> EFI Variable Support via sysfs
       [*] Apple Device Properties

And an example command line:

Processor type and features  --->
   [*] Built-in kernel command line
   (root=PARTUUID=2f21b76a-3f9b-411e-9160-37d2135f58fb video=SVIDEO-1:d) Built-in kernel command string

You can get the PARTUUID with blkid. video=SVIDEO-1:d is used to remove SVIDEO-1 to prevent a frame buffer crash on my system.

Now we need to create a boot partition, or possibly recreate it if you already have one. In my case this is going to be /dev/sda1, I have no idea if Apple's firmware allows it to be on a partition other than the first partition, but wouldn't be surprised if they don't. I use gdisk. I'm not going to describe how to partition drives, as I'm sure you know how. Just make sure the partition type is ef00 (EFI System Partition) and then format it as FAT32 with mkfs.vfat -F32.

Make sure you mount the partition on /boot.

Layout

Now we just need to use the right layout and put all the right things in the all the right places.

Install the kernel to /boot; on Gentoo run make install in your kernel sources directory, or for debian use apt to install the kernel you built (only ever build the kernel as a .deb package, this is cleaner).

Make the right directories, touch some files, and copy the kernel to boot.efi:

# mkdir -p /boot/EFI/System/Library/CoreServices
# touch /boot/EFI/mach_kernel /boot/EFI/System/Library/CoreServices/SystemVersion.plist
# cp /boot/vmlinuz-[current-version] /boot/EFI/System/Library/CoreServices/boot.efi

The end result will look something like this:

$ tree --noreport -I *.old /boot
/boot
├── config-4.19.27-gentoo-r1
├── EFI
│   ├── mach_kernel
│   └── System
│       └── Library
│           └── CoreServices
│               ├── boot.efi
│               └── SystemVersion.plist
├── System.map-4.19.27-gentoo-r1
└── vmlinuz-4.19.27-gentoo-r1
$

Wait, what?! Why is boot.efi in \EFI\System\Library\CoreServices? On a normal EFI system you put it in \EFI\BOOT\BOOTX64.efi, while that should work, I've had issues doing that on this Macbook. The easy solution is to put it where OS X would put it.

\EFI\mach_kernel and \EFI\System\Library\CoreServices\SystemVersion.plist are required to boot. AFAICT they don't need anything in them.

Now you need to add an entry for this system, e.g.:

# efibootmgr -c -d /dev/sda -p 1 -L "Gentoo" -l '\efi\System\Library\CoreServices\boot.efi'

You may want to remove other entries, or timeouts. You can get a list with:

# efibootmgr -v
BootCurrent: 0000
BootOrder: 0000
Boot0000* Gentoo	HD(1,GPT,178cc128-6d71-4944-9ef2-6cb025f29408,0x800,0x100000)/File(\efi\System\Library\CoreServices\boot.efi)
BootFFFF* 	PciRoot(0x0)/Pci(0x1a,0x0)/USB(1,0)/HD(3,0,00000000000000000000000000000000,0x158,0xfffc)/File(\efi\boot\bootx64.efi)
#

Consult the manpage or efibootmgr -h for more info.

Rebooting

For the first reboot, poweroff instead. Then power on. It may hang, but leave it for at least a minute, then you can force it off. This time when you power it on, it should boot the kernel's EFI stub directly. No GRUB, no systemd-boot (gummiboot), no syslinux. Just a plain old EFI stub.