Flashing coreboot on MacBooks without external programmer by using IFD hack
TLDR: use mmga.
In this article, I will show how to flash coreboot on certain models of Apple MacBooks without disassembling and using external SPI programmer. All you need is Linux and root access.
This is a very delicate procedure. Be very careful, check everything twice, especially the numbers. A single mistake may brick your machine and you'll have to flash the backup externally. Given this fact, you should have an external SPI programmer, just in case.
It was tested and confirmed to work on following models:
- MacBook Air 5,2
- MacBook Pro 8,1
- MacBook Pro 10,1
MacBook Air 4,2 and iMac 13,1 should work too, but were not tested.
Apple's "Think Different" slogan fits perfectly with their approach to
firmware security. Besides the fact that they do not use SMM_BWP to
protect SPI flash from being writable from userspace, they do not even
protect Flash Descriptor (
fd) and Management Engine (
The Intel Flash Descriptor is a data structure of fixed size (4KB)
stored on the flash chip (resides in
0x0000-0x0fff), that contains
various information such as space allocated for each region on the
flash, access permissions, some chipset configuration and more. In
particular, it contains access permissions for
Normally they should be read-only in production, but Apple, for whatever
reasons, keeps them read-write.
Instead, they decided to use SPI Protected Range Registers (PR0-PR4) to
set protection over
fd, but here they failed again. Due to a bug in
0x0000-0x0fff is not write-protected after cold boot
and becomes read-only only after resuming from S3. You can dump PRx
protections by running
flashrom -p internal.
This is what you should see after a cold boot (if so, then you can use this guide):
PR0: Warning: 0x00190000-0x0066ffff is read-only. PR1: Warning: 0x00692000-0x01ffffff is read-only.
And this is after resuming from S3:
PR0: Warning: 0x00000000-0x00000fff is read-only. PR1: Warning: 0x00190000-0x0066ffff is read-only. PR2: Warning: 0x00692000-0x01ffffff is read-only.
So, after cold boot flash descriptor is protected neither by PRx registers nor by access permission bits on the flash descriptor itself. Under certain circumstances, writable flash descriptor allows flashing whole SPI flash by using a couple of tricks.
The idea is that we can shrink ME firmware with me_cleaner, flash a small coreboot image on the freed space and move the reset vector there. Then power off, boot coreboot and flash full image, as there will be no more PRx set.
Stage 1. Flashing temporary BIOS
Dump your ROM:
# flashrom -p internal -r orig.bin
Please save this backup to an external drive. You may need it in case of failure.
Extract flash layout:
$ ifdtool -f orig_layout.txt orig.bin $ cat orig_layout.txt
You should see something like or exactly this:
00000000:00000fff fd 00190000:007fffff bios 00001000:0018ffff me
If you compare these regions with what's protected by PR0 and PR1,
you'll notice that
me regions are fully writable and only
bios is protected. Writable ME region gives us around 1.5 MB which we
can use for our goals.
Extract flash regions from the dump into separate files:
$ ifdtool -x orig.bin
You can see that 3 new files were created:
$ ls flash* flashregion_0_flashdescriptor.bin flashregion_1_bios.bin flashregion_2_intel_me.bin
The ME firmware is ~1.5 MB in size, but we can truncate it with me_cleaner:
$ me_cleaner.py -t -r -O flashregion_2_intel_me_truncated.bin flashregion_2_intel_me.bin
The truncated firmware should be around 90K:
$ stat --printf=%s flashregion_2_intel_me_truncated.bin 94208
Rename the original
flashregion_2_intel_me.bin file to not mix them up
$ mv flashregion_2_intel_me.bin flashregion_2_intel_me_orig.bin
Now we need to write a new flash layout. 128K is more than enough for
the "neutered" ME firmware. We can use the rest for new
but 892K is enough:
00000000:00000fff fd 00001000:00020fff me 00021000:000fffff bios 00100000:007fffff pd
Note that we must allocate the remaining
something to be able to address and flash it in future. Let's just mark
pd (which stands for "Platform Data") for now.
Save the new layout to a file
new_layout.txt and update regions in the
$ ifdtool -n new_layout.txt orig.bin
The updated image will be saved to
orig.bin.new. Move it to a
separate directory for convenience:
$ mkdir patched && cd $_ $ mv ../orig.bin.new .
Extract flash regions again, now from the updated image:
$ ifdtool -x orig.bin.new
By now we have new
flashregion_0_flashdescriptor.bin file with our
custom layout. Let's also move the patched ME here:
$ rm flashregion_2_intel_me.bin $ mv ../flashregion_2_intel_me_truncated.bin .
So far, so good. At this point our preparations for the first stage are finished and we're ready to configure coreboot.
Configuring and flashing coreboot
make menuconfig and configure as shown below. Note that you need
to change ROM chip size, CBFS size and specify paths to modified
Mainboard ---> Mainboard vendor (Apple) Mainboard model () # Set according to your model ROM chip size (1024 KB (1 MB)) (0xd0000) Size of CBFS filesystem in ROM Chipset ---> [*] Add Intel descriptor.bin file (/path/to/patched/flashregion_0_flashdescriptor.bin) Path and filename of the descriptor.bin file [*] Add Intel ME/TXE firmware (/path/to/patched/flashregion_2_intel_me_truncated.bin) Path to management engine firmware [ ] Verify the integrity of the supplied ME/TXE firmware [ ] Strip down the Intel ME/TXE firmware Protect flash regions (Unlock flash regions)
Then you need to decide which payload to use. For now, it's recommended to use GRUB2. Be sure to include a good config for it that can load a variety of setups. SeaBIOS works too, but currently needs a patch for internal MacBook's keyboard to work.
When configuration is done, run
make to build coreboot. In the end you
should have 1024 KB coreboot ROM at
build/coreboot.rom. Flashrom won't
accept it, because it's size must match the chip, so we have to make it
8 MB. Just add 7 MB of zeroes:
$ dd if=/dev/zero of=7M.bin bs=1024 count=7168
$ cat build/coreboot.rom 7M.bin > coreboot8.rom
Now cross your fingers and flash the new
using the layout file:
$ flashrom -p internal -w coreboot8.rom -l new_layout.txt -i fd -i me -i bios
The first stage is completed. Power off the machine now. Reboot won't work: new flash descriptor becomes active on cold boot.
On the next boot, if you're lucky and didn't do any mistake, coreboot
will be loaded from the new
bios region, and Apple's EFI, that still
0x190000-0x7fffff, will be ignored.
Stage 2. Flashing full ROM
Now we can flash whole 8 MB chip, because no PRx are set anymore. Let's re-layout the chip again.
If you want to continue using truncated ME:
00000000:00000fff fd 00001000:00020fff me 00021000:007fffff bios
If you want to flash full ME firmware back:
00000000:00000fff fd 00001000:0018ffff me 00190000:007fffff bios
Save it to
final_layout.txt and create new flash descriptor again.
$ mkdir patched2 && cd $_ $ cp ../orig.bin . $ ifdtool -n final_layout.txt orig.bin $ ifdtool -x orig.bin.new
Go back to coreboot directory and run
In the Mainboard section change ROM chip size back to 8 MB and
set Size of CBFS according to your needs: now you have plenty of
0x500000 or so should work just fine.
In the Chipset section, update paths to the flash descriptor and ME
firmware files. If you decided to stick to truncated ME, use
flashregion_2_intel_me_truncated.bin, otherwise use
Mainboard ---> ROM chip size (8192 KB (8 MB)) (0x500000) Size of CBFS filesystem in ROM Chipset ---> [*] Add Intel descriptor.bin file # Use the latest flashdescriptor from the patched2 directory (/path/to/patched2/flashregion_0_flashdescriptor.bin) Path and filename of the descriptor.bin file [*] Add Intel ME/TXE firmware (/path/to/patched/flashregion_2_intel_me_truncated.bin) Path to management engine firmware [ ] Verify the integrity of the supplied ME/TXE firmware [ ] Strip down the Intel ME/TXE firmware
Save the config and
make it again. Then flash
according to the
# flashrom -p internal -w build/coreboot.rom -l final_layout.txt -i fd -i bios
If you changed ME firmware back to original, flash it as well:
# flashrom -p internal -w build/coreboot.rom -l final_layout.txt -i me
Stage 2 is completed. Power off (reboot won't work again). On the next boot, you will have a completely corebooted MacBook. Congratulations!