"えっお前んちのマザーボードlinux入ってんの?" UEFI Built in Linuxのススメ

タイトル落ちです.
マザーボードのFlash ROMに直接linuxを埋め込んでブートします.
そのへんのWiFiルーターとかと大体同じようなブート方法だと思います.

## なにやんの?
UEFI FirmwareのBuilt in EFI ShellをEFI stub kernelに差し替えることでBuilt in Linuxを実現します.
EFI Shellが内蔵されていない場合は, この方法は使えません.

## EFI Shellって何?
https://ja.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface
WikipediaによるとUEFI上でのシェル環境らしいです.
ここで重要なのはEFI ShellはEFIアプリという事です.
そしてEFI_STUBを有効にしたlinux kernelもEFIアプリと言うことです.
ならFirmwareに組み込まれているEFIアプリを他のEFIアプリに置換できるのでは?というアイデアです.

## どうやって差し替えるの?
UEFIなfirmwareを編集できるツールがあるので, 今回はこれを使います.

UEFI Tool 0.21.5 https://github.com/LongSoft/UEFITool

マザーボードは
MSI X99A Gaming Pro Carbón
を使いました.

1. File->Open image fileを選択します.
![20170801-185309.png](https://qiita-image-store.s3.amazonaws.com/0/136515/5ac7ab62-a220-05c1-6923-b31c27ac5d03.png)

1. メーカーから配布されているuefi firmwareを選択します.
ものによってはFiles of typeをAll filesにしましょう.
msiのこのマザーボードは必要でした.
![20170801-185457.png](https://qiita-image-store.s3.amazonaws.com/0/136515/ca897900-25e2-eead-9fdd-edfb3d55f40a.png)

1. TextがShellになっているものを見つけます.
![20170801-185638.png](https://qiita-image-store.s3.amazonaws.com/0/136515/85cae3df-b458-8a6b-ab86-527122b33ec9.png)

1. 見つけたら+でツリーを下って行きPE32 image sectionを右クリック->Replace bodyを選択.
この時Replace as isを選択するとヘッダを破壊してしまうので注意.
![20170801-190402.png](https://qiita-image-store.s3.amazonaws.com/0/136515/04cc44bc-6ca8-e4f0-5347-25c782cfacd7.png)

1. お手元にあるINITRAMFS_SOURCE & EFI_STUBなkernelを選択します.
この辺需要があれば書きます.
![20170801-190500.png](https://qiita-image-store.s3.amazonaws.com/0/136515/7f77b3d6-4264-e821-c19f-3835a1b2ed78.png)

1. ActionがReplaceになっているPE32の項目がkernelです.
![20170801-205308.png](https://qiita-image-store.s3.amazonaws.com/0/136515/327df98b-85ce-102b-ede4-834fd5c0014c.png)

1. 最後にFile->Save image fileから保存します.
このときサイズがデカ過ぎると言われたら, Volume free spaceの項目が下の方にあるので, それを参考にしてkernelをダイエットします.
![20170801-205333.png](https://qiita-image-store.s3.amazonaws.com/0/136515/81679428-6c61-0d04-dd45-316741938de6.png)

1. できたfirmwareをマザーボードに焼きます.
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">「えっお前まだストレージからlinuxブートしてんの? だっさw」<br>※flashromにカーネルを埋め込むことでストレージレスでlinuxを起動することに成功した <a href="https://t.co/I6hMJclRoe">pic.twitter.com/I6hMJclRoe</a></p>&mdash; otakuto (@otakuto5648) <a href="https://twitter.com/otakuto5648/status/871022664344784896">2017年6月3日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

副産物として埋め込んだEFIアプリはセキュアブートのチェックが行われないことがわかりました.
もしかするとこのモデル特有なのかもしれません.
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">UEFI firmwareにstub kernelのせたやつ<br>なんか署名なくてもセキュアブートを突破できるぞ... <a href="https://t.co/aUFLyGtyzz">pic.twitter.com/aUFLyGtyzz</a></p>&mdash; otakuto (@otakuto5648) <a href="https://twitter.com/otakuto5648/status/872383802030649344">2017年6月7日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
firmwareのコードを読んだわけではないのでわかりませんが, 一般的にInternal EFI Shellは外部から改変される事がないのでチェックをかけていないのでしょうか?
あるいはUEFI仕様でOKなのかもしれません(これも読んでないのでわからん).
どのみちfirmwareを書き換えられる時点で, なんでもありのなので問題は無いと思います.

## デメリット

* カーネルの更新時にfirmwareの編集とFlash ROMに書き込みが必要になる.
* Flash ROMの空き容量にkernel+initramfsを収める必要がある.  
今回使ったFlash ROMの容量は16MBでしたがfirmwareを書き込むと5MBぐらいしか空きがありませんでした.
* 自動でブートできない.  
起動優先度の設定にEFI Shellを入れることはできませんでした.  
つまり起動する時は手動で選択してブートすることになります.  
他のマザーボードならいけるかもしれません.
* firmwareの改変を検知する仕組みがあると書き込めない.
* brickingの可能性がある.  
壊れたfirmwareを書き込んだとき, マザーボードがまな板になります.  
Flash ROMの切り替え機能がある場合は問題無さそうです.  
一応復旧する方法はあるらしいですが面倒くさそうです.

## メリット

* ストレージが必要ない  
まあ結局Flash ROMもストレージなんですけどね.
* セキュアブートの突破ができる.  
そもそもファームウェアを焼き直せる時にセキュアブートは意味をなさそうですが, Windowsしかブートできないようなマザーボードで役に立つかもしれません.

## まとめ
EFI stub kernelの他に自作のEFIアプリの動作も確認しました.
これによりFlash ROMの容量が少ないマザーボードでも, 小さいサイズのEFIアプリなら導入できると思います.

またOVMFでも同じことができるかもしれません.
しかしながらOVMFの場合はビルド時に内蔵するEFI Shellの指定ができるかもしれないので, この方法は周りくどいかもしれません.

## 参考文献
* UEFI Tool 0.21.5 https://github.com/LongSoft/UEFITool
* https://ja.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface