Upgrade to Pro — share decks privately, control downloads, hide ads and more …

カーネルVM関西 7回目 発表資料

orumin
December 06, 2014

カーネルVM関西 7回目 発表資料

忙しい(言い訳)という事でした.カーネルVM関西 7回目の発表資料です. 発表で口頭だった部分をちゃんと書きました.

orumin

December 06, 2014
Tweet

More Decks by orumin

Other Decks in Technology

Transcript

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

    View full-size slide


  2. orumin (@kotatsu_mi)

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

    View full-size slide


  3. UEFI!

    BIOS もう捨てよう

    GRUB も要らない (UEFI のブートセレクタで良
    い )

    View full-size slide


  4. UEFI とは ?

    UEFI Advent Calender 2014 参照 !

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

    View full-size slide

  5. ● 本題,ブートローダー

    View full-size slide


  6. UEFI の基本スペック

    C 言語な SDK , EDK(NTDDK 風味 )

    FAT32 なシステムパーティションを高レイヤに
    読み書きできる

    stdio.h もつかえるぜ !

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    gnu-efi でお茶を濁す

    View full-size slide


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

    View full-size slide


  11. Binutils とこれをビルドする必要……
    ● 今はなくなってる (?)

    apt-get install gnu-efi

    pacman -S gnu-efi-libs

    View full-size slide


  12. #include

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

    View full-size slide


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

    View full-size slide

  14. Makefile

    ARCH = x86_64

    EFIROOT = /usr

    HDDRROOT = $(EFIROOT)/include/efi

    INCLUDES = -I. -I$(HDDRROOT) -I$(HDDRROOT)/
    $(ARCH) -I$(HDDRROOT)/protocol

    EFIROOT は configure で /usr/local とかにインスト
    ール先指定してたらそこに変更

    View full-size slide

  15. 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)

    View full-size slide

  16. Makefile

    CFLAGS は -fPIC と -fshort-wchar ,あと x86_64
    ならば -DEFI_FUNCTION_WRAPPER を忘れなけ
    ればあとは好きなようにして大丈夫

    CPP にも FLAG を設定する必要アリ

    View full-size slide

  17. 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)

    View full-size slide

  18. Makefile
    ● リンカスクリプト,スタートアップルーチン,
    共に gnu-efi のものを使うようにする

    -nostdlib -shared
    ● 先にも書いたように -lefi と -lgnuefi で gnu-efi
    をリンク

    View full-size slide

  19. Makefile

    prefix =

    CC = $(prefix)gcc

    AS = $(prefix)as

    LD = $(prefix)ld

    AR = $(prefix)ar

    RANLIB = $(prefix)ranlib

    OBJCOPY = $(prefix)objcopy

    View full-size slide

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

    View full-size slide

  21. 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 $@

    View full-size slide

  22. Makefile

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

    View full-size slide

  23. Makefile
    ● 最後 *.efi を作る時はちょっと特殊

    so なファイルから objcopy で, ELF のセクショ
    ンの内必要な部分だけを取り出す

    View full-size slide

  24. ソースコード ( 前知識 )

    efi.h , gnuefi.h をインクルード
    ● エントリポイントは efi_main(EFI_HANDLE,
    EFI_SYSTEM_TABLE)

    UEFI は Protocol という API を使う
    ● 基本, OpenProtocol で Protocol の Handle を開いて,必
    要な Protocol の関数ポインタ群を対応する Protocol 構造
    体に詰めて,そこから Protocol の関数を呼ぶ

    View full-size slide

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

    View full-size slide

  26. ソースコード ( 前知識 )

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

    View full-size slide

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

    https://speakerdeck.com/orumin/driver

    View full-size slide

  28. ソースコード

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

    View full-size slide

  29. ソースコード

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

    View full-size slide

  30. ソースコード

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

    View full-size slide

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

    View full-size slide

  32. ソースコード

    rEFInd のコードがファイルシステムドライバの
    参考になります

    https://github.com/falstaff84/rEFInd/tree/master
    /filesystems

    View full-size slide

  33. ソースコード

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

    View full-size slide

  34. ソースコード

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

    View full-size slide

  35. ソースコード
    ● というわけにいかない.

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

    View full-size slide

  36. ソースコード

    StartImage を実行する前に……

    View full-size slide

  37. ソースコード

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

    View full-size slide

  38. ソースコード

    LoadedImage->LoadOptions = Options;

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

    View full-size slide

  39. ソースコード

    LoadOptions に渡してる Options

    CHAR16 *Options = L"root=/dev/sda2
    rootfstype=btrfs rw quiet splash";
    ● こんな感じで CHAR16 型の文字列を指定すれば
    良い

    CHAR16 型なので文字列リテラルのプレフィク
    スに L を付けておく ← 大事

    View full-size slide

  40. ソースコード
    ● 今度こそブートローダーとしての仕事完了 !

    LoadOptions を指定してから, StartImage!
    ● その後の 2,3 行は後処理コード

    View full-size slide

  41. ブートローダー

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

    View full-size slide

  42. ブートローダー
    ● 自作 OS でブートローダー書く時点で満足とい
    う話はおおかった.

    UEFI ではもはやそんな事はないよ !
    ● 自作 OS のカーネルも生バイナリつくって EFI
    システムパーティションに配置するだけで良い
    ● とってもラク !

    View full-size slide

  43. ● ちなみにこの発表は先ほどコーディングしたブ
    ートローダーを用いて起動した ArchLinux で行
    ないました.

    CHAR16 の変数を作るのに L を付けわすれてハ
    マった以外はバグが出ず,実行は一発で成功
    ● なんて簡単なんだ

    View full-size slide

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

    View full-size slide