macOSが起動するときに、早い段階で禁止マークが出て停止してしまうことがあります。多くの場合、カーネルを読込むメモリが確保できないエラーです。これをslideオプションで解決します。
追記:この記事はCloverを前提に書いてあります。OpenCoreに関してはこちらをご覧ください。またコメントいただいたように、CloverにOpenCoreのツールを流用して対応することも可能なようです。
macOSが起動する時、ブートローダがカーネルをメモリに読み込みます。この時、連続した十分な大きさのメモリ領域が確保できないと、起動に失敗して禁止マークが出ます。この状況への対処方法が、OpenCoreではCloverよりも充実しています。以前の記事では、メモリの使用状況(メモリマップ)を確認して、空き領域を起動オプションのslide値で指定することで、カーネル用メモリを確実に確保する方法を紹介しました。今回はそのOpenCore編です。以下のOpenCoreのガイドで、この問題への対処方法が詳しく説明されています。以下はこのサイ... カーネル読み込みメモリ空間を確保する (OpenCore編) - Boot macOS |
Table of Contents
症状
禁止マークが出て早々に起動停止してしまう場合に、-vオプションで起動すると、”Error Allocating xxxx pages at xxxxxxxx”というようなメッセージが出ていることがあります。例えば下のメッセージは、「0xe590000番地から0x11c01ページのメモリー空間を確保しようとしたけど失敗しました」という意味です。1ページは0x1000バイト、つまり4096バイトのことです。10進数を使ってわかりやすく書くと、「240メガバイトあたりの番地から298メガバイトくらいのメモリー空間を確保しようとしたけど失敗しました」といった感じのエラーです。100番台になってからのMSIマザーボードでよく発生するエラーです。
このエラーの特徴は、「何回か起動を試みるとそのうち成功することがある」とか、「大抵は起動するけどたまにこのエラーで起動しない」など、再現性が低いことです。
簡単な対策法
この問題を直してくれるのが以下のkextです。
- AptioMemoryFix.efi
- OsxAptioFix3Drv.efi
- OsxAptioFixDrv.efi
- OsxAptioFix2Drv-free2000.efi
以下の記事で説明したように、このリストの順番がお勧め順らしいので、上から試して、問題なく起動するkextを使います。一度に2個以上のkextを入れてしまうと、どちらが動くのか定まらないので避けるべきとのことです。
(古い情報です。最新情報はこちらをご覧ください。)---------------最近のClover*1でEFIドライバ類が色々と変更されている様です。気づいたところをメモしておきます。64bit移行のためと思われる名称変更EFIドライバフォルダ名だいぶまえにdrivers32というフォルダがなくなってdrivers64だけになっていたと思うのですが、最近ではdrivers64UEFIになったようです。新規にインストールするとこれだけになります。drivers64は空になる事もあります。drivers64は手作業で消してしまっても良いようです。また、Cloverのインストーラは、現... Cloverで使うEFIドライバ - Boot macOS |
リストの上の方ほど新しく開発されたkextで副作用が少ないらしいです。一番強力なのが最後のOsxAptioFix2Drv-free2000.efiです。その名前の通りメモリーを強制的に開放させます。どのマザーボードでもほぼ機能する最強のkextです。でも強力すぎるので、大丈夫なのかという意見はあります。Redditには、OsxAptioFix2Drv-free2000.efiの利用は避けで、slideオプションで対応した方が良いという意見もありました。とはいえ、私もMSIのマザーボードでOsxAptioFix2Drv-free2000.efiを使い続けていますが、特に問題はありません。でもせっかくですので、slideを使った正しい解決策を試してみます。
カーネル読込に失敗する理由
KASLR (カーネル番地乱数化)
そもそもどういうことが原因で、カーネル読み込みに失敗するのかを調べました。macOSが起動する際に、カーネルが0x100000以降の番地(10進数で1,048,576、1MB程度です)のメモリーに読み込まれます。実際に何番地に読み込まれるかは定まってなく、ランダムです。上記のエラーの例では、カーネル(またはその一部)を0xe590000番地から始まるメモリー空間に読み込もうとして失敗しています。読み込み番地はセキュリティのためにわざとランダムにしているそうです。この仕組みを、KASLR (Kernel Address Space Location Randomization: カーネル番地空間場所乱数化) というらしいです。カーネルが読み込まれる番地が常に一定だと、その番地を狙って悪さを試みるマルウェアが可能です。特定の番地からプログラムを動かしたり、特定の番地の内容を書き換えることでOS動作を操れてしまうからです。そういう操作をされないために、起動毎に違う番地にカーネルを読み込んでいるそうです。
失敗しないマザーボード
以下で説明した方法で、UEFIシェルを起動すると、
UEFIマザーボードには各種設定やファイル表示・変更ができるコマンドラインシェル機能があります。これを使用すればOSが起動する前にファイル変更・移動・削除できるので、誤設定で起動しなくなったマシンを救えます。UEFI Shellへの入り方UEFI Shellは、UEFI環境で動作するオープンソフトウェアです。Cloverをインストールすると、EFI/CLOVER/toolsの下にShell64.efiというような名前でインストールされます。Cloverの起動ドライブ設定画面から「Start UEFI Shell 64」を選択すると起動します。最近のマザーボードには、マザーボード... 起動しないマシンをUEFI Shellで救う - Boot macOS |
マザーボードのメモリーマップを見ることができます。これに使うコマンドがmemmapです。memmap -bとすると画面いっぱいになったところで一旦停止してくれます。また、fs0:などでドライブを指定して、必要ならばcdコマンドで適当なディレクトリに移動した後、memmap > memmap.txt などとタイプすると、その場所にメモリーマップをテキストファイルで保存します。
AptioMemoryFix.efiなどの穏当なドライバで問題なく起動するマザーボードもたくさんあります。そのメモリーマップを見てみましょう。以下は、ASUSのZ390マザーボードの例です。(memmapの出力にはAttributesという項目もありますが、長くなるので省略してます。)
Type Start End # Pages RT_Data 0000000000000000-0000000000000FFF 0000000000000001 Available 0000000000001000-000000000008FFFF 000000000000008F RT_Code 0000000000090000-0000000000090FFF 0000000000000001 Available 0000000000091000-000000000009EFFF 000000000000000E Reserved 000000000009F000-000000000009FFFF 0000000000000001 Available 0000000000100000-000000005315CFFF 000000000005305D BS_Data 000000005315D000-0000000055101FFF 0000000000001FA5
ここを見ると、カーネルが読み込まれる可能性のある0x100000番地を先頭に、0x5315CFFF番地までが空き地になっていることがわかります。ページ数にして0x5305D番地の空き地です。1.4GBくらい空いているようです。最初に示したエラーメッセージによると、カーネル読み込みに必要なメモリー量は300MB程度らしいのです。なので、これだけ空いていれば、どの番地に読み込んでも順当にメモリー確保できます。おかげで、このマザーボードではメモリーアロケーションのエラーが出たことはありません。
ちなみにこの空き領域の容量は、起動するたびに多少変化することがあるようです。それでも変動は高々100MB程度です。また、iGPU割り当てメモリー量を変更してもほとんど変化しません。ASUSのメモリーマップはmacOSに適しているようです。
失敗するマザーボード
一方、最近のMSIマザーボードでAptioMemoryFix.efiなどを使うと、かなりの確率で起動しません。MSIはZ170チップセットモデルからOsxAptioFix2Drv-free2000.efiが手放せなくなってしまいました。どんなメモリーマップになっているのか見てみます。以下はB350Mマザーボードのメモリーマップです。最近のMSIマザーボードでは、どれもこんな感じのメモリーマップです。
Type Start End # Pages BS_Code 0000000000000000-0000000000007FFF 0000000000000008 Available 0000000000008000-000000000005DFFF 0000000000000056 BS_Data 000000000005E000-000000000005EFFF 0000000000000001 Reserved 000000000005F000-000000000005FFFF 0000000000000001 BS_Code 0000000000060000-000000000009FFFF 0000000000000040 Available 0000000000100000-000000000FFFFFFF 000000000000FF00 BS_Code 0000000010000000-000000001000AFFF 000000000000000B Available 000000001000B000-000000007751AFFF 0000000000067510 BS_Data 000000007751B000-000000007755AFFF 0000000000000040
ASUSとMSIのメモリーマップを比較してみましょう。0x100000番地から先のページ数で示しています。ASUSは0x100000番地から0x53000ページ以上が空き地です。しかしMSIは0x100000番地から0xFF00ページが空き地で、その後0xBページ(10進数で11ページ)が使われていて(小さすぎて図では見えません)、その後、0x67510ページが空いています。
つまり、MSIマザーボードでは、0x100000番地からは、0xFF00ページ、つまり267MB程度のメモリーしか空いていません。その後に、0x10000000番地から、たったの11ページ、バイト数にして45MBだけ、誰かが使っています。BS_Codeというのがそれです。そしてその後、0x1000B000番地から0x7751AFFF番地まで大量に空き地があります。そのため、0x100000番地から300MB程度を確保しようとすると、使用中の45MBが邪魔をして確保できません。これでエラーが出ます。なんでこんな邪魔なところに使用中のメモリーがあるんだ、という気持ちになります。
ただ、何度も何度も起動を試みると、たまに起動することもあります。エラーになるかどうかは、上で説明したKASLRが使用する乱数次第です。0x1000B000より大きい番地からメモリー確保しようとした場合は、エラーは出ず、問題なく起動します。
slideオプションで解決する
slideの仕組み
config.plistなどに書くmacOSの起動オプションの中で、slide=x などと書くと、メモリーを確保する開始番地を指定できます。その規則は次のようになっているそうです。つまり起動オプションにslide=xと書くと、
- 最近のCPUでは、0x100000 + x * 0x200000が読み込み開始番地になります
- ただし、SandyBridgeまたはIvyBridgeの場合は、xが0x80から0xFFまでは、上記の計算ですが、xが0x80から0xFFまでの場合、0x 10300000 + x * 200000になります
となることが知られてます。最近のCPUだけを考えれば、slide値を0x200000倍して0x100000を足すだけなので簡単です。以下ではこの計算が適用できるCPUを前提に説明を進めます。
空き地番地をSlideで指定する
上記のMSIマザーボードの場合、0x1000B000から、使われていないメモリーが大量にあるので、この番地以降を割り当てたいです。ということは、
0x100000 + x * 0x200000 = 0x1000B000 x * 0x200000 = 0x1000B000 - 0x100000 = 0xFF0B000 x = 0xFF0B000 / 0x200000 = 0x7F = 127
という計算になり、slide=127 (もしくはそれ以上の値)とすれば良さそうです。でもこの計算は切り捨てになっているので、実は127ではスライド量が足りません。127 (0x7F) の場合、
0x100000 + 127 * 0x200000 = 0xFF00000
からのメモリー確保がされるので、0x1000b000に引っかかってしまいます。1増やして128 (0x80) にすれば、
0x100000 + 128 * 0x200000 = 0x10100000
になるので、ようやく0x1000B000より大きな番地からの割り当てになります。つまりconfig.plistのBoot, Argumentに、以下のようにslide=128パラメタを追加すれば、このようなメモリー空き地が不連続な問題を回避できます。
<key>Boot</key> <dict> <key>Arguments</key> <string>slide=128 (他に必要なパラメータが続く)</string>
KASLRを無効にする
ブートオプションでslide値を指定しても、KASLRが効いているとslide値としては相変わらず乱数(0x00から0xFFまでの乱数)が割り当てられます。ブートオプションのslide値は無視されるようです。slide値を指定すると同時にKASLRを無効にする必要があります。
KASLRを無効にするためには、SIPでCSR_ALLOW_UNRESTRICTED_NVRAMフラグを立てる必要があります。これはNVRAMの変更制限を解除するフラグですが、同時にKASLRも解除されるようです。このフラグは、Appleが公開しているソースコードによると、
#define CSR_ALLOW_UNRESTRICTED_NVRAM (1 << 6)
だそうです。1を6ビットだけ高位にシフトするという意味で、結果は0x40です。ということで、config.plistのRtVariables, CsrActiveConfigに0x40を書いておきます。
<key>RtVariables</key> <dict> <key>CsrActiveConfig</key> <string>0x40</string> </dict>
7ビット目が1ならば他の数字でも良いです。CsrActiveConfigとして、元々0x03という設定をされていたのであれば、0x43にすれば良いです。0x64という設定をしていたなら、これはすでに7ビット目が立っていますから、そのままで良いです。セキュリティをできるだけ確保するという観点からは、0x40が良いと思います。
これでブートオプションで指定したslide値が使用されるようになります。このようなconfig.plist設定で、MSIのマザーボードでもOsxAptioFix2Drv-free2000.efiを使わずに済むようになりました。AptioMemoryFix.efiで動きます。
ただ、CSR_ALLOW_UNRESTRICTED_NVRAMを許可すると、NVRAMの書き換えでSIPのすべての設定が変更可能になるので、セキュリティは脆弱になります。とはいえ、Hackintoshなのでconfig.plist書き換えて再起動すればSIPはどのようにも設定できるので、気にすることではないかもしれないです。さらにはslideパラメータでカーネル読み込みアドレスが固定されるのでKASLRの恩恵もなくなり、それも弱点になります。これに対しては、時々config.plistのslide値を微妙に書き換えて、人力KASLRしても良いかもしれません。
まとめ:マザボ別対処法
KASLRが機能していると、Slide値は0から0xFFまで乱数で設定されます。つまりカーネルが展開される先頭番地は、
0x100000 + 0 * 200000 = 0x100000 = 0番地から1MB程度先の番地
から
0x100000 + 0xFF * 200000 = 0x1FF00000 = 0番地から530MB程度先
になります。ここから、300MB程度の領域にカーネルが展開されるわけです。KASLRがどのようなslide値を指定しても、300MBのカーネルが読み込まれるためには、0x100000番地から900MB程度の空き地が必要ということになります。そこで0x100000番地からの空き地がどれくらいあるかによって次の対処法が考えられます。
なお、空き地容量の数値は、確保されるメモリーが300MB程度と仮定した場合の目安です。環境によってはもっと必要とされるかもしれません。
空き地が900MB以上あるマザボ
何もしなくて良いです。ASUSのマザーボードでは0x100000番地から1.4GBくらいの空き地がありました。なので、KASLRがどのように先頭番地を割り当てても平気です。SIPを弱めてKASLRを無効にする必要もありません。macOSに最適なメモリーマップを持つマザーボードと言えます。
空き地が300MB以下のマザボ
slideで空き地番地を指定します。MSIのマザーボードは、0x100000番地からの空き地が267MB程度しかありませんでした。次にある空き地が十分大きくてKASLRの範囲内にあるので、たまに起動しますが、大抵は起動に失敗します。このようなマザボでは、上記で説明したように、KASLRの範囲内にある300MB以上の空き地をslideで指定します。
空き地が300MB以上900MB未満のマザボ
slide=0にします。コメントで教えていただいたGIGABYTE Z390M GAMINGでは、0x100000番地からの空き地は480MBとのことです。この場合は、小さなslide値だと起動しますが(80以下程度)、KASLRで大きなslide値が指定されると失敗します。そこで、slide=0を指定して0x100000から必ず展開するように指定します。