EFI stubなArch Linuxのインストール
QEMU上の仮想マシンにEFI stubなArch Linuxをインストールする。 ここでのホストはUbuntu 16.04.4 TLSとする。 なお[1][2][3][4]を参考にした。
OVMFのダウンロードとArch Linuxのインストールディスクのダウンロード
最初にOVMFをダウンロードして解凍する。 OVMFはTianoCoreをベースにした仮想マシン向けのUEFI実装である。
$ wget https://www.kraxel.org/repos/jenkins/edk2/edk2.git-ovmf-x64-0-20180508.84.g7cd8a57599.noarch.rpm $ rpm2cpio edk2.git-ovmf-x64-0-20180508.84.g7cd8a57599.noarch.rpm | cpio -idmv $ cp usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd . $ cp usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd .
Arch Linuxのインストールディスクをダウンロードする。
$ wget http://ftp.jaist.ac.jp/pub/Linux/ArchLinux/iso/2018.05.01/archlinux-2018.05.01-x86_64.iso
インストール先のディスクイメージを作成する。
$ qemu-img create -f raw arch-amd64.img 32G
仮想マシンの起動と設定
インストールディスクから起動する。 これ以降は仮想マシンでの操作となる。
$ qemu-system-x86_64 -enable-kvm -k ja -m 2048 -localtime -drive if=pflash,format=raw,readonly,file=OVMF_CODE-pure-efi.fd -drive if=pflash,format=raw,file=OVMF_VARS-pure-efi.fd -hda arch-amd64.img -net nic,model=e1000 -net user -cdrom archlinux-2018.05.01-x86_64.iso -boot d
キーボードレイアウトを設定。
# loadkeys jp106
gdisk
でパーティションを作成。
ここでは/dev/sda1を512MBのEFIパーティション、
/dev/sda2を残りのすべての容量としたLinuxパーティションとした。
# gdisk /dev/sda
Command (? for help):o
Command (? for help):n Permission number: 1 First sector : Last sector : +512M Hex code or GUID : ef00
Command (? for help):n Permission number: 2 First sector : Last sector : Hex code or GUID : 8300
最後に書き込みを行う。
Command (? for help):w
各パーティションをフォーマット
# mkfs.vfat -F32 /dev/sda1 # mkfs.ext4 /dev/sda2
それぞれをマウント。
# mount /dev/sda2 # mkdir -p /mnt/boot # mount /dev/sda1 /mnt/boot
ダウンロードのミラーサイトを設定。
# nano /etc/pacman.d/mirrorlist
システムのインストール。
# pacstrap -i /mnt base base-devel
fstabの生成。
# genfstab -U -p /mnt >> /mnt/etc/fstab
localeの設定。
# locale-gen # echo LANG=en_US.UTF-8 > /etc/locale.conf # export LANG=en_US.UTF-8
キーボードマップの設定。
# echo KEYMAP=jp106 > /etc/vconsole.conf
localtimeの設定。
# rm /etc/localtime # ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
# hwclock -u -w
ホスト名の設定。
# echo "arch-vm" > /etc/hostname
DHCPクライアントの設定。
# systemctl enable dhcpcd.service
パスワードの設定。
# passwd
GRUB2の代わりにefibootmgr
でEFI Variableを設定。
# efibootmgr -d /dev/sda -p 1 -c -L "Arch Linux" -l /vmlinuz-linux -u "root=dev/sda2 rw initrd=initramfs-linux.img" # efibootmgr -v
exitしてhalt。
# exit # halt
以下で正しく起動することを確認。
$ qemu-system-x86_64 -enable-kvm -k ja -m 2048 -localtime -drive if=pflash,format=raw,readonly,file=OVMF_CODE-pure-efi.fd -drive if=pflash,format=raw,file=OVMF_VARS-pure-efi.fd -hda arch-amd64.img -net nic,model=e1000 -net user
EFI stubなLinux kernelのビルド
先の説明でインストールしたArch LinuxはEFI stubとなっている。 ここでは、ホストでLinux kernelをビルドしそれを仮想マシンにインストールしてみる。
Linuxカーネルのビルド
カーネルをダウンロードする。
$ git clone https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux.git --branch v4.17 --depth 1
ホストのconfigをコピーして多少の修正を行い、ホスト上でビルドする。
make menuconfig
でCONFIG_EFI_STUB=y
となっていることを確認する。
make
するときのjob数はホストのCPUのコア数に応じて変更する。
$ cp /boot/config-4.15.0-33-generic .config $ make menuconfig $ make -j9
Linux kernelの仮想マシンへのインストール
仮想マシンのディスクイメージをマウントする。
# /sbin/kpartx -av arch-amd64.img # mount /dev/mapper/loop0p2 /mnt
# make INSTALL_MOD_PATH=/mnt modules_install # mkdir -p /mnt/var/tmp # cp System.map /mnt/var/tmp # cp arch/x86_64/boot/bzImage /mnt/var/tmp # umount /mnt
最後に仮想マシン上でホストでビルドしたbzImageとSystem.mapをコピーし、 initramfsを生成する。
# cp -f /var/tmp/bzImage /boot/vmlinuz-linux # cp -f /var/tmp/System.map /boot/System.map # mkinitcpio -c /etc/mkinitcpio.conf -g /boot/initramfs-linux.img
最後に再起動し、ビルドしたバージョンのカーネルであることを確認する。
参考文献
技術書典5で「UEFI読本 基礎編 Linux編」を頒布
2018年10月8日に池袋サンシャインシティで開催される 技術書典5において「海洋軟件」として 「UEFI読本 基礎編 Linux編」を頒布する. 配置先は「お09」である.
UEFI読本 基礎編 Linux編
以下,本書「はじめに」より抜粋.
UEFIが2005年に登場してから10年以上が経過した。 現在のx86系CPUを搭載したコンピュータのほとんどが UEFIに対応しており、かつOSも当然のように対応している。 しかし、UEFIやそこからの起動を解説をした文書は非常に少なく、 規格書かソースコードを読み解くしかなかった。 本書はそのような現状に応えるべく企画された。
本書は「UEFI読本 基礎編 Linux編」というUEFIについての解説書である。 「基礎編」と「Linux編」の2部構成となっており、 「基礎編」ではUEFIでのOSの起動を知るためのUEFIの基礎について解説を行い、 「Linux編」ではLinux kernelのソースコードを実際に追跡してみていく。
LKL.js: Linux kernelを直接JavaScript上で動かす
Linux kernelを直接JavaScript上で動かした. つまり,JSLinuxのようにEmulatorをJavaScriptで作成し, その上でLinuxを動かすのではなく, JavaScriptで書かれたLinuxを生成し,それを動かす,ということである.
リポジトリは以下の通り.
なお lkl.js Demo にデモを用意した. SharedArrayBufferを有効にして試してみてほしい.
続きを読むRustでBrainfuck処理系を高速化して遊んでみる
Brainfuckとは><+-.,[]
の8つの命令からなるプログラミング言語である.
実装が簡単であるために,すでに多くの言語によって実装がなされている.
ここでは,
Adventures in JIT compilation: Part 1 - an interpreter
を参考にC++の実装をRustに移植し,そのパフォーマンスを計測し,比較をして遊んでみる.
Brainfuckの実装
実装したBrainfuckは
- simpleinterp
- optinterp
- optinterp2
- optinterp3
の4通りがある.
simpleinterpは素朴な実装であり,高速化はなされていない.
optinterpは[
と]
の対応表を実行時に作成することにより最適化を図っている.
optinterp2は8つの命令に加えて高水準な命令を定義し,
局所的に実行される命令をそれらの命令に置き換えることで最適化を行う.
optinterp3はさらに実行されるループに使われるパターンを高水準な命令に置き換え,最適化を行う.
実装の概要については BrainF*ckの高速化 において解説している.
ベンチマークと計測方法
先に挙げたブログ内では
mandelbrot
とfactor
の2つのスクリプトをベンチマークに用いている.
ここでもこれらのスクリプトを用いることとする.
実行速度の計測にはtime
を用いる.
計測時には--release
オプションを指定し,最適化がなされるようにした.
実行環境は
MacBook Air (11-inch, Early 2014) CPU: 1.7 GHz Intel Core i7 RAM: 8GB 1600 MHz DDR3 OS: macOS High Sierra 10.13.5 cargo: 1.26.0 rustc: 1.26.2
である.
mandelbrot
mandelbrot
の場合の比較.
simpleinterp: cargo run --release mandelbrot.bf 47.78s user 0.14s system 99% cpu 48.037 total optinterp: cargo run --release mandelbrot.bf 25.90s user 0.10s system 99% cpu 26.080 total optinterp2: cargo run --release mandelbrot.bf 8.30s user 0.08s system 99% cpu 8.443 total optinterp3: cargo run --release mandelbrot.bf 6.46s user 0.09s system 99% cpu 6.617 total
factor
factor
の場合の比較
simpleinterp: cargo run --release factor.bf < prime 17.16s user 0.10s system 99% cpu 17.321 total optinterp: cargo run --release factor.bf < prime 9.64s user 0.09s system 99% cpu 9.790 total optinterp2: cargo run --release factor.bf < prime 3.45s user 0.09s system 98% cpu 3.611 total optinterp3: cargo run --release factor.bf < prime 2.93s user 0.07s system 98% cpu 3.039 total
まとめ
いずれのスクリプトの場合でも simpleinterp > optinterp > optinterp2 > optinterp3 の順の実行時間であった.これは元記事とも一致する. さらに元記事のようにJITなどを用いて最適化することも考えられる.
参考文献
8cc in Lazy K
本日は4月1日で,エイプリルフールの日である. ただ,書いている現在は午後9時で,ちょっと嘘をつくには遅すぎる時間である.そこで,今回は何にも役に立たないものを作ってみようと思った. そこで,表題の通り,Lazy Kで書かれた8ccを生成して遊んでみた.
生成
- ELVMで生成された8cc.bf をダウンロード
bf2lazy.c をダウンロード
2.で1.をLazy Kに変換
gcc bf2lazy.c -o bf2lazy ./bf2lazy < 8cc.bf > 8cc.bf.lazy
- 生成されたLazy Kのコード
$ du -h 8cc.bf.lazy 12G 8cc.bf.lazy
バカみたいにでかくなる.
検証
おそらくは正しく動くはずであるが,8cc.bfだけで検証するのが相当かかるようなので,8cc.bf.lazyではさらに検証に時間がかかるはずなので,やらないこととする.
まとめ
適当にやったらLazy Kのコードがバカでかくなった. 生成されたコードは無用の長物なので,どこにもあげないが,簡単に生成できるので,暇な人はやってみよう.
Google V8 JavaScript EngineでのWebAssemblyのi32.addの実装を見てみる
WebAssembly(以下,wasm)については,既に多くの解説記事が存在するため,
wasmについての説明は割愛する.
ここでは,wasmにおいてとある命令i32.addがどのように実行されるのかを見ていく.
参照する実装はGoogle V8 JavaScript Engineの
1b254a25163fd84a7696ff62e87cb6dcde7e13f2
である.
Rustでcoreboot payload
この記事は自作OS Advent Calendar の19日目の記事として書かれた. ここでは,corebootのpayloadをRustを使って作ってみる. corebootはファームウェアなのでOSとは異なるが,そこはご愛嬌ということで.
続きを読むLinux kernelの5-Level Paging有効化部分を読んでみる
この記事はLinux Advent Calendar 14日目の記事として書かれた. 本記事ではLinuxにおける5-Level Paging(la57 paging)の実装を見ていく.
続きを読む