Raspberry Pi 4をOpenOCDでJTAGデバッグする

この記事は[自作OS Advent Calendar 2019](https://adventar.org/calendars/4027) 18日目の記事です。

# はじまり

Raspberry Pi 4が日本で買えるようになったので、早速JTAGを使ったハードウェアデバッグできるかを試してみました。

JTAGを使ったハードウェアデバッグができるとOSなど低レイヤーの開発にデバッガが使えるようになるので、非常に効率が上がります。

# 用意するもの

- OpenOCDに対応したJTAGアダプタ(3.3v対応のあるもの)
    - 例: [ARM-USB-TINY-H](https://strawberry-linux.com/catalog/items?code=15045)
- ジャンパワイヤ（オスメス）
    - https://www.switch-science.com/catalog/209/
- Raspberry Pi 4 Model B
- その他Raspberry Pi動かすのに必要なもの

# JTAGアダプタの接続

JTAGデバッグを行うには、JTAGアダプタの6(+2)つの端子をターゲット(今回はRaspberry Pi 4)に接続する必要があります。

- TRST
- RTCK
- TDO
- TCK
- TDI
- TMS
- (VTref)
- (GND)

Raspberry PiのJTAG端子はGPIOのAlternate Functionとして出ています。
そして、どのGPIOポートがどの機能に対応しているかは、Raspberry Pi 4に乗っているSoCであるBCM2711のデータシートにかかれています。
なので、このデータシートから各JTAG端子に対応したGPIOポートを調べれば、JTAGアダプタとの接続が行えるようになります。

- BCM2711のデータシート
    - https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0_preliminary.pdf

データシートpp.10にある「Table 5: Raspberry Pi 4 GPIO Alternate Functions」より、JTAG端子とGPIOポートは以下のように接続すれば良いとわかります。

| JTAG | GPIO |
|:-:|:-:|
| TRST | GPIO 22 |
| RTCK | GPIO 23 |
| TDO | GPIO 24 |
| TCK | GPIO 25 |
| TDI | GPIO 26 |
| TMS | GPIO 27 |

JTAG端子とGPIOポートの対応はわかりましたが、実際に接続を行うには どのJTAGアダプタのピン と どのRaspberry Pi 4のGPIOのピン を繋げばよいかの情報が必要になります。

例に上げたARM-USB-TINY-HはARM JTAG 20コネクタをもつJTAGアダプタです。
ARM JTAG 20コネクタの仕様はarm社のサイトで公開されているので、こちらを参照します。

- ARM の DSTREAM ターゲットのインタフェース接続 > ARM JTAG 20
    - http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0499dj/BEHEIHCE.html

Raspberry Pi 4側のGPIOとピン番号の対応は回路図のJ8コネクタにあるので、こちらを参照します。

- Raspberry Pi 4 Schematics
    - https://www.raspberrypi.org/documentation/hardware/raspberrypi/schematics/rpi_SCH_4b_4p0_reduced.pdf

以上より、JTAGアダプタとRaspberry Pi 4のGPIOコネクタは以下のピンを接続すれば良いとわかります。
(VTrefとGNDは、それぞれどれか1本を繋げば良い)

| JTAG | JTAGアダプタ | GPIOコネクタ | 
|:-:|:-:|:-:|
| TRST | 3 | 15 |
| RTCK | 11 | 16 |
| TDO | 13 | 18 |
| TCK | 9 | 22 |
| TDI | 5 | 37 |
| TMS | 7 | 13 |
| VTref | 1 | 1,17 |
| GND | 4,6,8,10,12,14,16,18,20 | 6,9,14,20,25,30,34,39 | 


# Raspberry Pi 4の設定

先程調べたように、Raspberry PiのJTAG端子はGPIOのAlternate Functionとして実装されているので、JTAG端子として利用するにはGPIOの設定が必要です。
この設定を行うため、昔は自分たちでプログラムを書く必要がありましたが、最近は以下のページを参考にconfig.txtに設定を書くだけでファームウェアが勝手に設定してくれます。
しかし、私が試したところこれだけではJTAG端子は有効になりませんでした。

- GPIO control in config.txt
    - https://www.raspberrypi.org/documentation/configuration/config-txt/gpio.md

調査の結果、以下のオプションをconfig.txtに記述するとGPIOの設定とJTAG端子の有効可が行われることを確認しました。
よって、config.txtに以下を追記してRaspberry Piを起動してください。
これでRaspberry PiがJTAGデバッグ可能な状態になります。

```
enable_jtag_gpio=1
```

- 参考
    - https://metebalci.com/blog/bare-metal-raspberry-pi-3b-jtag/
    - https://github.com/OP-TEE/optee_os/issues/1634
    - https://github.com/raspberrypi/firmware/issues/639


# OpenOCDの起動

## OpenOCDのインストール
JTAGアダプタの接続とRaspberry PiのJTAG有効可設定が完了したので、OpenOCDを利用してJTAGデバッグを行えるようにします。

現在OpenOCDの最新バージョンは0.10.0ですが、私の作った設定ファイルはgit headでないと動かないため、利用される方はgitのリポジトリから最新のOpenOCDをビルドしてインストールしてください。

- OpenOCD official mirror of this repository
    - http://repo.or.cz/w/openocd.git

なおMacをお使いの方はbrewで最新のOpenOCDをインストールできます。

```
brew install openocd --HEAD
```

## 設定ファイルの取得

OpenOCDでデバッグを行うには、JTAGアダプタの設定ファイルと接続先の情報が書かれた設定ファイルが必要になります。
JTAGアダプタの設定ファイルはOpenOCD側が持っていますが、Raspberry Pi 4のCPUに接続するための設定ファイルはありません。

この設定ファイルは自分で調べて作ることも可能ですが、今回は私が調べて書いたものを以下でダウンロードして使ってください。


- OpenOCD config for Raspberry Pi 4
    - https://gist.github.com/tnishinaga/46a3380e1f47f5e892bbb74e55b3cf3e

※ APのROMからアドレス調べてやる方法は、以前書いた[こちら](https://speakerdeck.com/tnishinaga/debugging-synquacer-with-jtag)の資料を参照してください

※ APのROMからアドレス調べた後に、6ヶ月前に[こちら](https://metebalci.com/blog/bare-metal-raspberry-pi-3b-jtag/)の記事のコメントにRaspberry Pi 4向けのアドレスが書かれているのを見つけました。海外の人ははやいです。

## OpenOCDの起動

先程ダウンロードしたRaspberry Pi 4向けの設定ファイル(raspi4.cfg)のあるディレクトリで以下のコマンドを実行してください。
(別のJTAGアダプタを利用している方は`/usr/local/share/openocd/scripts/interface`以下などから設定ファイルを探してコマンドを書き換えてください)

```
openocd -f interface/ftdi/olimex-arm-usb-tiny-h.cfg -f raspi4.cfg
```

うまく接続等ができていれば、以下のようなメッセージが出てきます。

```
~/p/s/s/s/s/openocd ❯❯❯ openocd -f interface/ftdi/olimex-arm-usb-tiny-h.cfg -f raspi4.cfg
Open On-Chip Debugger 0.10.0+dev-00865-g04ebb43f (2019-05-17-04:25)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 1000 kHz
Info : JTAG tap: auto0.tap tap/device found: 0x4ba00477 (mfg: 0x23b (ARM Ltd.), part: 0xba00, ver: 0x4)
Info : bcm2711.a72.0: hardware has 6 breakpoints, 4 watchpoints
Info : bcm2711.a72.1: hardware has 6 breakpoints, 4 watchpoints
Info : bcm2711.a72.2: hardware has 6 breakpoints, 4 watchpoints
Info : bcm2711.a72.3: hardware has 6 breakpoints, 4 watchpoints
Info : Listening on port 3333 for gdb connections
```

あとは以下のようにgdbでアクセスすればデバッグができるでしょう（筆者はdocker内に入れたgdb-multiarchで確認しました）（Raspberry Piは何もしないと32bit modeで起動するので、32bit armとしてつないでいます）。

```
~ ❯❯❯ docker run -it --rm debian:latest /bin/bash

root@5f39aa814899:/# apt -y install gdb-multiarch
root@5f39aa814899:/# gdb-multiarch

(gdb) set architecture armv8-a

(gdb) target remote host.docker.internal:3333
Remote debugging using host.docker.internal:3333
warning: Selected architecture armv8-a is not compatible with reported target architecture aarch64
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0xc021ce88 in ?? ()

(gdb) i r
r0             0x0                 0
r1             0x240b4             147636
r2             0xeff1d3f0          4025603056
r3             0xc021ce80          3223441024
r4             0xc1004dbc          3238022588
r5             0xc1000000          3238002688
r6             0xc1004e04          3238022660
r7             0x1                 1
r8             0xc109641a          3238618138
r9             0xc10a56c0          3238680256
r10            0xc0e67a38          3236330040
r11            0xc1001f34          3238010676
r12            0xc1001f38          3238010680
sp             0xc1001f28          0xc1001f28
lr             0xc0209a9c          0xc0209a9c
pc             0xc021ce88          0xc021ce88
cpsr           0x60000093          1610612883
fpscr          0x0                 0
```


おまけで、telnetで確認した結果はこちら

```
~ ❯❯❯ telnet localhost 4444                                                                                                      ✘ 130
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> reg
===== Aarch64 registers
(0) x0 (/64): 0x0000000000000000 (dirty)
(1) x1 (/64): 0x00000000000240B4
(2) x2 (/64): 0x00000000EFF1D3F0
(3) x3 (/64): 0x00000000C021CE80
(4) x4 (/64): 0x00000000C1004DBC
(5) x5 (/64): 0x00000000C1000000
(6) x6 (/64): 0x00000000C1004E04
(7) x7 (/64): 0x0000000000000001
(8) x8 (/64): 0x00000000C109641A
(9) x9 (/64): 0x00000000C10A56C0
(10) x10 (/64): 0x00000000C0E67A38
(11) x11 (/64): 0x00000000C1001F34
(12) x12 (/64): 0x00000000C1001F38
(13) x13 (/64): 0x00000000C1001F28
(14) x14 (/64): 0x00000000C0209A9C
(15) x15 (/64)
(16) x16 (/64)
(17) x17 (/64)
(18) x18 (/64)
(19) x19 (/64)
(20) x20 (/64)
(21) x21 (/64)
(22) x22 (/64)
(23) x23 (/64)
(24) x24 (/64)
(25) x25 (/64)
(26) x26 (/64)
(27) x27 (/64)
(28) x28 (/64)
(29) x29 (/64)
(30) x30 (/64)
(31) sp (/64)
(32) pc (/64): 0x00000000C021CE88 (dirty)
(33) cpsr (/32): 0x60000093 (dirty)
(34) v0 (/128)
(35) v1 (/128)
(36) v2 (/128)
(37) v3 (/128)
(38) v4 (/128)
(39) v5 (/128)
(40) v6 (/128)
(41) v7 (/128)
(42) v8 (/128)
(43) v9 (/128)
(44) v10 (/128)
(45) v11 (/128)
(46) v12 (/128)
(47) v13 (/128)
(48) v14 (/128)
(49) v15 (/128)
(50) v16 (/128)
(51) v17 (/128)
(52) v18 (/128)
(53) v19 (/128)
(54) v20 (/128)
(55) v21 (/128)
(56) v22 (/128)
(57) v23 (/128)
(58) v24 (/128)
(59) v25 (/128)
(60) v26 (/128)
(61) v27 (/128)
(62) v28 (/128)
(63) v29 (/128)
(64) v30 (/128)
(65) v31 (/128)
(66) fpsr (/32)
(67) fpcr (/32)
(68) ELR_EL1 (/64): 0x00000000C0209A9C
(69) ESR_EL1 (/32): 0x00000206
(70) SPSR_EL1 (/32): 0x40000013
(71) ELR_EL2 (/64)
(72) ESR_EL2 (/32)
(73) SPSR_EL2 (/32)
(74) ELR_EL3 (/64)
(75) ESR_EL3 (/32)
(76) SPSR_EL3 (/32)
===== Aarch32 registers
(77) r0 (/32): 0x00000000
(78) r1 (/32): 0x000240B4
(79) r2 (/32): 0xEFF1D3F0
(80) r3 (/32): 0xC021CE80
(81) r4 (/32): 0xC1004DBC
(82) r5 (/32): 0xC1000000
(83) r6 (/32): 0xC1004E04
(84) r7 (/32): 0x00000001
(85) r8 (/32): 0xC109641A
(86) r9 (/32): 0xC10A56C0
(87) r10 (/32): 0xC0E67A38
(88) r11 (/32): 0xC1001F34
(89) r12 (/32): 0xC1001F38
(90) sp (/32): 0xC1001F28
(91) lr (/32): 0xC0209A9C
(92) pc (/32): 0xC021CE88
(93) cpsr (/32): 0x60000093
(94) d0 (/64)
(95) d1 (/64)
(96) d2 (/64)
(97) d3 (/64)
(98) d4 (/64)
(99) d5 (/64)
(100) d6 (/64)
(101) d7 (/64)
(102) d8 (/64)
(103) d9 (/64)
(104) d10 (/64)
(105) d11 (/64)
(106) d12 (/64)
(107) d13 (/64)
(108) d14 (/64)
(109) d15 (/64)
(110) d16 (/64)
(111) d17 (/64)
(112) d18 (/64)
(113) d19 (/64)
(114) d20 (/64)
(115) d21 (/64)
(116) d22 (/64)
(117) d23 (/64)
(118) d24 (/64)
(119) d25 (/64)
(120) d26 (/64)
(121) d27 (/64)
(122) d28 (/64)
(123) d29 (/64)
(124) d30 (/64)
(125) d31 (/64)
(126) fpscr (/32)
```

# おしまい

Raspberry Pi 4で低レイヤー開発生活をお楽しみください。