[FreeBSD-users-jp 95973] UEFIモードでの起動パーティション切り替え

丸山直昌 maruyama @ ism.ac.jp
2016年 10月 7日 (金) 10:53:17 UTC


統計数理研究所の丸山です。

UEFIブートのマシンの disk(当然 GPTスキーム)に複数のFreeBSDの版を入れて、
起動時に切り替えるにはどうしたら良いか。

この問題は以前このMLで話題になったことがありますが、前よりも私の知識が増
えたので、書いておこうと思います。まあ、ご存知の方も多いかも知れませんが。

私自身は、最近手持ちの Toshiba dynabookN51というマシンで PC-BSD10.3を
UEFIブートする方法がわかったので、この問題について色々実験ができるように
なったわけですが、この記事に書くことは dynabookN51という特定のハードウェ
アや、PC-BSDに固有のことではなく、UEFIブートできるマシンでFreeBSD一般に
通用する内容であると考えています。

まず GPTスキームでの起動時の切り替えに関して、 BIOSブートとUEFIブートで
どのような違いがあるかを書いておきます。

GPTスキームのBIOSブートの場合、起動パーティションの変更は、私が知る限り

A. ブート時のキー入力による介入
B. /boot.config による変更
C. gpart の attribute 属性による変更

の3通りがあります。Aは起動時のインタラクティブな変更で、B, C はディス
クを少し書き換えておいてから reboot するという、非インタラクティブな変
更です。

以下少し具体的にこの3つを説明しますが、残念ながらFreeBSD10.2, 10.3,
11.0 ではこのいずれも UEFIブートでは対応する機能は現在サポートされてい
ません。

BIOSブートの場合、disk上には type freebsd-boot のパーティションが必要で、
仮にここではそのパーティションをada0p1としますと、

        gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 ada0

あるいは

        gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada0

としておく必要があります。前者が UFS用、後者は zfs 用です。

A. ブート時のキー入力による介入

は、man gptbootや man gptzfsboot には書いてありませんが、起動の初期の
段階で何かRETURN以外のキーを押すと、UFSの場合は

FreeBSD/x86 boot
Default: 0:ad(0p3)/boot/loader
boot: 

zfsの場合は

FreeBSD/x86 boot
Default: tank/ROOT/initial:/boot/zfsloader
boot: 

のような表示が現れて一旦ブートプロセスは停止し、別のパーティション、あ
るいは zfs pool上のloader を指定すると、そのパーティションから起動する
ことができます。この「起動時のキー入力による変更」は、/boot/menu.rc に
よるメニューが表示されてからでは間に合いません。

次に「B. /boot.config による変更」ですが、私は UFSの場合しか試していな
いのですが、起動されるパーティションが ada0p3の場合であれば、このパー
ティションのトップのディレクトリのboot.config というファイルに

0:ad(0p5)/boot/loader

と書いておくと、ada0p5 にある /boot/loader が起動される、という仕組み
です。この方法でも、Aの「ブート時のキー入力による介入」でも、書き方は
"ada"ではなく、"ad" です。

最後の「C. gpart の attribute 属性による変更」は、

gpart set -a bootme -i 5 ada0

として、起動パーティションの「印」を GPTスキームのパーティション情報に
書き込んでおく方法です。これで ada0p5 から起動されます。これは man
gpart に書いてあり、UFSでもzfsでも確かに働きます。

以上 A,B,C はBIOSブートの場合の話ででしたが、以下UEFIブートの場合を書
きます。

UEFIボートではディスク上に type efi のパーティションが必要で、それは
MSDOSのFAT16にフォーマットされ、適切な BOOTX64.EFIというファイルと、起
動構成データが書かれている(Windowsであれば bcdeditコマンドで書き込む)
必要がありますが、その詳細は

http://d.hatena.ne.jp/msll/20150527/1432708323
http://qiita.com/takawata19/items/3d1fb96cfde4f1626e3c
及び、 dynabookN51に関しては、私が書いた
    [FreeBSD-users-jp 95970] Date: Tue, 20 Sep 2016 11:17:43 +0900

に譲り、ここでは BOOTX64.EFI としては、 FreeBSDの場合には
/boot/boot1.efiを使う必要がある、という事に留めておきます。

さて BIOSブートの場合には

A. ブート時のキー入力による介入
B. /boot.config による変更
C. gpart の attribute 属性による変更

の3つの方法で起動パーティションを切り替えることができましたが、実験し
たところ、UEFIブートではすべて駄目でした。BIOSブートの場合A, B, Cは
type freebsd-boot のパーティションに書き込む /boot/gptboot あるいは
/boot/gptzfsboot によって実現されていると思われますが、 UEFIブートの場
合には、type efi のパーティションの BOOTX64.EFI にコピーされる
/boot/boot1.efi がそのような機能を実装していないということでしょう。こ
れは 10.2, 10.3, 11.0 いずれの /boot/boot1.efi を使ってみても駄目でし
た。(BとCは、それぞれman boot.config, man gpart に書いてあるのに、それ
は反則だろう、と言いたくなりますが。)

残る手段(最後の手段)は gnu-grubです。正確には grub2 ですが、BIOSブート
の場合には

   insmod ufs2
   insmod bsd
   set root=(hd0,5)
   kfreebsd /boot/loader
   set kFreeBSD.vfs.root.mountfrom=ufs:/dev/ada0p5
   set kFreeBSD.vfs.root.mountfrom.options=rw

という簡単なメニューエントリでUFSのパーティションから起動できます(zfs
は試していませんが、多分同じようにできると思います)。grub2のインストー
ル、設定などはここでは解説しませんが、仕組みを簡単に説明しておくと、
grub-install, grub-mkconfig を使って正しくインストール、設定が行われて
いれば、電源投入後

     diskのsector0 にある stage0 boot loader 
  -> type bios-boot のパーティションにある grub2

という順で起動され、その後は grub2 の文法に従って起動が進みますが、上
のようなメニューエントリが選ばれた場合には、

  -> /dev/ada0p5 の /boot/loader
  -> /boot/kernel/kernel

という順でFreeBSDが起動されます。なおこの流れの中では A, B, Cの変更方
法は機能しません。なぜかと言うと、A, B, Cの変更方法は type
freebsd-bootのパーティションにコピーされた /boot/gptboot や
/boot/gptzfsbootの機能であって、/boot/loader に制御が移った時点では最
早手遅れだからです。

以上の話はUEFIブートの場合の grub2 ではどうなるかと言いますと、

     (UEFIファームウェアが) type efi のパーティションにある BOOTX64.EFIを起動
  -> type bios-boot のパーティションにコピーされた grub2が起動

となって、以後は grub2の文法に従いますが、ここで上と全く同じメニューエ
ントリを選択しても起動できません!

これには当初私は途方に暮れました。理由がわかるのに少し時間がかかったの
ですが、答えは

https://www.gnu.org/software/grub/manual/grub.html#Supported-kernels

にありました、これを見ると

                        BIOS    Coreboot
BIOS chainloading       yes     no (1)
NTLDR                   yes     no (1)
Plan9                   yes     no (1)
Freedos                 yes     no (1)
FreeBSD bootloader      yes     crashes (1)
32-bit kFreeBSD         yes     crashes (2,6)
64-bit kFreeBSD         yes     crashes (2,6)
(途中略)
                        ia32 EFI        amd64 EFI
BIOS chainloading       no (1)          no (1)
NTLDR                   no (1)          no (1)
Plan9                   no (1)          no (1)
FreeDOS                 no (1)          no (1)
FreeBSD bootloader      crashes (1)     crashes (1)
32-bit kFreeBSD         headless        headless
64-bit kFreeBSD         headless        headless

と言う部分があります。この "FreeBSD bootloader" というのは/boot/loader
ですので、 BIOSブートでは"yes", つまりポートされていますが、 amd64 EFI
ではcrashする、というわけです。

ではこれで絶望かと言うと、そうではなく、最後の行の

64-bit kFreeBSD         headless        headless

に救いがあります。ここで headless というのは

"headless" means that the kernel works but lacks console drivers (you
can still use serial or network console).

だそうで、これを使うと

   insmod ufs2
   insmod bsd
   set root=(hd0,5)
   set kFreeBSD.vfs.root.mountfrom=ufs:/dev/ada0p5
   set kFreeBSD.vfs.root.mountfrom.options=rw
   kfreebsd /boot/kernel/kernel

というメニューエントリで、 /boot/loader を経ずに直接
/boot/kernel/kernelを起動することはできます。ですがこれだと通常
/boot/loader がやってくれていた作業(/boot/loader.conf を読み込んで
kernel module をload しておくとか、カーネル環境変数を設定しておく)が全
部飛ばされるので、実際にはOSは満足な動作をしません。その飛ばされる作業
を grub2 自身にやらせるために少し長いメニューエントリを書く必要があり
ます。例えば以下のようになります。

   insmod ufs2
   insmod bsd
   set root=(hd0,5)
   set kFreeBSD.vfs.root.mountfrom=ufs:/dev/ada0p5
   set kFreeBSD.vfs.root.mountfrom.options=rw
    kfreebsd /boot/kernel/kernel
    kfreebsd_loadenv /boot/device.hints
    kfreebsd_module_elf /boot/modules/nvidia.ko
    kfreebsd_module_elf /boot/kernel/drm.ko
    kfreebsd_module_elf /boot/kernel/drm2.ko
    kfreebsd_module_elf /boot/kernel/iicbus.ko
    kfreebsd_module_elf /boot/modules/vboxdrv.ko
    kfreebsd_module_elf /boot/kernel/crypto.ko
    kfreebsd_module_elf /boot/kernel/aesni.ko
    kfreebsd_module_elf /boot/kernel/geom_eli.ko
    kfreebsd_module_elf /boot/kernel/zfs.ko
    kfreebsd_module_elf /boot/kernel/opensolaris.ko
    kfreebsd_module_elf /boot/kernel/tmpfs.ko
    kfreebsd_module_elf /boot/kernel/linux.ko
    kfreebsd_module_elf /boot/kernel/linux_common.ko
    kfreebsd_module_elf /boot/kernel/geom_journal.ko
    kfreebsd_module_elf /boot/kernel/geom_mirror.ko
    kfreebsd_module_elf /boot/kernel/ums.ko
    set kFreeBSD.bootfile="kernel"
    set kFreeBSD.kernel="kernel"
    set kFreeBSD.kernel_options=""
    set kFreeBSD.kernelname="/boot/kernel/kernel"
    set kFreeBSD.module_path="/boot/kernel;/boot/modules"
    set kFreeBSD.kern.ipc.shmseg="1024"
    set kFreeBSD.kern.ipc.shmmni="1024"
    set kFreeBSD.kern.maxproc="10000"
    set kFreeBSD.legal.intel_ipw.license_ack="1"
    set kFreeBSD.legal.intel_iwi.license_ack="1"
    set kFreeBSD.legal.realtek.license_ack="1"
    set kFreeBSD.hint.acpi_throttle.0.disabled="1"
    set kFreeBSD.machdep.disable_mtrrs="1"
    set kFreeBSD.kern.geom.eli.visible_passphrase="2"
    set kFreeBSD.kern.cam.scsi_delay="500"
    set kFreeBSD.hw.memtest.tests="0"
    set kFreeBSD.kern.vty="vt"
    set kFreeBSD.kern.geom.label.gptid.enable="0"
    set kFreeBSD.kern.geom.label.disk_ident.enable="0"
    set kFreeBSD.kern.geom.label.ufsid.enable="0"
    set kFreeBSD.hw.pci.do_power_nodriver="3"
    set kFreeBSD.net.inet.ip.fw.default_to_accept="1"
    set kFreeBSD.net.inet.ip.fw.one_pass="0"
    set kFreeBSD.net.graph.maxdata="65536"
    set kFreeBSD.grub.platform="$grub_platform"
    set kFreeBSD.kern.geom.eli.passphrase="$pass"

以上はUFSの場合ですが、zfs なら

    insmod zfs
    search --no-floppy -s -l tank
    kfreebsd /ROOT/initial/@/boot/kernel/kernel
    kfreebsd_loadenv /ROOT/initial@/boot/device.hints
    kfreebsd_module /ROOT/initial/@/boot/zfs/zpool.cache type=/boot/zfs/zpool.cache
    set kFreeBSD.vfs.root.mountfrom=zfs:tank/ROOT/initial
    kfreebsd_module_elf /ROOT/initial/@/boot/modules/nvidia.ko
    kfreebsd_module_elf /ROOT/initial/@/boot/kernel/drm.ko
    kfreebsd_module_elf /ROOT/initial/@/boot/kernel/drm2.ko
    (以下略)

という調子です。

以上で、私がこの記事で書きたかったことはほぼ終わりましたが、最後に少し
grub2 のインストールに関係することを書いておきます。

UEFI のマシンに元祖FreeBSD10.3をインストールすることは、私は経験がない
のですが、どなたかの記事ではちゃんと起動できるようにインストールされる
と書いてありました。起動した状態で、 grub2 を pkg add し、grub-install
と grub-mkconfig をやればtype efiのパーティションにかかれた
BOOTX64.EFI (このファイル名が case sensitive なのかどうか私は知りませ
ん)が grub 用のものに書き換わり、grub のメニューが作成されて、rebootす
れば、 grub 経由で 10.3が立ち上がるようになるのでしょう(実験してなく
て申し訳ない、無保証です)。この時どのような初期メニューが作成されるの
か私は知りませんが、ともかくメニューは /boot/grub/grub.cfg と
/boot/grub/custom.cfgを見ればわかります。そして、/boot/grub/custom.cfg
を手動で直すか、あるいは /usr/local/etc/grub.d の下にあるファイルを手
動で直してgrub-mkconfig をやり直せば、メニューを思い通りに作り直すこと
は可能であるはずです(詳しくは grub2 の info に書いてあるのでしょう)。
この時、上に書いた事を参考にしてください。

PC-BSD10.3について書きますと、標準のインストールでは、grub2 が既定のブー
トローダーとしてインストールされ、インストールしたzfs を起動するメニュー
が初期状態として作られます。私は /boot/grub/custom.cfgや
/usr/local/etc/grub.d の下のファイルをいじくって、複数のパーティション
の切り替えを実現しています。

以前は「一個のディスクに複数の zfs(プール名は違える)を作り、ぞれぞれシ
ステムをインストールをすると、grub のメニューが面倒」と思い込んでいた
のですが、どうも grub-mkconfig はその点も「何とかしてくれる」らしいと
最近気が付きました。ただこれがgrub2 に元々備わった機能なのか、あるいは
grub2 に加えて PC-BSD固有の設定が関係しているのか、調べきれていません。

--------
丸山直昌@統計数理研究所


freebsd-users-jp メーリングリストの案内