#author("2024-01-06T17:21:04+09:00;2019-02-21T19:43:29+09:00","default:kaw","kaw") #author("2024-01-06T17:22:21+09:00;2019-02-21T19:43:29+09:00","default:kaw","kaw") #topicpath * 河豚板のUEFI/GPT対応 [#c3cde25d] RIGHT:EBUG 第68回会合 ~ 2019年2月23日、(有)銀座堂 ~ 川俣吉広、kaw@on.rim.or.jp #contents **発端 [#w413bf81] Twitterでの[[このtweet>https://twitter.com/ao_kenji/status/1073836123934183424]]がきっかけ。 このやりとりが発端となり、[[河豚板のUEFI対応>https://twitter.com/yoshi_kaw/status/1074110733577637888]]を開始する。 **UEFIとGPT [#f51c6c7a] :UEFI (Unified Extensible Firmware Interface)|OSとファームウェアとの間のソフトウェアインタフェースを定義する仕様([[Wikipedia>https://ja.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface]])~ 従来のBIOS (Legacy BIOS)による起動方法からの置換えを意図している。~ ・UEFI用のパーティション(ファイルシステムとしてはFAT)を持つ。~ ・そのパーティション内に起動用の実行バイナリが格納されている。このバイナリファイルは*.EFIの拡張子を持ち、UEFIアプリケーションと呼ばれる。 :GPT (GUID (Globally Unique Identifier) Partition Table)|ストレージデバイス上のパーティションテーブルの配置に関する標準規格([[Wikipedia>https://ja.wikipedia.org/wiki/GUID%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB]])~ Legacy BIOSで用いられてきたMBR (Master Boot Record)に替わるものとしてUEFIの一部として定義されている。~ ・MBRが扱えるのは最大2TBのディスクを4区画まで~ ・GPTでは最大8ZBを128区画まで分割可能 OpenBSDの場合に現状で考えられる起動方法とパーティショニングのパターンを以下に示す。 ***Legacy BIOS / MBR [#b5dbe277] #ref(MBR.png,wrap,around,right,,50%) UEFI登場以前から用いられてきた方法 [[(参考)>https://www.hazymoon.jp/OpenBSD/]] -ハードウェア上のファームウェアがHDDのセクタ0にあるMBRブートコードを実行する。 -MBRブートコードは、ブートフラグがONになっているパーティションの先頭セクタにあるPBR (Partition Boot Record)に実行を移す。 --OpenBSDでは、PBRは/usr/mdec/biosbootが用いられる。 -PBRはOpenBSDのルートファイルシステム上にある/bootを実行する。 --この/bootが「boot>」プロンプトを表示し、カーネル起動に関するユーザからの指定を受け付ける。 -bootは指定にしたがって、該当するカーネルを起動する。 -なお、OpenBSD用と設定された区画(右図の例ではentry 3)は、同時に[[disklabel(8)>https://man.openbsd.org/disklabel]]ユティリティによる管理範囲(OpenBSD Area)として設定される。 OpenBSD Areaの中にdisklabelによるBSDパーティションが作成され、ファイルシステムやスワップ領域として使われる(参考:[[他のBSDユーザのためのOpenBSD管理入門>http://fuguita.org/index.php?EBUG%CA%D9%B6%AF%B2%F1%2F20171118_OpenBSD%B4%C9%CD%FD%C6%FE%CC%E7#lde54a06]])。 #clear ***UEFI / GPT [#b9cb0a3e] #ref(GPT.png,wrap,around,right,,50%) 起動にUEFI、ディスクパーテショニングにGPTを用いるパターン -ハードウェア上のファームウェアがGPT内でEFI Sysパーティションを探し、この中に \efi\BOOT\BOOT*.EFIという実行ファイル(EFIアプリケーション)があれば実行する。~ このファイル名は、 --i386ではBOOTIA32.EFI --amd64はBOOTX64.EFI --arm64はBOOTAA64.EFI >となっている。 -OpenBSDのEFIアプリケーションは、「boot>」プロンプトを表示し、カーネル起動に関するユーザからの指定を受け付ける。~ これは、MBR起動の場合の/bootに相当する。 -EFIアプリケーションは指定にしたがって、該当するカーネルを起動する。 -なお、GPTの場合にも先頭セクタの部分に、従来のMBRとの互換性を考慮してprotective MBR (保護MBR)というものが書き込まれている。これはfdiskなどのMBRを扱うユティリティが誤ってMBRを書き換えないようにするためのものである。 -OpenBSD Area内の管理については、MBRの場合と同じ。 #clear ***UEFI / MBR [#g2c561a7] #ref(ARM64.png,wrap,around,right,,50%) UEFIとしてEFIアプリケーションがロードされるが、パーテショニングとしてMBRが使われるパターン。GPTは存在しない。~ amd64のインストールメディアやarm64で使われている。 amd64のインストールメディアとarm64とでは細かい箇所で異なっている部分がある(右図はarm64)。 -arm64ではUEFI用のパーティションIDは0C (FAT32L)、amd64インストーラではEF (EFI Sys)。 -arm64ではUEFI用のパーティション内にはU-BOOTも書き込まれている。 -amd64インストーラではMBR boot code、PBR、/bootもセットアップされ、Legacy BIOS・UEFIのどちらでも起動する。 #clear ***Hybrid MBR [#ffdd1155] #ref(Hyb.png,wrap,around,right,,50%) MBRとGPTの両方に同じパーテショニング情報を書込み、ブートローダもLegacy BIOS, UEFI共にセットアップしたもの。 -Legacy BIOS・UEFIのどちらでも起動することが期待できる。 -非標準。規格上は定義されていない方法。 -現状ではMBRとGPTとを同時に変更するツールがないため、作成後にパーテショニングを変更することが困難。 #clear **OpenBSDでのUEFI実装 2015年に安岡昌彦氏によりカーネルにUEFI対応の機能が[[実装された>https://marc.info/?l=openbsd-cvs&m=144115942223734&w=2]]。 現在では、インストーラ含め、関連ユティリティがUEFI/GPTに対応している。 ***インストーラ [#ybe73310] 前述のように、Legacy BIOS、UEFIのいずれからでも起動可。 Legacy BIOS、UEFIのどちらで起動したかによって、パーティショニングのデフォルトも変わる。 UEFIで起動した場合 Available disks are: wd0 wd1. Which disk is the root disk? ('?' for details) [wd0] wd1 No valid MBR or GPT. Use (W)hole disk MBR, whole disk (G)PT or (E)dit? [gpt] ←インストーラをUEFIで起動した Setting OpenBSD GPT partition to whole wd1...done. ので、GPTがデフォルト The auto-allocated layout for wd1 is: # size offset fstype [fsize bsize cpg] a: 819776.0K 1024 4.2BSD 2048 16384 1 # / b: 83090.0K 1640576 swap c: 2097152.0K 0 unused d: 930704.0K 1806784 4.2BSD 2048 16384 1 # /usr e: 263024.0K 3668192 4.2BSD 2048 16384 1 # /home i: 480.0K 64 MSDOS ←EFI Sys領域 Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout? [a] Legacy BIOSで起動した場合 Available disks are: wd0 wd1. Which disk is the root disk? ('?' for details) [wd0] wd1 No valid MBR or GPT. Use (W)hole disk MBR, whole disk (G)PT or (E)dit? [whole] ←MBRがデフォルト Setting OpenBSD MBR partition to whole wd1...done. The auto-allocated layout for wd1 is: # size offset fstype [fsize bsize cpg] a: 800.6M 64 4.2BSD 2048 16384 1 # / b: 81.1M 1639616 swap c: 2048.0M 0 unused d: 908.9M 1805824 4.2BSD 2048 16384 1 # /usr e: 256.9M 3667232 4.2BSD 2048 16384 1 # /home Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout? [a] ***fdisk [#ff75d474] パーティションのメンテナンスツール[[fdisk(8)>https://man.openbsd.org/fdisk]]は、パーティションの初期化時にオプションによってMBRかGPTかを指定する。 MBRで初期化 # fdisk -i wd1 Do you wish to write new MBR and partition table? [n] y Writing MBR at offset 0. # fdisk wd1 Disk: wd1 geometry: 520/128/63 [4194304 Sectors] Offset: 0 Signature: 0xAA55 Starting Ending LBA Info: #: id C H S - C H S [ start: size ] ------------------------------------------------------------------------------- 0: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused 1: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused 2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused *3: A6 0 1 2 - 519 127 63 [ 64: 4193216 ] OpenBSD GPTで初期化 # fdisk -i -g wd1 Do you wish to write new GPT? [n] y Writing MBR at offset 0. Writing GPT. # fdisk wd1 Disk: wd1 Usable LBA: 64 to 4194240 [4194304 Sectors] #: type [ start: size ] ------------------------------------------------------------------------ 3: OpenBSD [ 64: 4194177 ] パーティションの詳細を見る ↓ # fdisk -v wd1 Primary GPT: Disk: wd1 Usable LBA: 64 to 4194240 [4194304 Sectors] GUID: 09d92128-d884-40f8-9e17-a606721cd8c7 #: type [ start: size ] guid name ------------------------------------------------------------------------ 3: OpenBSD [ 64: 4194177 ] 376b4152-9c45-4d7a-8392-5f0f3e015d51 OpenBSD Area Secondary GPT: Disk: wd1 Usable LBA: 64 to 4194240 [4194304 Sectors] GUID: 09d92128-d884-40f8-9e17-a606721cd8c7 #: type [ start: size ] guid name ------------------------------------------------------------------------ 3: OpenBSD [ 64: 4194177 ] 376b4152-9c45-4d7a-8392-5f0f3e015d51 OpenBSD Area MBR: Disk: wd1 geometry: 520/128/63 [4194304 Sectors] Offset: 0 Signature: 0xAA55 Starting Ending LBA Info: #: id C H S - C H S [ start: size ] --------------------------------------------------------------------------- 0: EE 0 0 2 - 532610 4 4 [ 1: 4294967295 ] EFI GPT 1: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused 2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused 3: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused # ***installboot [#nd59602a] OpenBSDでは、ブートローダの書込みに[[installboot(8)>https://man.openbsd.org/installboot]]を使用する。 -対象ディスクがMBRで区画分けされている場合、PBRと/bootを書込む # mount /dev/wd1a /mnt # installboot -r /mnt wd1 /usr/mdec/biosboot /usr/mdec/boot ↑ ↑ PBR secondary loader # umount /mnt -対象ディスクがGPTで区画分けされている場合、EFIアプリケーションをEFI Sysパーティションに\efi\boot\*.EFIとして書込む # installboot wd1 **河豚板での実装 ***配布物 -ISO版は変更しない。 -img版は、以下のように対応する。 --i386版は、Legacy BIOS/MBRにする(UEFIアプリケーションが供給されていないため)。 --amd64版は、Hybrid MBRとし、Legacy BIOS/UEFIのいずれでも使用できるようにする。 --arm64版は、UEFI/MBRにする(オリジナルのOpenBSD/arm64と同じ)。 ***usbfadm [#v5311047] [[usbfadm (USB Flashdrive ADMinistration tool)>https://github.com/ykaw/FuguIta/blob/master/rdroot/boottmp/usbfadm]] - 河豚板付属のUSBメモリ管理ツール~ 実際はUSBメモリに限らず、ストレージデバイス全般で使用可 用途 :syncコマンド|TMPFS内のデータをUSBメモリに保存する~ 読み出しは次回起動時に「起動モード3」を指定する。 :newdriveコマンド|LiveUSB版の河豚板を新規作成する~ LiveUSB版河豚板のディスクイメージを作成する UEFI/GPT対応として、newdriveコマンドに -起動方法としてLegacy/BIOS and/or UEFIを指定出来るようにする -パーティショニングとしてMBR and/or GPTを指定出来るようにする #ref(FuguIta.png,wrap,around,right,,50%) 河豚板のパーティション構成 -EFI sys ... UEFIブートの際に必要 (Legacy BIOS、データ保存専用デバイスでは不要) -OpenBSD Area --a ---河豚板のファイルツリー(実際にはlndirを使ってtmpfsにマッピング) ---デバイスをファイル保存用のみに使う場合は不要 --d ---usbfadmを使ってTMPFS内のデータをUSBメモリに保存する場合に必要 -FAT ... ユーザの指示がある場合、ディスクに空きがあれば作成 Hybrid MBRを用い、かつパーティションを全て作成する場合、右図のようになる。 #clear ***プラットフォーム依存部 - 組み合わせとデフォルト [#debb7ae9] 「プラットフォーム毎の対応表」を見るとわかるように、ブート方法、及びパーティションテーブルの選択肢はプラットフォーム毎に変わってくる。例えばi386ではUEFIが、arm64ではLegacy BIOSを選択できてもOSが対応していないため、意味がない。~ さらに、ユーザが「データ保存のみ」を選択した場合はブートローダが不要となる。以上より選択の組合せは以下のようになる。 |CENTER:|CENTER:|CENTER:|CENTER:|CENTER:|c | |Legacy BIOS|UEFI |Hybrid|non-boot&br;(data only)|h |BGCOLOR(#e0e0e0):MBR |i386, amd64|amd64, arm64|N/A |all| |BGCOLOR(#e0e0e0):GPT |N/A |amd64, arm64|N/A |all| |BGCOLOR(#e0e0e0):Hybrid|N/A |N/A |amd64 |all| |>|>|>|>|CENTER:BGCOLOR(#d0d0e0):プラットフォーム毎の対応表| -i386はEFIアプリケーションが供給されていないため、UEFI起動不可 -arm64はU-BOOT起動のため、Legacy BIOS起動不可 この複雑さを緩和するため、今回の改修では/usr/fugutia/etc/usbfadm.confというファイルを新設した。~ 例えば、i386の場合、このファイルに~ disable_uefiboot=Yes などと記述することで、UEFI起動を選択肢からはずせるようにした。 一方arm64の場合、Legacy BIOSでは起動できないので、 disable_legacyboot=Yes となる。 またarm64の場合は以下の理由から、さらなる処理が必要となる。 -基本はUEFI/MBRの構成だが、MBRブートフラグとUEFI Sysのパーティションタイプを変更する必要がある。 -OpenBSD/arm64はHDMI出力が実装されておらずシリアルコンソールとなるため、OpenBSD/arm64をインストールする際はピンヘッダにUSBシリアルなどを用意し、シリアル端末を接続する必要がある。 --FuguIta/arm64では、インストール作業の敷居を下げるため、配布物をメディアに書き込んで起動した段階でSSHによるリモートアクセスが可能となっており、シリアルI/Fを用意せずとも使用できるようになっている。 --そのため、FuguIta/arm64は他のプラットフォーム用と異なり、SSHアクセスを可能にするための各種設定や、その設定を用いて自動的に起動する設定がdパーティションに書き込まれている。~ arm64のusbfadmでnewdriveを実行してリマスタリングを行う場合、これらdパーティションに書き込まれている内容もセットアップする必要がある。 --よって、新しいusbfadmでは、newdriveコマンドの実行の最後の部分に/usr/fuguita/etc/usbfadm_postproc.shというシェルスクリプトを実行するようにした。FuguIta/arm64では、[[usbfadm_postproc.sh>https://github.com/ykaw/FuguIta/blob/master/lib/usbfadm_postproc.sh.arm64]]に上で説明したようなプラットフォーム固有の処理が書き込まれている。 ***Hybrid MBRの作成 [#j43a6d35] [[OpenBSDでのUEFI実装>#k481c6c6]]で説明したように、OpenBSDのfdiskコマンドにはディスクをMBR、GPTそれぞれのパーティションで初期化する機能を持っているが、MBRとGPTを同時に作成しHybrid MBRとする機能は持っていない。 今回のUEFI対応では、以下のような方法でHybrid MBRを実装した。 #ref(mkHybrid.png,wrap,around,left,,50%) -1. ディスクをMBRで初期化する ... fdisk -i -2a. パーティションを作成し、newfs, newfs_msdosを行う -2b. installbootを実行する ... PBRと/bootが書き込まれる -2c. MBRをddで吸出しファイルとして保存しておく -3. ディスクをGPTで初期化する ... fdisk -i -g -4a. MBRと同じ構成でGPTパーティションを作成する -4b. installbootを実行する ... EFIアプリケーションが書き込まれる -4c. 保存しておいたMBRをddでprotective MBRに上書きする #clear **開発環境 [#vf676e5a] ***初期 [#if1cdd2c] #ref(QEMU.png,wrap,around,right,,50%) 開発の初期段階では、プロセッサエミュレータ[[QEMU>https://www.qemu.org/]]を用いて開発を行った。 -最初は、手動でfdiskなどを動かして挙動を調査したり、動作の安定していないスクリプトを動かしたりするため、実ディスクに誤ったパーテション情報を書き込んでしまうおそれがあるため。 -Legacy BIOSとUEFIを頻繁に切り替えて作業をするため、実ハードウェアの作業では手間と時間が掛かるため。 QEMUは、デフォルトではLegacy BIOS (SMBIOS)として動作する。~ UEFI起動を行うためには、-biosオプションで[[OVMF (Open Virtural Machine Firmware)>https://github.com/tianocore/tianocore.github.io/wiki/OVMF]]を指定する。 #clear Legacy BIOS起動 # qemu-system-x86_64 -m 512 -hda install64.fs -hdb testhdd.img UEFI起動 # qemu-system-x86_64 -m 512 -hda install64.fs -hdb testhdd.img -bios OVMF.fd パーティション操作を行う対象となるディスクイメージファイルはrawイメージとした。~ これにより、[[vnconfig(8)>https://man.openbsd.org/vnconfig]]を用いて[[vnodeデバイス>https://man.openbsd.org/vnd]]のディスクとして実環境でも扱えるようになった。 ***中期以降 [#ecebd6df] usbfadmが一通り動作するようになってからはプラットフォーム依存部分を開発・動作確認したり、河豚板のリリースに組込む必要があるため、実ハードウェアにて開発を行った。 手順は概ね以下のとおり; #ref(RealDev.png,wrap,around,right,,50%) -河豚板のソースコードはfuguita.org上のCVSリポジトリで管理されている。ここから同じfuguita.org上でcvs checkoutし、working copyを作成する。 -各プラットフォームの開発/テスト機はworking copyをNFSマウントし、ソースコードを共有する。~ →開発者は一人だけなので、同じファイルを同時に編集することはない。 -すべてのプラットフォームでの作業が終了したら、fuguita.org上でcommitを行い、CVSリポジトリを更新する。 -プラットフォーム毎のビルドマシンでcvs updateを行い。更新を取り込む。その後、各プラットフォーム用のリリースを作成する。 -リリースの動作テストがOKなら、完成したリリースをMirror Originに転送する。 -mirror.ginzado.ne.jpとlivecd-mirror.ebug.jpの2つのミラーサーバからは一定間隔でrsyncにより接続が行われ。各ミラーとOriginとの同期が取れる。~ この時点で河豚板が一般公開されたことになる。 -一方、CVSリポジトリは、git cvsimportによりGitに変換され、git pushにより[[GitHub上のリポジトリ>https://github.com/ykaw/FuguIta]]も更新される。 #clear **その他諸々 ***OVMFの不具合(?) 開発当初、OVMFを使ってQEMUを起動したところ不具合が発生。 -/bootが河豚板のカーネルを読み込んでいる途中で、読込みが停止する。 -発生するのは河豚板のカーネルのみ、OpenBSDのGENERICカーネルや、インストール用カーネルは正常に起動 何回か試行を行っていたところ正常に起動し、その後現象の再発なし。結局原因は不明 ***installbootの不具合 installbootは、EFIアプリケーションのインストールに失敗する。 # installboot -v -r /mnt vnd0 /fuguita/usr/mdec/biosboot /fuguita/usr/mdec/boot Using /mnt as root installing bootstrap on /dev/rvnd0c using first-stage /fuguita/usr/mdec/biosboot, second-stage /fuguita/usr/mdec/boot newfs_msdos: /dev/rdfe15236c496b5df.i: No such file or directory ←デバイスの指定がDUIDで行われている installboot: unable to mount EFI System partition: No such file or directory newfs_msdosによりEFI Sysのパーティションをフォーマットする段階で、なぜかデバイス名の指定でDUIDが使われるが、ここでコマンドが異常終了するため、処理が正常に完了しない。 UEFIのセットアップはEFI SysのパーティションをFATでフォーマットし、その中にディレトクトリを掘ってEFIアプリケーションを/usr/mdecからこのディレクトリにコピーするだけなので、installbootが失敗した場合に上記処理を行うworkaroundコードを追加した。 ---- #topicpath