Slide 1

Slide 1 text

忙しくて時間がなくなったので 10 分でブートローダーをつくっ た話で茶濁す

Slide 2

Slide 2 text

● orumin (@kotatsu_mi) ● Google Summer of Code 2014 で OSv に Ruby を移植してました. ● 以前 UEFI 遊びしてた

Slide 3

Slide 3 text

● UEFI! ● BIOS もう捨てよう ● GRUB も要らない (UEFI のブートセレクタで良 い )

Slide 4

Slide 4 text

● UEFI とは ? ● UEFI Advent Calender 2014 参照 ! ● http://qiita.com/advent-calendar/2014/uefi ● 質問でも BIOS なブートローダーでも u-boot で も良いのでそれっぽいネタ大募集中

Slide 5

Slide 5 text

● 本題,ブートローダー

Slide 6

Slide 6 text

● UEFI の基本スペック ● C 言語な SDK , EDK(NTDDK 風味 ) ● FAT32 なシステムパーティションを高レイヤに 読み書きできる ● stdio.h もつかえるぜ !

Slide 7

Slide 7 text

● でも今回は EDK つかいません

Slide 8

Slide 8 text

● 表題通り時間がなかったので

Slide 9

Slide 9 text

● 表題通り時間がなかったので ● gnu-efi でお茶を濁す

Slide 10

Slide 10 text

● gnu-efi: ● 名前が GNU なのに License は BSDL ● なぜ ???

Slide 11

Slide 11 text

● Binutils とこれをビルドする必要…… ● 今はなくなってる (?) ● apt-get install gnu-efi ● pacman -S gnu-efi-libs

Slide 12

Slide 12 text

● #include ● #include ● リンカオプションに -lefi -lgnuefi ● あとはコード書くだけ !

Slide 13

Slide 13 text

● https://github.com/orumin/SimpleMyLoader

Slide 14

Slide 14 text

Makefile ● ARCH = x86_64 ● EFIROOT = /usr ● HDDRROOT = $(EFIROOT)/include/efi ● INCLUDES = -I. -I$(HDDRROOT) -I$(HDDRROOT)/ $(ARCH) -I$(HDDRROOT)/protocol ● EFIROOT は configure で /usr/local とかにインスト ール先指定してたらそこに変更

Slide 15

Slide 15 text

Makefile ● CFLAGS = -O2 -fPIC -Wall -fshort-wchar -fno-strict-aliasing -fno-merge-constants -mno- red-zone ● ifeq ($(ARCH),x86_64) ● CFLAGS += -DEFI_FUNCTION_WRAPPER ● endif ● ● CPPFLAGS = -DCONFIG_$(ARCH)

Slide 16

Slide 16 text

Makefile ● CFLAGS は -fPIC と -fshort-wchar ,あと x86_64 ならば -DEFI_FUNCTION_WRAPPER を忘れなけ ればあとは好きなようにして大丈夫 ● CPP にも FLAG を設定する必要アリ

Slide 17

Slide 17 text

Makefile ● CRTOBJS= $(EFIROOT)/lib/crt0-efi-$(ARCH).o ● FORMAT = efi-app-$(ARCH) ● INSTALL = install ● LDFLAGS = -nostdlib ● LDSCRIPT = $(EFIROOT)/lib/elf_$(ARCH)_efi.lds ● LDFLAGS += -T $(LDSCRIPT) -shared -Bsymbolic -L$(EFIROOT)/lib $(CRTOBJS) ● LOADLIBS = -lefi -lgnuefi $(shell $(CC) -print- libgcc-file-name)

Slide 18

Slide 18 text

Makefile ● リンカスクリプト,スタートアップルーチン, 共に gnu-efi のものを使うようにする ● -nostdlib -shared ● 先にも書いたように -lefi と -lgnuefi で gnu-efi をリンク

Slide 19

Slide 19 text

Makefile ● prefix = ● CC = $(prefix)gcc ● AS = $(prefix)as ● LD = $(prefix)ld ● AR = $(prefix)ar ● RANLIB = $(prefix)ranlib ● OBJCOPY = $(prefix)objcopy

Slide 20

Slide 20 text

Makefile ● もし binutils や gcc を UEFI アプリケーションの ためにビルドしてどこか特定のディレクトリに 入れたなら prefix を指定.

Slide 21

Slide 21 text

Makefile ● %.efi: %.so ● $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \ ● -j .rela -j .reloc --target=$(FORMAT) $*.so $@ ● ● %.so: %.o ● $(LD) $(LDFLAGS) $^ -o $@ $(LOADLIBS) ● ● %.o: %.c ● $(CC) $(INCLUDES) $(CFLAGS) $(CPPFLAGS) -c $< -o $@

Slide 22

Slide 22 text

Makefile ● C のソースをオブジェクトファイルにするとこ ろ,オブジェクトファイルを so なリロケータ ブルバイナリにするのには先に指定したフラグ を使う. ● とりたてて変わったことはしない.

Slide 23

Slide 23 text

Makefile ● 最後 *.efi を作る時はちょっと特殊 ● so なファイルから objcopy で, ELF のセクショ ンの内必要な部分だけを取り出す

Slide 24

Slide 24 text

ソースコード ( 前知識 ) ● efi.h , gnuefi.h をインクルード ● エントリポイントは efi_main(EFI_HANDLE, EFI_SYSTEM_TABLE) ● UEFI は Protocol という API を使う ● 基本, OpenProtocol で Protocol の Handle を開いて,必 要な Protocol の関数ポインタ群を対応する Protocol 構造 体に詰めて,そこから Protocol の関数を呼ぶ ●

Slide 25

Slide 25 text

ソースコード ( 前知識 ) ● 本来は,たとえば BS->OpenProtocol() みたいに Protocol から直接関数呼び出し ● しかし Microsoft な ABI……

Slide 26

Slide 26 text

ソースコード ( 前知識 ) ● gnu-efi の場合 uefi_call_wrapper() で第一引数に UEFI の Protocol の関数ポインタ指定してつかう ● なお,ソースコード中の BS は BootServices で, EFI_BOOT_SERVICES というブートローダーに 必要なプロトコルのハンドラ.これは OpenProtocol とかしなくても最初からグローバルの BS という変 数に詰めてある ( でないと OpenProtocol が呼べな い )

Slide 27

Slide 27 text

ソースコード ( 前知識 ) ● なお EDK での前準備については 過去発表を参照されたし ● https://speakerdeck.com/orumin/driver

Slide 28

Slide 28 text

ソースコード ● efi_main の引数の ImageHandle , SystemTable ● 前者は自分自身のハンドル,後者はデバイスへ のハンドルやパスを手繰るための親のハンドル や関数が入ってる

Slide 29

Slide 29 text

ソースコード ● uefi_call_wrapper(BS->OpenProtocol, 6, ImageHandle, &LoadedImageProtocol, &LoadedImageParent, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); ● まず自分自身のハンドルから,既に読み込まれ てるメモリイメージ ( 実行バイナリイメージ ) についての Protocol を取得

Slide 30

Slide 30 text

ソースコード ● Path = FileDevicePath(LoadedImageParent- >DeviceHandle, L"\\vmlinuz"); ● 先ほど取得した Protocol ハンドルにあるデバイ スへのハンドルと, Linux カーネルへのパスを 引数に,カーネルのファイルへのデバイスパス を取得

Slide 31

Slide 31 text

ソースコード ● この時指定するパスは, EFI のシステムパーテ ィションのパス. ● カーネルもそこに置かないといけない ● もし普通に ext4 とかに置かれたものを読みた ければ, ext4 について EFI_SIMPLE_FILESYSTEM プロトコルを提供でき るように EFI Driver を書くしかない

Slide 32

Slide 32 text

ソースコード ● rEFInd のコードがファイルシステムドライバの 参考になります ● https://github.com/falstaff84/rEFInd/tree/master /filesystems

Slide 33

Slide 33 text

ソースコード ● uefi_call_wrapper(BS->LoadImage, 6, FALSE, ImageHandle, Path, NULL, 0, &Image); ● 取得したデバイスパスをつかって,新たにメモ リイメージ ( 実行バイナリイメージ ) をメモリ にロード

Slide 34

Slide 34 text

ソースコード ● uefi_call_wrapper(BS->StartImage, 3, Image, NULL, NULL); ● そして読み込んだイメージを起動 ! 完了 !

Slide 35

Slide 35 text

ソースコード ● というわけにいかない. ● Linux の起動に root のパーティションとか指定 するカーネルコマンドライン ( 起動オプショ ン ) を指定したい

Slide 36

Slide 36 text

ソースコード ● StartImage を実行する前に……

Slide 37

Slide 37 text

ソースコード ● uefi_call_wrapper(BS->OpenProtocol, 6, Image, &LoadedImageProtocol, &LoadedImage, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); ● また LoadedImage の Protocol ● 今度は自分自身のイメージではなく先ほど読み込 んだカーネルのイメージについて Protocol を取得

Slide 38

Slide 38 text

ソースコード ● LoadedImage->LoadOptions = Options; ● LoadedImage->LoadOptionsSize = (StrLen(LoadedImage->LoadOptions)+1) * sizeof(CHAR16); ● この Protocol のメンバに起動オプションのため のフィールドがあるので指定するだけ

Slide 39

Slide 39 text

ソースコード ● LoadOptions に渡してる Options ● CHAR16 *Options = L"root=/dev/sda2 rootfstype=btrfs rw quiet splash"; ● こんな感じで CHAR16 型の文字列を指定すれば 良い ● CHAR16 型なので文字列リテラルのプレフィク スに L を付けておく ← 大事

Slide 40

Slide 40 text

ソースコード ● 今度こそブートローダーとしての仕事完了 ! ● LoadOptions を指定してから, StartImage! ● その後の 2,3 行は後処理コード

Slide 41

Slide 41 text

ブートローダー ● initrd 対応サボってるのでファイルシステムのドラ イバが組み込みじゃなくてモジュールになっちゃっ てる ( よくあるディストリビューション標準の ) カ ーネルは起動時に root mount でカーネルパニックし ます. ● ハードコードしてるカーネルのファイルパスと起動 オプションは環境に合わせると誰でも使えると思 う.

Slide 42

Slide 42 text

ブートローダー ● 自作 OS でブートローダー書く時点で満足とい う話はおおかった. ● UEFI ではもはやそんな事はないよ ! ● 自作 OS のカーネルも生バイナリつくって EFI システムパーティションに配置するだけで良い ● とってもラク !

Slide 43

Slide 43 text

● ちなみにこの発表は先ほどコーディングしたブ ートローダーを用いて起動した ArchLinux で行 ないました. ● CHAR16 の変数を作るのに L を付けわすれてハ マった以外はバグが出ず,実行は一発で成功 ● なんて簡単なんだ

Slide 44

Slide 44 text

● さいごに : ● みんなもスライドはちゃんと用意しましょう