Hatena::Grouplinux2

Going My Linux Way このページをアンテナに追加 RSSフィード

2014年11月19日(水)

Ubuntu14.04 と GPT と UEFI

| 21:44 | Ubuntu14.04 と GPT と UEFI - Going My Linux Way を含むブックマーク はてなブックマーク - Ubuntu14.04 と GPT と UEFI - Going My Linux Way Ubuntu14.04 と GPT と UEFI - Going My Linux Way のブックマークコメント

気まぐれで、以下の変更をした。

PC の 2台の HDDパーティションテーブルを MBR から GPT に変更した。

データを一旦もう一方のHDDに退避しつつ、GParted でパーティションテーブルを gpt で作成し直してパーティションを切り直した。

起動モードも以下の URI を参考に UEFI に変更した。

ESP の容量は Ubuntuインストーラにお任せにしたら500MBくらいにしたので、512MBにした。

上の URI に書いてあるが、ESP(EFI System Partition) は fat32 でフォーマットして boot フラグを設定し、Ubuntuインストーラで 「EFI システムパーティション」 として指定すればよい。

(UEFI モードでインストーラが起動してない場合 ESP が指定できないらしい)

ついでにファイルシステムext4 から xfs に変更しようとしたけれど、UEFIモードに変更した後、起動できなかった。

そんな情報はあまりネットで書かれていないので xfs が原因ではないかも知れないけど、ルートファイルシステムext4 にした。(/home1 だけ xfs)

----

ついこの前記事にしたばかりだけど HDD の構成が変わりました。現在の HDD 構成。

1台目の HDD (/dev/sda)

デバイスファイルシステムマウントポイントラベル容量
/dev/sda1fat32/boot/efiEFI512MB
/dev/sda2ext4(未使用)sda2512MB
/dev/sda3linux-swap(スワップ)SWAP164GB
/dev/sda4ext4/ROOT164GB
/dev/sda5ext4(未使用)sda564GB
(以下、空き)----

2台目のHDD (/dev/sdb)

デバイスファイルシステムマウントポイントラベル容量
/dev/sdb1ext4(未使用)sdb1512MB
/dev/sdb2ext4(未使用)sdb2512MB
/dev/sdb3ext4(未使用)sdb364GB
/dev/sdb4ext4(未使用)sdb464GB
/dev/sdb5ext4(通常はマウントしない)ISO64GB
/dev/sdb6xfs/home1HOME1256GB
(以下、空き)----

UEFI Shell

私は、マザーボードASUS の P8B75-M を使用しているけれど、これには EFI Shell が組み込まれていない。ただし、UEFI-BIOS というセットアップ画面を退出(exit)する時に、ファイルシステムUEFIシェルを起動する、という選択項目がある。

マザーボードマニュアルには USBストレージデバイスからシェルを起動する、ようなことが書かれているがデバイスはどこでもいいらしく、1台目のHDDESPでもよい。

Ubuntu が起動した状態の /boot/efi/ に Shellx64.efi を置けばよい。

(Shellx64.efi はネットでダウンロードしてください)

この環境では Shellx64.efi の ver1 しか動作しないようである(ver2はダメみたい)。

ちなみにシェルに入ったら、

> fs0:
> cd EFI
> cd ubuntu
> grubx64

Grub が起動する。DOSライクでデバイスは接尾語「:」をつけるようだ。1番目のESPは「fs0」、2番目は「fs1」になるみたいである。また、パスの区切りは「\」である。

Grub を起動する EFI アプリケーション

fs0:\EFI\ubuntu\grubx64.efi (Ubuntu では /boot/efi/EFI/ubuntu/grubx64.efi)

なので cd でディレクトリを移動しつつ、grubx64.efi を起動すればよい。

(一気にフルパスを指定してもいい)

起動時に拡張子「.efi」はつけてもつけなくともいいようである。

ディレクトリを見るときは「ls」か「dir」で見ることができる。

シェルを使う時は、キーボード配置がどうやら 101(US英語キーボード)配置になるようである。私のキーボード(109)では、「:」はShift+「;」、「\」は「]」になるようである。

Grub のメニュー

で、困ったことが一つ起きて Grub を起動してもメニューが見えなくなった。

画面端の紫の枠だけ見えて何も見えない。

この辺は、今後の課題。おいおい調べる。

rEFInd

で、そのかわり rEFInd をインストールした。

参考は以下の URI

上の URI に書いてあるとおり、

$ sudo apt-add-repository ppa:rodsmith/refind
$ sudo apt-get update
$ sudo apt-get install refind

をしたらインストールセットアップ終了。後はシャットダウンして、再度起動すれば Mac ライクなブート画面が表示される。

主なメニューは、「Shellx64」と「Grubx64」とカーネルの直接起動。カーネルアップデートされた時メニューも追従して更新されるか、ちょっと心配。

(shellx64 については、それがある場合自動構成されるらしい)

/boot/refind_linux.conf という構成ファイルがある。

VirtualBoxUEFI

VirtualBox でも UEFI が使用できる。

が、仮想マシンの電源をオフにすると Boot Option を忘れてしまうので、(Ubutnu を仮想マシンインストールした場合)一度電源オフした後、直接起動できなくなってしまう。

VirtualBox仮想マシンには UEFI Shell が組み込まれているので、Shell が起動したら、そこから上の要領で grubx64 を起動すれば、手動では起動することができる。

直接起動させる対処法だが、デフォルトの fs0:\EFI\boot\bootx64.efi を作成する方法がある。Ubuntu で以下を実施すればよい。(Grubx64.efi を bootx64.efi としてコピー)

$ cd /boot/efi/EFI
$ sudo mkdir boot
$ sudo cp ubuntu/grubx64.efi boot/bootx64.efi

もうひとつの方法は、やはりデフォルトで起動される fs0:\EFI\boot\startup.nsh というスクリプトファイルを作成しておくことである。

$ cd /boot/efi/EFI
$ sudo mkdir boot
$ sudo vi boot/startup.nsh     # 中身は下の記述して保存

(startup.nsh の中身)

fs0:\EFI\ubuntu\grubx64

startup.nsh はスクリプトであり UEFI Shell コマンドが書けるので、好みに合わせて適当に修正するのもいい。

EFIファイルの形式

/boot/efi (fs0:) の *.efi を file で見た結果

i$ file /boot/efi -iname '*.efi' -exec file {} \;
/boot/efi/EFI/ubuntu/shimx64.efi: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows
/boot/efi/EFI/ubuntu/grubx64.efi: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows
/boot/efi/EFI/ubuntu/MokManager.efi: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows
/boot/efi/EFI/refind/grubx64.efi: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows
/boot/efi/EFI/refind/drivers_x64/ext4_x64.efi: PE32+ executable (EFI boot service driver) x86-64 (stripped to external PDB), for MS Windows
/boot/efi/EFI/refind/shimx64.efi: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows
/boot/efi/EFI/refind/MokManager.efi: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows
/boot/efi/Shellx64.efi: MS-DOS executable

PE (Windows実行ファイル形式) または DOS EXE フォーマットと表示される。

$ file /usr/lib/refit/tools/gptsync.efi 
/usr/lib/refit/tools/gptsync.efi: Universal EFI binary with 2 architectures, i386, x86_64

上のような形式と表示される efi は私のマザーボードでは動かなかった。

QEMU とOVMF でちょっと EFI Shell を試す

OVMF でちょっと EFI Shell を試す場合は以下のようにする。

qemu と ovmf がインストールされてない場合はインストール

$ sudo apt-get install qemu ovmf

で、以下のようにすると仮想マシンEFI Shell が起動する。

$ qemu-system-x86_64 -bios /usr/share/ovmf/OVMD.fd

仮想マシンは最初に PXE の構成をしようとする(失敗になるまで待たされる)ので、「Press Control-B ...」のメッセージが出たら Ctrl-B を素早く押して一旦 iPXE Shell に入って exit で抜けたほうが、EFI Shell 起動に早くたどりつく。

QEMUマウスポインタGrubされてしまった場合、Ctrl+Alt を押すと抜けられる。

参考は以下。

EFI アプリケーション

以下の記事に Hello, world の出し方が書いてあるので Ubuntu で試した。

筆者の方が Github にサンプルコードをアップされているのでダウンロードして展開。Github の README に書かれている通り make qemu 一発で試すことができた。

Ubuntu で実施する場合に必要なパッケージは多分以下でいいと思う。

$ sudo apt-get install qemu mingw-w64

ダウンロードしたサンプルコードの zip ファイルの展開と試走は以下のようにする。

$ unzip uefi-simple-master.zip
$ cd uefi-simple-master/
$ make qemu

技術的内容についてはよくわからないので私には解説不可。上の記事を参考に。

chcc

オマケ。私は以下のような bash 関数を .bashrc で定義している。

function chcc {
    case "$1" in
        --unset)        unset CC ; unset CXX ;;

        clang)          export CC=clang                     ; export CXX=clang++                    ;;
        gcc)            export CC=gcc                       ; export CXX=g++                        ;;
        distcc)         export CC=distcc                    ; export CXX=distcc                     ;;
        llvm-gcc)       export CC=llvm-gcc                  ; export CXX=llvm-g++-                  ;;
        llvm-gcc-4.8)   export CC=llvm-gcc-4.8              ; export CXX=llvm-g++-4.8               ;;
        arm)            export CC=arm-linux-gnueabi-gcc     ; export CXX=arm-linux-gnueabi-g++      ;;
        armhf)          export CC=arm-linux-gnueabihf-gcc   ; export CXX=arm-linux-gnueabihf-g++    ;;
        mingw64)        export CC=x86_64-w64-mingw32-gcc    ; export CXX=x86_64-w64-mingw32-g++     ;;

                    # ex) chcc -m32
        -*)         [ "$CC" = "" ] || export CC="$CC $1"
                    ;;

        /*)         local option=$( echo "$1" | sed 's/./-/' )
                    [ "$CXX" = "" ] || export CXX="$CC $option"
                    ;;

                    # ex) chcc +ccache
        +*)         local prefix=$( echo "$1" | sed 's/.//' )
                    [ "$CC"  = "" ] || export CC="$prefix $CC"
                    [ "$CXX" = "" ] || export CXX="$prefix $CXX"
                    ;;

        "")         ;;

        *)          echo "invalid argument: '$1'" 1>&2 ;;
    esac

    echo "CC=$CC"
    echo "CXX=$CXX"
}

使い方の例は以下。

$ chcc gcc        # <-- 環境変数 CC と CXX を gcc と g++ に変更
$ chcc clang      # <--    〃              を clang と clang++ に変更
$ chcc armhf      # <--    〃              を arm-linux-gnueabihf-gcc と arm-linux-gnueabihf-g++ に変更
$ chcc mingw64    # <--    〃              を x86_64-w64-mingw32-gcc と x86_64-w64-mingw32-g++ に変更
$ chcc -m32       # <-- 環境変数 CC の後ろに " -m32" を追記
$ chcc +ccache    # <-- 環境変数 CC と CXX の前に "ccache " を追記
$ chcc --unset    # <-- 環境変数 CC と CXX を unset する
$ chcc            # <-- 環境変数 CC と CXX を表示する

ブラッシュアップしたらどっかにアップしよう。。。

hello-c

もひとつオマケ。

これはシェルスクリプト。作りの工夫は全く無いが結構使う。

~/bin/hello-c

#!/bin/bash

/bin/cat <<HELLO_C
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    printf("hello, world\n");

    return EXIT_SUCCESS;
}
HELLO_C

Gnu-efi で hello, world

さらに、Gnu-efi で hello,world を出力するプログラムを書く。

以下の URI を参考にした。(書いてある通りやった)

Ubuntu では、以下のパッケージをインストールする必要がある(と思う)。

$ sudo apt-get install qemu ovmf gnu-efi

プログラムソースは上の URI のページに書いてある通り。ファイル名は main.c にする。

#include <efi.h>
#include <efilib.h>

EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
   InitializeLib(ImageHandle, SystemTable);
   Print(L"Hello, world!\n");

   return EFI_SUCCESS;
}

Makefile は、LIB と EFILIB を Ubuntu 環境に合わせて修正した。利便性のため .PHONY ターゲットと clean ターゲットも追加した。

ARCH            = $(shell uname -m | sed s,i[3456789]86,ia32,)

OBJS            = main.o
TARGET          = hello.efi

EFIINC          = /usr/include/efi
EFIINCS         = -I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol
#LIB             = /usr/lib64
#EFILIB          = /usr/lib64/gnuefi
LIB             = /usr/lib
EFILIB          = $(LIB)
EFI_CRT_OBJS    = $(EFILIB)/crt0-efi-$(ARCH).o
EFI_LDS         = $(EFILIB)/elf_$(ARCH)_efi.lds

CFLAGS          = $(EFIINCS) -fno-stack-protector -fpic \
                              -fshort-wchar -mno-red-zone -Wall
ifeq ($(ARCH),x86_64)
    CFLAGS += -DEFI_FUNCTION_WRAPPER
endif

LDFLAGS         = -nostdlib -znocombreloc -T $(EFI_LDS) -shared \
                  -Bsymbolic -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS)

.PHONY: all clean

all: $(TARGET)

hello.so: $(OBJS)
    ld $(LDFLAGS) $(OBJS) -o $@ -lefi -lgnuefi

%.efi: %.so
    objcopy -j .text -j .sdata -j .data -j .dynamic \
            -j .dynsym  -j .rel -j .rela -j .reloc \
            --target=efi-app-$(ARCH) $^ $@

clean:
    ${RM} *.o *.so *.efi

ビルドは make 一発。Cコンパイラgcc でも clang でも llvm-gcc でもよい。

$ make

カレントディレクトリに hello.efi ができる。

利便性のため以下のようなエイリアスを切る。

$ alias ovmf='qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd'

で、QEmu を起動。

$ ovmf -hda fat:.

QEmu 上で UEFI シェルが起動したら、上の URI に書いてある通り実行すると Hello, world が表示される。

Shell> fs0:

fs0:\> hello.efi
Hello, world!
トラックバック - http://linux2.g.hatena.ne.jp/lnznt/20141119