Ubuntu Weekly Recipe

第743回Ubuntuの標準ブートローダーであるGRUBを改めて見直す

Ubuntuは、標準のブートローダーとしてGRUBを採用しています。ほとんどの利用者は、インストール時に自動設定されるGRUBをそのまま利用し続けていることでしょう。今回は初心者向けに、このGRUBがどう動いているのか、そしてどのように設定可能なのかを紹介します。

図1 Ubuntuインストール時の無骨なGRUB
図1

セキュアブート時代のGRUB

GNU GRUB(GRand Unified Bootloader)は、言わずとしれたLinuxをはじめとする各種OSを起動するための「ブートローダー」です。一般的なコンピューターは電源が入ると、CPUを含む最低限のデバイスのリセットが解除され、ROM上にある小さなプログラムが実行されることによりRAM上に大きなプログラムがロードされ、さらにいくつかのデバイスのリセットが解除されて最終的に必要なプログラムが実行されるという、⁠ブートストラップ」な方法によって起動が行われます。

普段Ubuntuをインストールして使っているPCも例外ではなく、CPUがDRAMが使えるようになったあとはまずフラッシュROMに記録されたBIOSが起動処理を担ったあとに、ストレージ上に記録された起動プログラムを立ち上げます。PCの文脈では、この最初に起動するプログラムが「ブートローダー」⁠ブートストラップローダー)です。Linuxの場合はGRUBが最も使われており、他にもLive ISOでよく使われるSYSLINUX、組み込みデバイスでは定番のU-Bootなどがあります[1]。Windowsならあまり意識することはありませんが、Windows Boot Managerがブートローダーです。ちなみに、最近はUEFIが高機能になったこともあって、UEFI BIOSから直接Linuxを起動することも可能になっています。

GRUBには現行バージョンとは別に、古いコードベースであるGRUB Legacyが存在します。こちらは開発元でもUbuntuでもメンテナンスされていないソフトウェアです。Ubuntuの利用者なら気にする必要はないでしょう[2]。また、さらに対応システムによって、現行バージョンも次の2種類にわけられます。

  • grub-efi:UEFI BIOSが搭載されたシステム向けのGRUB
  • grub-pc:UEFIが搭載されていないLegacy BIOS向けのGRUB

UEFI版のGRUBは、ストレージ上のESP(EFI System Partition)にEFIアプリケーションとしてGRUBをインストールします。それに対してLegacy BIOS版のGRUBは、ストレージのMBR(Master Boot Record)にバイナリデータとして書き込む必要があります。GRUBのインストール方法だけでなく、パーティション構成も関わってくるため、UEFI版とLegacy BIOS版のどちらのGRUBを使うかの決定はUbuntuのインストール時です。また、通常はインストール時の状態をそのまま使うことになります。

おそらくここ数年の、一般的なPCであればそのほとんどがUEFIになっているでしょう。BIOS側の設定で、Legacy BIOSにできないものも多いかもしれません。よって、今回はすべてUEFI版のみを前提に説明します。

UEFI版のGRUBで、これまでと大きく異なるのが「ESP」「セキュアブート」への対応です。ESP(EFI System Partition)は、UEFIが起動できるUEFIアプリケーションを配置するパーティションです。ESPとして適切に設定パーティションが設定されている場合、UEFIはそこにあるUEFIアプリケーションを直接起動できます。さらにUEFI BIOSの設定画面の「Boot」セクションなどから選択できるようになります。UEFI BIOSにおいて起動デバイスを未設定にしていても、EFI/BOOT/BOOTX64.EFI⁠amd64の場合)があれば、それを自動起動します。

セキュアブートは第444回のUbuntuにおけるセキュアブートの仕組みでも紹介したように、起動するブートローダーやOSの正当性を検証し、安全にシステムを起動する(改ざんされたシステムを起動しない)仕組みです。セキュアブートに対応するためには、カーネルやブートローダーがUEFI BIOSに記録された証明書で検証可能でなくてはなりません。一般的なPCは、MicrosoftないしPCベンダーの証明書のみが記録されています。GRUBがUEFIから起動される場合、GRUBの更新のたびにMicrosoftやPCベンダーに再署名してもらう必要があります。これは現実的ではありません。

そこでUbuntuを含むLinuxディストリビューションの多くはshimと呼ばれる小さないUEFIプログラムを、⁠ブートローダー」として採用しています。つまりほぼ更新のないshimのみをMicrosoftの署名サービスを使って署名し、shimがその中に持っている証明書を使ってGRUB以降を検証・起動する流れです。これによりGRUBやカーネルを更新する場合も、shimやGRUBに含まれる証明書と紐づくディストリビューター独自の鍵を使って署名できるようなります。

つまりUbuntuでGRUBを細かくカスタマイズするには、ESPやセキュアブートもある程度把握しておく必要があるのです。今回はESP・セキュアブートをあまり意識しなくて良い、基礎的な使い方のみを紹介します。実際に必要になるのはおおよそ次のようなケースでしょう。

基本的にGRUBの設定変更はトラブルの元であり、インストール時の状態を維持することを強くおすすめします。どうしても設定変更が必要になったときに備えて、基本的な考え方を抑えておこうというのが今回の記事の趣旨となります[3]

設定ファイルの基本構造

GRUBのカスタマイズは「設定ファイルを編集し」⁠それをESP上のデータに反映する」の2ステップが基本となります。そこでまずは、設定ファイルの基本構造を見ていきましょう。いくつかファイルがあるもののポイントになるのは主に次のファイルとなります。

  • /boot/grub/grub.cfg
  • /etc/default/grub
  • /etc/grub.d/

「GRUBの設定」という意味だと、/boot/grub/grub.cfgが唯一の設定ファイルです。このファイルから他のファイルを読み込むことも可能ではあるものの、Ubuntuの設定だと起動時にはこのファイルのみが使われると思っておけば良いでしょう。ただし、このファイルを直接編集することはあまりありません。他のファイルを編集し、update-grubコマンドによってその内容が/boot/grub/grub.cfgに反映される仕組みになっています。

その代わりに編集するのが/etc/default/grubです。これは単なる変数の塊となっており、そんなに複雑でもありませんので、全体を順番に確認していきましょう。

# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

冒頭に書かれているように、このファイルを編集するだけではGRUBの設定には反映されません。編集した後はsudo update-grubを実行する必要があります。

起動エントリーの指定

/etc/default/grubは他の/etc/default以下のファイルと同じように、ただの環境変数変数名=値の羅列となっています。update-grubはこのファイルを読み込んで、設定されている内容を変数として参照することになります。最初に登場するのがGRUB_DEFAULTです。

GRUB_DEFAULT=0

GRUB_DEFAULTは、自動で選択するGRUBのメニューエントリーの番号です。Ubuntuの場合は、通常次のようなメニューエントリーの表示がされると思います。操作しない時にどのメニューエントリーを選ぶかをここで設定します。

図2 メニューエントリーの一覧。エントリー番号としては一番上が0となる
図2
図3 サブメニューエントリーの中身
図3

指定できるのは次の4種類です。

  • エントリータイトル
  • エントリーID
  • エントリー番号
  • 「saved」キーワード

GRUBのメニューエントリーは、/boot/grub/grub.cfgにも記述されていますので、まずはそちらを確認すると良いでしょう。

$ sudo grep -E "menuentry |submenu " /boot/grub/grub.cfg
menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-c2133eaa-03ff-4f18-b978-c12f55e5af23' {
submenu 'Advanced options for Ubuntu' $menuentry_id_option 'gnulinux-advanced-c2133eaa-03ff-4f18-b978-c12f55e5af23' {
        menuentry 'Ubuntu, with Linux 5.15.0-56-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.0-56-generic-advanced-c2133eaa-03ff-4f18-b978-c12f55e5af23' {
        menuentry 'Ubuntu, with Linux 5.15.0-56-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.0-56-generic-recovery-c2133eaa-03ff-4f18-b978-c12f55e5af23' {
        menuentry 'Ubuntu, with Linux 5.15.0-53-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.0-53-generic-advanced-c2133eaa-03ff-4f18-b978-c12f55e5af23' {
        menuentry 'Ubuntu, with Linux 5.15.0-53-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.0-53-generic-recovery-c2133eaa-03ff-4f18-b978-c12f55e5af23' {
menuentry 'UEFI Firmware Settings' $menuentry_id_option 'uefi-firmware' {

エントリー番号は、上から順番に0、1、2と番号が振られます。図1や上記の例だとUbuntuが0で、Advanced options for Ubuntuが1となります。また後者はサブメニューがあり、サブメニューを開いたのが図2です。サブメニューには、Ubuntu, with Linux 5.15.0-56-genericやリカバリーモードなどが存在します。

UbuntuAdvanced options for Ubuntuはエントリータイトルであり、$menuentry_id_optionの後ろがメニューIDです。いずれもGRUB_DEFAULTに指定可能な値となります。

submenuの下にあるmenuentry「サブメニュー」扱いになります。サブメニューを指定したい場合はメインメニュー>サブメニューのように設定設定する必要があるのです。つまり、ID=1のサブメニューのリカバリーモード(サブID=1)を選択したい場合は次のように記述します。

GRUB_DEFAULT="1>1"

もし一時的に起動エントリーを変更したいだけなら、grub-rebootコマンドが使えます。たとえば次のように指定すると、Ubuntuしかインストールされていない環境なら次回起動時に「UEFI BIOSの設定画面(UEFI Firmware Settings⁠⁠」に遷移してくれるはずです。

$ sudo grub-reboot '2'

上記コマンドを実施後に再起動すると、GRUB_DEFAULTを書き換えた時と同様の状態になります。ただし反映されるのは1度きりです。次の起動時にはGRUB_DEFAULTの値が使われます。次の起動のときだけ、一時的にUEFI設定画面を起動したり、リカバリーモードにしたい場合などに便利でしょう。

また、⁠最後に手動で選択したエントリーを常に利用する」という設定も可能です。この場合は次のようにGRUB_SAVEDEFAULTも追加します。

GRUB_DEFAULT="saved"
GRUB_SAVEDEFAULT="true"

これにより起動メニューで選択したエントリーが、次の起動でも使われるようになります。

起動メニューの表示

Ubuntuは特定の条件を満たすか、特に設定していないと前述の起動メニューは表示されません。手動で表示するためには、起動直後のGRUB処理中にESCキーを押す必要があります。タイミングはかなりシビアなので、もし必要なら常に表示するようにしておくと良いでしょう。その設定が次の2行です。

GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0

GRUB_TIMEOUT_STYLEはメニューの表示方式をコントロールします。menuを設定するとインタラクティブ操作できるメニューを表示し、countdownは起動までの秒数をカウントダウン形式で表示、hiddenは何も表示しません。

GRUB_TIMEOUTはメニューの表示方式によって見え方が異なるものの、基本的に設定したエントリーを起動するまでの秒数です。このmenuならこの時間以内に何かキー入力をすれば、カウントダウンが停止します。countdownhiddenだと、まずはESCキーなどでメニューを表示する必要があります。⁠0」を設定するとすぐにエントリーが起動し、⁠-1」を設定するとずっと待ち続けます。

Ubuntuのインストール時の設定ではhidden「0」なので、メニューは表示されず、選択済みのエントリーがすぐに起動する設定です。もし常にメニューを表示したければ次のようにmenuにし、適切なタイムアウト時間を設定すると良いでしょう。

GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=10

ディストリビューターの設定

次の行は、Linuxディストリビューションの設定です。lsb_release -i -sの実行結果なので、単純に「Ubuntu」が設定されます。この文字列はメニューエントリーの文字列として使われます。

GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`

カーネルのコマンドラインの設定

カーネルは起動時に指定できるコマンドラインパラメーターによって、様々挙動を変更できます。また、systemdを含むいくつかのシステムアプリケーションも、このパラメーターを参照して挙動を変えています。よって何かデバッグをしたい場合は、このコマンドラインパラメーターが強力なツールになってくれるのです。

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""

名前が似ていてわかりにくいのですが、Ubuntuの場合はおおよそ次のように使い分けます。

  • GRUB_CMDLINE_LINUX_DEFAULT:通常のカーネルのみに設定したいもの
  • GRUB_CMDLINE_LINUX:通常のカーネルとリカバリーモードの両方で設定したいもの

上記で設定されているquiet splashは起動時のログの表示を抑制し、代わりにロゴを表示するための設定です。通常の起動時はこれを表示しますが、リカバリーモードのときは起動メッセージを表示したほうがデバッグに役立ちます。そこでGRUB_CMDLINE_LINUX_DEFAULTのみに設定しているのです。

ちなみに何か設定を追加したい場合はダブルクオートの中に追記してもいいのですが、次のように別の行に追加すると、差分がわかりやすくなって便利です。

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT foo"
GRUB_CMDLINE_LINUX=""

カーネルモジュールのロードや無効化、パラメーターの設定にも使えます。よって、NVIDIAに代表される特別扱いしないとへそを曲げるタイプのモジュールの設定や、IOMMUみたいな便利だけどどの環境でも問答無用で有効化するのは憚られる設定などのように、プロダクション用途でもお世話になることがあることでしょう。

もし一時的に設定を変更したいのであれば、起動時のメニューエントリーからカーソルキーで変更したいエントリーを選択した上で、eキーを押して編集モードに入ります。⁠linux」で始まる行がLinuxカーネルとコマンドラインパラメーターの設定行なので、そこを直接編集してしまいましょう。編集が完了したらCtrl-Xで起動です。

図4 メニューエントリーの編集画面
図4

その他の設定

/etc/default/grubには他にも設定変数が掲載されていますが、いずれもコメントアウトされています。コメントにいくつか説明が書かれているものの、ほとんど使うことはありません。

GRUB_BADRAMを使うと、メモリの壊れていると思われる部分の使用を除外できます。GRUB_BADRAMの書式はアドレス,マスクです。メモリテストでエラーが見つかった領域を、一時的に除外して使いたい場合に設定します。

# Uncomment to enable BadRAM filtering, modify to suit your needs
# This works with Linux (no patch required) and with any kernel that obtains
# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...)
#GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef"

GRUB_TERMINALGRUB_TERMINAL_INPUTGRUB_TERMINAL_OUTPUTを一括して設定する変数で、一般的にシリアルコンソールを使用したい場合に使います。具体的な設定方法は第638回のUbuntuに『普通に』ログインするいろいろな方法を参照してください。

# Uncomment to disable graphical terminal (grub-pc only)
#GRUB_TERMINAL=console

# The resolution used on graphical terminal
# note that you can use only modes which your graphic card supports via VBE
# you can see them in real GRUB with the command `vbeinfo'
#GRUB_GFXMODE=640x480

GRUB_GFXMODEも同様に出力先の画面解像度の設定です。

update-grubでメニューエントリーを作成する際、ルートファイルシステムはデバイスパスではなくUUIDで指定する形のエントリーroot=UUID=xxxが作成されます。上記のコメントアウトを解除すると、UUID形式ではなく、ストレージのパーティション方式でエントリーを作成します。UUID形式だとうまくいかない場合にのみ使う設定です。

# Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux
#GRUB_DISABLE_LINUX_UUID=true

GRUB_DISABLE_RECOVERYはリカバリーモードのメニューエントリーを無効化する設定です。

# Uncomment to disable generation of recovery mode menu entries
#GRUB_DISABLE_RECOVERY="true"

GRUB_INIT_TUNEは起動時にビープ音を鳴らす設定です。最初の数字がBPMで、残りはHzとその音の継続時間のペアを繰り返します。上記の例だと480BPMで1拍なので1/8秒間、440Hzの音(NHKの時報のピッの音)を鳴らします。

# Uncomment to get a beep at grub start
#GRUB_INIT_TUNE="480 440 1"

他にも変数による設定が存在します。詳細はGRUBのSimple configuration handlingを参照してください。

GRUBの環境変数ブロック

GRUBには「環境変数ブロック」という概念もあります。これはUbuntuの場合は/boot/grub/grubenvに保存されたバイナリデータであり、/boot/grub/grub.cfgからload_envコマンドを実行することでロードされます。

これは主にGRUBの実行時に生成された情報を、どこかに保存したい場合に使用します。具体的にはGRUB_SAVEDEFAULTを設定したときのような「今回どのメニューエントリーを指定したか」といった情報や、⁠任意のコマンドが成功したか失敗したか」などの情報です。また、カーネルの起動に成功したかどうかの情報にも使われます[4]

環境変数の設定はGRUBからならsave_envコマンドで実現しますが、Linuxからでもgrub-editenvで変更可能です。

環境変数の一覧表示
$ grub-editenv - list
(何も表示されない)

環境変数fooの設定
$ sudo grub-editenv - set foo=bar
$ grub-editenv - list
foo=bar

環境変数fooの削除
$ sudo grub-editenv - unset foo
$ grub-editenv - list
(何も表示されない)

grub-editenvの第一引数には環境変数ブロック/boot/grub/grubenvを指定します。-を指定するとデフォルトの値が使われます。

/boot/grub/grub.cfgの中で$fooのように参照されているのが実際の変数です。同ファイルの中ではset foo=barのように指定していますが、grub-editenvを使えば、load_env以降はset foo=barされたのと同じ扱いにできます。よってGRUB_CMDLINE_LINUXを次のように変更することで、カーネルの起動パラメーターをgrub-editenvコマンドだけでコントロールできるようになります。

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT \$foo"

fooが未設定だったら何も指定しませんし、一時的にオプションを追加したければsudo grub-editenv - set foo=barとすればいいだけです。頻繁に起動パラメーターを変更したい場合に便利でしょう。

おすすめ記事

記事・ニュース一覧