summaryrefslogtreecommitdiff
path: root/README.md
blob: cf5008749915e1a8e58678d217d5fba1bdbaa8b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# mmga: Make MacBook Great Again

**mmga** is a script to help flashing coreboot on some MacBook Air and Pro
models without using external SPI programmer. See
[this blog post](https://ch1p.io/coreboot-macbook-internal-flashing/) on how to
do the same manually.

### Supported devices

As of time of writing, following devices are supported in coreboot. Other models
might be supported in future.

* MacBook Pro 8,1 (13'' Early 2011) (`macbookpro8_1`)
* MacBook Pro 10,1 (15'' Mid 2012 Retina) (`macbookpro10_1`)
* MacBook Air 5,2 (13'' Mid 2012) (`macbookair5_2`)
* MacBook Air 4,2 (13'' Mid 2011) (`macbookair4_2`).
  
  **Attention!** This model hasn't been tested by me, simply because
  I don't have it. There were reports that it doesn't work, and by the nature of
  those reports I suggest that coreboot port for MBA 4,2 is somehow broken.
  Please don't try to use mmga on 4,2 until it's fixed or confirmed to work.

iMac 13,1 is a candidate for support too, but [coreboot port](https://review.coreboot.org/c/coreboot/+/38883)
for this device is not actively maintained at the moment and it may fail to build.
I'll add iMac 13,1 support later when it's fixed.

### System requirements

* Recent Linux distribution booted with `iomem=relaxed` kernel parameter
  (required for internal programmer to work);
* Build dependencies. Here's a list for Debian-based distros:
    ```
    # apt install bison build-essential curl flex git gnat libncurses5-dev m4 zlib1g-dev make libpci-dev libusb-1.0-0-dev
    ```

    If you plan to use GRUB2 as a payload:
    ```
    # apt install libfreetype-dev unifont
    ```

    On other distros package names might differ. Be sure to install **gnat**
    prior to building coreboot toolchain.

### Building flashrom

First of all, grab recent flashrom sources and build it:
```
$ git clone https://review.coreboot.org/flashrom.git && cd flashrom
$ make
```

Optionally, install it to `/usr/local/sbin`:
```
$ sudo make install
```

## How it works

The firmware of the devices covered by this project is stored on SPI chip. It
consists of various regions: `fd` (Flash Descriptor), `me` (Intel ME) and `bios`
(BIOS, or Apple EFI). Sometimes there are more regions, for example there may be
`gbe` region for Gigabit Ethernet or `ec` region with EC firmware, but for now,
let's focus on our MacBooks.

The most important region in context of this story is `fd`, the Intel Flash
Descriptor.

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 `fd` and `me` regions.

This is the flash chip layout used in MacBook Air 5,2 (which has 8 MiB flash chip).
It can be extracted from stock ROM image with `ifdtool`:
```
00000000:00000fff fd
00190000:007fffff bios
00001000:0018ffff me
```

Normally, the `fd` and `me` regions should be read-only in production, but this
is not the case with MacBooks. Apparently, Apple's "Think Different" thing
applies to firmware security as well.

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 (I hope),
`0x0000-0x0fff` is not write-protected after cold boot and becomes read-only
only after resuming from S3.

You can dump PRx protections on your device by running `flashrom -p internal`.
If it doesn't work, make sure to boot with `iomem=relaxed` or try
`-p internal:laptop=force_I_want_a_brick`.

This is what you should see after a cold boot (and, if so, mmga should work on
your device):
```
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 neat tricks, and that is what mmga script does.

Writable `me` region gives us around 1.5 MiB of writable space. The idea is that
we can shrink ME firmware image with me_cleaner to about ~128 KiB and use the
freed space for a small temporary coreboot image. Writable `fd` gives us ability
to change flash layout and move reset vector. We combine all this, flash modified
regions, then power off (new flash descriptor becomes active on cold boot, so
reboot won't work). Then boot our small temporary coreboot and flash the whole
SPI chip, as there will be no more PRx protections set. So this is a two-stage
process.

Let's write a new layout:
```
00000000:00000fff fd
00001000:00020fff me
00021000:000fffff bios
00100000:007fffff pd
```

In this layout, we allocate 128 KiB for `me` and 892 KiB for `bios`. To fit the
original 1.5 MiB ME image into the 128 KiB region, it has to be truncated with
[me_cleaner](https://github.com/corna/me_cleaner) with `-t` and `-r` arguments,
the size of resulting image is ~92 KiB. We also have to allocate the remaining
`0x100000-0x7fffff` region for *something*, to be able to address and flash it
in future. So we just mark it as `pd`, which is commonly used for "Platform Data".

After the new layout is ready, we build small coreboot ROM that fits into the
allocated 892 KiB bios region. Then we flash **`fd`** (`0x0000-0x0fff`),
**`me`** (`0x1000-0x20fff`) and **`bios`** (`0x21000-0xfffff`) according to the
new layout. On the next cold boot, coreboot will be loaded from the
`0x21000-0xfffff` region, and the old firmware, which still resides in
`0x190000-0x7fffff`, will be ignored. This is **stage1**.

After we boot with small temporary coreboot ROM, we're able to flash the whole
8 MiB chip, because there are no more PRx protections set. We repartition the
chip again, and the new layout looks like this:
```
00000000:00000fff fd
00001000:00020fff me
00021000:007fffff bios
```

It's almost the same, except that `bios` fills all the remaining space. Then we
build coreboot again, flash `fd`, `me` and `bios` and power off again. On the
next cold boot we will have completely corebooted MacBook. This is **stage2**.

## Usage instructions

The **mmga** script automates steps described above and does all the dirty work.

### Usage:

```
./mmga <options> ACTION
```

##### Options:

```
-h, --help: show help
```

##### stage1 actions:

```
          dump: dump flash contents
         fetch: fetch board tree from Gerrit (if needed)
prepare-stage1: patch IFD, neutralize and truncate ME
 config-stage1: make coreboot config (for manual use)
  build-stage1: make config and build ROM (for auto use)
  flash-stage1: flash ROM ($COREBOOT_PATH/build/coreboot.rom)
```

##### stage2 actions:

```
prepare-stage2: patch IFD (if needed)
 config-stage2: make coreboot config (for manual use)
  build-stage2: make config and build ROM (for auto use)
  flash-stage2: flash ROM ($COREBOOT_PATH/build/coreboot.rom)
```

##### other actions:

```
     flash-oem: flash OEM firmware back
```

### Warning

You **should** have external means of flashing for a backup, **just in case**.
The procedure described above is quite delicate and error-prone and any mistake
may lead to a brick. In that case, you should have a copy of your original ROM
**on external drive**. Please make a backup of `work/oem/dump.bin` after
running `mmga dump`, or just copy the whole mmga directory.

These posts may be a little helpful if you ever need to flash externally: 

- [MacBook Air 5,2](https://ch1p.io/coreboot-mba52-flashing/)
- [MacBook Pro 10,1](https://ch1p.io/coreboot-mbp101-flashing/)

### Choosing the payload

Currently, SeaBIOS and GRUB are supported by mmga. SeaBIOS supports legacy boot,
it will most probably boot from an old school MBR partition. On the other hand,
it may not boot from GPT (I'm not sure about it, correct me if you know more)
and certainly it will not boot an EFI installation.

Sometimes GRUB is a better choice, but it all depends on your system. If you are
not sure, do some research before flashing or seek for help.

It may be a good idea to prepare a USB drive with some live system. I can imagine
a situation where you have chosen the wrong payload and cannot boot into your
system after power off/on cycle to complete the second stage, because, for instance,
SeaBIOS doesn't recognize your partition. In that case, you could try to boot
from live USB to fix your system (if possible) or to complete second stage and
change the payload.

Of course, in order to do that, you should backup the whole mmga directory after
the completion of the first stage but **before** the reboot.

**Attention!** Recent SeaBIOS versions break internal keyboard and touchpad on
MacBooks, for now it's recommended to use GRUB until it's fixed.

### Configuration

Before you start, you have to update variables the `config.inc` file:
- **`PAYLOAD`**: which payload to use, supported values are `grub` and `seabios`
- **`MODEL`**: put your macbook model here, example: `macbookair5_2`
- **`GRUB_CFG_PATH`**: only if you use `grub` payload; if empty, default
  `grub.cfg` will be used
- **`COREBOOT_PATH`**: path to cloned coreboot repository (see below)
- **`FLASHROM`**: path to flashrom binary
- **`FLASHROM_ARGS`**: in case if flashrom detects multiple flash chips, put
  `"-c CHIP_MODEL"` here
- **`STAGE2_USE_FULL_ME`**: if you want to use original Intel ME image in the
  final ROM for some reason, set to `1`

#### stage1

Get coreboot:
```
$ git clone --recurse-submodules https://review.coreboot.org/coreboot.git && cd coreboot
```

Build coreboot toolchain. You must have gnat compiler installed, it is required
for graphics initialization (libgfxinit is written in Ada):
```
$ make crossgcc-i386 CPUS=$(nproc)
$ make iasl
```

Dump the flash chip contents:
```
# ./mmga dump
```

**Create a backup of the stock ROM dump on external drive!**

Create patched FD and neutralize ME:
```
$ ./mmga prepare-stage1
```

If your board's port hasn't been merged to coreboot master yet or you don't know,
run:
```
$ ./mmga fetch
```

Create coreboot config and build the ROM:
```
$ ./mmga build-stage1
```

(If you're experienced coreboot user or developer, you may want to configure and
build coreboot yourself. In that case, run `config-stage1` instead of
`build-stage1`. It will create config that you can then copy to `$COREBOOT_PATH`,
make your changes and build.)

Flash it:
```
# ./mmga flash-stage1
```

If it's done and there were no errors, you have to **shutdown** the laptop.
Do not reboot, the new flash descriptor is only active after cold boot, so it
won't work and may lead to weird stuff as you've just messed with firmware. Wait
a few seconds and power it back on.

#### stage2

Make patched flash descriptor for the next flash:
```
$ ./mmga prepare-stage2
```

Create new coreboot config and build the ROM (for experienced users,
`config-stage2` is also available):
```
$ ./mmga build-stage2
```

Flash it:
```
# ./mmga flash-stage2
```

This may take a while, don't interrupt it and let it finish.

Again, if there were no errors, power off the machine again, wait a few seconds,
and power on.

## FAQ

**My device is not listed, will it work?**

No, but it might be possible to support it.

**Will macOS continue to work with coreboot?**

No. It's theoretically possible to turn a corebooted MacBook into a hackintosh
by using TianoCore and Clover or OpenCore. Basically, if you want to use macOS,
don't install coreboot.

**Switching between iGPU and dGPU on MacBook Pro 10,1**

Last time I checked, [hacks](https://wiki.archlinux.org/index.php/MacBookPro10,x#Graphics_2)
were needed to use Linux with integrated GPU on this model. It seems that Apple EFI
forces dGPU when OS is not macOS. I've integrated hybrid graphics driver into
coreboot, it automatically switches to the configured GPU and you don't need to
care about it anymore. By default, integrated GPU is used. The setting is stored
in CMOS and you can change it with `nvramtool`.

Note that to use discrete GPU you need to extract VGA ROM from the stock firmware
dump and add it to CBFS, and configure coreboot to run VGA Option ROMs.

## TODO

- Support custom FMAP for larger first-stage `bios` partition
- Support multiple payloads at once
- Support TianoCore as a payload

## Misc

The script was tested on MacBook Air 5,2, MacBook Pro 8,1 and MacBook Pro 10,1.
If you have successfully corebooted your macbook with it and it worked, please
let me know.

If you have any problems, contact me via GitHub issues or by email (see the
copyright header in the script).

## License

GPLv2