Macbook Early 2016にUbuntu MATE 16.10をインストールする

#はじめに
[SRA Advent Calendar](http://qiita.com/advent-calendar/2016/sra)の14日目です。
ネットワークシステムサービス第１事業部の ふじまき です。

MacBook Early 2016(Model:A1534)でLinuxを動かそうとするとわりと面倒という情報を入手したのでヨドバシで買ってきてUbuntu MATEをインストールしてみました。

https://wiki.archlinux.org/index.php/MacBook#April_2016_12.22_-_Version_9.2C1
https://wiki.archlinuxjp.org/index.php/MacBook#April_2016_12.22_-_.E3.83.90.E3.83.BC.E3.82.B8.E3.83.A7.E3.83.B3_9.2C1

# 注意事項
- このページの情報は**無保証**です。試す場合は**自己責任で**お願いします。
- MacOSに未練がある人は**リカバリ手段を確保してから**行ってください。
- キーボード、トラックパッドは**動作しません**。USB接続のキーボード、マウスは必須です。
    - Archのwikiに記載のとおり、Work In Progressな[ドライバ](https://github.com/cb22/macbook12-spi-driver)が存在しますが、それを入れる作業の際にキーボードが必要です。なお、このドライバは2015年版のMacBookでは**動きません**(今のところ)
- デフォルト設定でインストールすると通常起動はUbuntu MATEになります。**MacOSは(自動では)起動しなくなります**。
- 802.11acが**不安定**です。(パケットが流れなくなる。原因不明)アクセスポイントが802.11ac対応かつ802.11acのみを無効化できない場合、**まともに生活出来ません**。
- **音は鳴りません**。(録音は出来るらしいですが未確認)
- ここで説明する手順はインストーラーをカスタマイズしてMacbookにUbuntu MATEをインストールします。なので、**インストーラー作成用のマシンが別途必要**です。
- 2015年版のMacBookにはおそらくインストールできますが、キーボードとトラックパッドを動かす手段が**見つかっていない**のでお勧めしません。

# MacBook Early 2016へのUbuntu MATE 16.10インストール

Arch Linuxの[Wiki](https://wiki.archlinux.org/index.php/MacBook#April_2016_12.22_-_Version_9.2C1)を見るとkernel 4.6以上ならインストーラー起動後、コンソールで

```
# modprobe nvme
# echo 106b 2003 > /sys/bus/pci/drivers/nvme/new_id
```

と入力すれば内蔵NVMeコントローラーが動作するはずですが、Ubuntu MATE 16.10(kernel 4.8)の場合、"D3 stateから復帰できない～"みたいなエラーが出て内蔵SSDを認識できず。Debian StretchインストーラーAlpha7(kernel 4.6)だと内蔵SSDを認識するがインストール中に頻繁にフリーズしてまともにインストールが出来ませんでした。(Debianは5,6回リトライし続けてかろうじてインストール出来ましたが)

なのでUbuntu MATE 16.10のインストーラーのカーネルを差し替えて、さくっとインストール出来るインストーラーを作ります。

## インストーラーカスタマイズ用の作業環境作成
適当なマシン(仮想でもOK)にUbuntu MATE 16.10をインストールします。ハードディスクの容量は50GBくらいあった方がよいです。(40GBだとギリギリ)

以降、この環境を「作業環境」と呼びます。

### 作業環境のカーネル更新
sshで作業するので、openssh-serverをインストール

```
$ sudo apt-get update
$ sudo apt-get -y install openssh-server
```

カーネルパッケージ作成に必要なパッケージをインストール

```
$ sudo apt-get -y install dpkg-dev debhelper dh-systemd kernel-wedge makedumpfile \
 libelf-dev libnewt-dev libiberty-dev libdw-dev libpci-dev pkg-config \
 flex bison libunwind8-dev liblzma-dev libssl-dev libaudit-dev \
 python-dev libudev-dev autoconf automake libtool uuid-dev xmlto \
 docbook-utils transfig sharutils asciidoc
```

ホームディレクトリに作業ディレクトリ"work"を作成してその中にカーネルソースをダウンロード。
(launchpad.netからlinux_4.8.0.orig.tar.gzをダウンロードするのは時間がかかるのでミラーサイトからダウンロードしてコンバートしています)

```
$ mkdir ~/work
$ cd ~/work
$ wget http://ftp.jaist.ac.jp/pub/Linux/kernel.org/linux/kernel/v4.x/linux-4.8.tar.xz
$ xz -dc linux-4.8.tar.xz | gzip -9 > linux_4.8.0.orig.tar.gz
$ rm linux-4.8.tar.xz
$ wget https://launchpad.net/ubuntu/+archive/primary/+files/linux_4.8.0-27.29.diff.gz
```

カーネルソースを展開し、パッケージのパッチを適用

```
$ tar zxf linux_4.8.0.orig.tar.gz
$ cd linux-4.8/
$ gzip -dc ../linux_4.8.0-27.29.diff.gz | patch -p1
```

Linux4.8のNVMeドライバはMacBook Early 2016のNVMeコントローラーのProduct IDが入っていないので追加します。

```
$ cp -p drivers/nvme/host/pci.c drivers/nvme/host/pci.c_orig
$ vi drivers/nvme/host/pci.c
```

以下のとおり変更

```diff
--- drivers/nvme/host/pci.c_orig        2016-10-03 08:24:33.000000000 +0900
+++ drivers/nvme/host/pci.c     2016-11-20 14:17:53.892000000 +0900
@@ -1635,7 +1635,7 @@
         * Temporary fix for the Apple controller found in the MacBook8,1 and
         * some MacBook7,1 to avoid controller resets and data loss.
         */
-       if (pdev->vendor == PCI_VENDOR_ID_APPLE && pdev->device == 0x2001) {
+       if (pdev->vendor == PCI_VENDOR_ID_APPLE && (pdev->device == 0x2001 || pdev->device == 0x2003)) {
                dev->q_depth = 2;
                dev_warn(dev->dev, "detected Apple NVMe controller, set "
                        "queue depth=%u to work around controller resets\n",
@@ -2121,6 +2121,7 @@
                .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, },
        { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) },
        { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) },
+       { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) },
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, nvme_id_table);
```

バックアップファイル削除

```
$ rm drivers/nvme/host/pci.c_orig
```

上記の修正は http://lists.infradead.org/pipermail/linux-nvme/2016-May/004618.html より。前半の部分はオリジナルのパッチでは不要とされてましたがインストール中は明らかにあったほうが良いので追加。(無いとパーティショニングのところで高確率でフリーズします、という突破出来ない)

カーネルパッケージビルド。環境依存かもしれませんが、手元の環境だとsshログインした状態でカーネルパッケージ作成を行うと途中で通信が切れるのでここだけローカルでログインした状態で実行した方がよいかも。

```
$ dpkg-buildpackage -rfakeroot -us -uc --build=binary
```

正常に完了すると一つ上のディレクトリにカーネルパッケージが作成されます。
移動してカーネルパッケージをインストール

```
$ cd ~/work/
$ sudo dpkg -i linux-headers-4.8.0-27_4.8.0-27.29_all.deb \
  linux-headers-4.8.0-27-generic_4.8.0-27.29_amd64.deb \
  linux-image-4.8.0-27-generic_4.8.0-27.29_amd64.deb \
  linux-image-extra-4.8.0-27-generic_4.8.0-27.29_amd64.deb \
  linux-tools-4.8.0-27_4.8.0-27.29_amd64.deb \
  linux-tools-4.8.0-27-generic_4.8.0-27.29_amd64.deb \
  linux-tools-common_4.8.0-27.29_all.deb
```

インストール後、再起動

```
$ sudo reboot
```

再起動後ログイン。修正したNVMeドライバにProduct IDが追加されていることを確認

```
$ modinfo nvme
filename:       /lib/modules/4.8.0-27-generic/kernel/drivers/nvme/host/nvme.ko
version:        1.0
license:        GPL
author:         Matthew Wilcox <willy@linux.intel.com>
srcversion:     A270F19E73A377AD815935F
alias:          pci:v0000106Bd00002003sv*sd*bc*sc*i*   /* d00002003があればOK */
alias:          pci:v0000106Bd00002001sv*sd*bc*sc*i*
(以下略)
```

aliasのところにDevice-ID==2003があることを確認。作成したカーネルパッケージはこの後のインストーラーのカスタマイズでも使用(インストール)します。

## インストーラーのカスタマイズ

参考サイト

- https://help.ubuntu.com/community/InstallCDCustomization
- https://help.ubuntu.com/community/LiveCDCustomization

インストーラーの中身をファイルシステム上に展開して、カーネルをNVMeドライバを修正したものと差し替えます。

### インストーラーの展開

作業環境でUbuntu MATE 16.10のインストーラーを/mnt/にマウント

```
$ sudo mount /dev/sr0 /mnt/
```

インストーラーの中身を/opt/cd-imageにコピー

```
$ sudo mkdir /opt/cd-image
$ sudo rsync -av /mnt/ /opt/cd-image/
```

作業ディレクトリとして/opt/workを作成し、インストーラーのルートファイルシステム(Squashfs)を展開。"squashfs-root"というディレクトリが作成されその中に展開されます。

```
$ sudo mkdir /opt/work
$ cd /opt/work
$ sudo unsquashfs /opt/cd-image/casper/filesystem.squashfs
```

### インストーラーのinitrd変更

インストーラーのinitrdの中身を展開

```
$ sudo mkdir /opt/work/installer-initrd
$ cd /opt/work/installer-initrd/
$ lzma -dc -S .lz /opt/cd-image/casper/initrd.lz | sudo cpio -imvd --no-absolute-filenames
```

Ubuntu MATE 16.10のインストーラーのカーネルは"4.8.0-22"というバージョンでinitrdの中身も4.8.0-22になっています。これを作業環境にインストールした"4.8.0-27"に差し替えます。

差し替えの事前準備として作業環境のinitrdにaufsを追加

```
$ echo aufs | sudo tee -a /etc/initramfs-tools/modules
$ sudo update-initramfs -c -k all
```

更新した作業環境のinitrdを/opt/work/basesystem-initrdに展開

```
$ sudo mkdir /opt/work/basesystem-initrd
$ cd /opt/work/basesystem-initrd/
$ gzip -dc /boot/initrd.img-4.8.0-27-generic | sudo cpio -imvd --no-absolute-filenames
```

この時点でインストーラーのinitrdを/opt/work/installer-initrd/ 、作業環境のinitrdを/opt/work/basesystem-initrdに展開した状態になっています。
/opt/work/installer-initrd/から"4.8.0-22"関連を削除

```
$ sudo rm -rf /opt/work/installer-initrd/lib/modules/4.8.0-22-generic
$ sudo rm -rf /opt/work/installer-initrd/lib/firmware/4.8.0-22-generic
$ sudo rm /opt/work/installer-initrd/lib/modprobe.d/blacklist_linux_4.8.0-22-generic.conf
```

代わりにNVMeドライバを修正した作業環境の"4.8.0-27"関連ファイルを/opt/work/installer-initrd/へコピー

```
$ sudo cp -a /opt/work/basesystem-initrd/lib/modules/4.8.0-27-generic /opt/work/installer-initrd/lib/modules/
$ sudo cp -a /opt/work/basesystem-initrd/lib/firmware/4.8.0-27-generic /opt/work/installer-initrd/lib/firmware/
$ sudo cp -p /opt/work/basesystem-initrd/lib/modprobe.d/blacklist_linux_4.8.0-27-generic.conf /opt/work/installer-initrd/lib/modprobe.d/
```

修正したinitrdイメージ再作成

```
$ cd /opt/work/installer-initrd/
$ sudo find . | sudo cpio --quiet --dereference -o -H newc | lzma -7 | sudo dd of=/opt/cd-image/casper/initrd.lz
```

### インストーラーのカーネル更新
ファイルを1個コピーするだけ。

```
$ sudo cp /boot/vmlinuz-4.8.0-27-generic  /opt/cd-image/casper/vmlinuz.efi
```

### インストーラーのファイルシステム編集

Squashfsを展開した中へchrootして編集します。
事前準備として/proc等をSquashfs展開先にマウント。

```
$ sudo mount -o bind /proc /opt/work/squashfs-root/proc
$ sudo mount -o bind /dev /opt/work/squashfs-root/dev
$ sudo mount -t devpts none /opt/work/squashfs-root/dev/pts/
$ sudo mount -t sysfs none /opt/work/squashfs-root/sys/
```

作業環境のカーネル更新でパッケージを作成したディレクトリもSquashfs展開先にマウントしておきます。

```
$ sudo mkdir /opt/work/squashfs-root/work
$ sudo mount -o bind ~/work/ /opt/work/squashfs-root/work
```

Squashfsを展開した中へchroot

```
$ sudo chroot /opt/work/squashfs-root/
```

以下、chroot内作業(プロンプトが"#")

修正したカーネルパッケージをインストール
(chroot内でのコマンド実行でlocale関連のWarningが出ますが無視してOK) 

```
# cd /work/
# dpkg -i linux-headers-4.8.0-27_4.8.0-27.29_all.deb \
  linux-headers-4.8.0-27-generic_4.8.0-27.29_amd64.deb \
  linux-image-4.8.0-27-generic_4.8.0-27.29_amd64.deb \
  linux-image-extra-4.8.0-27-generic_4.8.0-27.29_amd64.deb \
  linux-tools-4.8.0-27-generic_4.8.0-27.29_amd64.deb \
  linux-tools-4.8.0-27_4.8.0-27.29_amd64.deb \
  linux-tools-common_4.8.0-27.29_all.deb
```

不要パッケージ(古いカーネルパッケージ)を削除

```
# apt-get purge linux-signed-image-4.8.0-22-generic \
  linux-signed-image-generic linux-signed-generic \
  linux-headers-4.8.0-22-generic linux-headers-4.8.0-22 \
  linux-image-4.8.0-22-generic linux-image-extra-4.8.0-22-generic \
  linux-tools-4.8.0-22 linux-tools-4.8.0-22-generic
```

grubのデフォルト設定変更。intremap=nosidを追加して更新(キーボード＆トラックパッドのドライバの動作に必要)

```
# sed -i 's/^kopt_params=""/kopt_params="intremap=nosid"/' /usr/share/grub-installer/grub-installer
# ucf --purge /etc/default/grub
# sed -i 's/^GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="intremap=nosid"/' /etc/default/grub
# ucf /etc/default/grub /etc/default/grub
# update-grub
```

以上。chrootから抜ける

```
# exit
```

Squashfs展開先にマウントしておいたファイルシステムをアンマウント

```
$ sudo umount /opt/work/squashfs-root/proc
$ sudo umount /opt/work/squashfs-root/dev/pts
$ sudo umount /opt/work/squashfs-root/dev
$ sudo umount /opt/work/squashfs-root/sys
$ sudo umount /opt/work/squashfs-root/work
```

一時的に作成したディレクトリ削除

```
$ sudo rmdir /opt/work/squashfs-root/work
```

改変したSquashfsイメージ再作成

```
$ sudo chmod +w /opt/cd-image/casper/filesystem.manifest
$ sudo chroot /opt/work/squashfs-root  dpkg-query -W  | sudo tee /opt/cd-image/casper/filesystem.manifest
$ sudo rm /opt/cd-image/casper/filesystem.squashfs
$ sudo mksquashfs /opt/work/squashfs-root  /opt/cd-image/casper/filesystem.squashfs  -comp xz
```

### インストーラーイメージ再作成

インストーラーのカーネルオプション変更。インストール時に必要なnoapicを追加。(isolinuxとgrubの両方を変えていますがMacBookで使われるのはgrub)

```
$ sudo sed -i 's/initrd.lz quiet/initrd.lz noapic quiet/' /opt/cd-image/isolinux/txt.cfg
$ sudo sed -i 's/quiet splash/quiet noapic splash/' /opt/cd-image/boot/grub/grub.cfg
```

ファイルリストとMD5チェックサム作成

```
$ printf $(sudo du -sx --block-size=1 /opt/work/squashfs-root | cut -f1) | sudo tee /opt/cd-image/casper/filesystem.size
$ sudo rm /opt/cd-image/md5sum.txt
$ cd /opt/cd-image/
$ sudo find -type f -print0 | sudo xargs -0 md5sum | grep -v isolinux/boot.cat | sudo tee md5sum.txt
```

ISOイメージ作成

```
$ sudo mkdir /opt/newimage
$ cd /opt/cd-image/
$ sudo IMAGE=/opt/newimage/custom.iso BUILD=/opt/cd-image/  mkisofs -r -V "Custom Test" -cache-inodes -J -l \
 -b isolinux/isolinux.bin -c isolinux/boot.cat \
 -no-emul-boot -boot-load-size 4 -boot-info-table \
 -o /opt/newimage/custom.iso /opt/cd-image/
```

/opt/newimage/custom.iso に1.4GBくらいのハイブリッドイメージが出来るのでUSBメモリに書き出しておきます。

## カスタムインストーラーを使ったMacBook Early 2016へのUbuntu MATE 16.10インストール

カスタマイズしたインストーラーで起動しUbuntu MATEをインストールします。
説明は特に必要ないと思いますが、注意点としては

- MacOSも使い続けたい場合は事前にMacOS上で空きパーティションを作っておいた方がよいでしょう。
- インストール中にネットワークに繋げるとせっかくNVMeドライバを修正したカーネルが上書きされてしまう恐れがあるので、インストール中はネットワークに繋げない方が確実です。