QEMUでRaspbianのsandbox環境を構築する

# はじめに

色々なソフトウェアをRasberry Piでコンパイルして動かしたい！
Rasberry Piで動くソフトウェアを作りたい！
でも・・・本番環境(SD)は汚したくない・・・・

　:hatching_chick: QEMU だ！ :hatching_chick:

# 環境

```shell-session:bash
demo@rasp $ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
demo@rasp $ qemu-system-arm --version
QEMU emulator version 2.5.0 (Debian 1:2.5+dfsg-5ubuntu10.9), Copyright (c) 2003-2008 Fabrice Bellard
demo@rasp $ pulseaudio --version
pulseaudio 8.0
demo@rasp $ git --version
git version 2.7.4
demo@rasp $ wget --version
GNU Wget 1.17.1 built on linux-gnu.
```

※ qemu や pulseaudio が入ってない場合はインストールしましょう。

```console
sudo apt-get install qemu-system-arm pulseaudio
```

# イメージの準備

## 作業フォルダを作成する

```shell-session:bash
demo@rasp $ cd ~/
demo@rasp $ mkdir qemu_workspace
demo@rasp $ cd qemu_workspace
```

## 公式イメージをダウンロードする
[Raspbian](https://www.raspberrypi.org/downloads/raspbian/) をダウンロードします。
※例は lite 版なので、Pixel 版の方は適宜読みかえてください。

```shell-session:bash
demo@rasp $ wget -O raspbian_img.zip https://downloads.raspberrypi.org/raspbian_lite_latest
#### 省略 ####
`raspbian_img.zip' へ保存完了 [308489959/308489959]
demo@rasp $ unzip raspbian_img.zip
Archive:  raspbian_img.zip
  inflating: 2017-03-02-raspbian-jessie-lite.img
```

## イメージを編集して QEMU に対応させる
まずはイメージをマウントします。

```shell-session:bash
demo@rasp $ sudo kpartx -a ~/qemu_workspace/2017-03-02-raspbian-jessie-lite.img
[sudo] metalels のパスワード:
demo@rasp $ ls -l /dev/mapper/loop*
lrwxrwxrwx 1 root root 7  3月 16 17:39 /dev/mapper/loop0p1 -> ../dm-2
lrwxrwxrwx 1 root root 7  3月 16 17:39 /dev/mapper/loop0p2 -> ../dm-3
demo@rasp $ sudo mount /dev/mapper/loop0p2 /mnt/ -o loop,rw
demo@rasp $ ls -l /mnt
合計 88
drwxr-xr-x  2 root root  4096  3月  4 00:29 bin/
drwxr-xr-x  2 root root  4096  3月  4 01:17 boot/
drwxr-xr-x  4 root root  4096  3月  4 00:23 dev/
drwxr-xr-x 82 root root  4096  3月  4 01:18 etc/
drwxr-xr-x  3 root root  4096  3月  4 00:27 home/
drwxr-xr-x 17 root root  4096  3月  4 00:33 lib/
#### 省略 ####
```

イメージ内の以下を変更します。

```console
sudo vi <ファイルパス>
```

・libarmmem.so をロードしないように変更

```diff:/mnt/etc/ld.so.preload
- /usr/lib/arm-linux-gnueabihf/libarmmem.so
+ #/usr/lib/arm-linux-gnueabihf/libarmmem.so
```

・デバイスの名称を実機同等に変更(ファイル作成)

```diff:/mnt/etc/udev/rules.d/90-qemu.rules
+ KERNEL=="sda", SYMLINK+="mmcblk0"
+ KERNEL=="sda?", SYMLINK+="mmcblk0p%n"
+ KERNEL=="sda2", SYMLINK+="root"
```

イメージをアンマウントします。

```console
sudo umount /mnt
```

※完成したイメージはバックアップしておく事をお勧めします。

```shell-session:bash
demo@rasp $ mv 2017-03-02-raspbian-jessie-lite.img 2017-03-02-raspbian-jessie-lite-mod-qemu.img
demo@rasp $ gzip 2017-03-02-raspbian-jessie-lite-mod-qemu.img
demo@rasp $ ls -l
合計 301280
-rw-r--r-- 1 metalels metalels 308496209  3月 16 17:48 2017-03-02-raspbian-jessie-lite-mod-qemu.img.gz
demo@rasp $ mkdir backup
demo@rasp $ cp 2017-03-02-raspbian-jessie-lite-mod-qemu.img.gz backup/.
demo@rasp $ ls -lR
ls -lR
.:
合計 301284
-rw-r--r-- 1 metalels metalels 308496209  3月 16 17:48 2017-03-02-raspbian-jessie-lite-mod-qemu.img.gz
drwxrwxr-x 2 metalels metalels      4096  3月 16 17:56 backup/

./backup:
合計 301280
-rw-r--r-- 1 metalels metalels 308496209  3月 16 17:56 2017-03-02-raspbian-jessie-lite-mod-qemu.img.gz
```

# QEMUで実行

## 実行用カーネルの準備

```shell-session:bash
demo@rasp $ git clone https://github.com/dhruvvyas90/qemu-rpi-kernel.git
Cloning into 'qemu-rpi-kernel'...
remote: Counting objects: 169, done.
remote: Total 169 (delta 0), reused 0 (delta 0), pack-reused 169
Receiving objects: 100% (169/169), 21.26 MiB | 1.83 MiB/s, done.
Resolving deltas: 100% (82/82), done.
Checking connectivity... done.
```

## 実行用スクリプトの作成

```shell-session:bash
demo@rasp $ cd ~/qemu_workspace
demo@rasp $ vi run_qemu_raspbian.sh
demo@rasp $ chmod u+x run_qemu_raspbian.sh
demo@rasp $ ls -lh
合計 1.3G
-rw-r--r-- 1 metalels metalels 1.3G  3月 16 17:48 2017-03-02-raspbian-jessie-lite-mod-qemu.img
drwxrwxr-x 2 metalels metalels 4.0K  3月 16 17:56 backup/
-rwxrw-r-- 1 metalels metalels  289  3月 16 18:00 run_qemu_raspbian.sh*
```

```bash:run_qemu_raspbian.sh
#!/bin/bash
qemu-system-arm \
  -M versatilepb \
  -cpu arm1176 \
  -m 256M \
  -no-reboot \
  -hda 2017-03-02-raspbian-jessie-lite-mod-qemu.img \
  -kernel qemu-rpi-kernel/kernel-qemu-4.4.34-jessie \
  -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw console=ttyAMA0" \
  -serial stdio
```
※オプションは適宜変更してください。

## 実行する

実行には X Window System の設定が必要です。  
UbuntuのGUI等で実行している場合は問題ありませんが、私は Windows10 から TeraTerm で接続してるので、SSH転送 + Xming を利用しています。

```shell-session:bash
demo@rasp $ ./run_qemu_raspbian.sh
WARNING: Image format was not specified for '2017-03-02-raspbian-jessie-lite-mod-qemu.img' and probing guessed raw.
         Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
         Specify the 'raw' format explicitly to remove the restrictions.
pulseaudio: set_sink_input_volume() failed
pulseaudio: Reason: Invalid argument
pulseaudio: set_sink_input_mute() failed
pulseaudio: Reason: Invalid argument
Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0x0
Initializing cgroup subsys cpuset
Initializing cgroup subsys cpu
Initializing cgroup subsys cpuacct
#### 省略 ####
[  OK  ] Started Permit User Sessions.
My IP address is 10.0.2.15
[  OK  ] Started /etc/rc.local Compatibility.
         Starting Hold until boot process finishes up...
         Starting Terminate Plymouth Boot Screen...

Raspbian GNU/Linux 8 raspberrypi ttyAMA0

raspberrypi login:
```

- X Window System(TeraTermのSSH転送 + Xming) の様子

![xming.png](https://qiita-image-store.s3.amazonaws.com/0/25413/6e5da0de-f8f1-7ca7-fe9e-de256ae9c8e7.png)

- TeraTerm の様子

![teraterm.png](https://qiita-image-store.s3.amazonaws.com/0/25413/67bf6c0f-2396-fd45-6d42-dbdb6b3ff765.png)

初期ログインユーザーは下記が用意されているようです。
USER: pi
PASS: raspberry

終了するときは普通にシャットダウン。

```shell-session:raspbian
pi@raspberrypi:~$ sudo shutdown -h now
[  OK  ] Started Show Plymouth Power Off Screen.
[  OK  ] Stopped target Network.
         Stopping dhcpcd on all interfaces...
[  OK  ] Stopped target Remote File Systems.
[  OK  ] Stopped target Remote File Systems (Pre).
[  OK  ] Stopped dhcpcd on all interfaces.
#### 省略 ####
```

# 容量拡張
そのままのイメージファイルだとサイズが小さすぎてすぐ容量不足になってしまうので、拡張します。

## イメージの拡張

```shell-session:bash
demo@rasp $ qemu-img resize 2017-03-02-raspbian-jessie-lite-mod-qemu.img +2G
WARNING: Image format was not specified for '2017-03-02-raspbian-jessie-lite-mod-qemu.img' and probing guessed raw.
         Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
         Specify the 'raw' format explicitly to remove the restrictions.
Image resized.
```

## ファイルシステムを拡張
拡張したイメージでraspbianを起動しログイン後、パーティショニングを変更する。

```shell-session:raspbian
pi@raspberrypi:~$ sudo fdisk /dev/sda

Welcome to fdisk (util-linux 2.25.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Command (m for help): p
Disk /dev/sda: 3.3 GiB, 3541041152 bytes, 6916096 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xb2455b06

Device     Boot  Start     End Sectors  Size Id Type
/dev/sda1         8192  137215  129024   63M  c W95 FAT32 (LBA)
/dev/sda2       137216 2721791 2584576  1.2G 83 Linux

###################################
### /dev/sda2のStartを控えておく ###
###################################

Command (m for help): d
Partition number (1,2, default 2): 2

Partition 2 has been deleted.

###################################
### First sectorは上で控えたStart###
###################################

Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2): 2
First sector (2048-6916095, default 2048): 137216
Last sector, +sectors or +size{K,M,G,T,P} (137216-6916095, default 6916095):

Created a new partition 2 of type 'Linux' and of size 3.2 GiB.

Command (m for help): p
Disk /dev/sda: 3.3 GiB, 3541041152 bytes, 6916096 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xb2455b06

Device     Boot  Start     End Sectors  Size Id Type
/dev/sda1         8192  137215  129024   63M  c W95 FAT32 (LBA)
/dev/sda2       137216 6916095 6778880  3.2G 83 Linux

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Re-reading the partition table failed.: デバイスもしくはリソースがビジー状態です

The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8).

pi@raspberrypi:~$ sudo shutdown -r now
###################################
### 再起動を待って再度ログインする ###
###################################
pi@raspberrypi:~$ sudo resize2fs /dev/sda2
resize2fs 1.42.12 (29-Aug-2014)
Filesystem at /dev/sda2 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 1
The filesystem on /dev/sda2 is now 847360 (4k) blocks long.

pi@raspberrypi:~$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
/dev/root        3.2G  1.1G  2.0G   34% /
devtmpfs         124M     0  124M    0% /dev
tmpfs            124M     0  124M    0% /dev/shm
tmpfs            124M  4.4M  120M    4% /run
tmpfs            5.0M     0  5.0M    0% /run/lock
tmpfs            124M     0  124M    0% /sys/fs/cgroup
/dev/sda1         63M   21M   42M   33% /boot
```

おしまい！

# 参考
- [Emulating Raspberry Pi on Linux](http://embedonix.com/articles/linux/emulating-raspberry-pi-on-linux/)