玩式草子─ソフトウェアとたわむれる日々

第79回Plamo LinuxのGPT/UEFI対応その3]

前回はUEFIがGPT形式のHDDからOSを起動する仕組みを解説し、Plamo Linux 6.0をGPT形式のHDDにインストールする手順を紹介しました。前回は省略したものの、UEFI環境にインストールするにはもう一つ越えなければならない壁があります。それはUEFIにインストールするにはUEFIから起動しなければいけないという条件です。

従来のSystem BIOSではHDDの起動順序等の設定はBIOSのメニュー画面からしか操作できなかったのに対し、UEFIではさまざまな設定をOSから操作する機能が提供され、起動順序等をより柔軟に設定できるようになりました。

一方、この機能を使うにはOSがUEFIモードで起動している必要があります。たとえばLinuxの場合、UEFIの設定はefivarfsという専用のファイルシステム経由で利用するようになっており、このファイルシステムはカーネルがUEFIモードで起動しないと有効になりません。

すなわち、GPT/UEFI環境にインストールするにはインストーラをGPT/UEFI対応にするだけではなく、インストーラ自身をUEFIから起動する必要があるわけで、そのための方法をあれこれ調べることになりました。

USBメモリからUEFI起動

前回紹介したように、UEFIはHDDからOSを起動する場合、専用のパーティションIDを持ったFAT32形式のEFIシステムパーティション(ESP)にあるブートローダを調べます。一方、USBメモリのように通常は一つのパーティションしか持たないリムーバブルメディアから起動する場合、ESPとは関係なくメディアのルートディレクトリにある\EFI\BOOT\ディレクトリを探します。このディレクトリに置くブートローダは、x86_64アーキテクチャの場合BOOTX64.EFI32ビットのx86アーキテクチャの場合はBOOTIA32.EFIという名前にすることになっています。

前回紹介したように、--with-platform=efiオプションを指定してビルドすればUEFI用のgrubはビルドできるものの、この状態のgrubは機能が細かく分割されたモジュール群でしかありません。

$ ls -l /usr/lib64/grub/x86_64-efi/
合計 20,307,968
-rw-r--r-- 1 root root    16,432 10月 30日  17:11 acpi.mod
-rwxr-xr-x 1 root root   102,088 10月 30日  17:11 acpi.module*
-rw-r--r-- 1 root root     1,928 10月 30日  17:11 adler32.mod
-rwxr-xr-x 1 root root    13,328 10月 30日  17:11 adler32.module*
-rw-r--r-- 1 root root     8,272 10月 30日  17:11 affs.mod
-rwxr-xr-x 1 root root    48,344 10月 30日  17:11 affs.module*
-rw-r--r-- 1 root root     9,016 10月 30日  17:11 afs.mod
-rwxr-xr-x 1 root root    57,488 10月 30日  17:11 afs.module*
...

grubがブートローダとして機能するためには、これらモジュールのうち必要なものをgrubコアに組み込む必要があります。grub-installコマンドを使ってインストールする場合、grub-mkconfigコマンドが自動的に起動され、インストールする環境に応じたモジュールを組み込むための設定ファイルgrub.cfgを生成してくれます。

骨組みだけしかないgrubコアにgrub.cfgで必要なモジュールを組み込むという仕組みは、さまざまな環境に柔軟かつ効率的に対応できる利点はあるものの、どのような環境で使われるかが事前には分からないインストーラでは使えません。

そのような場合のために用意されているのがgrub-mkimageコマンドです。このコマンドは、指定したモジュールをgrubコアに組み込んで、モジュールを追加ロードしなくても動作するバイナリファイルを生成します。

$ grub-mkimage --help
使い方: grub-mkimage [OPTION...] [OPTION]... [MODULES]
Make a bootable image of GRUB.

  -c, --config=FILE          embed FILE as an early config
  -C, --compression=(xz|none|auto)
                             choose the compression to use for core image
  -d, --directory=DIR        use images and modules under DIR
                             [default=/usr/lib64/grub/<platform>]
  -k, --pubkey=FILE          embed FILE as public key for signature checking
  -m,                              --memdisk=FILE         embed FILE as a memdisk image
Implies `-p (memdisk)/boot/grub' and overrides
                             any prefix supplied previously, but the prefix
                             itself can be overridden by later options
  -n, --note                 add NOTE segment for CHRP IEEE1275
  -o, --output=FILE          output a generated image to FILE [default=stdout]
  -O, --format=FORMAT        generate an image in FORMAT
                             available formats: i386-coreboot, i386-multiboot,
                             i386-pc, i386-pc-pxe, i386-pc-eltorito, i386-efi,
                             i386-ieee1275, i386-qemu, x86_64-efi, i386-xen,
  ...

grubには250近いモジュールが用意されており、どのモジュールを組み込むかを考えるのも一苦労なものの、今回はUSBメモリと後述するDVDからの起動に限定して以下のようなモジュールを組み込むことにしました。

$ grub-mkimage -v -p '' -o bootx64.efi -O x86_64-efi fat part_msdos iso9660 gzio \
      all_video gfxterm font terminal normal linux echo test search configfile cpuid minicmd
grub-mkimage: 情報: the total module size is 0x6ffb8.
grub-mkimage: 情報: reading /usr/lib64/grub/x86_64-efi/kernel.img.
grub-mkimage: 情報: locating the section .text at 0x0.
grub-mkimage: 情報: locating the section .rodata at 0x9600.
grub-mkimage: 情報: locating the section .rodata.str1.1 at 0x9798.
...
grub-mkimage: 情報: kernel_img=0x7fcad3119010, kernel_size=0x18200.
grub-mkimage: 情報: the core size is 0x881b8.
grub-mkimage: 情報: writing 0x89400 bytes.

今回組み込んだモジュールは、FATファイルシステム用のfatDOSパーティション用のpart_msdosCD/DVD用のiso9660圧縮ファイル用のgzio各種ビデオカードに対応するためのall_videoグラフィカルターミナル用のgfxtermフォント回りを扱うfontターミナル表示用のterminal通常モード用のnormalカーネルをロードするためのlinuxスクリプト中で各機能を有効にするechotest指定されたファイルシステムを探すsearch設定ファイルを操作するconfigfileCPUの種類を調べるcpuid最低限のコマンドを実装するminicmdということになります。

-p ''は起動時に必要なファイルを探すディレクトリを変更するための指定で、EFI版のgrubでは -p '' と指定すると/EFI/BOOT/以下にある設定ファイルを読みこむことになります。この指定をしないとデフォルトである/boot/grub/以下を探します。

grub-mkimageはgrubコアにこれらのモジュールを組み込み、バイナリ形式もPE32+に変更してEFIアプリケーションを生成します。

$ ls -lh bootx64.efi 
-rw-r--r-- 1 kojima users 549K  2月 23日  23:12 bootx64.efi
$ file bootx64.efi 
bootx64.efi: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows

次に、grub.cfgでこのgrubから読み込むカーネルとinitrd.gzを指定します。initrd.gzにはインストーラを動かすための小規模なLinux環境が入っています。Plamo Linuxのインストールメディアではカーネルとinitrd.gzはisolinuxディレクトリに置いているので、USBメモリから起動するための最小限の設定はこうなりました。

1:  menuentry "UEFI Plamo Linux install from USB memory" {
2:    linux (hd0)/isolinux/vmlinuz root=/dev/ram0 rw nomodeset vga16 unicon=eucjp vt.default_utf8=0 kbd=usbkbd
3:    initrd (hd0)/isolinux/initrd.gz
4:  }

2行目が読み込むカーネルと起動時のオプションの指定、3行目がinitrd.gzの指定です。なお、この設定はパーテイションを切らずにフォーマットしたUSBメモリの例で、パーティションを切ってフォーマットしたUSBメモリでは(hd0)の部分が(hd0,msdos1)のようになります。

こうして作成したbootx64.efiとgrub.cfgをUSBメモリの\EFI\BOOTディレクトリに、カーネルとinitrd.gzを\isolinuxディレクトリに置けば、UEFIはUSBメモリをUEFI対応メディアとして認識し、そこから起動できるようになりました。

図1 UEFIが起動メディアとして認識したUSBメモリ
図1 UEFIが起動メディアとして認識したUSBメモリ

DVDからのUEFI起動

USBメモリは小さなHDDと見なせるのでUEFIへの対応は比較的簡単なものの、CDやDVDからUEFIモードで起動するにはもう一工夫が必要です。というのも、Plamo Linuxのインストール用DVDでは、syslinux由来のisolinux.binを使ってSystem BIOSから起動する形にしており、そのままではUEFIに対応できないためです。

さてどうしたものか、とあれこれ調べたところ、mkisofsでDVDイメージを作成する際に-eltorito-alt-bootオプションで複数のブートイメージを指定できることに気づきました。

UEFIから起動する場合、この-eltorito-alt-bootオプションで指定したブートイメージがESPとして認識されるので、このブートイメージの中に\EFI\BOOTディレクトリを作ってbootx64.efiを置けばUEFIはそれをOSのブートローダとして認識してくれそうです。

El Torito用のブートイメージは仮想的なFDとして作成するので、まずはddコマンドでFDサイズ(1.44MB)のファイルを作り、そのファイルをFAT形式にフォーマットします。

$ dd if=/dev/zero of=efiboot.img bs=1k count=1440
$ /usr/sbin/mkfs.msdos -F 12 -M 0xf8 efiboot.img
mkfs.fat 3.0.26 (2014-03-07)
$

次にこのefiboot.imgをループバックでマウントし、必要なディレクトリを作ってブートローダをコピーします。

$ mkdir /tmp/loop
$ sudo mount efiboot.img /tmp/loop -o loop
$ sudo mkdir -p /tmp/loop/EFI/BOOT
$ sudo cp bootx64.efi /tmp/loop/EFI/BOOT
$ sudo umount /tmp/loop

DVDイメージはmkisofsを用いて作成します。mkisofsは指定したディレクトリにある全てのファイルをDVDイメージに書き込むので、作業用ディレクトリ(DVD_contents)にPlamo-6.x用のisolinuxディレクトリを持ってきて、そこに先ほど作ったefiboot.imgを置くことにします。

$ mkdir DVD_contents
$ cp -av /mnt2/Plamo-6.x/x86_64/isolinux ./DVD_contents/
$ cp efiboot.img ./DVD_contents/isolinux/

今回利用しているbootx64.efiはUSBメモリ用に作成したものを流用しているので、設定ファイルは/EFI/BOOT/grub.cfgを読むことになります。bootx64.efiはefiboot.img中の/EFI/BOOT/ディレクトリに置くものの、この仮想FDはUEFIがブートローダを探すためにのみ用いられbootx64.efiからは見えません。そのためgrub.cfgはDVDイメージの中の/EFI/BOOT/に置く必要があります。

$ mkdir -p ./DVD_contents/EFI/BOOT
$ cat << 'EOF' > ./DVD_contents/EFI/BOOT/grub.cfg
menuentry "UEFI Plamo Linux install from DVD" {
  linux (cd0)/isolinux/vmlinuz root=/dev/ram0 rw nomodeset vga16 unicon=eucjp vt.default_utf8=0 kbd=usbkbd
  initrd (cd0)/isolinux/initrd.gz
}
EOF

必要なファイルが揃ったのでmkisofsを実行します。最初の2行がデフォルト(1つめ)のブートイメージの指定で、System BIOSから起動すると、ここで指定したisolinux.binを利用します。3行目が2つめのブートイメージの指定で、UEFIから起動するとefiboot.imgを用いることになります。

$ sudo mkisofs -v -J -r -b isolinux/isolinux.bin -c isolinux/boot.cat \
      -no-emul-boot -boot-load-size 4 -boot-info-table \
      -eltorito-alt-boot  -eltorito-platform efi -eltorito-boot isolinux/efiboot.img \
      -V UEFI-test -o UEFI-test.iso DVD_contents
Setting input-charset to 'UTF-8' from locale.
3.01 (x86_64-unknown-linux-gnu)
Scanning DVD_contents
Scanning DVD_contents/isolinux
Excluded by match: DVD_contents/isolinux/boot.cat
Writing:   Initial Padblock                        Start Block 0
Done with: Initial Padblock                        Block(s)    16
...
75.29% done, estimate finish Wed Feb 24 16:29:21 2016
94.12% done, estimate finish Wed Feb 24 16:29:21 2016
Total translation table size: 2048
Total rockridge attributes bytes: 1690
Total directory bytes: 4096
Path table size(bytes): 26
Done with: The File(s)                             Block(s)    26390
Writing:   Ending Padblock                         Start Block 26425
Done with: Ending Padblock                         Block(s)    150
Max brk space used 0
26575 extents written (51 MB)
$

なお、手元のisolinuxディレクトリにはefiboot.img以外にも下記のようなファイルを置いています。これらのうち、isolinux.binc32の拡張子を持つファイルはsyslinuxに由来し、isolinux.binの起動を補助します。isolinux.cfgはisolinux.bin用の設定ファイルです。その他はPlamo Linuxで作成したファイルで、vmlinuzSystem.mapはカーネルとシンボルテーブル、initrd.gzがインストーラの入ったミニLinux環境、plamo[46]1.lssはisolinux起動時に表示されるスプラッシュイメージ、sample.msgは起動時に表示されるメッセージ、boot.catはmkisofsが生成するEl Torito用のカタログファイルになります。

$ ls DVD_cotents/isolinux/
System.map  efiboot.img  isolinux.bin  ldlinux.c32   libutil.c32  plamo61.lss  vesamenu.c32
boot.cat    initrd.gz    isolinux.cfg  libcom32.c32  plamo41.lss  sample.msg   vmlinuz

こうして作ったDVDイメージ(UEFI-test.iso)は、UEFI環境から起動するとgrubが、System BIOS環境から起動するとisolinuxがそれぞれ起動し、起動環境に応じてインストールできるようになりました。

図2 UEFIから起動したgrub.cfgの起動画面
図2 UEFIから起動したgrub.cfgの起動画面
図3 System BIOSから起動したisolinux.cfgの起動画面
図3 System BIOSから起動したisolinux.cfgの起動画面

今回紹介したような形で、Plamo-6.xは一枚のDVDでUEFIとSystem BIOSに対応できるようになったものの、DVDイメージをベタ書きしたUSBメモリはSystem BIOSからしか起動できないようです。DVDイメージの中身をUSBメモリにコピーしてやればUEFIからも起動できるものの、USBメモリからUEFIブートする場合のみ手順が異なるのは気になるところです。DVDイメージには--uefiオプションを指定してisohybrid処理をしているので、本来はUEFIからでもベタ書きしたUSBメモリを起動できるはずですが、このあたりはもう少し修行が必要なようです。

おすすめ記事

記事・ニュース一覧