GRUB2で起動するカーネルを切り替える

Linuxカーネルをインストールするとデフォルトの起動カーネルが変化してしまう。 元のカーネルを使ったり次の一度だけ新しいカーネルを利用する設定にして試すなどしたい時がある。その時のメモ。

ブートローダ

OSを読み込むブートローダは色々ある。LinuxディストリビューションではLILO, GRUB, GRUB2が定番になっている。Ubuntuでは9.10からGRUB2が採用されている。 ここでは GRUB2の設定ファイルの位置とデフォルトのブートの設定方法、一回だけ変更する方法を説明します。

環境の準備

シリアルポートを表示しておきたかったのでVagrantファイルでUnixドメインソケットとして公開しておきました。 uart1 としてCOM1を選び、出力先を uartmode1 で指定してます。 COM1 はI/O BaseとIRQで直接指定してます。公式リファレンスにuart<N> フラグの使い方VirtulBoxの仮想マシンがどの値を COM1 に割り当てているか書かれていました。

単純にファイルにした方が簡単だったと思っています。ファイルにする場合は –uartmode1 のタイプを server から file にします。

ファイルサイズやメモリサイズを大きくしてるのはカーネルビルドで必要だったため。 ディスクサイズにはプラグインが必要なので vagrant plugin install vagrant-disksize をつかって事前にインストールしておいてください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Vagrant.configure("2") do |config|
  config.vm.provision "shell", inline: "echo Hello"

  config.vm.define "kernel-build" do |host|
    host.vm.box = "ubuntu/bionic64"
    host.vm.hostname = "kernel-build"
    host.disksize.size = '140GB'

    host.vm.network "public_network"
    host.vm.network "private_network", ip: "172.16.0.10"

    host.vm.provider "virtualbox" do |v|
      v.customize ["modifyvm", :id, "--uart1", "0x3f8", "4"]
      v.customize ["modifyvm", :id, "--uartmode1", "server", "/tmp/#{host.vm.hostname}_com"]
      v.memory = 4096
    end
  end
end

この定義で起動するとOSとカーネルバージョンは下の通りになります。

1
2
$ uname -a
Linux ubuntu-bionic 4.15.0-117-generic #118-Ubuntu SMP Fri Sep 4 20:02:41 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

ここからカーネルイメージをインストールしてアップデートします。

1
$ sudo apt-get install linux-image-4.18.0-13-generic

再起動してみると、無事に新しいカーネルが使われています。

1
2
3
4
$ sudo reboot now
.......
$ uname -a
Linux ubuntu-bionic 4.18.0-13-generic #14~18.04.1-Ubuntu SMP Thu Dec 6 14:09:52 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Grub 2を使い古いカーネルで起動する

冒頭で書いたようにUbuntuはGRUB2を標準で使っている。ネットではGRUB2は grub2-* というコマンドを使うと書かれているがUbuntuでは grub-* を使うので知らないと混乱する。

GRUB2の動作で大事なファイルを並べと下の4つになる。このうち /etc/default 配下の2つはユーザーが編集する。 /boot/grub/ 以下の2つはコマンド経由で生成しないとならない。

/etc/default/grub # 管理者が編集する
/etc/default/grub.d/*

/boot/grub/grub.cfg # ブート時に使われる設定ファイル
/boot/grub/grubenv  # ブート時に使われる変数定義ファイル

デフォルトで読み込まれるカーネルのバージョンに古い4.15.0-117を指定してみます。 メニュー自体に触るつもりはありません。採用するデフォルトのエントリを変更するだけで済みます。 ブートのデフォルトは /etc/default/grub 内の GRUB_DEFAULT 変数で指定されます。 値には後述するエントリ名と0はじまりのインデックスの両方が使えます。(他には saved も使えますが今回は考えません)

下のようにすでに生成されている設定ファイルから選択肢を集めてみました。

1
2
3
4
5
6
7
$ grep -E '(menuentry|submenu) ' /boot/grub/grub.cfg
menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-d2c19aaf-d33b-49cb-84af-045694514743' {
submenu 'Advanced options for Ubuntu' $menuentry_id_option 'gnulinux-advanced-d2c19aaf-d33b-49cb-84af-045694514743' {
        menuentry 'Ubuntu, with Linux 4.18.0-13-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.18.0-13-generic-advanced-d2c19aaf-d33b-49cb-84af-045694514743' {
        menuentry 'Ubuntu, with Linux 4.18.0-13-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.18.0-13-generic-recovery-d2c19aaf-d33b-49cb-84af-045694514743' {
        menuentry 'Ubuntu, with Linux 4.15.0-117-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.15.0-117-generic-advanced-d2c19aaf-d33b-49cb-84af-045694514743' {
        menuentry 'Ubuntu, with Linux 4.15.0-117-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.15.0-117-generic-recovery-d2c19aaf-d33b-49cb-84af-045694514743' {

ここから望みのブートオプションの Ubuntu, with Linux 4.15.0-117-generic を見つけます。 submenu もインデックスに数えられるので GRUB_DEFAULT に指定する値は 1>2 または Advanced options for Ubuntu>Ubuntu, with Linux 4.15.0-117-generic となります。エントリ名を使った方が混乱は少ないです。

1
2
3
GRUB_DEFAULT="1>2"
# or
GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 4.15.0-117-generic"

このように指定したのちに、実際に使われる設定ファイルを生成します。 したの2つのコマンドは実質的に同じなのでどちらを使っても構いません。

1
2
$ sudo update-grub
$ sudo grub-mkconfig -o /boot/grub/grub.cfg

これで無事に古いカーネルで起動することができます。

GRUB2で新しいカーネルで一時的に起動する

安定した古いカーネルで起動するように変更しました。次に1回だけ新しいカーネルで起動を試してみます。 さっきと同様にメニューエントリを確認して欲しいエントリのインデックスを決めます。ここでは Ubuntu, with Linux 4.18.0-13-generic を使いたいので下のようにします。

1
$ sudo grub-reboot '1>0'

これで次回の起動では 1>0 が使われます。どのように実現しているかと言うと next_entry という grub.cfg で使われている変数を定義し選択を強制しています。次のように確認することができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ sudo grub-editenv list
next_entry=1>0

$ hexdump -C /boot/grub/grubenv
00000000  23 20 47 52 55 42 20 45  6e 76 69 72 6f 6e 6d 65  |# GRUB Environme|
00000010  6e 74 20 42 6c 6f 63 6b  0a 6e 65 78 74 5f 65 6e  |nt Block.next_en|
00000020  74 72 79 3d 31 3e 30 0a  23 23 23 23 23 23 23 23  |try=1>0.########|
00000030  23 23 23 23 23 23 23 23  23 23 23 23 23 23 23 23  |################|
*
00000400

おわりに

ブートの切り替えについてまとめました。GRUB2を使っていることは知っていたのですぐできると思ったのですが submenu がインデックスに数えられることがわからずハマってしまいました。 今後はLinuxの起動オプションを指定したり、起動ディスクを作ったり、また自作のLinuxディストリビューションをisoイメージとして作ってVagrant経由でVirtualBox、またはQEMUで実行・デバッグしたいと思います。

comments powered by Disqus