Ubuntu Weekly Recipe

第603回RISC-VのDebianイメージをQEMUで動かす

第505回「オープン規格の新しい命令セットアーキテクチャRISC-V入門 ツールチェインを用意する」ではRISC-Vシミュレーターであるspikeを使ってRISC-Vバイナリを実行しました。今回はRISC-V対応QEMUを使って、RISC-Vの仮想マシン上でDebianを起動してみましょう。

ツールチェインとDebianのRISC-V対応状況

RISC-V対応のLinuxシステムを作るためには、単にコンパイラがRISC-Vに対応しているだけではなく、そのコンパイラを用いてカーネルやユーザーランドの各種ツールのRISC-Vバイナリを作る必要があります。さらに実機がない場合は、QEMUのような仮想マシンエミュレーターも必要です。spikeを使ってカーネルを実行することは可能ではありますが、今回は他のアーキテクチャーと同様にQEMUを使います。

2020年1月時点で、Linuxシステムを構築するために最低限必要なツールのほとんどがRISC-Vに対応しています。少なくとも次のバージョンであればRISC-VベースのLinuxシステムを構築することが可能です。

  • binutils:2.28以上
  • GCC:7.0以上
  • glibc:2.27以上
  • Linux:4.19以上
  • QEMU:2.12以上
  • dpkg:1.19.0.5以上
  • U-Boot:2018.11以上

そしてこれらはすべて、最新のDebianにも取り込まれています。よってDebianのルートファイルシステムであれば、比較的簡単に構築できるのです。

もちろん登場したばかりのアーキテクチャーであるため、新しければ新しいほど安定して動く可能性が高くなります。またLinuxシステムとして使うためには当然のことながら上記以外にも必要なソフトウェアがたくさんあります。これらも必要に応じて順次RISC-V対応が行われていくことでしょう。

Debianでは公式でサポートしているCPUアーキテクチャー(やカーネル)以外にもPorts(移植版)と呼ばれる公式ではないサポートアーキテクチャーが存在します。公式リポジトリに存在するパッケージは、Ports向けのアーキテクチャでビルドされ、Debian Ports用のリポジトリで公開されるのです。このため、Portsにあげられているアーキテクチャーであればapt経由でパッケージをインストールできます。

RISC-V向けのアーキテクチャーとしてはriscv64がPortsに登録されています。これはRISC-Vで言うところの「RV64GCU」であり「RV64IMAFDCU」の省略形です。第505回の「RISC-Vの拡張性」で取り上げたように64ビットアーキテクチャーの基本整数・整数の乗算除算・アトミック・単精度の浮動小数点・倍精度の浮動小数点それぞれの命令と、16ビット幅の命令をサポートしていることがわかります。最後のUは「The RISC-V Instruction Set Manual」「Volume II: Privileged Architecture」に記載があるように、ユーザーモードをサポートしていることを意味します。

残念ながらUbuntuはRISC-Vをサポートしていません。もし今後サポートすることになるとしても、ricsv64がDebianの公式サポートアーキテクチャーになることが前提となるでしょう。riscv64ではまだビルドできないソフトウェアもそれなりにあることから、何か大きな力が働かない限りは、当面はUbuntuでサポートすることはなさそうです。

ただしUbuntu上でRISC-Vのバイナリをビルドしたり、QEMUでRISC-V版のDebianを動かすことは可能です。そこで今回はUbuntu上で、Debianイメージの作成と起動までを説明します。

構築環境の準備

Ubuntu上でRISC-V環境を構築するためにはUbuntu 19.10以降が必要です。これはQEMU上でRISC-Vを動かすためのファームウェアとしてOpenSBIを使用しており[1]⁠、このOpenSBIのパッケージが19.10からしか提供されていないためです。またQEMU自身も18.04のバージョンだと少し古いのでRISC-Vのバイナリをうまく動かせません。もちろんOpenSBIやQEMUの最新版を手動でインストールしても良いのですが、それよりはより新しいUbuntuを用意したほうが楽でしょう。

今回はより新しい環境を求めて、2020年4月にリリース予定のUbuntu 20.04 LTSの開発版を使うことにします。ただし今回紹介する手順は19.10でも動くことを確認済みです。

20.04を実機にインストールしても良いのですが、いかんせん開発版であることから、仮想マシン上にインストールしたほうが安心です[2]⁠。そこで第590回「Windows/macOS/Linuxで使える仮想マシン管理ツール『multipass⁠⁠」を使用しましょう。multipassならUbuntuだけでなくWindowsやmacOSでも使えますし、すぐに20.04の実行環境を用意できます。

まずはmultipassをインストールします。

$ sudo snap install multipass --classic

ちなみに第590回で紹介したあと、2019年12月に初の正式版である1.0.0がリリースされました。標準で作成されるユーザーが「multipass」から「ubuntu」になるなど、細かいところに変更はありますが、原則として第590回で紹介した使用方法がそのまま使えるはずです。

次に20.04のインスタンスを「riscv」の名前で作成します。

$ multipass launch -n riscv -d 40G -m 4G daily:20.04

ルートファイルシステムを作る都合上、ストレージのサイズは40GBと多めにとっています。また、仮想マシン上でRISC-VのQEMUイメージを起動することから、メモリーも4GBに設定しておきました。

しばらく時間が必要ですが、無事にインスタンスが起動したらログインしておきましょう。

$ multipass shell riscv

ここから先の手順はすべて、riscvインスタンス内で実行したものとします。

RISC-Vのルートファイルシステムの作成

まずはルートファイルシステム構築に必要なパッケージをインストールしておきましょう。

$ sudo apt install debootstrap \
    debian-ports-archive-keyring \
    qemu-user-static binfmt-support

debootstrapはDebianのベースシステムを構築するためのツールで[3]⁠、debian-ports-archive-keyringはDebian Ports用のリポジトリを検証するための公開鍵です。

qemu-user-staticとbinfmt-supportは、構築したルートファイルシステムでRISC-Vバイナリを実行するためにインストールしています。

次にルートファイルシステムを作成します。

$ mkdir riscv && cd $_
$ sudo debootstrap --arch=riscv64 \
  --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg \
  --include=debian-ports-archive-keyring \
  unstable rootfs-riscv64 \
  http://deb.debian.org/debian-ports

--keyringでは先ほどインストールしたPorts用のリポジトリ鍵を指定しています。さらに--includeでルートファイルシステムにPorts用のリポジトリ鍵を含むパッケージをインストールしています。

ちなみに今回はコード名にunstableを指定しています。Ports向けリポジトリで他に指定できるのはexperimentalとPorts固有のテスト環境であるunreleasedのみです。そのためタイミングが悪いと、特にたまたま依存関係を満たせない変更が行われた場合などに、構築に失敗することがあります。たとえば2020年1月18日時点では、isc-dhcp-clientの4.4.1-2がlibdns-export1104とlibisc-export1100に依存しているものの、これらを提供するbind9の最新版である1:9.11.14+dfsg-3までにおいて、それぞれlibdns-export1107libisc-export1104になってしまったために、isc-dhcp-clientをインストールできないという問題がありました。

もしうまくルートファイルシステムを作れないようならsnapshot.debian.orgのリポジトリを使うと良いでしょう。RISC-Vだと少なくとも2019年12月19日時点のリポジトリだと問題はありませんでした[4]⁠。

特定の日付のリポジトリを使うなら、次のように最後の引数にあるリポジトリURLを変更します。

$ sudo debootstrap --arch=riscv64 \
  --keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg \
  --include=debian-ports-archive-keyring \
  unstable rootfs-riscv64 \
  https://snapshot.debian.org/archive/debian-ports/20191219T030209Z

ただしこの場合、最新のパッケージを導入できません。ルートファイルシステム構築後に/etc/sources.listに両方のリポジトリを書くなど、なんらかの対応を行っておきましょう。具体的な手順は後ほど説明します。

この時点でルートファイルシステムのサイズは300MiB弱程度のようです。

$ sudo du -hs rootfs-riscv64/
295M    rootfs-riscv64/

ルートファイルシステムを構築したら、そのルートファイルシステムにchrootし、追加の設定を行います。

$ sudo chroot rootfs-riscv64
root@riscv:/#

まずはリポジトリデータのアップデートです。特にsnapshot.debian.orgを使用して構築したのなら、Portsのリポジトリも指定してパッケージを更新しておいたほうがいいかもしれません。

root@riscv:/# echo "deb http://deb.debian.org/debian-ports unstable main" \
  >> /etc/apt/sources.list
root@riscv:/# apt update
Get:1 http://deb.debian.org/debian-ports unstable InRelease [56.0 kB]
Hit:2 https://snapshot.debian.org/archive/debian-ports/20191219T030209Z unstable InRelease
Get:3 http://deb.debian.org/debian-ports unstable/main all Packages [8379 kB]
Get:4 http://deb.debian.org/debian-ports unstable/main riscv64 Packages [20.4 MB]
Fetched 28.8 MB in 17s (1658 kB/s)
Reading package lists... Done
Building dependency tree... Done
24 packages can be upgraded. Run 'apt list --upgradable' to see them.
root@riscv:/# apt full-upgrade

ネットワーク設定が空なので、QEMUで動かした時のためにDHCPでアドレスを取得できるようにしておきます。

root@riscv:/# cat /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)
# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d

ローカルループバック用の設定
root@riscv:/# cat >>/etc/network/interfaces.d/lo <<EOF
auto lo
iface lo inet loopback
EOF

eth0用の設定
root@riscv:/# cat >>/etc/network/interfaces.d/eth0 <<EOF
auto eth0
iface eth0 inet dhcp
EOF

次にアカウントを作成しておきましょう。

root@riscv:/# adduser shibata
Adding user `shibata' ...
Adding new group `shibata' (1000) ...
Adding new user `shibata' (1000) with group `shibata' ...
Creating home directory `/home/shibata' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for shibata
Enter the new value, or press ENTER for the default
        Full Name []: Mitsuya Shibata
        Room Number []:
        Work Phone []:
        Home Phone []:
        Other []:
Is the information correct? [Y/n]

rootアカウントのパスワードはロックされています。管理者権限に昇格できるよう、sudoパッケージをインストールして、先ほど作成したアカウントをsudoグループに追加しておきます。あくまでテスト用と割り切るなら、passwdコマンドでrootアカウントのパスワードを設定して、rootアカウントのまま生活する方法もあります。

root@riscv:/# apt install sudo
root@riscv:/# usermod -aG sudo shibata
root@riscv:/# id shibata
uid=1000(shibata) gid=1000(shibata) groups=1000(shibata),27(sudo)

QEMU上だとhvc0はttyS0と同じデバイスになるため、hvc0側のgettyの起動を抑制しておきます。

root@riscv:/# ln -sf /dev/null /etc/systemd/system/serial-getty@hvc0.service

カーネルとブートローダーをインストールして、ブートローダーの設定も行っておきましょう。

root@riscv:/# apt install linux-image-riscv64 u-boot-menu

カーネルのインストール時に次のようなエラーが大量に表示されるかもしれません。

depmod: ERROR: ../libkmod/libkmod.c:515 lookup_builtin_file() could not open builtin file '/lib/modules/5.4.0-2-riscv64/modules.builtin.bin'
depmod: ERROR: ../libkmod/libkmod.c:515 lookup_builtin_file() could not open builtin file '/var/tmp/mkinitramfs_SdQixj/lib/modules/5.4.0-2-ris
cv64/modules.builtin.bin'

どうやらこれはinitramfs-toolsの不具合のようです。とりあえずパッケージに組み込まれているinitramfsをそのまま使うのであれば実害はないはずなので、現時点では無視してください[5]⁠。

ブートローダーはU-Bootを使います。そこで次のように設定し、その値を反映しておきます。

root@riscv:/# cat >>/etc/default/u-boot <<EOF
U_BOOT_PARAMETERS="rw noquiet root=/dev/vda1"
U_BOOT_FDT_DIR="noexist"
EOF
root@riscv:/# u-boot-update
P: Checking for EXTLINUX directory... found.
/usr/bin/cat: /proc/cmdline: No such file or directory
P: Writing config for vmlinux-5.4.0-2-riscv64...
P: Updating /boot/extlinux/extlinux.conf...

U_BOOT_FDT_DIR="noexist"には「存在しないディレクトリ」を指定します。これはDebianのカーネルが5.3.0からRISC-V用のDTBをビルドするようになったことに対する回避策です。通常は適切なディレクトリにSoCごとのDTBをインストールし、起動時にそれを選択することが正しいやりかたです。しかしながらQEMU用のDTBはインストールされていません。そこで「存在しないディレクトリ」を指定することでDTBが見つからないようにしておき、QEMUインスタンスの特定のアドレスにあるDTBデータを使うようにしているというわけです。このあたりは将来的にもう少しスマートな方法に変わる予定です。

ルートファイルシステム内部の設定が終わったので、chrootから抜けておきましょう。

root@riscv:/# exit

最後に作成したルートファイルシステムをQEMUイメージに変換します。方法はいくつか存在しますが、ここではvirt-make-fsコマンドを使いましょう。

$ sudo apt install --no-install-recommends libguestfs-tools
$ sudo virt-make-fs --partition --type=ext4 --size=10G rootfs-riscv64/ rootfs-riscv64.img
$ sudo chown ${USER} rootfs-riscv64.img

これでイメージの作成は完了です。

QEMUからRISC-Vイメージを立ち上げる

作成したイメージをQEMUから立ち上げましょう。必要なのはRISC-V用のエミュレーターとファームウェア、それにRISC-V向けのU-Boot本体です。

$ sudo apt install --no-install-recommends \
  qemu-system-misc opensbi

amd64などのアーキテクチャーと異なり、Portsに所属するようなアーキテクチャー向けのエミューレーターはqemu-system-miscパッケージにひとまとめになっているようです。

RISC-V向けのU-Boot本体は残念ながらUbuntuのリポジトリに存在しません。U-Boot 2019.10あたりのパッケージで導入されたものであり、Ubuntuは20.04でも最新が2019.07であるためです。そこでDebianからパッケージをダウンロードし、それをインストールします。

$ wget http://ftp.jp.debian.org/debian/pool/main/u/u-boot/u-boot-qemu_2020.01+dfsg-1_all.deb
$ sudo apt install ./u-boot-qemu_2020.01+dfsg-1_all.deb

本パッケージはQEMU上で動かすバイナリの集合体のようなものであり、依存関係もないため、この方法でインストールしても問題ないでしょう。ちなみにU-Boot 2020.01は2020年1月8日にunstable取り込まれましたので、Ubuntuでもうまくいけば20.04で使えるようになるかもしれません。

さて、ようやくRISC-Vの仮想マシンを起動できるところまでたどりつきました。少し長いですが、次のようなコマンドを実行しましょう。

$ qemu-system-riscv64 -nographic -machine virt -m 2G \
 -kernel /usr/lib/riscv64-linux-gnu/opensbi/qemu/virt/fw_jump.elf \
 -device loader,file=/usr/lib/u-boot/qemu-riscv64_smode/u-boot.bin,addr=0x80200000 \
 -object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-device,rng=rng0 \
 -append "console=ttyS0 rw root=/dev/vda1" \
 -device virtio-blk-device,drive=hd0 -drive file=rootfs-riscv64.img,format=raw,id=hd0 \
 -device virtio-net-device,netdev=usernet -netdev user,id=usernet,hostfwd=tcp::22222-:22

まずはU-Bootが立ち上がります。

OpenSBI v0.5 (Oct 12 2019 06:02:51)
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name          : QEMU Virt Machine
Platform HART Features : RV64ACDFIMSU
Platform Max HARTs     : 8
Current Hart           : 0
Firmware Base          : 0x80000000
Firmware Size          : 116 KB
Runtime SBI Version    : 0.2

PMP0: 0x0000000080000000-0x000000008001ffff (A)
PMP1: 0x0000000000000000-0xffffffffffffffff (A,R,W,X)


U-Boot 2020.01+dfsg-1 (Jan 08 2020 - 08:19:44 +0000)

CPU:   rv64imafdcsu
Model: riscv-virtio,qemu
DRAM:  2 GiB
In:    uart@10000000
Out:   uart@10000000
Err:   uart@10000000
Net:
Warning: virtio-net#2 using MAC address from ROM
eth0: virtio-net#2
Hit any key to stop autoboot:  0

Device 0: QEMU VirtIO Block Device
            Type: Hard Disk
            Capacity: 10240.0 MB = 10.0 GB (20971520 x 512)
... is now current device
Scanning virtio 0:1...
Found /boot/extlinux/extlinux.conf
Retrieving file: /boot/extlinux/extlinux.conf
611 bytes read in 1 ms (596.7 KiB/s)
U-Boot menu
1:      Debian GNU/Linux kernel 5.4.0-2-riscv64
2:      Debian GNU/Linux kernel 5.4.0-2-riscv64 (rescue target)
Enter choice: 1

「U-Boot menu」のところで起動するイメージを選択してください。基本は「1」を選びます。

1:      Debian GNU/Linux kernel 5.4.0-2-riscv64
Retrieving file: /boot/initrd.img-5.4.0-2-riscv64
49472068 bytes read in 47 ms (1003.8 MiB/s)
Retrieving file: /boot/vmlinux-5.4.0-2-riscv64
9381656 bytes read in 13 ms (688.2 MiB/s)
append: rw noquiet root=/dev/vda1
## Flattened Device Tree blob at ff7410c0
   Booting using the fdt blob at 0xff7410c0
   Using Device Tree in place at 00000000ff7410c0, end 00000000ff744d99

Starting kernel ...

[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] Linux version 5.4.0-2-riscv64 (debian-kernel@lists.debian.org) (gcc version 9.2.1 20200104 (Debian 9.2.1-22)) #1 SMP Debian 5.4
.8-1 (2020-01-05)
[    0.000000] Initial ramdisk at: 0x(____ptrval____) (49472068 bytes)
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000ffffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080200000-0x00000000ffffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x00000000ffffffff]
[    0.000000] software IO TLB: mapped [mem 0xf9741000-0xfd741000] (64MB)
[    0.000000] elf_hwcap is 0x112d
[    0.000000] percpu: Embedded 26 pages/cpu s68440 r8192 d29864 u106496
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 515592
[    0.000000] Kernel command line: rw noquiet root=/dev/vda1

カーネルとinitramfsがロードされて起動を開始します。⁠Starting kernel …」のあとはコンソールデバイスの初期化が完了してログを出力するようになるまで時間がかかるのでしばらく待ちましょう。

Debian GNU/Linux bullseye/sid riscv ttyS0

riscv login: shibata
Password:
Linux riscv 5.4.0-2-riscv64 #1 SMP Debian 5.4.8-1 (2020-01-05) riscv64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
shibata@riscv:~$

systemdも起動したらログインバナーが表示されるので、先ほど作成したアカウントでログインしましょう。

/proc/cpuinfoを確認するとRISC-VのISAモードが表示されていることがわかります。

shibata@riscv:~$ cat /proc/cpuinfo
processor       : 0
hart            : 0
isa             : rv64imafdcu
mmu             : sv48

もちろん、実行されるバイナリもriscv64向けのバイナリです。

shibata@riscv:~$ file /usr/bin/busybox
/usr/bin/busybox: ELF 64-bit LSB shared object, UCB RISC-V, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, for GNU/Linux 4.15.0, BuildID[sha1]=5cbc897e13ba8b5015132999916418f8423d5728, stripped

あとは「ごく普通のどこにでも存在するDebian」なので、必要なパッケージをインストールしてエンジョイしましょう。

ちなみに動いているのはQEMUの上なので、⁠Ctrl-a c」と入力すればQEMUモニターに切り替えられます。レジスタのダンプも可能なので、何か問題になったときもすぐに原因がわかりますね!

QEMU 4.0.0 monitor - type 'help' for more information
(qemu) info registers
 pc       ffffffe000085596
 mhartid  0000000000000000
 mstatus  0000000000000000
 mip      0000000000000000
 mie      00000000000002aa
 mideleg  0000000000000222
 medeleg  000000000000b109
 mtvec    0000000080000488
 mepc     ffffffe00046d810
 mcause   0000000000000009
 zero 0000000000000000 ra   ffffffe0000bdf94 sp   ffffffe0007d3f30 gp   ffffffe00087f080
 tp   ffffffe0007da300 t0   0000000000000000 t1   0000000000006000 t2   0000000000003933
 s0   ffffffe0007d3f40 s1   ffffffe00087f7a0 a0   ffffffe0000bdf94 a1   0000000000000001
 a2   0000000000000000 a3   ffffffe0008b6b58 a4   000000000000083e a5   0000000000000000
 a6   000000005bd58b6b a7   0000000000000000 s2   0000000000000000 s3   ffffffe00087f96c
 s4   0000000000000001 s5   ffffffe00087f618 s6   ffffffe000036008 s7   0000000000000000
 s8   00000000fff63cc6 s9   0000000000000003 s10  0000000000000000 s11  00000000ff770b10
 t3   0000000000000000 t4   0000000000000001 t5   ffffffe000705838 t6   0000000000000000
 ft0  0000000000000000 ft1  0000000000000000 ft2  0000000000000000 ft3  0000000000000000
 ft4  0000000000000000 ft5  0000000000000000 ft6  0000000000000000 ft7  0000000000000000
 fs0  0000000000000000 fs1  0000000000000000 fa0  0000000000000000 fa1  0000000000000000
 fa2  0000000000000000 fa3  0000000000000000 fa4  0000000000000000 fa5  0000000000000000
 fa6  0000000000000000 fa7  0000000000000000 fs2  0000000000000000 fs3  0000000000000000
 fs4  0000000000000000 fs5  0000000000000000 fs6  0000000000000000 fs7  0000000000000000
 fs8  0000000000000000 fs9  0000000000000000 fs10 0000000000000000 fs11 0000000000000000
 ft8  0000000000000000 ft9  0000000000000000 ft10 0000000000000000 ft11 0000000000000000

おすすめ記事

記事・ニュース一覧