Macbook 2016年モデルにUbuntu MATE 17.10をインストールする

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

[昨年のネタ](https://qiita.com/uskf/items/4eb40c2a1a395bf36a89)のアップデートです。 変更点は

- MacBook自身のキーボード、トラックパッドが使用可能になったのでUSB接続キーボード、マウスを用意する必要がなくなりました。:grinning:
- MacBook Pro Late 2016 のタッチバーのドライバが含まれています。私は持っていませんが、LinuxではデフォルトでESCおよび各種ファンクションキーが表示されるらしいです。

MacBook Proの2016年モデルについては以下にまとまっています。
https://github.com/Dunedan/mbp-2016-linux

#注意事項
冒頭に書いた変更点以外は[昨年](https://qiita.com/uskf/items/4eb40c2a1a395bf36a89#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A0%85)と同じです。
無線LANの問題はMacBook Pro Late 2016にも存在＆Pro無しよりも重症らしいです。

#インストーラーのカスタマイズ作業環境の準備
##Ubuntu Mate 17.10のインストール
適当なマシン(無論、MacBook 2016年モデル以外)にUbuntu Mate 17.10 desktopをインストールします。
仮想の場合、ディスク容量は50GBくらい必要です。

ちなみに、フレーバー固有の部分は無いので、本家Ubuntu 17.10 desktopでも手順は同じです。

##追加パッケージのインストール
最初にsshサーバを入れて、sshログイン経由で作業出来るようにします。

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

後の作業で必要になるパッケージをインストール

```bash
$ sudo apt-get install -y dpkg-dev fakeroot 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 gawk libudev-dev \
  autoconf automake libtool uuid-dev binutils-dev libnuma-dev \
  xmlto docbook-utils transfig sharutils asciidoc python-sphinx \
  python-sphinx-rtd-theme xorriso isolinux
```

キャッシュ削除

```bash
$ sudo apt-get clean
```

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

# MacBook向けカーネルパッケージの作成
作業環境でMacBook 2016年モデル(一部Pro)向けの修正、ドライバを追加したカーネルのパッケージを作成します。

作業用のディレクトリを作成し、そこにUbuntuのカーネルパッケージ作成に必要なファイルをダウンロード

```bash
$ mkdir ~/work
$ cd ~/work
$ wget http://archive.ubuntu.com/ubuntu/pool/main/l/linux/linux_4.13.0-17.20.diff.gz
$ wget http://ftp.jaist.ac.jp/pub/Linux/kernel.org/linux/kernel/v4.x/linux-4.13.tar.xz
$ xz -dc linux-4.13.tar.xz | gzip -9 > linux_4.13.0.orig.tar.gz
$ rm linux-4.13.tar.xz
```

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

```bash
$ tar zxf linux_4.13.0.orig.tar.gz
$ cd linux-4.13/
$ gzip -dc ../linux_4.13.0-17.20.diff.gz | patch -p1
```

##カーネルの修正
Ubuntuのパッチに含まれていないものを手動で直します。

###NVMeドライバの修正
デバイスID自体はvanilla kernelに取り込まれていますが、インストール時限定（？）でおかしいので。
修正内容は昨年と同じ。

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

変更箇所

```diff
$ diff -u drivers/nvme/host/pci.c_orig drivers/nvme/host/pci.c
--- drivers/nvme/host/pci.c_orig        2017-11-28 23:11:07.152000000 +0900
+++ drivers/nvme/host/pci.c     2017-11-28 23:11:43.680000000 +0900
@@ -1950,7 +1950,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->ctrl.device, "detected Apple NVMe controller, "
                        "set queue depth=%u to work around controller resets\n",
```

バックアップ削除

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

###ACPI周りの修正
キーボード＆トラックパッドが使えない原因。[昨年のネタ](https://qiita.com/uskf/items/4eb40c2a1a395bf36a89)を書いていた時点ではDSDTを書き換える必要があり、すごい面倒だった（なので、昨年は書かなかった）のですが、kernel 4.14で対応されました。
が、残念ながらUbuntu 17.10のカーネルはまだ4.13なのでパッチを個別に適用します。
https://github.com/Dunedan/mbp-2016-linux#misc

```bash
$ wget -O - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/patch/?id=899596e090 | patch -p1
$ wget -O - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/patch/?id=75fc70e073 | patch -p1
$ wget -O - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/patch/?id=630b3aff8a | patch -p1 -F 3
$ wget -O - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/patch/?id=ca9ef3ab68 | patch -p1
```
ちょっと警告が出ますがとりあえず平気です。:grinning:
バックアップファイル削除

```bash
$ find . -name '*.orig' -exec rm {} \;
```

###キーボード＆トラックパッドのドライバ追加
[最初の作者](https://github.com/cb22/macbook12-spi-driver)よりも、[タッチバーのドライバの作者](https://github.com/roadrunner2/macbook12-spi-driver)の方がちょっと新しいので、そちらからダウンロード

```bash
$ wget -O - https://github.com/roadrunner2/macbook12-spi-driver/raw/touchbar-driver-hid-driver/applespi.c > drivers/input/misc/applespi.c
```

Kconfigに追加

```bash
$ cp -p drivers/input/misc/Kconfig drivers/input/misc/Kconfig_orig
$ vi drivers/input/misc/Kconfig
```

変更箇所

```diff
$ diff -u drivers/input/misc/Kconfig_orig drivers/input/misc/Kconfig
--- drivers/input/misc/Kconfig_orig     2017-09-04 05:56:17.000000000 +0900
+++ drivers/input/misc/Kconfig  2017-11-28 23:17:16.808000000 +0900
@@ -72,6 +72,13 @@
          To compile this driver as a module, choose M here: the
          module will be called ad714x-spi.
 
+config INPUT_APPLE_SPI
+       tristate "MacBook 2016 keyboard and trackpad"
+       depends on SPI
+       help
+         Say Y to enable support MacBook Early 2016/Late 2016
+         Keyboard and Trackpad.
+
 config INPUT_ARIZONA_HAPTICS
        tristate "Arizona haptics support"
        depends on MFD_ARIZONA && SND_SOC
```

Makefileにも追加

```bash
$ cp -p drivers/input/misc/Makefile drivers/input/misc/Makefile_orig
$ vi drivers/input/misc/Makefile
```

変更箇所

```diff
$ diff -u drivers/input/misc/Makefile_orig drivers/input/misc/Makefile
--- drivers/input/misc/Makefile_orig    2017-09-04 05:56:17.000000000 +0900
+++ drivers/input/misc/Makefile 2017-11-28 23:18:23.356000000 +0900
@@ -14,6 +14,7 @@
 obj-$(CONFIG_INPUT_ADXL34X_I2C)                += adxl34x-i2c.o
 obj-$(CONFIG_INPUT_ADXL34X_SPI)                += adxl34x-spi.o
 obj-$(CONFIG_INPUT_APANEL)             += apanel.o
+obj-$(CONFIG_INPUT_APPLE_SPI)          += applespi.o
 obj-$(CONFIG_INPUT_ARIZONA_HAPTICS)    += arizona-haptics.o
 obj-$(CONFIG_INPUT_ATI_REMOTE2)                += ati_remote2.o
 obj-$(CONFIG_INPUT_ATLAS_BTNS)         += atlas_btns.o
```

バックアップ削除

```bash
$ rm drivers/input/misc/Makefile_orig drivers/input/misc/Kconfig_orig
```

###タッチバーのドライバ追加
ダウンロード

```bash
$ wget -O - https://github.com/roadrunner2/macbook12-spi-driver/raw/touchbar-driver-hid-driver/appletb.c > drivers/hid/appletb.c
```

Kconfigに追加

```bash
$ cp -p drivers/hid/Kconfig drivers/hid/Kconfig_orig
$ vi drivers/hid/Kconfig
```

変更箇所

```diff
$ diff -u drivers/hid/Kconfig_orig drivers/hid/Kconfig
--- drivers/hid/Kconfig_orig    2017-09-04 05:56:17.000000000 +0900
+++ drivers/hid/Kconfig 2017-11-28 23:20:27.736000000 +0900
@@ -146,6 +146,12 @@
 
        Say Y here if you want support for Apple infrared remote control.
 
+config HID_APPLE_TOUCHBAR
+       tristate "Apple Touchbar"
+       depends on (USB_HID)
+       ---help---
+       MacBook Pro 2016 Touchbar support.
+
 config HID_ASUS
        tristate "Asus"
        depends on LEDS_CLASS
```

Makefileにも追加

```bash
$ cp -p drivers/hid/Makefile drivers/hid/Makefile_orig
$ vi drivers/hid/Makefile
```

変更箇所

```diff
$ diff -u drivers/hid/Makefile_orig drivers/hid/Makefile
--- drivers/hid/Makefile_orig   2017-09-04 05:56:17.000000000 +0900
+++ drivers/hid/Makefile        2017-11-28 23:21:36.808000000 +0900
@@ -26,6 +26,7 @@
 obj-$(CONFIG_HID_ACRUX)                += hid-axff.o
 obj-$(CONFIG_HID_APPLE)                += hid-apple.o
 obj-$(CONFIG_HID_APPLEIR)      += hid-appleir.o
+obj-$(CONFIG_HID_APPLE_TOUCHBAR)       += appletb.o
 obj-$(CONFIG_HID_ASUS)         += hid-asus.o
 obj-$(CONFIG_HID_AUREAL)       += hid-aureal.o
 obj-$(CONFIG_HID_BELKIN)       += hid-belkin.o
```

##パッケージ作成

パッケージのconfigに追加したドライバの記述を追加

```bash
$ cp -p debian.master/config/config.common.ubuntu debian.master/config/config.common.ubuntu_orig
$ vi debian.master/config/config.common.ubuntu
```

変更箇所

```diff
$ diff -u debian.master/config/config.common.ubuntu_orig debian.master/config/config.common.ubuntu
--- debian.master/config/config.common.ubuntu_orig      2017-11-28 23:11:07.080000000 +0900
+++ debian.master/config/config.common.ubuntu   2017-11-28 23:23:05.788000000 +0900
@@ -3370,6 +3370,7 @@
 CONFIG_HID_ALPS=m
 CONFIG_HID_APPLE=m
 CONFIG_HID_APPLEIR=m
+CONFIG_HID_APPLE_TOUCHBAR=m
 CONFIG_HID_ASUS=m
 CONFIG_HID_AUREAL=m
 CONFIG_HID_BATTERY_STRENGTH=y
@@ -3955,6 +3956,7 @@
 CONFIG_INPUT_ADXL34X_I2C=m
 CONFIG_INPUT_ADXL34X_SPI=m
 CONFIG_INPUT_APANEL=m
+CONFIG_INPUT_APPLE_SPI=m
 CONFIG_INPUT_ARIZONA_HAPTICS=m
 CONFIG_INPUT_ATI_REMOTE2=m
 CONFIG_INPUT_ATLAS_BTNS=m
```

モジュールリスト変更

```bash
$ cp -p debian.master/abi/4.13.0-16.19/amd64/generic.modules debian.master/abi/4.13.0-16.19/amd64/generic.modules_orig
$ vi debian.master/abi/4.13.0-16.19/amd64/generic.modules
```

変更箇所

```diff
$ diff -u debian.master/abi/4.13.0-16.19/amd64/generic.modules_orig debian.master/abi/4.13.0-16.19/amd64/generic.modules
--- debian.master/abi/4.13.0-16.19/amd64/generic.modules_orig   2017-11-28 23:11:07.028000000 +0900
+++ debian.master/abi/4.13.0-16.19/amd64/generic.modules        2017-11-28 23:25:06.284000000 +0900
@@ -326,7 +326,9 @@
 apple_bl
 appledisplay
 applesmc
+applespi
 appletalk
+appletb
 appletouch
 applicom
 aquantia
```

バックアップ削除

```bash
$ rm debian.master/config/config.common.ubuntu_orig
$ rm debian.master/abi/4.13.0-16.19/amd64/generic.modules_orig
```

個別修正は以上で終わり。
パッケージをビルド

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

ビルドが完了したらカーネルソースを展開したディレクトリは不要なので削除します。（作業環境のディスクが50GBだとこれを消さないと後の作業中にディスクが溢れます）

```bash
$ cd ..
$ rm -rf linux-4.13
```

#作業環境へのカーネルパッケージインストール

作成したカーネルパッケージを作業環境にインストールします。

```bash
$ cd ~/work
$ sudo dpkg -i linux-headers-4.13.0-17_4.13.0-17.20_all.deb \
  linux-headers-4.13.0-17-generic_4.13.0-17.20_amd64.deb \
  linux-image-4.13.0-17-generic_4.13.0-17.20_amd64.deb \
  linux-image-extra-4.13.0-17-generic_4.13.0-17.20_amd64.deb \
  linux-libc-dev_4.13.0-17.20_amd64.deb
```

再起動

```bash
$ sudo reboot
```

再起動後ログインして、追加したモジュールがあることを確認

```bash
$ modinfo applespi appletb | grep filename
filename:       /lib/modules/4.13.0-17-generic/kernel/drivers/input/misc/applespi.ko
filename:       /lib/modules/4.13.0-17-generic/kernel/drivers/hid/appletb.ko
```

古いカーネルパッケージ削除

```bash
$ sudo apt-get purge linux-headers-4.13.0-16 \
  linux-headers-4.13.0-16-generic \
  linux-image-4.13.0-16-generic \
  linux-image-extra-4.13.0-16-generic
```

#カスタムインストールイメージ作成
ここからがインストーラーのカスタマイズ作業になります。
まず、Ubuntu Mate 17.10 desktop AMD64のDVDを作業環境の/mntにマウント

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

作業用ディレクトリ /opt/cd-image を作成して、インストーラーの中身をコピー

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

インストーラーのsquashfsを作業用ディレクトリに展開

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

別の作業用ディレクトリを作成し、インストーラーのinitrdの中身をその中へ展開

```bash
$ 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
```

作業環境のinitrdにaufsを追加

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

さらに別の作業用ディレクトリを作成して、作業環境のinitrdを展開

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

インストーラーのinitrdを展開したディレクトリから、古いカーネルのファイルを削除

```bash
$ sudo rm -rf /opt/work/installer-initrd/lib/modules/4.13.0-16-generic
$ sudo rm -rf /opt/work/installer-initrd/lib/firmware/4.13.0-16-generic
$ sudo rm /opt/work/installer-initrd/lib/modprobe.d/blacklist_linux_4.13.0-16-generic.conf
```

代わりに稼働中のシステムのinitrdの中身をコピー

```bash
$ sudo cp -a /opt/work/basesystem-initrd/lib/modules/4.13.0-17-generic /opt/work/installer-initrd/lib/modules/
$ sudo cp -a /opt/work/basesystem-initrd/lib/firmware/4.13.0-17-generic /opt/work/installer-initrd/lib/firmware/
$ sudo cp -p /opt/work/basesystem-initrd/lib/modprobe.d/blacklist_linux_4.13.0-17-generic.conf /opt/work/installer-initrd/lib/modprobe.d/
```

修正したinitrdのファイルをアーカイブし直す。

```bash
$ 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
```

稼働中のカーネルイメージをカスタムインストーラーのディレクトリにコピー

```bash
$ sudo cp /boot/vmlinuz-4.13.0-17-generic  /opt/cd-image/casper/vmlinuz.efi
```

インストーラーのsquashfsをカスタマイズ。
まず、squashfsを展開したディレクトリ内に/proc等をマウント

```bash
$ 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/
```

カーネルパッケージを作成したディレクトリもbind mount

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

squashfsを展開した中へchroot

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

以下、プロンプト"#"はchroot内での作業
パッチ適用したカーネルパッケージをインストール

```
# cd work/
# dpkg -i linux-headers-4.13.0-17_4.13.0-17.20_all.deb \
  linux-headers-4.13.0-17-generic_4.13.0-17.20_amd64.deb \
  linux-image-4.13.0-17-generic_4.13.0-17.20_amd64.deb \
  linux-image-extra-4.13.0-17-generic_4.13.0-17.20_amd64.deb \
  linux-libc-dev_4.13.0-17.20_amd64.deb
```

不要となった古いカーネルパッケージを削除

```
# apt-get purge linux-image-4.13.0-16-generic \
  linux-image-extra-4.13.0-16-generic \
  linux-signed-image-4.13.0-16-generic \
  linux-headers-4.13.0-16-generic
```

grubのデフォルト設定変更。intremap=nosidを追加
（[ここ](https://github.com/Dunedan/mbp-2016-linux#misc)の情報だと不要になっているようですが、私が試したところ必要っぽい）

```
# 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展開先にマウントしたディレクトリをアンマウント

```bash
$ 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
```

一時ディレクトリも削除

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

squashfsを再作成

```bash
$ 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
```

インストーラー起動時のカーネルオプション変更
isolinuxとgrubの設定ファイルにintremap=nosidを追加

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

ファイルリストとmd5チェックサム生成

```bash
$ 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イメージ作成

```bash
$ sudo mkdir /opt/newimage
$ cd /opt/cd-image/
$ sudo xorriso -as mkisofs \
 -o /opt/newimage/custom.iso \
 -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin \
 -c isolinux/boot.cat \
 -b isolinux/isolinux.bin \
 -no-emul-boot \
 -boot-load-size 4 \
 -boot-info-table \
 -eltorito-alt-boot \
 -e boot/grub/efi.img \
 -no-emul-boot \
 -isohybrid-gpt-basdat \
 /opt/cd-image/
```

カスタマイズしたインストーラーのISOイメージが /opt/newimage/custom.iso として生成されます。

# MacBookへのインストール
作成したイメージをddでUSBメモリ(デバイス名は適宜変更）に書き込み、MacBookで起動するとインストーラーが起動します。

```
# dd if=/opt/newimage/custom.iso of=/dev/sdz
```

毎回カーネルパッケージを作るのは面倒なので、インストール後はカーネルを4.14以上に上げて(野良ビルド)、追加ドライバはdkmsに移行したほうが良いでしょう。（NVMeの修正はインストール後は無くても平気なはず）

作るのが面倒な人向けに、上記の手順で作ったものを[ここ](https://drive.google.com/file/d/1Krs_rAM-XiM_JWRpgGrOB5sjgZRn-i4j/view?usp=sharing)に置いておきます。