Arm64仮想マシンの作成 (2015/08/16)
前回は Arm64 のシングルボードコンピュータ(SBC) である Dragonboard 410C を紹介しましたが、「実機を入手するまでもないよ」と考えている人向けに Arm64 の仮想マシンの作り方を紹介します。
Arm64 というのは通称のようなもので、正式には「'''ARMv8''' アーキテクチャの '''AArch64''' 実行状態をサポートする '''A64''' 命令セット」といった表現が正しいようですが、分かり難いので Arm64 で行きます。Arm64 のアセンブリプログラミングをしてみたくて、Dragonboard 410C を入手しましたが、Raspberry Pi と違って、まだほとんど普及していないようです。 CPUの違いはCコンパイラが吸収してくれるため、Linux が動作さえすれば動作速度や消費電力が違うぐらいでほとんどの人には関係ありません。 それでも新しいマシンが手に入れば色々探ってみたくなります。 Arm64の実機を持っていない人のために Arm64 の仮想サーバで Arm64用の Ubuntu 15.04 を動作させることにします。 ちょっと長い道のりですが、NetworkManager やら、systemd やら色々と最近の Linux の常識の変化を味わうことができます。 面倒な人は サクッとHikey とか Dragonboard 410Cなど を買ってしまいましょう。
ホストマシンとしてはちょっと古いですが、Core2Duo (2.66GHz) のマシンに新たに ubuntu-15.04-desktop-amd64 をインストールしました。Ubuntu 15.04 (Vivid Vervet) のインストールの方法は アチコチ で解説されているので省略します。
デスクトップ版には sshd がインストールされていないので、openssh-server をインストールしました。
jun@ubuntu1504:~$ sudo apt install openssh-server
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
libck-connector0 ncurses-term openssh-sftp-server ssh-import-id
提案パッケージ:
rssh molly-guard monkeysphere
以下のパッケージが新たにインストールされます:
libck-connector0 ncurses-term openssh-server openssh-sftp-server
ssh-import-id
アップグレード: 0 個、新規インストール: 5 個、削除: 0 個、保留: 207 個。
639 kB のアーカイブを取得する必要があります。
この操作後に追加で 3,498 kB のディスク容量が消費されます。
続行しますか? [Y/n] y
Arm64 用QEMU をインストール
Arm64 用の仮想マシンエミュレータは qemu-system-aarch64 です。パッケージ名に qemu-system-aarch64 を指定しても qemu-system-arm がインストールされるようです。
jun@ubuntu1504:~$ sudo apt install qemu-system-aarch64 パッケージリストを読み込んでいます... 完了 依存関係ツリーを作成しています 状態情報を読み取っています... 完了 注意、'qemu-system-aarch64' の代わりに 'qemu-system-arm' を選択しています 以下の追加パッケージがインストールされます: ipxe-qemu libboost-thread1.55.0 libfdt1 librados2 librbd1 libsdl1.2debian libseccomp2 libusbredirparser1 libxen-4.5 libxenstore3.0 qemu-system-common qemu-utils sharutils 提案パッケージ: vde2 debootstrap bsd-mailx mailx 以下のパッケージが新たにインストールされます: ipxe-qemu libboost-thread1.55.0 libfdt1 librados2 librbd1 libsdl1.2debian libseccomp2 libusbredirparser1 libxen-4.5 libxenstore3.0 qemu-system-arm qemu-system-common qemu-utils sharutils アップグレード: 0 個、新規インストール: 14 個、削除: 0 個、保留: 15 個。 7,454 kB のアーカイブを取得する必要があります。 この操作後に追加で 33.2 MB のディスク容量が消費されます。 続行しますか? [Y/n] y : 略 :
インストールされた qemu 関連のパッケージを確認します。
$ dpkg -l |grep qemu
ii ipxe-qemu 1.0.0+git-20141004.86285d1-1ubuntu3 all PXE boot firmware - ROM images for qemu
ii qemu-system-arm 1:2.2+dfsg-5expubuntu9.3 amd64 QEMU full system emulation binaries (arm)
ii qemu-system-common 1:2.2+dfsg-5expubuntu9.3 amd64 QEMU full system emulation binaries (common files)
ii qemu-utils 1:2.2+dfsg-5expubuntu9.3 amd64 QEMU utilities
インストールされた qemu のコマンドを確認します。
jun@ubuntu1504:~$ ls -l /usr/bin/qemu*
-rwxr-xr-x 1 root root 884576 Jul 28 04:27 /usr/bin/qemu-img
-rwxr-xr-x 1 root root 939928 Jul 28 04:27 /usr/bin/qemu-io
-rwxr-xr-x 1 root root 860640 Jul 28 04:27 /usr/bin/qemu-nbd
-rwxr-xr-x 1 root root 6752176 Jul 28 04:27 /usr/bin/qemu-system-aarch64
-rwxr-xr-x 1 root root 6530992 Jul 28 04:27 /usr/bin/qemu-system-arm
qemu のバージョンを確認します。
$ qemu-system-aarch64 -version
QEMU emulator version 2.2.0 (Debian 1:2.2+dfsg-5expubuntu9.3),
Copyright (c) 2003-2008 Fabrice Bellard
Arm の 64 ビット版の CPU である cortex-a57 がサポートされていることを確認します。
$ qemu-system-aarch64 -cpu help -machine virt Available CPUs: arm1026 arm1136 arm1136-r2 arm1176 arm11mpcore arm926 arm946 cortex-a15 cortex-a57 cortex-a8 cortex-a9 cortex-m3 pxa250 pxa255 pxa260 pxa261 pxa262 pxa270-a0 pxa270-a1 pxa270 pxa270-b0 pxa270-b1 pxa270-c0 pxa270-c5 sa1100 sa1110 ti925t
Arm64 システムのエミュレータとして qemu-system-aarch64 が使えることを確認できました。 仮想マシンが用意出来たので、Arm64用のLinuxをインストールしましょう。
Arm64用 Ubuntu イメージの入手
Arm64用 Ubuntu のディスクイメージは https://cloud-images.ubuntu.com/vivid/current/ から入手できます。私は 2015/08/12 版のディスクイメージをダウンロードしました。
vivid-server-cloudimg-arm64-disk1.img 07-Aug-2015 08:16 348M
作業用のディレクトリとして、ホームディレクトリ(/home/jun) の下に qemuarm64 というディレクトリを作成して、そこにディスクイメージを置きました。
jun@ubuntu1504:~$ mkdir qemuarm64 jun@ubuntu1504:~/qemuarm64$ mv ../vivid-server-cloudimg-arm64-disk1.img . jun@ubuntu1504:~/qemuarm64$ ls -lt total 356516 -rw-rw-r-- 1 jun jun 364708352 Aug 12 18:14 vivid-server-cloudimg-arm64-disk1.img
initrd と カーネルの取得
QEMU でディスクイメージから Linux を起動するため、カーネル (vmlinuz) とイニシャル RAM ディスクイメージ(initrd.img) をQEMU のコマンドラインから指定する必要があります。 vmlinuz と initrd.img を Ubuntu のArm64用ディスクイメージ(vivid-server-cloudimg-arm64-disk1.img) から取り出します。 Ubuntuのディスクイメージをマウントするために qemu-nbd を使用します。先ほど作成した qemuarm64 ディレクトリ内で作業することにします。マウントポイントとして qemuarm64/mnt というディレクトリを作ります。
$ sudo modprobe nbd max_part=63 $ sudo qemu-nbd -c /dev/nbd0 vivid-server-cloudimg-arm64-disk1.img $ mkdir mnt $ sudo mount /dev/nbd0p1 mnt
これで、カレントディレクトリ (qemuarm64) の mnt ディレクトリ以下にUbuntu のArm64用ディスクイメージの内容が見えるようになります。
jun@ubuntu1504:~/qemuarm64$ ls -lt mnt/ drwxr-xr-x 95 root root 4096 Aug 7 16:58 etc drwx------ 2 root root 16384 Aug 7 16:40 lost+found drwxrwxrwt 2 root root 4096 Aug 7 16:40 tmp drwxr-xr-x 2 root root 4096 Aug 7 16:40 sbin drwxr-xr-x 2 root root 4096 Aug 7 16:40 bin drwxr-xr-x 12 root root 4096 Aug 7 16:40 var drwxr-xr-x 3 root root 4096 Aug 7 16:40 boot drwx------ 2 root root 4096 Aug 7 16:39 root drwxr-xr-x 2 root root 4096 Aug 7 16:38 run lrwxrwxrwx 1 root root 33 Aug 7 16:36 initrd.img -> boot/initrd.img-3.19.0-25-generic lrwxrwxrwx 1 root root 30 Aug 7 16:36 vmlinuz -> boot/vmlinuz-3.19.0-25-generic drwxr-xr-x 22 root root 4096 Aug 7 16:33 lib drwxr-xr-x 4 root root 4096 Aug 7 16:31 dev drwxr-xr-x 10 root root 4096 Aug 7 16:22 usr drwxr-xr-x 2 root root 4096 Aug 7 16:22 media drwxr-xr-x 2 root root 4096 Aug 7 16:22 opt drwxr-xr-x 2 root root 4096 Aug 7 16:22 srv drwxr-xr-x 2 root root 4096 Apr 18 06:38 home drwxr-xr-x 2 root root 4096 Apr 18 06:38 mnt drwxr-xr-x 2 root root 4096 Apr 18 06:38 proc drwxr-xr-x 2 root root 4096 Apr 7 03:44 sys
必要なファイルが boot/initrd.img-3.19.0-25-generic と boot/vmlinuz-3.19.0-25-generic であることが確認できました。これらのファイルを qemuarm64(カレントディレクトリ) にコピーします。
$ sudo cp mnt/boot/vmlinuz-3.19.0-25-generic . $ sudo cp mnt/boot/initrd.img-3.19.0-25-generic .
マウントした Arm64 用ディスクイメージをアンマウントします。
$ sudo umount mnt $ sudo qemu-nbd -d /dev/nbd0 /dev/nbd0 disconnected
ファイルを確認
コピーしたファイルを確認します。
$ ls -lt
-rw-r--r-- 1 root root 17025801 Aug 12 19:23 initrd.img-3.19.0-25-generic
-rw------- 1 root root 12197888 Aug 12 19:22 vmlinuz-3.19.0-25-generic
-rw-rw-r-- 1 jun jun 365035520 Aug 12 19:22 vivid-server-cloudimg-arm64-disk1.img
drwxr-xr-x 21 root root 4096 Aug 7 16:40 mnt
initrd.img-3.19.0-25-generic と vmlinuz-3.19.0-25-generic の所有者が root になっているため、自分に変更しておきます。
jun@ubuntu1504:~/qemuarm64$ sudo chown jun:jun *-generic jun@ubuntu1504:~/qemuarm64$ ls -lt total 385360 -rw-rw-r-- 1 jun jun 365035520 Aug 12 19:23 vivid-server-cloudimg-arm64-disk1.img -rw-rw-rw- 1 jun jun 17025801 Aug 12 19:23 initrd.img-3.19.0-25-generic -rw-rw-rw- 1 jun jun 12197888 Aug 12 19:22 vmlinuz-3.19.0-25-generic drwxr-xr-x 21 root root 4096 Aug 7 16:40 mnt
Arm64 用 Ubuntu を起動してみる
これだけでArm64 用 Ubuntu を起動することができます。ネットワーク等の設定は後にしてとりあえず起動してみましょう。 コマンドはqemu-system-aarch64 ですが引数が非常に長いです。次の行をすべてコピーして、コンソールの画面に貼りつけて下さい。以下のオプションでは管理者権限は不要なので sudo する必要はありません。5行目でユーザ「ubuntu」のパスワードを「upass」と指定しています。
qemu-system-aarch64 -m 1024 -cpu cortex-a57 -nographic -machine virt \ -kernel vmlinuz-3.19.0-25-generic \ -append 'root=/dev/vda1 rw rootwait mem=1024M console=ttyS0 \ console=ttyAMA0,38400n8 init=/usr/lib/cloud-init/uncloud-init \ ds=nocloud ubuntu-pass=upass' \ -drive if=none,id=image,file=vivid-server-cloudimg-arm64-disk1.img \ -initrd initrd.img-3.19.0-25-generic \ -device virtio-blk-device,drive=image \ -netdev user,id=user0 \ -device virtio-net-device,netdev=user0
画面上に次のように表示されるはずです。ログインできるまで数分かかります。
[ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Initializing cgroup subsys cpuset [ 0.000000] Initializing cgroup subsys cpu [ 0.000000] Initializing cgroup subsys cpuacct [ 0.000000] Linux version 3.19.0-25-generic (buildd@auburn) (gcc version 4.9.2 (Ubuntu/Linaro 4.9.2-10ubuntu13) ) #26-Ubuntu SMP Fri Jul 24 21:23:02 UTC 2015 (Ubuntu 3.19.0-25.26-generic 3.19.8-ckt2) [ 0.000000] CPU: AArch64 Processor [411fd070] revision 0 [ 0.000000] Detected PIPT I-cache on CPU0 [ 0.000000] alternatives: enabling workaround for ARM erratum 832075 [ 0.000000] Memory limited to 1024MB [ 0.000000] efi: Getting EFI parameters from FDT: [ 0.000000] efi: UEFI not found. [ 0.000000] psci: probing for conduit method from DT. [ 0.000000] psci: PSCIv0.2 detected in firmware. [ 0.000000] psci: Using standard PSCI v0.2 function IDs : 略 : Ubuntu 15.04 ubuntu ttyAMA0 ubuntu login: ubuntu Password: Last login: Mon Feb 16 21:01:04 UTC 2015 on ttyAMA0 To run a command as administrator (user "root"), use "sudo <command>". See "man sudo_root" for details. ubuntu@ubuntu:~$
ユーザ名は「ubuntu」、パスワードはコマンドラインで指定した「upass」です。パスワードは qemu-system-aarch64 コマンドの引数で変更できます。これで普通に Linux が動作します。x86_64 なマシン上の kvm と異なり、 Arm64 はソフトウェアのエミュレータとして動作します。 しかし、コマンドラインで使う上では特に遅く感じること無く動作します。
ちょっとゲスト上 (Arm64 用 Ubuntu) でコマンドを試してみましょう。
ubuntu@ubuntu:/$ uname -a Linux ubuntu 3.19.0-25-generic #26-Ubuntu SMP Fri Jul 24 21:23:02 UTC 2015 aarch64 aarch64 aarch64 GNU/Linux ubuntu@ubuntu:/$ cat /proc/cpuinfo processor : 0 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 CPU implementer : 0x41 CPU architecture: 8 CPU variant : 0x1 CPU part : 0xd07 CPU revision : 0 ubuntu@ubuntu:/$ ps ax 略 280 ? Ss 0:04 /lib/systemd/systemd-journald 290 ? Ss 0:01 /lib/systemd/systemd-udevd 391 ? Ssl 0:01 /lib/systemd/systemd-timesyncd 601 ? Ss 0:00 /usr/sbin/atd -f 606 ? Ssl 0:01 /usr/lib/accountsservice/accounts-daemon 608 ? Ss 0:00 /lib/systemd/systemd-logind 614 ? Ssl 0:01 /usr/sbin/rsyslogd -n 624 ? Ss 0:00 /usr/sbin/cron -f 634 ? Ss 0:00 /usr/bin/dbus-daemon --system --address=systemd: --no 673 ? Ssl 0:00 /usr/lib/policykit-1/polkitd --no-debug 789 ? Ss 0:00 /usr/sbin/sshd -D 795 ? Ss 0:00 /bin/login -- 796 tty1 Ss+ 0:00 /sbin/agetty --noclear tty1 linux 811 ? S 0:00 [kworker/u2:2] 895 ? Ss 0:00 /lib/systemd/systemd --user 897 ? S 0:00 (sd-pam) 899 ttyAMA0 S 0:01 -bash 923 ? S 0:00 [kworker/0:1] 928 ttyAMA0 R+ 0:00 ps ax
ネットワークの状態を調べてみます。これもゲスト上 (Arm64 用 Ubuntu) での作業です。
ubuntu@ubuntu:~$ ifconfig -a
eth0 Link encap:Ethernet HWaddr 52:54:00:12:34:56
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:64 errors:0 dropped:0 overruns:0 frame:0
TX packets:73 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:24638 (24.6 KB) TX bytes:6515 (6.5 KB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:160 errors:0 dropped:0 overruns:0 frame:0
TX packets:160 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:12960 (12.9 KB) TX bytes:12960 (12.9 KB)
ubuntu@ubuntu:~$
ip アドレスが 10.0.2.15 になっていますが、ホスト(qemu-system-aarch64 コマンドを実行した実機)がインターネットに接続できる状態ならば、Arm64の仮想PCからもネットワークに接続できます。https://google.com/のファイルを取得してみます。
ubuntu@ubuntu:~$ wget https://google.com/ --2015-08-12 12:17:32-- https://google.com/ Resolving google.com (google.com)... 216.58.221.206, 2404:6800:400a:805::200e Connecting to google.com (google.com)|216.58.221.206|:80... connected. HTTP request sent, awaiting response... 302 Found Location: https://www.google.co.jp/?gfe_rd=cr&ei=XDnLVaa7IcfD8Ae9qKy4Bw>[following] --2015-08-12 12:17:32-- https://www.google.co.jp/?gfe_rd=cr&ei=XDnLVaa7IcfD8Ae9qKy4Bw Resolving www.google.co.jp (www.google.co.jp)... 173.194.117.175, 173.194.117.183, 173.194.117.184, ... Connecting to www.google.co.jp (www.google.co.jp)|173.194.117.175|:80... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [text/html] Saving to: ‘index.html’ index.html [ <=> ] 18.44K --.-KB/s in 0.003s 2015-08-12 12:17:32 (7.08 MB/s) - ‘index.html’ saved [18882] ubuntu@ubuntu:~$ ls -l total 20 -rw-rw-r-- 1 ubuntu ubuntu 18882 Aug 12 12:17 index.html
無事にインターネットに接続できることが確認できました。しかしLAN内の別のマシンから仮想サーバにはアクセスできません。これを可能にするには後述のブリッジの設定が必要です。
終了
qemu-system-aarch64 コマンドを終了するには、仮想マシンの中(ゲスト)で shutdown を実行します。 仮想マシンのOSが停止すると qemu-system-aarch64 コマンド自体も終了します。 または、qemu-system-aarch64 コマンドのモニター機能を使って終了することもできます。コントロールを押しながらAキーを押した後、C を押すと、 (qemu) と表示されます。ここで q を押すとqemu-system-aarch64 コマンドは終了します。モニター機能からの終了は、仮想マシンからすると電源を突然止められたようなものなので shutdown したほうがいいかもしれません。何らかの理由で仮想マシンのOSがフリーズしてもモニター機能からの終了は可能です。
ubuntu@ubuntu:~$ sudo shutdown -h now
sudo: unable to resolve host ubuntu
PolicyKit daemon disconnected from the bus.
We are no longer a registered authentication agent.
[ 1192.928198] reboot: Power down
jun@ubuntu1504:~/qemuarm64$
Arm64 仮想サーバの構築
LAN内の別のマシンから実機のサーバのようにネットワーク越しにアクセスできるようにするためには、仮想サーバをホストマシンに作成したブリッジを介して接続する必要があります。ホストマシン側のネットワークの設定を変更します。
ブリッジ
ブリッジとは Ethernet Bridge のことで、物理的には普通のスイッチングハブ(レイヤー2スイッチ)と考えることができます。 ホストマシンの内部に仮想的にスイッチングハブ(ブリッジ)を用意して、ホストもゲスト(仮想マシン)もそのハブに繋ぐイメージです。 通常はPCのネットワークカード(NIC)は自分宛のEthernetフレーム(IP層より1つ下のデータリンク層)だけを受付けて、別マシン宛のフレームは無視します。 しかし仮想マシン宛のフレームも取り込むために、ホストマシンのネットワークカードを宛先に関係なくEthernetフレームを受け付けるプロミスキャスモード(promiscuous mode)に変更します。プロミスキャスモードにすることでEthernetフレームの宛先MACアドレスが自分と異なる場合でも読み込むことができるため、スイッチングハブ(ブリッジ)として動作できるようになります。
ホスト内にブリッジを生成して、eth0をブリッジに繋ぎかえます。 bridge-utils パッケージに含まれるコマンドを使って 「brctl addbr ブリッジ名」で生成できます。
bridge-utils のインストール
ホストマシンの Ubuntu 15.04 にbridge-utils をインストールします。
jun@ubuntu1504:~/qemuarm64$ sudo apt install bridge-utils
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
bridge-utils
0 upgraded, 1 newly installed, 0 to remove and 15 not upgraded.
Need to get 29.1 kB of archives.
After this operation, 146 kB of additional disk space will be used.
Get:1 https://jp.archive.ubuntu.com/ubuntu/vivid/main bridge-utils amd64 1.5-7ubuntu1 [29.1 kB]
Fetched 29.1 kB in 0s (198 kB/s)
ブリッジの追加
ブリッジを生成して、eth0を繋ぎかえる時にネットワークは一旦切断されるため、シェルスクリプトにしておけばリモートからでも連続してコマンドを実行できます。ブリッジにIPアドレスを設定するとホストも外部ネットワークに接続できます。ネットワークは一旦切断されるため、リモートからSSHで接続している場合は再接続する必要があります。
次に示す「set_bridge.sh」では Ubuntu15.04 のNetworkManager がデフォルトで設定する "Wired connection 1" を削除した後に、ブリッジを追加し、ブリッジのIPアドレスを 172.18.21.80 に、ゲートウェイを 172.18.21.1 、DNSを 172.18.21.1 に設定するスクリプトなっています。 eth0 は "Wired connection 1" から "eth0" に名称を変更しています。 私は成り行き上、クラスBのプライベートIPアドレス (172.16.0.0 - 172.31.255.255) を使っていますが、クラスC (192.168.0.0 - 192.168.255.255) の範囲が普通ではないかと思います。 各自のネットワーク環境にあわせて変更してください。「sudo sh ./set_bridge.sh」で実行できます。NetworkManager のコマンドラインクライアントの nmcli コマンドを使います。 Ubuntu + KVM で仮想サーバ で行っているような /etc/network/interfaces を編集する古い方法はもう使いません。nmcli コマンドは非常に豊富な機能を持っています。「man nmcli」で1000行以上のマニュアルが読めます。
jun@ubuntu1504:~$ cat set_bridge.sh
#!/bin/sh # eth0 を削除 nmcli connection delete "Wired connection 1" # ブリッジを追加 nmcli connection add type bridge \ autoconnect yes \ ifname br0 \ con-name br0 # ブリッジのIPアドレスを設定 nmcli connection modify br0 \ ipv4.method manual ipv4.addresses "172.18.21.80/24 172.18.21.1" \ ipv4.dns 172.18.21.1 ipv6.method ignore bridge.stp no # eth0 を作成 nmcli connection add type bridge-slave \ autoconnect yes \ ifname eth0 \ con-name eth0 \ master br0
ブリッジを削除する方法
元に戻すためのスクリプトも用意しておきます。 ブリッジを削除して、DHCPで自動設定するデフォルトの動作に戻ります。
jun@ubuntu1504:~$ cat reset_bridge.sh
#!/bin/sh # ブリッジの削除 nmcli connection delete "br0" nmcli connection delete "eth0" # 通常の eth0 を作成 nmcli connection add type ethernet \ ifname eth0 \ con-name "Wired connection 1" \ autoconnect yes nmcli connection modify "Wired connection 1" \ ipv4.method auto
実際にブリッジの作成と削除ができることを確認してみます。ブリッジの固定IPとして 172.18.21.80 が、DHCPに振られるIPとして 172.18.21.14 になっていることが確認できます。以下の操作はホストマシンで直接操作して記録しました。ssh のリモート接続では IPが切り替わった時点で表示が停止するためです。切り替わった新しい IPアドレスに対して接続し直せばアクセスできます。
jun@ubuntu1504:~$ sudo sh -x ./set_bridge.sh ; ifconfig + nmcli connection delete Wired connection 1 + nmcli connection add type bridge autoconnect yes ifname br0 con-name br0 (process:6945): libnm-glib-WARNING **: async_got_type: could not read properties for /org/freedesktop/NetworkManager/ActiveConnection/21: Method "Get" with signature "ss" on interface "org.freedesktop.DBus.Properties" doesn't exist Connection 'br0' (f7454081-6178-4acd-916c-f111b3958704) successfully added. + nmcli connection modify br0 ipv4.method manual ipv4.addresses 172.18.21.80/24 172.18.21.1 ipv4.dns 172.18.21.1 ipv6.method ignore bridge.stp no + nmcli connection add type bridge-slave autoconnect yes ifname eth0 con-name eth0 master br0 Connection 'eth0' (dffbb94d-4b99-41ac-93c6-bd205fec0283) successfully added. br0 Link encap:イーサネット ハードウェアアドレス 00:1d:60:6d:ae:b4 inetアドレス:172.18.21.80 ブロードキャスト:172.18.21.255 マスク:255.255.255.0 UP BROADCAST MULTICAST MTU:1500 メトリック:1 RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0 TXパケット:0 エラー:0 損失:0 オーバラン:0 キャリア:0 衝突(Collisions):0 TXキュー長:0 RXバイト:0 (0.0 B) TXバイト:0 (0.0 B) eth0 Link encap:イーサネット ハードウェアアドレス 00:1d:60:6d:ae:b4 UP BROADCAST RUNNING MULTICAST MTU:1500 メトリック:1 RXパケット:2421116 エラー:0 損失:0 オーバラン:0 フレーム:0 TXパケット:1209100 エラー:0 損失:0 オーバラン:0 キャリア:0 衝突(Collisions):0 TXキュー長:1000 RXバイト:583618646 (583.6 MB) TXバイト:821306211 (821.3 MB) 割り込み:17 lo Link encap:ローカルループバック inetアドレス:127.0.0.1 マスク:255.0.0.0 inet6アドレス: ::1/128 範囲:ホスト UP LOOPBACK RUNNING MTU:65536 メトリック:1 RXパケット:7615 エラー:0 損失:0 オーバラン:0 フレーム:0 TXパケット:7615 エラー:0 損失:0 オーバラン:0 キャリア:0 衝突(Collisions):0 TXキュー長:0 RXバイト:935140 (935.1 KB) TXバイト:935140 (935.1 KB) jun@ubuntu1504:~$ sudo sh -x ./reset_bridge.sh ; ifconfig + nmcli connection delete br0 + nmcli connection delete eth0 + nmcli connection add type ethernet ifname eth0 con-name Wired connection 1 autoconnect yes Connection 'Wired connection 1' (8026e5c6-4230-46cc-b1d4-c70c3be920ab) successfully added. + nmcli connection modify Wired connection 1 ipv4.method auto eth0 Link encap:イーサネット ハードウェアアドレス 00:1d:60:6d:ae:b4 inetアドレス:172.18.21.14 ブロードキャスト:172.18.21.255 マスク:255.255.255.0 inet6アドレス: fe80::21d:60ff:fe6d:aeb4/64 範囲:リンク UP BROADCAST RUNNING MULTICAST MTU:1500 メトリック:1 RXパケット:2421117 エラー:0 損失:0 オーバラン:0 フレーム:0 TXパケット:1209106 エラー:0 損失:0 オーバラン:0 キャリア:0 衝突(Collisions):0 TXキュー長:1000 RXバイト:583619236 (583.6 MB) TXバイト:821307143 (821.3 MB) 割り込み:17 lo Link encap:ローカルループバック inetアドレス:127.0.0.1 マスク:255.0.0.0 inet6アドレス: ::1/128 範囲:ホスト UP LOOPBACK RUNNING MTU:65536 メトリック:1 RXパケット:7644 エラー:0 損失:0 オーバラン:0 フレーム:0 TXパケット:7644 エラー:0 損失:0 オーバラン:0 キャリア:0 衝突(Collisions):0 TXキュー長:0 RXバイト:936929 (936.9 KB) TXバイト:936929 (936.9 KB) jun@ubuntu1504:~$ sudo sh -x ./set_bridge.sh ; ifconfig + nmcli connection delete Wired connection 1 + nmcli connection add type bridge autoconnect yes ifname br0 con-name br0 (process:7414): libnm-glib-WARNING **: async_got_type: could not read properties for /org/freedesktop/NetworkManager/ActiveConnection/24: Method "Get" with signature "ss" on interface "org.freedesktop.DBus.Properties" doesn't exist Connection 'br0' (c1ce8f9c-6b44-4490-9eff-63c4d1f2a191) successfully added. + nmcli connection modify br0 ipv4.method manual ipv4.addresses 172.18.21.80/24 172.18.21.1 ipv4.dns 172.18.21.1 ipv6.method ignore bridge.stp no + nmcli connection add type bridge-slave autoconnect yes ifname eth0 con-name eth0 master br0 Connection 'eth0' (330554d0-c11e-46b7-9705-0de9c42c6a7c) successfully added. br0 Link encap:イーサネット ハードウェアアドレス 00:1d:60:6d:ae:b4 inetアドレス:172.18.21.80 ブロードキャスト:172.18.21.255 マスク:255.255.255.0 UP BROADCAST MULTICAST MTU:1500 メトリック:1 RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0 TXパケット:0 エラー:0 損失:0 オーバラン:0 キャリア:0 衝突(Collisions):0 TXキュー長:0 RXバイト:0 (0.0 B) TXバイト:0 (0.0 B) eth0 Link encap:イーサネット ハードウェアアドレス 00:1d:60:6d:ae:b4 UP BROADCAST RUNNING MULTICAST MTU:1500 メトリック:1 RXパケット:2421136 エラー:0 損失:0 オーバラン:0 フレーム:0 TXパケット:1209165 エラー:0 損失:0 オーバラン:0 キャリア:0 衝突(Collisions):0 TXキュー長:1000 RXバイト:583622791 (583.6 MB) TXバイト:821315537 (821.3 MB) 割り込み:17 lo Link encap:ローカルループバック inetアドレス:127.0.0.1 マスク:255.0.0.0 inet6アドレス: ::1/128 範囲:ホスト UP LOOPBACK RUNNING MTU:65536 メトリック:1 RXパケット:7696 エラー:0 損失:0 オーバラン:0 フレーム:0 TXパケット:7696 エラー:0 損失:0 オーバラン:0 キャリア:0 衝突(Collisions):0 TXキュー長:0 RXバイト:941701 (941.7 KB) TXバイト:941701 (941.7 KB)
さて、これでホスタ側の準備は終了です。
Arm64 仮想サーバの起動
qemu-system-aarch64 を実行して、Arm64 仮想サーバをゲストマシンとして起動します。 ブリッジに接続するオプションを付けて qemu-system-aarch64 を起動する場合は管理者権限が必要です。 sudo の後ろで qemu-system-aarch64 を実行します。 長いコマンドラインを何度もキー入力できないので、スクリプトにしておきます。 たとえば start.sh というファイル名で以下のスクリプトを作成します。バックスラッシュ(または円マーク)の直後は改行にしておいて下さい。 「sudo sh start.sh」として実行するとArm64 仮想サーバが起動します。
#!/bin/sh qemu-system-aarch64 -m 1024 -cpu cortex-a57 -nographic -machine virt \ -kernel vmlinuz-3.19.0-25-generic \ -append 'root=/dev/vda1 rw rootwait mem=1024M console=ttyS0 \ console=ttyAMA0,38400n8 init=/usr/lib/cloud-init/uncloud-init \ ds=nocloud ubuntu-pass=pass' \ -drive if=none,id=image,file=vivid-server-cloudimg-arm64-disk1.img \ -initrd initrd.img-3.19.0-25-generic \ -device virtio-blk-device,drive=image \ -device virtio-net-device,netdev=tap0 \ -netdev tap,id=tap0,script=/etc/qemu-ifup
ホストマシン上、またはssh のコンソール上で上記のスクリプトを実行すると、しばらくゲストOSの起動メッセージが表示された後に、ゲスト(Arm64 仮想マシン)にログインできるようになります。このままArm64 仮想マシンを固定IPに変更してしまいましょう。
Arm64 仮想サーバのネットワーク設定
サーバとしてネットワーク越しにアクセスするので、ゲスト側のArm64仮想マシンのIPアドレスも固定します。 こちらは NetworkManager の管理になっていないため、慣れた古い方法で設定します。ここではIPアドレスを 172.18.21.90 に、ゲートウェイアドレスを172.18.21.1に設定します。
まず、ifconfig コマンドと、 route コマンドで固定のIPを振ってみます。以下のコマンドを実行すると、ネットワーク上の別マシンから ssh で接続できるようになるはずです。
ubuntu@ubuntu:~$ sudo ifconfig eth0 172.18.21.90 netmask 255.255.255.0 ubuntu@ubuntu:~$ sudo route add default gw 172.18.21.1 ubuntu@ubuntu:~$ ifconfig eth0 Link encap:Ethernet HWaddr 52:54:00:12:34:56 inet addr:172.18.21.90 Bcast:172.18.21.255 Mask:255.255.255.0 inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:263 errors:0 dropped:0 overruns:0 frame:0 TX packets:31 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:31173 (31.1 KB) TX bytes:8514 (8.5 KB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:1352 errors:0 dropped:0 overruns:0 frame:0 TX packets:1352 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:108880 (108.8 KB) TX bytes:108880 (108.8 KB)
次回から起動時に固定IPになるように /etc/network/interfaces.d/eth0.cfg の内容を修正します。ネームサーバとして 172.18.21.1 を指定しています。
jun@ubuntu:~$ sudo vi /etc/network/interfaces.d/eth0.cfg
私の環境では次のように変更しました。
# The primary network interface auto eth0 #iface eth0 inet dhcp iface eth0 inet static address 172.18.21.90 network i172.18.21.0 netmask 255.255.255.0 broadcast 172.18.21.255 gateway 172.18.21.1 dns-nameservers 172.18.21.1
ssh でリモート接続 (ゲスト)
Arm64 仮想サーバ(ゲスト)を再起動すると固定IPで別マシンから接続できるようになります。
jun-no-MacBook-Pro:~ jun$ ssh -l jun 172.18.21.90
jun@172.18.21.90's password:
Welcome to Ubuntu 15.04 (GNU/Linux 3.19.0-25-generic aarch64)
* Documentation: https://help.ubuntu.com/
System information as of Thu Aug 13 08:35:57 UTC 2015
System load: 0.69 Processes: 62
Usage of /: 51.7% of 2.10GB Users logged in: 0
Memory usage: 9% IP address for eth0: 172.18.21.90
Swap usage: 0%
Graph this data and manage this system at:
https://landscape.canonical.com/
Get cloud support with Ubuntu Advantage Cloud Guest:
https://www.ubuntu.com/business/services/cloud
Last login: Wed Aug 12 16:06:00 2015
jun@ubuntu:~$
ユーザの追加 (ゲスト)
ここで、ubuntu 以外のユーザを追加しておきましょう。ユーザ名を jun として、sudo が可能な権限を付与します。
ubuntu@ubuntu:~$ sudo adduser jun sudo: unable to resolve host ubuntu Adding user `jun' ... Adding new group `jun' (1001) ... Adding new user `jun' (1001) with group `jun' ... Creating home directory `/home/jun' ... Copying files from `/etc/skel' ... Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully Changing the user information for jun Enter the new value, or press ENTER for the default Full Name []: Room Number []: Work Phone []: Home Phone []: Other []: Is the information correct? [Y/n] y ubuntu@ubuntu:~$ sudo gpasswd -a jun sudo Adding user jun to group sudo
samba のインストール (ゲスト)
仮想サーバ内のファイルを外部からやりとりできるように
jun@ubuntu:~$ sudo apt install samba sudo: unable to resolve host ubuntu Reading package lists... Done Building dependency tree Reading state information... Done The following extra packages will be installed: attr libaio1 libavahi-client3 libavahi-common-data libavahi-common3 libcups2 libfile-copy-recursive-perl libhdb9-heimdal libkdc2-heimdal libldb1 libntdb1 libtalloc2 libtdb1 libtevent0 libwbclient0 python-crypto python-dnspython python-ldb python-ntdb python-samba python-talloc python-tdb samba-common samba-common-bin samba-dsdb-modules samba-libs samba-vfs-modules tdb-tools update-inetd Suggested packages: cups-common python-crypto-dbg python-crypto-doc bind9 bind9utils ldb-tools ntp smbldap-tools winbind heimdal-clients The following NEW packages will be installed: attr libaio1 libavahi-client3 libavahi-common-data libavahi-common3 libcups2 libfile-copy-recursive-perl libhdb9-heimdal libkdc2-heimdal libldb1 libntdb1 libtalloc2 libtdb1 libtevent0 libwbclient0 python-crypto python-dnspython python-ldb python-ntdb python-samba python-talloc python-tdb samba samba-common samba-common-bin samba-dsdb-modules samba-libs samba-vfs-modules tdb-tools update-inetd 0 upgraded, 30 newly installed, 0 to remove and 0 not upgraded. Need to get 6,741 kB of archives. After this operation, 43.5 MB of additional disk space will be used. Do you want to continue? [Y/n] y : 略 :
sambaの設定 (ゲスト)
管理者権限でエディタを起動して /etc/samba/smb.conf の193行目付近の[homes]のコメントになっている部分を修正します。
[homes] comment = Home Directories browseable = yes # By default, the home directories are exported read-only. Change the # next parameter to 'no' if you want to be able to write to them. read only = no # File creation mask is set to 0700 for security reasons. If you want to # create files with group=rw permissions, set next parameter to 0775. create mask = 0775 # Directory creation mask is set to 0700 for security reasons. If you want tt o # create dirs. with group=rw permissions, set next parameter to 0775. directory mask = 0775 # By default, \\server\username shares can be connected to by anyone # with access to the samba server. # Un-comment the following parameter to make sure that only "username" # can connect to \\server\username # This might need tweaking when using external authentication schemes valid users = %S
samba用のユーザ追加 (ゲスト)
$ sudo pdbedit -a -u jun
sudo: unable to resolve host ubuntu
new password:
retype new password:
Unix username: jun
NT username:
Account Flags: [U ]
User SID: S-1-5-21-3385742284-1000632516-37778874-1000
Primary Group SID: S-1-5-21-3385742284-1000632516-37778874-513
Full Name:
Home Directory: \\ubuntu\jun
HomeDir Drive:
Logon Script:
Profile Path: \\ubuntu\jun\profile
Domain: UBUNTU
Account desc:
Workstations:
Munged dial:
Logon time: 0
Logoff time: Wed, 06 Feb 2036 15:06:39 UTC
Kickoff time: Wed, 06 Feb 2036 15:06:39 UTC
Password last set: Wed, 12 Aug 2015 15:40:34 UTC
Password can change: Wed, 12 Aug 2015 15:40:34 UTC
Password must change: never
Last bad password : 0
Bad password count : 0
Logon hours : FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
設定を反映します。
jun@ubuntu:~$ sudo service samba restart
Failed to restart samba.service: Unit samba.service is masked.
ムムッ、マスクされているってどういうこと?
で、再起動してファイル共有できることを確認しました。
Arm64 のアセンブラを使ってみる (ゲスト)
アセンブラ (GNU as) やリンカ(ld) は binutils に含まれているのでインストールする必要があります。
jun@ubuntu:~$ sudo apt-get install binutils
Reading package lists... Done
Building dependency tree
Reading state information... Done
Suggested packages:
binutils-doc
The following NEW packages will be installed:
binutils
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 2,357 kB of archives.
After this operation, 14.1 MB of additional disk space will be used.
Get:1 https://ports.ubuntu.com/ubuntu-ports/vivid/main binutils arm64 2.25-5ubuntu7 [2,357 kB]
Fetched 2,357 kB in 8s (271 kB/s)
Selecting previously unselected package binutils.
(Reading database ... 57987 files and directories currently installed.)
Preparing to unpack .../binutils_2.25-5ubuntu7_arm64.deb ...
Unpacking binutils (2.25-5ubuntu7) ...
Processing triggers for man-db (2.7.0.2-5) ...
Setting up binutils (2.25-5ubuntu7) ...
Processing triggers for libc-bin (2.21-0ubuntu4) ...
Arm64 のアセンブラで「hello、world」
Arm64 のアセンブラで書いた「hello、world」です。ファイルなどに文字列を出力する Linux のシステムコールである sys_write とプログラムを終了させる sys_exit を使っています。x8 レジスタにシステムコール番号を設定して、「svc #0」命令を実行すると x0 から x5 レジスタの値を引数としてシステムコールと呼ばれる Linux カーネルの各種の機能を使うことができます。メモリ中の位置を示すラベルと呼ぶ _start: と msg: の間の8行がプログラムの実体です。 各命令は4バイトなので文字列データを除けばプログラムは32バイトです。
// Arm64 .text .global _start _start: mov x2, #13 // x2 length adr x1, msg // x1 string address mov x0, #1 // x0 stdout mov x8, #64 svc #0 // sys_write mov x0, xzr mov x8, #93 svc #0 // exit msg: .asciz "hello, world\n"
Arm64 仮想マシンで実行
jun@ubuntu:~$ as -o hello.o hello.s jun@ubuntu:~$ ld -o hello hello.o jun@ubuntu:~$ ./hello hello, world jun@ubuntu:~$ file hello hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped
Dragonboard 410Cで実行
Cortex-A53 という64ビット版のARMを使っている Dragonboard 410C で実行してみましょう。
jun@linaro-alip:~$ as -o hello.o hello.s jun@linaro-alip:~$ ld -o hello hello.o jun@linaro-alip:~$ ./hello hello, world jun@linaro-alip:~$ file hello hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped
当然ですが、Dragonboard 410C でも同じように動作します。
32ビット版のhello, world
比較のためにARMの32ビット版の「hello, world」 です。 短いプログラムですが、色々異なるところがあります。Arm64はコードの字面は32ビット版と似ていますが、内部的な違いが大きいCPUと思います。詳細はまたそのうち。
- コメントが // ではなく、@ になっている。
- レジスタが r0 のように rで始まっている。
- ゼロレジスタ (xzr) がない。
- システムコールの番号を設定するレジスタが x8 でなくて r7。
- システムコールの番号が異なる。
- システムコールの命令が svc でなくて swi になっている。
@ Arm32 .text .global _start _start: mov r2, #13 @ length adr r1, msg @ address mov r0, #1 @ stdout mov r7, #4 @ sys_write swi 0 mov r0, #0 mov r7, #1 @ sys_exit swi 0 msg: .asciz "hello, world\n"
Raspberry Pi2 で実行
ARMの32ビット版のCPU (Cortex-A7) で実行してみます。
jun@raspberrypi ~ $ as -o hello32.o hello32.s jun@raspberrypi ~ $ ld -o hello32 hello32.o jun@raspberrypi ~ $ ./hello32 hello, world
Dragonboard 410Cで実行
Dragonboard 410C は Cortex-A53 という64ビット版のARMを使っていますが、32ビット用のプログラムを実行してみましょう。
jun@linaro-alip:~$ ./hello32 hello, world jun@linaro-alip:~/arm64$ file hello32 hello32: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
64ビット版のARM用のLinux では 32ビットLinux用のプログラムも実行できました。
Arm64 仮想マシンで実行
Arm64 仮想マシンでも32ビット用のプログラムを試してみます。
jun@ubuntu:~$ ./hello32 hello, world jun@ubuntu:~$ file hello32 hello32: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, stripped
問題無いですね。
Dragonboard 410C との互換性?
次の hello2.s というファイル名のコードは、9行目と10行目にx0レジスタをスタックにプッシュして、ポップするコードを追加したものです。
jun@ubuntu:~$ cat hello2.s .text .global _start _start: mov x2, #13 // x2 length adr x1, msg // x1 string address mov x0, #1 // x0 stdout mov x8, #64 svc #0 // sys_write ldr x0, [sp, #-8]! // push x0 str x0, [sp], #8 // pop x0 mov x0, xzr mov x8, #93 svc #0 // exit msg: .asciz "hello, world\n" jun@ubuntu:~$ as -o hello2.o hello2.s jun@ubuntu:~$ ld -o hello2 hello2.o jun@ubuntu:~$ ./hello2 hello, world
仮想Arm64マシン上では普通に実行できます。同じことを Dragonboard 410C で実行します。
jun@linaro-alip:~$ as -o hello2.o hello2.s jun@linaro-alip:~$ ld -o hello2 hello2.o jun@linaro-alip:~$ ./hello2 hello, world Bus error
「Bus error」と表示されて、なにかエラーが起きています。このコードでは「hello, world」を表示したら終了するだけなので分かり難いですが、もっと長いプログラムでは「Bus error」が発生した時点でプログラムは終了します。何が起きているのか調査するためにデバッガ (gdb) で実行してみます。
jun@linaro-alip:~$ gdb ./hello2 : 略 : (gdb) run Starting program: /home/jun/arm64/hello2 hello, world Program received signal SIGBUS, Bus error. 0x0000000000400090 in _start () (gdb) disassemble Dump of assembler code for function _start: 0x0000000000400078 <+0>: mov x2, #0xd // #13 0x000000000040007c <+4>: adr x1, 0x4000a0 <msg> 0x0000000000400080 <+8>: mov x0, #0x1 // #1 0x0000000000400084 <+12>: mov x8, #0x40 // #64 0x0000000000400088 <+16>: svc #0x0 0x000000000040008c <+20>: ldr x0, [sp,#-8]! => 0x0000000000400090 <+24>: str x0, [sp],#8 0x0000000000400094 <+28>: mov x0, xzr 0x0000000000400098 <+32>: mov x8, #0x5d // #93 0x000000000040009c <+36>: svc #0x0 End of assembler dump.
スタックポインタが示すメモリに値を書き込む命令で例外が発生しています。スタックポインタの値は16バイトの境界に合わせる必要があるそうです。そこで、次のようにスタックポインタの値を64ビットレジスタ2つ分の16バイトを単位として変化させるとエラーは発生しません。
jun@linaro-alip:~$ cat hello3.s .text .global _start _start: mov x2, #13 // x2 length adr x1, msg // x1 string address mov x0, #1 // x0 stdout mov x8, #64 svc #0 // sys_write ldr x0, [sp, #-16]! // push x0 str x0, [sp], #16 // pop x0 mov x0, xzr mov x8, #93 svc #0 // exit msg: .asciz "hello, world\n" jun@linaro-alip:~$ as -o hello3.o hello3.s jun@linaro-alip:~$ ld -o hello3 hello3.o jun@linaro-alip:~$ ./hello3 hello, world
QEMUも万能ではないようです。Arm64の実機を購入する決心がつきましたか? (笑)