5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

BitVisor のインストール(USBメモリ)

Last updated at Posted at 2015-12-15

#はじめに
BIOS環境やEFI環境(64bit)でBitVisorを起動できるUSBメモリの作り方です。

USBメモリから先に起動する設定にしておくことで、USBメモリを差し込むだけで BitVisor を起動できます。
また、1つのUSBメモリでBIOS環境とEFI環境の両方に対応しており、Linux, Windows, Mac で BitVisor を起動できます。

USBメモリにすることでローカルディスクの変更が不要になるほか、
必要なときだけBitVisorを起動したり不要なときに外したりが簡単にできます。

#準備
###USBメモリ
内容が壊れてもいいUSBメモリを用意します。

###コンパイル済みバイナリ
コンパイル済みの BitVisor のバイナリ(bitvisor.efl)やブートローダ(bootloaderusb, loadvmm.efi)、インストール用スクリプト(install.sh)を用意します。

BitVisor Advent Calendar 20158日目の記事に書いた方法でコンパイルしてください。

説明の都合上、上記のファイルがホームディレクトリにコピーしてあることとします。

$ cd
$ cp bitvisor/bitvisor.elf .
$ cp bitvisor/boot/loader/bootloaderusb .
$ cp bitvisor/boot/loader/install.sh .
$ cp bitvisor/uefi-loader/loadvmm .

コンパイル環境がない場合や面倒な場合は、www.bitvisor.org においてある Nightly Build を使うことも出来ます。

$ wget http://www.bitvisor.org/bitvisor/bitvisor.elf
$ wget http://www.bitvisor.org/bitvisor/boot/loader/bootloaderusb
$ wget http://www.bitvisor.org/bitvisor/boot/loader/install.sh
$ wget http://www.bitvisor.org/bitvisor/boot/uefi-loader/loadvmm.efi

#Linux
##基本編
###USBメモリのデバイス名取得

USBメモリのデバイス名(sd?)を取得します。

dmseg コマンドで確認したり、以下の様なコマンドを入力するなどして、間違えないように十分に確認します。

$ cd /sys/block; for dev in sd?; do echo $dev:; cat $dev/device/vendor $dev/device/model; echo $(($(cat $dev/size)/2048))MiB; done; cd

デバイス名が間違っているとシステムを破壊する危険性がありますので、十分に注意してください。

ここではUSBメモリのデバイス名が sdb であると仮定します。

###パーティションの再作成

BIOS環境で起動する bitvisor.elf とブートローダを格納する領域を確保します。
パーティションを切り直して、先頭パーティションの前に128MiBの空き領域を作成します。

$ sudo dd if=/dev/zero of=/dev/sdb bs=512 count=1
$ sudo parted /dev/sdb mklabel msdos
$ sudo parted /dev/sdb "mkpart p fat32 262144s -0"
$ sudo mkfs.vfat -c /dev/sdb1

###EFI環境のためのインストール

FAT 領域をマウントして、loadvmm.efi と bitvisor.elf を EFI 環境で起動できるように書き込みます。

$ mkdir /tmp/mnt
$ sudo mount /dev/sdb1 /tmp/mnt
$ sudo mkdir -p /tmp/mnt/EFI/BOOT
$ sudo cp ./loadvmm.efi /tmp/mnt/EFI/BOOT/BOOTX64.EFI
$ sudo cp ./bitvisor.elf /tmp/mnt/EFI/BOOT/BITVISOR.ELF
$ sudo umount /tmp/mnt
$ rmdir /tmp/mnt

###BIOS環境のためのインストール

bitvisor.elf とブートローダ(bootloaderusb)をUSBメモリの先頭部分に書き込みます。

$ sudo ./install.sh -f /dev/sdb 0 8 ./bootloaderusb ./bitvisor.elf

2回目以降に書き込むときは、-f オプションは不要です。

###後処理
念のため sync してから eject します。

$ sync
$ eject /dev/sdb

##発展編

基本編のやり方だと、bitvisor.elf がUSBメモリの先頭とFATパーティションとの2箇所に格納されていて無駄な感じがするので、これらを一つにまとめてみます。

###USBメモリの準備

普通のフォーマット済みUSBメモリを準備します。基本編でパーティションを変更した場合は、下記のようにして元に戻しておきます。

$ sudo dd if=/dev/zero of=/dev/sdb bs=512 count=1
$ sudo parted /dev/sdb mklabel msdos
$ sudo parted /dev/sdb "mkpart p fat32 2048s -0"
$ sudo mkfs.vfat -c /dev/sdb1

###コピースクリプトの作成

bitvisor.elf をコピーするためのスクリプトを作成します。

以下のスクリプトを copyelf.sh という名前で保存してください。これは、install.sh から抜き出したものです。

copyelf.sh
getbsssize(){
	bsssize=0
	if ! dd if="$elf" bs=1 skip=0 count=4 | od |
		grep -q '^0000000 042577 043114'
	then
		echo "ELF header not found in \`$elf'." >&2
		exit 1
	fi
	set -- $(dd if="$elf" bs=1 skip=28 count=4 | od -i)
	phoff=$2
	set -- $(dd if="$elf" bs=1 skip=42 count=2 | od -i)
	phentsize=$2
	set -- $(dd if="$elf" bs=1 skip=44 count=2 | od -i)
	phnum=$2
	while test $phnum -gt 0
	do
		phnum=$(($phnum-1))
		set -- $(($phoff+$phentsize*$phnum))
		set -- $(($1+16)) $(dd if="$elf" bs=1 skip=$1 count=4 | od -i)
		case "$3" in
		1)	;;
		*)	continue;;
		esac
		set -- $(($1+4)) $(dd if="$elf" bs=1 skip=$1 count=4 | od -i)
		set -- "$3" $(dd if="$elf" bs=1 skip=$1 count=4 | od -i)
		bsssize=$(($bsssize+$3-$1))
	done
}

elf=$1
device=$2

getbsssize

{
	cat "$1"
	shift
	if test $bsssize -ge 512
	then
		dd if=/dev/zero count=$(($bsssize/512))
		bsssize=$(($bsssize%512))
	fi
	if test $bsssize -gt 0
	then
		dd if=/dev/zero bs=1 count=$bsssize
	fi
} | dd of="$device" conv=notrunc

###EFI環境のためのインストール

以下のようにしてFAT領域をマウントしてから、loadvmm.efi と bitvisor.elf をコピーします。

$ mkdir /tmp/mnt
$ sudo mount /dev/sdb1 /tmp/mnt
$ sudo mkdir -p /tmp/mnt/EFI/BOOT
$ sudo cp ./loadvmm.efi /tmp/mnt/EFI/BOOT/BOOTX64.EFI
$ sudo sh copyelf.sh ./bitvisor.elf /tmp/mnt/EFI/BOOT/BITVISOR.ELF

###bitvisor.elf の場所(LBA)の特定

bitvisor.elf がどこに格納されたか LBA を特定します。

$ sudo hdparm --fibmap /tmp/mnt/EFI/BOOT/BITVISOR.ELF 

出力結果は、例えば以下のようになります。

/tmp/mnt/EFI/BOOT/BITVISOR.ELF:
 filesystem blocksize 512, begins at LBA 2048; assuming 512 byte sectors.
 byte_offset  begin_LBA    end_LBA    sectors
           0       2728      49135      46408

begin_LBA の項目に書いてある値(2728)がこの場合の LBA の値になります。
複数の行が表示されるときは、フラグメンテーションを起こしていますので、うまく動作しません。デフラグしてください。

###BIOS環境のためのインストール

bitvisor.elf の LBA の値を install.sh の3番めの引数に指定して実行します。
既に bitvisor.elf が書き込まれているところに上書きするので、-f オプションは不要です。

$ sudo ./install.sh /dev/sdb 0 2728 ./bootloaderusb ./bitvisor.elf

###後処理

アンマウントして、USBメモリを抜きます。

$ sudo umount /tmp/mnt
$ rmdir /tmp/mnt
$ sync
$ sudo eject /dev/sdb

Windows

BitVisor Advent Calendar 20158日目の記事に書いた方法で MSYS2 をインストールしてあることが前提です。
Linuxと違い、いきなり発展編でやります。

ここでは USB メモリのドライブ名は D: ドライブであると仮定します。

###コピースクリプトの作成

bitvisor.elf をコピーするためのスクリプトを作成します。

以下のスクリプトを copyelf.sh という名前で保存してください。これは install.sh から抜き出したものです。

copyelf.sh
getbsssize(){
	bsssize=0
	if ! dd if="$elf" bs=1 skip=0 count=4 | od |
		grep -q '^0000000 042577 043114'
	then
		echo "ELF header not found in \`$elf'." >&2
		exit 1
	fi
	set -- $(dd if="$elf" bs=1 skip=28 count=4 | od -i)
	phoff=$2
	set -- $(dd if="$elf" bs=1 skip=42 count=2 | od -i)
	phentsize=$2
	set -- $(dd if="$elf" bs=1 skip=44 count=2 | od -i)
	phnum=$2
	while test $phnum -gt 0
	do
		phnum=$(($phnum-1))
		set -- $(($phoff+$phentsize*$phnum))
		set -- $(($1+16)) $(dd if="$elf" bs=1 skip=$1 count=4 | od -i)
		case "$3" in
		1)	;;
		*)	continue;;
		esac
		set -- $(($1+4)) $(dd if="$elf" bs=1 skip=$1 count=4 | od -i)
		set -- "$3" $(dd if="$elf" bs=1 skip=$1 count=4 | od -i)
		bsssize=$(($bsssize+$3-$1))
	done
}

elf=$1
device=$2

getbsssize

{
	cat "$1"
	shift
	if test $bsssize -ge 512
	then
		dd if=/dev/zero count=$(($bsssize/512))
		bsssize=$(($bsssize%512))
	fi
	if test $bsssize -gt 0
	then
		dd if=/dev/zero bs=1 count=$bsssize
	fi
} | dd of="$device" conv=notrunc

###EFI環境のためのインストール
以下のようにしてFAT領域に loadvmm.efi と bitvisor.elf をコピーします。

$ mkdir -p d:/EFI/BOOT
$ cp ./loadvmm.efi d:/EFI/BOOT/BOOTX64.EFI
$ sh ./copyelf.sh ./bitvisor.elf d:/EFI/BOOT/BITVISOR.ELF

###bitvisor.elf の場所(LBA)の特定

以下のプログラムを getlba.c という名前で保存してください。

getlba.c
#include <stdio.h>
#include <string.h>
#include <windows.h>

int main(int argc, char *argv[])
{
    BOOL success = TRUE;
    DWORD byte, spc, bps;
    TCHAR vol[MAX_PATH], drive[] = "\\\\.\\D:", fs[MAX_PATH];
    UCHAR bs[512];

    success &= GetVolumePathName(argv[1], vol, sizeof(vol));
    success &= GetVolumeInformation(vol, NULL, 0, NULL, NULL, NULL,
                                    fs, sizeof(fs));
    success &= (strncmp(fs, "FAT", 3) == 0);
    drive[4] = vol[0];
    HANDLE vhandle = CreateFile(drive, GENERIC_READ,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL, OPEN_EXISTING, 0, NULL);
    success &= ReadFile(vhandle, &bs, sizeof(bs), &byte, NULL);
    success &= (byte == 512);
    ULONG sz16 = *(USHORT*)&bs[22], sz32 = *(ULONG*)&bs[36];
    ULONG sz = sz16 != 0 ? sz16 : sz32;
    ULONG start = *(USHORT*)&bs[14] + sz * bs[16]
        + (*(USHORT*)&bs[17] * 32) / 512;
    HANDLE handle = CreateFile (argv[1], GENERIC_READ,
                                FILE_SHARE_READ|FILE_SHARE_WRITE,
                                NULL, OPEN_EXISTING, 0, NULL);
    STARTING_VCN_INPUT_BUFFER vcn = {0};
    RETRIEVAL_POINTERS_BUFFER rp;
    success &= DeviceIoControl(handle, FSCTL_GET_RETRIEVAL_POINTERS,
                               &vcn, sizeof(vcn), &rp, sizeof(rp),
                               NULL, NULL);
    success &= GetDiskFreeSpace(vol, &spc, &bps, NULL, NULL);
    byte = GetFileSize(handle, NULL);
    success &= (byte <= rp.Extents[0].NextVcn.QuadPart * spc * bps);
    LONGLONG loff = rp.Extents[0].Lcn.QuadPart * spc * bps;
    struct {
        ULONG    NumberOfPhysicalOffsets;
        struct {
            ULONG    DiskNumber;
            LONGLONG Offset;
        };
    } poff;
    success &= DeviceIoControl(vhandle, IOCTL_VOLUME_LOGICAL_TO_PHYSICAL,
                               &loff, sizeof(loff), &poff, sizeof(poff),
                               NULL, NULL);
    if (success)
        printf("LBA: %d\n", start + poff.Offset / bps);
}

gcc でコンパイルして、BITVISOR.ELF のパス名を指定して実行します。このとき、MSYS のパス名("/d/" など)を使わないでください。

$ gcc getlba.c -o getlba
$ ./getlba.exe d:/EFI/BOOT/BITVISOR.ELF

エラーが発生しなければ、LBAの値が表示されます。何も表示されなければ、何らかのエラーが発生していますので、getlba.c を適当に書き換えてエラーの原因を突き止めてください。

LBA: 2728

###デバイス名の特定

まずはUSBメモリのデバイスID(\\.\PhysicalDrive?)を特定します

$ wmic diskdrive list brief

結果は例えば以下の様になります。この場合、USBメモリのデバイスIDは \\.\PHYSICALDRIVE1 です。

Caption                            DeviceID            Model                              Partitions  Size          
SAMSUNG MZHPU512HCGL-00004         \\.\PHYSICALDRIVE0  SAMSUNG MZHPU512HCGL-00004         3           512105932800  
Ut165 USB2FlashStorage USB Device  \\.\PHYSICALDRIVE1  Ut165 USB2FlashStorage USB Device  1           1003484160    

/dev の下にあるデバイスIDに対応するデバイスファイルを見つけます。\\.\PHYSICALDRIVE0 なら /dev/sda、\\.\PHYSICALDRIVE1 なら /dev/sdb になるはずです。

ここではUSBメモリのデバイスファイル名が /dev/sdb であると仮定します。

デバイス名が間違っているとシステムを破壊する危険性がありますので、十分に注意してください。

###BIOS環境のためのインストール

特定したデバイスファイルと LBA を指定して、以下のようにインストールします。
既に bitvisor.elf が書き込まれているところに上書きするので、-f オプションは不要です。
ELFヘッダが見つからないので -f オプションをつけろと言われた場合は LBA が間違っている可能性があります。

$ ./install.sh /dev/sdb 0 2728 ./bootloaderusb ./bitvisor.elf

###後処理

Windows の Explorer でUSBメモリの取り外し処理をしてからUSBメモリを取り外します。

#Mac
###USBメモリのデバイス名取得
USBメモリを挿します。Mac ではUSBメモリのボリューム名と同じ名前のディレクトリが /Volumes 以下に作成されて、そこにUSBメモリの内容がマウントされます。

$ ls /Volumes
BITVISOR	Macintosh HD

ここでは、ボリューム名は BITVISOR であるとします。

次に、デバイス名を特定します。次のように df コマンドの引数にマウントされたディレクトリを指定して実行してください。

$ df /Volumes/BITVISOR
Filesystem   512-blocks  Used Available Capacity iused ifree %iused  Mounted on
/dev/disk2s1   62554112 23488  62530624     1%       0     0  100%   /Volumes/BITVISOR

"Filesystem" の項目にある /dev/disk2s1 から s1 を取り除いたものがデバイス名です。ここでは disk2 とします。

デバイス名が間違っているとシステムを破壊する危険性がありますので、十分に注意してください。

必要なら以下のようにしてUSBメモリを再フォーマットしておきます。

diskutil eraseDisk FAT32 BITVISOR MBR disk2

###コピースクリプトの作成

bitvisor.elf をコピーするためのスクリプトを作成します。

以下のスクリプトを copyelf.sh という名前で保存してください。これは install.sh から抜き出したものを Mac 向けに少し修正したものです。

copyelf.sh
getbsssize(){
    bsssize=0
    if ! dd if="$elf" bs=1 skip=0 count=4 | od |
        egrep '^0000000\s+042577\s+043114'
    then
        echo "ELF header not found in \`$elf'." >&2
        exit 1
    fi
    set -- $(dd if="$elf" bs=1 skip=28 count=4 | od -i)
    phoff=$2
    set -- $(dd if="$elf" bs=1 skip=42 count=2 | od -i)
    phentsize=$2
    set -- $(dd if="$elf" bs=1 skip=44 count=2 | od -i)
    phnum=$2
    while test $phnum -gt 0
    do
        phnum=$(($phnum-1))
        set -- $(($phoff+$phentsize*$phnum))
        set -- $(($1+16)) $(dd if="$elf" bs=1 skip=$1 count=4 | od -i)
        case "$3" in
        1)  ;;
        *)  continue;;
        esac
        set -- $(($1+4)) $(dd if="$elf" bs=1 skip=$1 count=4 | od -i)
        set -- "$3" $(dd if="$elf" bs=1 skip=$1 count=4 | od -i)
        bsssize=$(($bsssize+$3-$1))
    done
}

elf=$1
device=$2

getbsssize
{
    cat "$1"
    shift
    if test $bsssize -ge 512
    then
        dd if=/dev/zero count=$(($bsssize/512))
        bsssize=$(($bsssize%512))
    fi
    if test $bsssize -gt 0
    then
        dd if=/dev/zero bs=1 count=$bsssize
    fi
} | dd of="$device" conv=notrunc

###EFI環境のためのインストール
以下のようにしてFAT領域に loadvmm.efi と bitvisor.elf をコピーします。

$ mkdir -p /Volumes/BITVISOR/EFI/BOOT
$ cp ./loadvmm.efi /Volumes/BITVISOR/EFI/BOOT/BOOTX64.EFI
$ sh ./copyelf.sh ./bitvisor.elf /Volumes/BITVISOR/EFI/BOOT/BITVISOR.ELF

###bitvisor.elf の場所(LBA)の特定

以下のプログラムを getlba.c という名前で保存してください。

getlba.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/mount.h>

int main(int argc, char *argv[])
{
    int fd, status;
    off_t lba = 0, filesize, offset;
    struct log2phys phys;
    struct statfs stat;
    char mbr[512];

    fd = open(argv[1], O_RDONLY); if (fd < 0) return 1;
    filesize = lseek(fd, 0, SEEK_END); if (filesize < 0) return 2;
    for (offset = 0; offset < filesize; offset += 4096) {
        offset = lseek(fd, offset, SEEK_SET); if (offset < 0) return 3;
        status = fcntl(fd, F_LOG2PHYS, &phys); if (status < 0) return 4;
        if (lba != 0) {
            if (phys.l2p_devoffset - offset != lba)
            { printf("fragmented\n"); return 5; }
        } else
            lba = phys.l2p_devoffset;
    }

    status = statfs(argv[1], &stat); if (status < 0) return 6;
    stat.f_mntfromname[10] = '\0';
    fd = open(stat.f_mntfromname, O_RDONLY); if (fd < 0) return 7;
    status = read(fd, mbr, sizeof(mbr)); if (status < 512) return 8;
    offset = *(uint32_t *)&mbr[0x1c6];
    
    printf("LBA: %lld\n", offset + lba / 512);
}

gcc でコンパイルして、BITVISOR.ELF のパス名を指定して実行します。

$ gcc getlba.c -o getlba
$ sudo ./getlba /Volumes/BITVISOR/EFI/BOOT/BITVISOR.ELF

エラーが発生しなければ、LBAの値が表示されます。何も表示されなければ、何らかのエラーが発生していますので、getlba.c を適当に書き換えてエラーの原因を突き止めてください。

LBA: 90220

Mac の場合、フラグメンテーションを起こす確率が高いようです。そこでフラグメンテーションが起きた時だけエラーメッセージを表示するようにしてあります。

フラグメンテーションが起きた時は、フラグメンテーションが起きなくなるまで bitvisor.elf を別の名前でコピーを繰り返してください。例えば、以下の様なコマンドを実行します。

$ path=/Volumes/BITVISOR/EFI/BOOT/
$ for i in {0..9}; do f=$path/BITVISOR${i}.ELF; sh ./copyelf.sh ./bitvisor.elf $f >& /dev/null; sudo ./getlba $f && break; done
$ mv $f $path/BITVISOR.ELF
$ rm $path/BITVISOR?.ELF

最終的に表示された LBA を覚えておきます。

###BIOS環境のためのインストール

ボリュームをアンマウントしてから、特定したデバイス名と LBA を指定して、以下のようにインストールします。
既に bitvisor.elf が書き込まれているところに上書きするので、-f オプションは不要です。
ELFヘッダが見つからないので -f オプションをつけろと言われた場合は LBA が間違っている可能性があります。

$ diskutil umountDisk /dev/disk2
$ ./install.sh /dev/disk2 0 90220 ./bootloaderusb ./bitvisor.elf

###後処理

以下のコマンドを実行してから USB メモリを取り外します。

diskutil eject /dev/disk2
5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?