はじめに
他のサイトでもいろいろな構成でbuildされているが、今回は、OpenSBIというBoot Loaderを用いて、linux kernel(v5.3.6)を動かしてみた。
/sbin/init
以下で動作させるためのスクリプトも用意する必要があるが、これはbusyboxというユーザーランドでの実行ファイル/コマンド群を生成してくれるツールを使う。各種distibutionに頼っていないので、構成としてはコンパクトだと思う。
Note) 自分で調べた限りだと、busybear-linuxやbuildrootがあるが、これらはbusyboxをbuildの最中で構成しているし、結構いろいろなライブラリを突っ込んでいるので実験的にlinux kernelをbuildするのには少し仕組みが見えにくいかな、と思った。
reference
[1] https://pdos.csail.mit.edu/6.828/2019/tools.html 主にqemu辺りで参考にした。
[2] https://github.com/riscv/riscv-linux/issues/120#issuecomment-354359675 linux-kernelのビルドを参考にした
[3] https://github.com/riscv/riscv-gnu-toolchain 現時点では、
d5bea51083ec38172b84b7cd5ee99bfcb8d2e7b0
[4] https://github.com/riscv/opensbi/tree/v0.5 現時点ではv0.5。https://github.com/riscv/opensbi/blob/v0.5/docs/platform/qemu_virt.md#execution-on-qemu-risc-v-64-bit も参考になる
[5] https://static.dev.sifive.com/U54-MC-RVCoreIP.pdf SIFIVE社のRISC-V multiprocessors 仕様書(https://github.com/torvalds/linux/blob/v4.19/drivers/irqchip/irq-sifive-plic.c#L23から)
[6] https://github.com/UCanLinux/riscv64-sample#create-a-disk-with-50mb-capacity rootfsの作成の際に参考にした。
手順
準備
vagrant imageの "ubuntu/bionic64"(18.04)をベースに作成する:
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" config.vm.synced_folder "./", "/home/vagrant/shared" config.vm.provider "virtualbox" do |vb| vb.cpus = 2 vb.memory = 4096 vb.customize ["modifyvm", :id, "--ioapic", "on"] end config.disksize.size = '100GB' end
今回は、15~20GBくらいのライブラリを展開することになるので、vagrant-disksizeというプラグインを用いている。導入手順は https://qiita.com/yut_h1979/items/c84c490053877beee5c1 を参考のこと。
注意) config.vm.synced_folder
の上で(上の設定の場合、"/home/vagrant/shared"
のディレクトリ以下で)ビルドを実行すると、busyboxのbuild時のハードリンク作成コマンド(ln -f
)で失敗してしまう(おそらく、mount filesystem https://forums.virtualbox.org/viewtopic.php?f=3&t=91596 が原因)ため、避けたほうが良い。
vagrant up && vagrant ssh
として、ゲストOSに入った後、以下のライブラリをinstallしておく:
sudo apt-get update # for riscv-gnu-toolchain sudo apt-get install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev # for qemu sudo apt-get install pkg-config libglib2.0-dev libpixman-1-dev # for linux-kernel menuconfig sudo apt-get install libncurses-dev cd ~/ # move to working directory
riscv-gnu-toolchain
これは、gccやらGNUのRISC-V versionのツール群であり、RISC-Vで動く種々のコードをビルドするのに必要である。
最初に以下のようにRISCV, PATHという環境変数をセットしておく:
export RISCV=/opt/riscv # ~/.bashrc export PATH="$RISCV/bin:$PATH" # ~/.profile
# https://github.com/riscv/riscv-gnu-toolchain (@ d5bea51083ec38172b84b7cd5ee99bfcb8d2e7b0) git clone --recursive https://github.com/riscv/riscv-gnu-toolchain cd riscv-gnu-toolchain ./configure --prefix=$RISCV ## `make` or `make newlib` sudo make newlib # generates riscv64-unknown-elf-** for opensbi sudo make linux # for generates riscv64-unknown-linux-gnu-** for building linux-kernel cd ..
Note) riscv64-unknown-linux-gnu-
とriscv64-unknown-elf-
どちらもビルドする必要がある。riscv64-unknown-elf-
はopensbiのbuildに必要であり、riscv64-unknown-linux-gnu-
はlinux kernelのbuildに必要である(linuxをbuildするのにriscv64-unknown-elf-
を用いると、linkerの部分で落ちる)。
qemu
qemuは2019/10/15時点での最新バージョン4.1.0を用いている:
# download && install qemu(See Reference[1]) wget https://download.qemu.org/qemu-4.1.0.tar.xz tar -Jxvf qemu-4.1.0.tar.xz cd qemu-4.1.0 ./configure --disable-werror --prefix=$RISCV --target-list="riscv64-softmmu" make sudo make install cd ../
qemuとtoolchainのinstallで以下の実行可能ファイルが生成されているはずだ:
$ ls /opt/riscv/bin/ ivshmem-client riscv64-unknown-elf-run ivshmem-server riscv64-unknown-elf-size qemu-edid riscv64-unknown-elf-strings qemu-ga riscv64-unknown-elf-strip qemu-img riscv64-unknown-linux-gnu-addr2line qemu-io riscv64-unknown-linux-gnu-ar qemu-nbd riscv64-unknown-linux-gnu-as qemu-pr-helper riscv64-unknown-linux-gnu-c++ qemu-system-riscv64 riscv64-unknown-linux-gnu-c++filt riscv64-unknown-elf-addr2line riscv64-unknown-linux-gnu-cpp riscv64-unknown-elf-ar riscv64-unknown-linux-gnu-elfedit riscv64-unknown-elf-as riscv64-unknown-linux-gnu-g++ riscv64-unknown-elf-c++ riscv64-unknown-linux-gnu-gcc riscv64-unknown-elf-c++filt riscv64-unknown-linux-gnu-gcc-9.2.0 riscv64-unknown-elf-cpp riscv64-unknown-linux-gnu-gcc-ar riscv64-unknown-elf-elfedit riscv64-unknown-linux-gnu-gcc-nm riscv64-unknown-elf-g++ riscv64-unknown-linux-gnu-gcc-ranlib riscv64-unknown-elf-gcc riscv64-unknown-linux-gnu-gcov riscv64-unknown-elf-gcc-9.2.0 riscv64-unknown-linux-gnu-gcov-dump riscv64-unknown-elf-gcc-ar riscv64-unknown-linux-gnu-gcov-tool riscv64-unknown-elf-gcc-nm riscv64-unknown-linux-gnu-gdb riscv64-unknown-elf-gcc-ranlib riscv64-unknown-linux-gnu-gdb-add-index riscv64-unknown-elf-gcov riscv64-unknown-linux-gnu-gfortran riscv64-unknown-elf-gcov-dump riscv64-unknown-linux-gnu-gprof riscv64-unknown-elf-gcov-tool riscv64-unknown-linux-gnu-ld riscv64-unknown-elf-gdb riscv64-unknown-linux-gnu-ld.bfd riscv64-unknown-elf-gdb-add-index riscv64-unknown-linux-gnu-nm riscv64-unknown-elf-gprof riscv64-unknown-linux-gnu-objcopy riscv64-unknown-elf-ld riscv64-unknown-linux-gnu-objdump riscv64-unknown-elf-ld.bfd riscv64-unknown-linux-gnu-ranlib riscv64-unknown-elf-nm riscv64-unknown-linux-gnu-readelf riscv64-unknown-elf-objcopy riscv64-unknown-linux-gnu-run riscv64-unknown-elf-objdump riscv64-unknown-linux-gnu-size riscv64-unknown-elf-ranlib riscv64-unknown-linux-gnu-strings riscv64-unknown-elf-readelf riscv64-unknown-linux-gnu-strip $ which qemu-system-riscv64 /opt/riscv/bin/qemu-system-riscv64 $ which riscv64-unknown-elf-gcc /opt/riscv/bin/riscv64-unknown-elf-gcc $ which riscv64-unknown-linux-gnu-gcc /opt/riscv/bin/riscv64-unknown-linux-gnu-gcc
opensbi
同様のbootloaderにriscv-pkというのもある。これは、BBL(Berkeley Boot Loader)を生成するためのパッケージであるが、legacyな扱いらしい1。コード読んでもこちらのほうが詳細なので、こちらを用いる。
no payload
とりあえず、opensbiだけを動作させるには、qemuとriscv64-unknown-elf-gccがあればよい。以下のコマンドで起動する(Reference[4] (https://github.com/riscv/opensbi/blob/v0.5/docs/platform/qemu_virt.md#execution-on-qemu-risc-v-64-bit)の"No Payload Case"の部分を参考にした):
# build(https://github.com/riscv/opensbi/blob/v0.5/docs/platform/qemu_virt.md#execution-on-qemu-risc-v-64-bitの"No Payload Case"より) make CROSS_COMPILE=riscv64-unknown-elf- PLATFORM=qemu/virt # run qemu-system-riscv64 -M virt -m 256M -nographic -kernel build/platform/qemu/virt/firmware/fw_payload.elf
build時はCLOSS_COMPILEがないと、以下のようなエラーが出力されてしまうので、付加する:
CC lib/sbi/riscv_asm.o cc: error: unrecognized argument in option ‘-mabi=lp64’ cc: note: valid arguments to ‘-mabi=’ are: ms sysv cc: error: unrecognized argument in option ‘-mcmodel=medany’ cc: note: valid arguments to ‘-mcmodel=’ are: 32 kernel large medium small; did you mean ‘medium’? cc: error: unrecognized command line option ‘-mno-save-restore’; did you mean ‘-Wno-selector’? cc: error: unrecognized command line option ‘-mstrict-align’; did you mean ‘-Wstrict-aliasing’? Makefile:296: recipe for target '/home/vagrant/opensbi/build/lib/sbi/riscv_asm.o' failed
build時は以下のコマンドが実行される => https://gist.github.com/knknkn1162/d7bca477883c898e33111b311bb7244b
結果は以下のようになり、うまくいく => https://gist.github.com/knknkn1162/cfbe72349c427e6aed16d32056fb3705
Linux Kernel Payload
が必要でこれらを準備した後、qemuを用いてbootする。
linux-kernel
# create .config git clone -b v5.3.6 --depth 1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git # root directoryがlinux-stableになる。 make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- menuconfig make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- all // 省略 DTC arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dtb GEN .version CHK include/generated/compile.h LD vmlinux.o MODPOST vmlinux.o MODINFO modules.builtin.modinfo KSYM .tmp_kallsyms1.o KSYM .tmp_kallsyms2.o LD vmlinux SYSMAP System.map Building modules, stage 2. MODPOST 2 modules CC drivers/video/backlight/lcd.mod.o LD [M] drivers/video/backlight/lcd.ko CC fs/nfs/flexfilelayout/nfs_layout_flexfiles.mod.o LD [M] fs/nfs/flexfilelayout/nfs_layout_flexfiles.ko OBJCOPY arch/riscv/boot/Image GZIP arch/riscv/boot/Image.gz
arch/riscv/boot以下で作成されたファイルは https://gist.github.com/knknkn1162/bf862b4361ca04458ad76e6210f829fa のようになった。
OpenSBIのbuild
linux kernelのbuildでarch/riscv/boot/Imageが用意できたので、OpenSBIのbuild処理が実行できる:
make CROSS_COMPILE=riscv64-unknown-elf- PLATFORM=qemu/virt FW_PAYLOAD_PATH=../linux-stable/arch/riscv/boot/Image
build時に実行されるコマンドは以下の通り => https://gist.github.com/knknkn1162/dab88ab196a1b915469a9e3c03e536a0
busyboxのinstall, buildとrootfsの作成
sudo apt-get install -y libncurses5-dev # https://github.com/mirror/busybox/tree/1_31_0 git clone -b 1_31_0 --depth 1 https://github.com/mirror/busybox.git cd busybox make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig # check Busybox Setttings => Build BusyBox as a static binary (no shared libs) make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- menuconfig make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- install cd ../
make install
することで、_installというdirectoryができる。これはrootfsの種になるもので以下のようなファイルで構成されている:
$ tree -d ~/busybox/_install /home/vagrant/busybox/_install ├── bin ├── sbin └── usr ├── bin └── sbin $ ls -ls ~/busybox/_install/sbin/init 0 lrwxrwxrwx 1 vagrant vagrant 14 Oct 14 07:25 /home/vagrant/busybox/_install/sbin/init -> ../bin/busybox
、後に、/mnt/rootfs
の中に突っ込んでおく。
rootfsの作成に関しては、reference[6]を参考にした。
# in ${HOME} directory dd if=/dev/zero of=rootfs.img bs=1M count=300 mkfs.ext2 -L riscv-rootfs rootfs.img sudo mkdir /mnt/rootfs sudo mount rootfs.img /mnt/rootfs # inside /mnt/rootfs sudo cp -ar ../busybox/_install/* /mnt/rootfs mkdir /mnt/rootfs/{dev,home,mnt,proc,sys,tmp,var} sudo chown -R -h root:root /mnt/rootfs/ $ df /mnt/rootfs Filesystem 1K-blocks Used Available Use% Mounted on /dev/loop1 297485 3683 278442 2% /mnt/rootfs $ mount /home/vagrant/rootfs.img on /mnt/rootfs type ext2 (rw,relatime)
Note) /mnt/rootfsの中にdev directoryを作らないと、以下のようなmessageが出続けてしまう:
can't open /dev/tty4: No such file or directory can't open /dev/tty3: No such file or directory can't open /dev/tty2: No such file or directory can't open /dev/tty4: No such file or directory can't open /dev/tty3: No such file or directory can't open /dev/tty2: No such file or directory
OpenSBIのrun
opensbiのdirectoryにて以下を実行する。主に、https://github.com/riscv/opensbi/blob/v0.5/docs/platform/qemu_virt.md#execution-on-qemu-risc-v-64-bit を参考にした:
# in opensbi directory qemu-system-riscv64 -M virt -m 256M -nographic -kernel build/platform/qemu/virt/firmware/fw_payload.elf -drive file=../rootfs.img,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -append "root=/dev/vda rw console=ttyS0
結果は以下のようになり、うまくいっている => https://gist.github.com/knknkn1162/047f186106fdcaa016261eff3ac95854
終わりに
個人的にはrootfsの作成が不慣れなためにかなり時間がかかってしまった。次の記事ではopensbi(boot loader)のコード解説をやっていきたいと思う。xv6-riscvのboot部分より一回り規模の大きなコードになっているが、基本のところはxv6-riscvと共通しているので、慣れればそれほど難しくなさそう。