Linux上にRaspberry Pi OS(RaspbianBuster)をQEMUエミュレーターで動かす(Raspi2、Raspi3マシン)

# はじめに
前回、Windows 10とMacにてRaspberry Pi OSをQEMUエミュレータで動かしましたが、versatilepbマシンでありCPUのアーキテクチャが`armv6`(Raspberry Pi初代やRaspberry Pi Zero系統)が対象でした。

* [Windows10上にRaspberry Pi OS(RaspbianBuster)をQEMUエミュレーターで動かす(versatilepbマシン)](https://qiita.com/yaju/items/4bf9e39416e0cb1bdaa9)
* [Mac上にRaspberry Pi OS(RaspbianBuster)をQEMUエミュレーターで動かす(versatilepbマシン)](https://qiita.com/yaju/items/9bb341c1b222590a92ba)

Visual Studio Codeがサポートしているのは、CPUのアーキテクチャが`armv7l`のRaspberry Pi 2系統とRaspberry Pi 3系統なので、Raspi2マシンとRaspi3マシンでなんとかQEMUエミュレータを動かしたいと思いました。
しかし、簡単には行きませんでした。画面こそ表示されたのですがマウスやキーボードも操作できません、画面は諦めてせめてSSHができればと思ったのですが、これもダメでした。

ネットで検索しまくり、LinuxであればSSHが動作しているサイトを見つけました。QEMUのインストール方法が違う以外は、この通りに実施してみるとSSHが繋がりました。liteイメージからDesktopイメージに変更してオプションを幾つか変更すると、画面が表示されマウスやキーボードが操作できたのです。

https://peterbabic.dev/blog/how-emulate-raspios-natively-qemu/

【2023/10/24追記】
raspios-bullseye で構築された記事を見つけましたのでリンクしておきます。

https://hiro20180901.com/2023/04/24/raspberry-pi-os-64-bit-runs-qemu-720-vritual-machine/

# 環境
* Ubuntu 20.04 LTS ※Mac Book Pro(2.3 GHz 8コアIntel Core i9) Big Sur 11.3上のVirtualBox 6.1.22で使用
* Ubuntu 21.04 *Windows 10上のVirtualBox 6.1.22で使用
* QEMU 5.2

【2021/07/20追記】
WindowsならWSLを使用できるので記事を書きました。

https://qiita.com/yaju/items/c3eea72a3463eb26dfde

# 導入手順
## QEMUのインストール
QEMUは、CPUエミュレーションをするためのソフトウェアになります。

Ubuntu 20.04 LTSでは「sudo apt install -y qemu-system」とすると、QEMU 4.2.1がインストールされます。
しかし、QEMU 5.1以降でUSBコントローラをサポートしたことでネットワーク等が使用できるようになったので、QEMU 4.2.1では意味がありません。
最新のQEMU 6.0があるのですが、コンパイル方法で躓いて諦めました。

UbuntuにはサーバーバックポートのPPA（Personal Package Archive）があり、QEMU 5.２をインストールすることができました。
[第419回　長期サポート版で改めて見直す，PPAとのお付き合い](https://gihyo.jp/admin/serial/01/ubuntu-recipe/0419)

https://launchpad.net/~canonical-server/+archive/ubuntu/server-backports

```bash
$ sudo add-apt-repository ppa:canonical-server/server-backports
$ sudo apt-get update
$ sudo apt install -y qemu-system
```

Ubuntu 21.04なら「sudo apt install -y qemu-system」だけで、QEMU 5.2がインストールされます。

## イメージファイルのダウンロード
CPU別(armhf[^1],arm64[^２]など)にイメージファイルには3種類用意されています。

[^1]: armhf…ARMというCPUアーキテクチャ上で動かす、浮動小数点数命令を使うバージョン。Raspberry Piなどがこちらを使います。
[^２]: amd64…x86を64ビットに拡張したx64対応のもの。政治的な事情もあってこの命令セットのCPUをAMDが先に出したため、Intelのx64でもamd64版となります。ほとんどのパソコンはこちらです。

* Raspberry Pi OS 軽量版(lite)
* Raspberry Pi OS デスクトップ(Desktop)
* デスクトップと追加パッケージを備えた Raspberry Pi OS(full)

日本からなら北陸先端大にあるミラーサーバーからの方が速くダウンロードできる。
http://ftp.jaist.ac.jp/pub/raspberrypi/

### 使用するイメージファイル
動作した参考サイトのイメージファイルは「2020-08-20-raspios-buster-armhf-lite.img」でした。イメージファイルを「2020-08-20-raspios-buster-arm64.img」と`armhf`から`arm64`にしても画面が表示されて動作するのを確認できました。
そこで、最新(2021年6月時点)の「2021-05-07-raspios-buster-arm64.img」に変更してみると、何も起こらない、メッセージ表示すら出ないのです。一つ前のイメージファイル「2021-03-04-raspios-buster-arm64.img」にしても同じでした。

>「QEMUは黒い画面では何もしません」は、ほとんどの場合、「QEMUは正常に実行されていますが、ゲストコードがクラッシュするか、出力を送信せずにブートプロセスの早い段階で停止しました」を意味します。ほぼ確実に、カーネルが正しく構成されていないか、QEMUコマンドラインが間違っています。
[QEMU Showing Black Screen Only - stackoverflow](https://stackoverflow.com/questions/49099947/qemu-showing-black-screen-only)

カーネルファイル「kernel8.img」とデバイスツリーソースファイル「bcm2710-rpi-3-b.dtb」は、イメージファイルの中に入っているものを抽出して使用しています。
自分が一番最初に行ったのは、[https://github.com/dhruvvyas90/qemu-rpi-kernel](https://github.com/dhruvvyas90/qemu-rpi-kernel) からダウンロードした「qemu-rpi-kernel-master.zip」に入っていた「kernel8.img」と「bcm2710-rpi-3-b.dtb」ファイルでした。これだと画面は表示されるのですが、マウスやキーボードやSSHが繋がりません。

よって、下記のイメージファイルを使用することにしました。

* Raspi2マシン用に「[2020-08-20-raspios-buster-armhf](http://ftp.jaist.ac.jp/pub/raspberrypi/raspios_armhf/images/raspios_armhf-2020-08-24/)」
* Raspi3マシン用に「[2020-08-20-raspios-buster-arm64](http://ftp.jaist.ac.jp/pub/raspberrypi/raspios_arm64/images/raspios_arm64-2020-08-24/)」

※Raspi3マシン用は「2020-08-20-raspios-buster-armhf」でも使用可能です。

その後の調査で、Raspi2マシン用(qemu-system-arm)に「[2021-05-07-raspios-buster-armhf](http://ftp.jaist.ac.jp/pub/raspberrypi/raspios_armhf/images/raspios_armhf-2021-05-28/)」まで動作することが分かりました。Raspi3マシン用(qemu-system-aarch64)で「2021-05-07-raspios-buster-armhf」を使用するとやはりダメでした。


# Raspi2マシン
## フォルダ作成
ホーム配下に「RaspberryPi2」フォルダを作成し、カレントディレクトリを「RaspberryPi2」フォルダにします。

```bash
$ mkdir RaspberryPi2 
$ cd　RaspberryPi2
```

ダウンロードした「2020-08-20-raspios-buster-armhf.zip」を「RaspberryPi2」フォルダに展開します。

## カーネルとデバイスツリーソースファイルの抽出 
イメージファイルの中からファイルを抽出したいので、ループバック・デバイスを使います。

```bash
$ sudo losetup --show -fP 2020-08-20-raspios-buster-armhf.img
/dev/loop5
```

loop5の部分は、PCによって違うので書き換えてください。

```bash
$ sudo mount /dev/loop5p1 /mnt/raspios
$ cp /mnt/raspios/kernel7.img /mnt/raspios/bcm2709-rpi-2-b.dtb .
$ sudo umount /mnt/raspios
$ sudo losetup -d /dev/loop5
```

これで、RaspberryPi2フォルダには3ファイルがある状態になっています。

* 2020-08-20-raspios-buster-armhf.img
* kernel7.img
* bcm2709-rpi-2-b.dtb

## イメージファイルのリサイズ
実行する際のイメージファイルは2のべき乗になっている必要があります。そうしないと下記エラーになります。
「Invalid SD card size: 3.56 GiB SD card size has to be a power of 2, e.g. 4 GiB.You can resize disk images with 'qemu-img resize <imagefile> <new-size>'(note that this will lose data if you make the image smaller than it currently is).」

```bash
$ qemu-img resize 2020-08-20-raspios-buster-armhf.img 8G

WARNING: Image format was not specified for '2020-08-20-raspios-buster-armhf.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.
```

## 初回起動
RaspberryPi2フォルダに下記の「run.sh」を作成します。
※raspi2マシンの場合、qemu-system-aarch64でも動作します。
2021/07/06 appendオプションに画面サイズを追記

```bash:run.sh
#!/bin/sh
cd `dirname $0`

qemu-system-arm \
-m 1024 \
-M raspi2 \
-kernel kernel7.img \
-dtb bcm2709-rpi-2-b.dtb \
-drive format=raw,file=2020-08-20-raspios-buster-armhf.img \
-append "console=ttyAMA0 root=/dev/mmcblk0p2 rw rootwait rootfstype=ext4 dwc_otg.fiq_fsm_enable=0 bcm2708_fb.fbwidth=1280 bcm2708_fb.fbheight=720" \
-serial stdio \
-no-reboot \
-device usb-kbd \
-device usb-tablet \
-device usb-net,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::2222-:22
```

ターミナル上でrun.shに実行権限を追加してから実行すれば、 数分でRaspberry Pi OSが起動します。

```bash
$ chmod 755 run.sh
$ ./run.sh
```

# Raspi3マシン
## フォルダ作成
ホーム配下に「RaspberryPi3」フォルダを作成し、カレントディレクトリを「RaspberryPi3」フォルダにします。

```bash
$ mkdir RaspberryPi3 
$ cd　RaspberryPi3
```

ダウンロードした「2020-08-20-raspios-buster-arm64.zip」を「RaspberryPi3」フォルダに展開します。

## カーネルとデバイスツリーソースファイルの抽出 
イメージファイルの中からファイルを抽出したいので、ループバック・デバイスを使います。

```bash
$ sudo losetup --show -fP 2020-08-20-raspios-buster-arm64.img
/dev/loop5
```

loop5の部分は、PCによって違うので書き換えてください。

```bash
$ sudo mount /dev/loop5p1 /mnt/raspios
$ cp /mnt/raspios/kernel8.img /mnt/raspios/bcm2710-rpi-3-b.dtb .
$ sudo umount /mnt/raspios
$ sudo losetup -d /dev/loop5
```

これで、RaspberryPi3フォルダには3ファイルがある状態になっています。

* 2020-08-20-raspios-buster-arm64.img
* kernel8.img
* bcm2710-rpi-3-b.dtb

## イメージファイルのリサイズ
実行する際のイメージファイルは2のべき乗になっている必要があります。そうしないと下記エラーになります。
「Invalid SD card size: 3.56 GiB SD card size has to be a power of 2, e.g. 4 GiB.You can resize disk images with 'qemu-img resize <imagefile> <new-size>'(note that this will lose data if you make the image smaller than it currently is).」

```bash
$ qemu-img resize 2020-08-20-raspios-buster-arm64.img 8G

WARNING: Image format was not specified for '2020-08-20-raspios-buster-arm64.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.
```

## 初回起動
RaspberryPi3フォルダに下記の「run.sh」を作成します。
2021/07/06 appendオプションに画面サイズを追記

```bash:run.sh
#!/bin/sh
cd `dirname $0`

qemu-system-aarch64 \
-m 1024 \
-M raspi3 \
-kernel kernel8.img \
-dtb bcm2710-rpi-3-b.dtb \
-drive format=raw,file=2020-08-20-raspios-buster-arm64.img \
-append "console=ttyAMA0 root=/dev/mmcblk0p2 rw rootwait rootfstype=ext4 dwc_otg.fiq_fsm_enable=0 bcm2708_fb.fbwidth=1280 bcm2708_fb.fbheight=720" \
-serial stdio \
-no-reboot \
-device usb-kbd \
-device usb-mouse \
-device usb-net,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::2222-:22
```

ターミナル上でrun.shに実行権限を追加してから実行すれば、 数分でRaspberry Pi OSが起動します。

```bash
$ chmod 755 run.sh
$ ./run.sh
```

# 共通
ここからは、Raspi2マシンとRaspi3マシン共通となります。

## QEMUエミュレータ
|エミュレータ|Raspberry Piモデル|
|:--|:--|
|qemu-system-arm|RaspberryPi 2向けのCortex-A53|
|qemu-system-aarch64|RaspberryPi 2、3向けのCortex-A53|

## QEMUオプション
[QEMUオプション](https://wiki.gentoo.org/wiki/QEMU/Options)

主なQEMU起動時のオプションについての説明をまとめておきます。

### -M マシン名
エミュレートするマシン名を指定します。
最新情報は、次のコマンドで一覧が表示できます。

```
qemu-system-arm -M help
or
qemu-system-aarch64 -M help
```

### -m メモリ数
RAMの容量を指定します。
raspi2マシンとraspi３マシンを指定した場合は最大１GiBで、それ以上を指定するとエラー「Invalid RAM size, should be 1 GiB」になります。

### -append 画面サイズ
【2021/07/06追記】
mt08 さんからコメントを頂き、appendオプションに画面サイズを指定することで画面サイズを変更することが出来ました。
bcm2708_fb.fbwidth=XXX bcm2708_fb.fbheight=YYY

```text:例
-append "console=ttyAMA0 root=/dev/mmcblk0p2 rw rootwait rootfstype=ext4 dwc_otg.fiq_fsm_enable=0 bcm2708_fb.fbwidth=1280 bcm2708_fb.fbheight=720"
```
しかも、画面サイズ800x480で背景が青系統だったのが、画面サイズ1280x720で背景が本来のオレンジ系統になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/5599818b-abe8-5f95-0367-bd0b9f6b9fb2.png)

## マウス操作
### QEMUを抜ける
マウスカーソルがラズパイの画面に持っていかれた場合、抜けるにはQEMUウィンドウのタイトルバー「QEMU-Press Ctrl + Alt + G release grab」に記載されているように、「Ctrl + Alt + G」を同時に押します。
ここは改善されたのか、ラズパイの画面範囲を超えると自動的に抜けるようになっています。

## 起動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/f20f1500-de36-c163-acb3-cfa82996a1e1.png)
ログインを求められますが、入力しても、しなくても問題ありません。
デフォルトの管理者 ユーザー名:pi パスワード:raspberry

## 初期設定画面
背景画像がversatilepbマシン時はオレンジ系統でしたが、今回は青系統になっています。

初期設定画面では国の設定以外は Nextボタンをクリック(パスワード変更なども)、アップグレードは空き容量が足りないのでSkipボタンをクリックして最終的にDone(Later)ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/ffbe3dee-84f9-22fa-6de6-27c61f0e4581.png)

# 空き容量を増やす
空き容量が195.1 MiBしかありません。後述する方法で空き容量を2.5GiB(8Gの場合)まで増やすことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/6c896cc5-f8ce-1ec8-eac0-b15129db4959.png)
ターミナルで以下を実行し、ディスクの容量分だけ利用できるようにファイルシステムを拡張する。

```bash
$ sudo raspi-config  --expand-rootfs
```

容量は再起動後に反映されますので、一旦QEMU画面を閉じます。
ターミナル上で「run.sh」を実行して、Raspberry Pi OSを再度起動します。

# スワップ領域増加
スワップ領域も少ないので下記コマンドで修正を行います。
初心者にも比較的使いやすい nanoテキストエディターを使用します。
アンダーバーが入力できないので数値のみを変更する。

```bash
$ sudo nano /etc/dphys-swapfile
/etc/dphys-swapfile

CONF_SWAPSIZE=2048
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/df41f771-4a02-4447-0ecf-9cf93688e680.png)

```bash
$ sudo dphys-swapfile install
$ sudo dphys-swapfile swapon
$ swapon　-s
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/cbd1a328-3676-7884-eb40-3b14dc76d598.png)

# アップデート
空き容量を増えたので、アップデートしておきます。

```bash
$ sudo apt update
```

# その他
## SSH
スタートボタンメニューから「設定」の「Raspberry Pi設定」をクリックします。
Raspberry Pi設定画面のインターフェイスタブにて、SSHを有効にして「OK」ボタンをクリックします。
設定後に再起動確認画面が出てくるので「はい」ボタンをクリックします。
[Raspberry Pi 4にSSHとVNCで接続してみた](https://dev.classmethod.jp/articles/raspberry-pi-4-ssh-vnc-remote/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/32c99173-2e33-c18f-09d7-715d3963cf6c.png)

ターミナル上で「run.sh」を実行して、Raspberry Pi OSを再度起動します。

Raspberry Pi OS上のターミナル画面から、「hostname -i」でホスト名のipアドレスを確認できます。

```bash
# ホスト名のipアドレスを表示
$ hostname -i
127.0.0.1
```

ターミナルをもう一つ起動してSSHに接続します。
Raspberry Piのホスト名が「127.0.0.1」になっているので、下記を入力します。

```
$ ssh pi@127.0.0.1 -p 2222

パスワード入力 raspberry
```

Raspberry Pi OS上に接続できたので、lsb_releaseコマンドでバージョンを確認してみます。

```bash
pi@raspberrypi ~ $ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 10 (buster)
Release:        10
Codename:       buster
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/3f86da38-eb34-02de-b3c0-e2c6a3c25f2b.png)
sshコマンド時に「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」が出たら下記サイトを参考に対応して下さい。
[SSH接続時にエラー](https://qiita.com/hanlio/items/179add527421c3b7839c)

## Web Browser
Web Browserのアイコンをクリックすれば、数分かかりますがブラウザが起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/0c252b7f-d98b-cb6b-1d68-56a50a693eb9.png)

## VSCode
Raspberry Piのクロスコンパイルをしたかったので、一番インストールしたいアプリケーションであった。

```bash
$ sudo apt install code
```

インストールが完了するまで時間がかなりかかります。
スリープするとネットワークまで停止するので、一時的にスリープを解除しておくといいでしょう。

インストールが完了するとメニューのプログラミング配下に「Visual Studio Code」が追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/06c40b9d-c87d-997f-306c-604f1b98b290.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/dff2d789-4bfe-3722-db5c-894b131a7d00.png)
動きますが、ものすごく重たいです。また、C++とかコンポーネントのダウンロード中で進まない状態など使用するには難あり。

### Remort SSH
**Remort SSHするだけなら、Raspberry Pi OS側にVSCodeをインストールする必要はありません。**

親側のUbuntuにもVisual Studio Codeをインストールして、「Remort SSH」で動くことを期待したのですが、残念ながらリモート側にVSCodeをインストールしようとして応答なしになってしまいます。

【2021/07/10追記】
リモート側がUbuntuだとvscode-serverは遅いながらもインストールできたものの、そこから何度やってもRemort SSHからの接続が安定せず断念しました。
そこで、リモート側をMacまたはWindows10に切り替えました。
VirtualBoxで動作させているので、Ubuntu側のファイアーウォールの2222ポートを空けます

```bash
$ sudo ufw enable
$ sudo ufw allow 2222
$ sudo uwf reload
```

さらにVirtualBoxマネージャーから対象のVMを選択、設定のネットワークタブにある高度を展開し、「ポートフォワーディング」ボタンをクリックして下図のように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/c1e3313e-fcdc-051b-15f0-66fd26cf768e.png)

リモート側をMacまたはWindows10にすると接続が安定しないながらも何度かリトライすることで、Remort SSHが実現することが出来ました。ネットワークが遅いので Downloading with wget から Download complete まで20分程度かかります。Macだと途中経過の表示がないので停止しているのかと心配になりました。一時的にMacの設定のバッテリーにある電源アダプターの「ディスプレイがオフのときにコンピュータを自動でスリープさせない」にチェックをつけるなどして待機しておくといいでしょう。

開発言語にC++として拡張機能をインストールしました。

* C/C++
* C/C++ IntelliSense

Hello Worldではつまらないのでフィボナッチ数列を求めるプログラムを作成し、デバッグしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/75b6d96e-8590-d767-9a91-43b3105909ef.png)

# 不満点
* アンダーバーとパイプがキーボードで入力できない
* ~~画面サイズが変更できない~~
* Visual Studio CodeでRemort SSHが動作しない

画面サイズは、Raspberry Pi設定にあるDisplayタブの解像度の設定はDefaltしか存在しない。下記サイトを参考に「/boot/config.txt」の設定を変更してみたのですが何も変わりませんでした。
[RaspberryPiの解像度が変えられない時の対処法](https://www.mechatronahibi.com/raspi-disp-resolution-config/)

## キー入力の調査結果
【2021/07/08追記】
versatilepbマシンではアンダーバーとパイプと円マークは入力できるので、xevでキーコードを調べるとKeyCodeは97(underscore)と132(yen bar)でした。Raspi2、Raspi3マシンでxevでキーコードを調べようとしても反応しないのです。
つまり、キースキャンができないのでXmodmapでKeyCode 97と132に割り当てしても反応しない。
そのため、空いているところに(例 シフト+スペースキーにアンダーバー)割り当てることなら可能です。

# 最後に
Linuxで動作することができたので、MacとWindowsでも試してみると同様に動作させることが出来ました。
MacとWindowsについてはそれぞれ別記事にします。

https://qiita.com/yaju/items/5ec5c4df1db1f8ebbac7

https://qiita.com/yaju/items/36ecee632d2eeee38d63

今回の件でネットでかなり検索しましたが、raspi2マシンとraspi３マシンで画面まで表示しているサイトはありませんでした。需要がないのか、SSHのみならlite版で済ますからそこまでやらないのかって感じですかね。


不満点のキーボードと~~画面サイズ~~の件が分かる方がいたらコメントを頂けると嬉しいです。

# 参考
* [How to emulate Raspios natively in QEMU](https://peterbabic.dev/blog/how-emulate-raspios-natively-qemu/)
* [Emulate RaspberryPi 2 on Ubuntu 20.04 using qemu](https://lynxbee.com/emulate-raspberrypi-2-on-ubuntu-20-04-using-qemu/)
* [QEMUとRaspberry PI - 山の上のブログ](http://www.yamanoue.atnifty.com/2020/08/qemuraspberry-pi.html)
* [QEMU on WindowsでネイティブのRaspberry Pi OS (64-bit) を動かす](https://blog.k-san.info/raspios-arm64-on-qemu-on-win)
* [QEMU on M1 Mac](https://qiita.com/hjmr/items/abeb9a4567d08ddcc3b9)
* [arm raspi2/raspi3 emulation has no USB support](https://bugs.launchpad.net/qemu/+bug/1772165)