Blog posts by @retrage

mirror of https://retrage.github.io/

Allwinner NezhaにJTAGで接続する

Allwinner NezhaはD1という64-bit RISC-VなSoCが載ったSBCである。Linuxがちゃんと動くRISC-Vマシンとしてはかなり安価なため一部で人気がある。 ここではこれにJTAGで接続してみた話をメモ程度に書いておく。なお、今回は接続してOpenOCDで認識できた程度でその先のGDBでのデバッグはできていない。

JTAGの端子について

NezhaにはUARTの端子が DEBUG として用意されているが、JTAGには専用の端子が用意されていない。その代わりにmicroSDの端子と共通になっている。D1 SoCのデータシート4.3 GPIO Multiplex Functionから対応するピンと役割を読むと、PF0-PF6のI/Oがあり、SDカードのI/Oとしての役割がSDC0-*としてあり、その隣にJTAG関連のI/OがJTAG-*として記載されている。詳しくは回路図などを参照してもらいたいが、まとると以下のようになっている。

PF0: JTAG-MS - SDC0-D1  <--> DAT1
PF1: JTAG-DI - SDC0-D0  <--> DAT0
PF3: JTAG-DO - SDC0-CMD <--> CMD
PF5: JTAG-CK - SDC0-D2  <--> DAT2

これらの端子を引き出すために適当な延長アダプタを利用した。理想的には以下のようなブレークアウトボードを使うのが望ましい。

JTAGアダプタ

こちらの記事ではSipeed RV-Debugger-Plus (BL702C-A0)という別のRISC-VベースのJTAGアダプタを利用している。今回は手元にあったBus PirateをJTAGアダプタとして利用した。

Bus Pirate JTAG connections for OpenOCDにある通り以下のように接続した。

BP   - JTAG
GND  - GND
MOSI - TDI
MISO - TDO
CLK  - TCK
CS   - TMS 

OpenOCDの実行

こちらの設定ファイルをベースに以下のようにBus Pirate向けに書き換えた設定ファイルを作成した。

# Based on: https://github.com/orangecms/RV-Debugger-BL702/blob/main/tools/openocd/openocd-usb-sipeed.cfg

source [find interface/buspirate.cfg]

buspirate_vreg 0
buspirate_mode open-drain
buspirate_pullup 1

buspirate_port /dev/tty.usbserial-AH03FKZ4

transport select jtag
# adapter speed 1000

set _CHIPNAME riscv
#jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000001
#jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x0
# wrong?
# jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00185900
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x08052b43

set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
#$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
#$_TARGETNAME.0 configure -work-area-phys 0x50000000 -work-area-size 32768 -work-area-backup 0

riscv set_prefer_sba on
# riscv set_mem_access progbuf

FEL modeへの切り替えとJTAGの有効化

AllwinnerのSoCにはFEL modeというモードがあり、起動時にこのモードに入ることでJTAGの有効化を含むさまざまな低レベルな操作が可能となっている。 FEL modeではNezhaのOTG USB経由でホストから操作を行う必要がある。今回はxboot/xfelというFELのツールをホストで動かしてJTAGを有効にした。

具体的には次の手順でFEL modeに入ってJTAGを有効化する。

  1. Nezhaにある FEL ボタンを長押しする
  2. 1.のままNezhaのOTG USBとホストを接続する
  3. 2秒待ってボタンを離す
  4. これでFEL modeに入る
  5. ホストで ./xfel sid を実行して値が取れることを確認
  6. ホストで ./xfel jtag を実行してJTAGを有効化
  7. (ホストで ./xfel ddr ddr3を実行してDRAMを初期化)

JTAGが有効になったら上記の設定ファイルでOpenOCDを実行する。

openocd -f openocd-buspirate.cfg

うまくいくと以下のように riscv.cpuJTAG tapが見える。

f:id:retrage01:20220116131141p:plain
Allwinner Nezha JTAG OpenOCD

ただし、現時点では

Error: riscv.cpu: IR capture error; saw 0x1e not 0x01

とあるようにエラーがあり、GDBでの接続ができていない。

参考文献

Apple File Systemの下にはEFI driverが埋まっている

Apple File System (APFS)はAppleが自社製品向けに開発したファイルシステムである.APFSの仕様は公開されており以下で参照できる.

その目次の中で特に興味を引いたのが"EFI Jumpstart"の章である.現代的なデバイスではEFIを含めブートローダファイルシステムを参照してOSを起動する.このとき当然ながらブートローダはそのファイルシステムを扱える必要がある.特にEFIでは仕様上対応していなければならないのはEFI System Partition (ESP)で使われるFATのみでその他のファイルシステムが事前にサポートされていることは期待できない.このため例えばWindowsであればESPに配置されたWindows専用のブートローダNTFSをサポートすることでカーネルの起動を行なっている.

これに対してmacOSではAPFS自身がAPFSのEFI driverをパーティションのブロックに直接埋め込む形で提供するという別のアプローチをとっている.ドキュメントによれば

This design intentionally simplifies the steps needed to boot, which means the code needed to boot a piece of hardware or virtualization software can likewise be simpler.

とのことで起動プロセスをシンプルにすることが目的のようである.

どのようにEFI driverが埋め込まれているのかをみていく. 最初の前提としてディスクのGPTエントリを読み,以下のUUIDのようなAPFSのパーティションがあることがわかったとする.

#define APFS_GPT_PARTITION_UUID ”7C3457EF-0000-11AA-AA11-00306543ECAC”

APFSパーティションは一つのAPFS containerオブジェクトとして存在し,その先頭にcontainerの情報を持ったcontainer superblockが配置されている.

container superblockを表すnx_superblock_tにはnx_efi_jumpstartというEFI Jumpstartの情報を持ったnx_efi_jumpstart_tへの物理アドレス(containerの先頭からのAPFSでのブロックサイズ単位でのオフセット)のフィールドが用意されている.

その参照先には以下のようなnx_efi_jumpstart_tがある.

typedef struct {
  obj_phys_t  nej_o;
  uint32_t    nej_magic;
  uint32_t    nej_version;
  uint32_t    nej_efi_file_len;
  uint32_t    nej_num_extents;
  uint64_t    nej_reserved[16];
  prange_t    nej_rec_extents[0];
} nx_efi_jumpstart_t;

このnej_rec_extentsにあるブロックを読むことでAPFSパーティションに直接埋め込まれたEFI driverを読み込むことができる.

ユーザ空間で以上のようなことを行いraw disk imageからEFI driverを抽出する簡単なアプリケーションを実装した.本来は色々と検証を行う必要があるが,ここでは省略した.

抽出したバイナリは以下のように確かにPE32+のEFI driverとして認識されている.

$ file apfs.efi
apfs.efi: PE32+ executable (EFI boot service driver) x86-64 (stripped to external PDB), for MS Windows

せっかくなので抜き出したEFI driverを覗いてみる.バイナリのハッシュは1e780147f2cee614ab7e9a63c4e86525f06c5a18d72a6b8ec572752ffd95dea0である..debugセクションの指す9E9FChをみると"MTOC"の文字列と"apfs.efi.macho"という文字列が見えるのでこのバイナリの名前は"apfs.efi"でMach-OバイナリをApple謹製のmtocでPE32+に変換して生成されたようである.

ちなみにClover EFI bootloaderでは先にたどっていったような手順でapfs.efiをロードするEFI driverがあり,これでAPFS対応を行なっているようである.

https://sourceforge.net/p/cloverefiboot/code/HEAD/tree/FileSystems/ApfsDriverLoader/

話を元に戻してバイナリをみていくと,.textセクションの先頭に"2021/08/30"や"06:36:21"のような日時があるのでこれがこのバイナリがビルドされた日時だと考えられる.対象としたmacOS Bit Sur 11.6は2021年9月13日にリリースされたので少なくとも2週間程度前にはビルドされたようである. 他にもみどころはたくさんありそうだがこれぐらいにしておく.

以上はIntel Mac上での話である.ではARM64ベースのm1 Macの場合はどうだろうか?

$ uname -a
Darwin 7261.local 20.5.0 Darwin Kernel Version 20.5.0: Sat May  8 05:10:31 PDT 2021; root:xnu-7195.121.3~9/RELEASE_ARM64_T8101 arm64
$ ./dumper ~/Desktop/recovery.img
superblock at 280000000
nx_block_size: 0x1000
nx_block_count: 0x13fff5
nx_efi_jumpstart: 0x0
APFS EFI Jumpstart not found

残念ながら答えは否のようである.すでに広く知られているように,m1 MacではEFIではなくiPhoneなどと同じiBootから起動するカスタマイズされたファームウェアであるため,サードパーティでの実行環境を考慮しなければEFI Jumpstartの機能は不要であるためだと考えられる.へその緒のようにあったら面白かったのだが.ということでm1 MacのAPFSではnx_efi_jumpstart_tnx_efi_jumpstartは使われないフィールドとなってしまったようである.

というわけでIntel MacのAPFSの下にはEFI driverが埋まっているのでIntel Macをお持ちの方は酒宴でも開きましょう.

UEFI向け9P File Systemを作ってクラウドからネットワークブートできるようにした

UEFI向け9P File Systemを実装した. これにより9Pサーバからネットワークブートができるようになった. さらにFUSEと組み合わせることで少ない労力で9Pサーバ経由で クラウドからネットワークブートができるようになった.

ソースコードと発表資料と発表の録画は以下で公開している.

続きを読む

EDK2におけるDebugPrintErrorLevel

EDK2のコードにはDebugPrint()が多く埋め込まれている. この関数は第一引数にErrorLevelをとり, ビルド時に与えるPcdDebugPrintErrorLevelを変えることによりデバッグ出力を制御できる. この値は*.dscに以下のような記述をすることで設定できる.

[PcdsFixedAtBuild.common]
  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000

このとき,どのような値を設定すればいいのかわからなくなるのでメモとしてまとめた.

続きを読む

OVMFのデバッグ

ここではgdbを用いたOVMFのデバッグ方法について説明する. すでにOVMFのデバッグについて書かれた記事[1]が存在するが, ここでは特別なツールなどは使わずに通常のgdbでOVMFをデバッグする.

続きを読む

LLVMのEFI Byte Codeバックエンドを作る

ここでは開発しているLLVMEFI Byte Code (EBC)バックエンドの概要と EBCバックエンド固有の問題などについてみていく.

ソースコードは以下で公開している.

続きを読む

文鎮化したMinnowboardを復旧させる

MinnowboardIntel Atomを搭載した シングルボードコンピュータである. Minnowboardを使った実験で誤ったファームウェアを書き込んでしまい brick (文鎮化) させてしまったので SPI Flashを外部から書き換えることで復旧させる.

続きを読む