RP2040(Raspberry Pi Pico) - ブートの仕組みとブートローダ
長くなりすぎたので分割された、前の記事の後半。本記事では自作ブートローダを実装するときに固有の技術について説明する。前半では、RP2040のRustでのテンプレートプロジェクトやboot2 bootloaderについて解説している。
実質的に、開発メモ(NOTE.md)の要約である。開発メモなのでまとまっておらず読みにくいが、細かい点はそちらも参照してほしい。
- Cargoでのマルチターゲットプロジェクトの構成
- boot2-ram-memcpy(Flashメモリから内蔵SRAMにコピーして動作するboot2プログラム)
- イメージ操作ツール(自作のイメージ操作ツール、それをブートローダ、ブートされる組み込みアプリケーション、イメージ操作用のコマンドラインツールで兼用する)
- アドレスを指定したメモリアドレスからの直読み
- OpenOCD + GDB でデバッグ
汎用的な内容は別記事にまとめた
作りたいブートローダ
マイコン内蔵製品のファームウエアの場合、ファームウエアのアップデートが必要になることが多い。そのような場合、ブートローダとアプリケーションに分割し、通常時はブートローダがアプリケーションを起動し、アップデートが必要な場合はブートローダがアプリケーションを書き換える、という構成をとることが多い。
今回作ろうとするブートローダは、アプリケーションの起動をサポートすること、アプリケーションのアップデートをサポートすることを目標としたい。
以下の説明では、今回作るブートローダの名称として”boot-k”と呼称する。
- プロジェクトの名称として”boot-k”
- ブートローダのディレクトリ名は”bootloader”だが、それを呼ぶ時は”boot-k”と呼ぶことにする
- “boot-k”から実行されるアプリケーションとして、LEDが点滅する”app-blinky”。ディレクトリ名としても”app-blinky”
- アプリケーションのイメージヘッダをハンドルするライブラリとして”blxlib”。ディレクトリ名としても”blxlib”
- イメージヘッダを操作するコマンドラインツールとして”bintool”。ディレクトリ名としても”bintool”
Cargoでのマルチターゲット・ワークスペースの構成
このようなことをしたい場合、複数のコンポーネントが連携して、メモリマップやインターフェイスの情報を共有しながら動作する。Rustのプロジェクト管理ツールであるCargoはワークスペースという機能でそれをサポートしている。
ワークスペースの作成
本ブートローダ・システムを実現するためには、ブートローダ、アプリケーションの2つのプロジェクトが必要となってくる。 Cargoのワークスペースの機能をつかい、2つのプロジェクトを1つのワークスペースで管理する。
https://doc.rust-jp.rs/book-ja/ch14-03-cargo-workspaces.html
$ mkdir boot-k
$ cd boot-k
$ code .
ルートのCargo.toml
の[workspace]
→ members
に子プロジェクトを指定する。edition 2021 を使うために resolver = "2"
も指定しておく。
[workspace]
members = [
"bootloader",
"app-blinky",
]
resolver = "2"
その後、コマンドラインで次を実行すれば bootloader
, app-blinky
という子プロジェクトが生成される。
この時点で cargo build
や cargo clippy
などが双方のプロジェクトに対して実行できる。cargo run
はどちらのプロジェクトを実行すればよいのかを、ワークスペースの Cargo.toml
の default-run
で指定しなければならない。
❯ cargo new --bin bootloader
warning: compiling this new package may not work due to invalid workspace configuration
failed to load manifest for workspace member `/.../boot-k/app-blinky`
Caused by:
failed to read `/.../boot-k/app-blinky/Cargo.toml`
Caused by:
No such file or directory (os error 2)
Created binary (application) `bootloader` package
❯ cargo new --bin app-blinky
Created binary (application) `app-blinky` package
❯ cargo build
Compiling app-blinky v0.1.0 (/.../boot-k/app-blinky)
Compiling bootloader v0.1.0 (/.../boot-k/bootloader)
Finished dev [unoptimized + debuginfo] target(s) in 0.52s
❯ cargo test
Compiling bootloader v0.1.0 (/.../boot-k/bootloader)
Compiling app-blinky v0.1.0 (/.../boot-k/app-blinky)
Finished test [unoptimized + debuginfo] target(s) in 0.08s
Running unittests src/main.rs (target/debug/deps/app_blinky-72fb7b958e84668f)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/debug/deps/bootloader-2419ad1a1251e783)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
❯ cargo clippy
Checking bootloader v0.1.0 (/.../boot-k/bootloader)
Checking app-blinky v0.1.0 (/.../boot-k/app-blinky)
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
同様に、ライブラリであるblxlib
と、コマンドラインツールであるbintool
を作成する。
blxlib
はアプリケーションのイメージヘッダのためのlibクレートである。イメージヘッダの構造体とそれを操作する関数たちが定義されている。ブートローダはイメージを操作するためにヘッダの情報が必要だし、アプリケーションも自分自身の情報管理のためにヘッダの情報が必要だ。これらはターゲットであるRP2040上で動作する。
blxlib
の中にはCRC32を計算するサブルーチンがある。そのような計算サブルーチンについてはRust標準のテストフレームワーク(mod tests
)を使ってテストが構成されている。それはホスト上でcargo test
で実行できる。アーキテクチャ依存性を無視すれば、ターゲットで動くコードも、このようにホスト上でテスト可能となる。
❯ cargo test
Finished test [optimized + debuginfo] target(s) in 0.09s
Running unittests src/lib.rs (.../boot-k/target/debug/deps/blxlib-12ebb4783387e3a1)
running 2 tests
test crc32::tests::test_crc32 ... ok
test image_header::tests::test_calc_crc32 ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests blxlib
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
また、ホストで動作するイメージ操作のためのコマンドラインツールであるbintool
も作成する。これはホスト(LinuxなりMac)で動作するコマンドラインツールであり、binクレートである。このツールもイメージヘッダの情報が必要であり、同じイメージヘッダのライブラリblxlib
を使用する。
Rustは、ターゲットのアーキテクチャに依存する部分が、うまく分離されているので、組み込みターゲットで動くコードでもホストで動くコードでも、このように共用可能となる。イメージヘッダを読み込む”bootloader”(ターゲット上で動作)、イメージヘッダを提供する”app-blinky”(ターゲット上で動作)、イメージヘッダを作成する”bintool”(ホスト上で動作)で、共通のblxlibのコードが使える。
また、練習プロジェクトなど、ビルド対象としないサブディレクトリはexclude
に指定しておく。
[workspace]
members = [
"bootloader",
"app-blinky",
"bintool",
"blxlib",
]
exclude = [
"rp2040-project-template",
"rp2040-boot2",
"boot-mem",
]
具体的な様子は、プロジェクトのソースコードを参照してほしい。
メモリマップの設計
RP2040のメモリ構成は次のとおりだ。
- 0x0000_0000 から 16KB(0x400): 内蔵ROM
- 0x1000_0000 から 2MB(0x20_0000): 外付けFlashがマップされている
- 0x2000_0000 から 256KB(0x4_0000): 内蔵SRAM
- 0x4000_0000 移行: ペリフェラルレジスタ
通常のプロジェクト・テンプレートどおりにビルドすると、次のようなメモリマップになる。詳細は前編参照。
address | size | コンテンツ |
---|---|---|
0x0000_0000 | 16K(0x400) | Internal ROM |
0x1000_0000 | 0x100 | .boot2 |
0x1000_0100 | 0xc0 | .vector_table |
0x1000_01c0 | .text | |
0x2000_0000 | 256K(0x4_0000) | Internal SRAM |
0x4000_0000 | Peripherals |
ブートローダの機能を考慮した場合、次のような構成になる。
Address | size | Physical | project | Segment | Address | size | Alias |
---|---|---|---|---|---|---|---|
0x0000_0000 | 16K( 0x400) | Internal ROM | 0x0000_0000 | ROM_BASE | |||
0x1000_0000 | 2048K(0x20_0000) | QSPI Flash(XIP) | boot-k | .boot2 | 0x1000_0000 | 0x100(256B) | total 0x2_0000(128KB) |
.vector_table | 0x1000_0100 | 0x0c0(192B) | |||||
.text | 0x1000_01c0 | ||||||
0x1002_0000 | |||||||
app-blinky | .image_header | 0x1002_0000 | 0x100(256B) | total 0xe_0000(896KB) | |||
.vector_table | 0x1002_0100 | 0x0c0(192B) | |||||
.text | 0x1002_01c0 | ||||||
0x1010_0000 | |||||||
app_update | .image_header | 0x1010_0000 | 0x100(256B) | total 0xe_0000(896KB) | |||
.vector_table | 0x1010_0100 | 0x0c0(192B) | |||||
.text | 0x1010_01c0 | ||||||
0x101e_0000 | |||||||
swap | 0x101e_0000 | 0x2_0000(128KB) | |||||
0x1020_0000 | QSPI_END | ||||||
0x2000_0000 | 256K( 0x4_0000) | SRAM | boot-k | 0x2000_0000 | 0x2_0000(128KB) | copied boot-k | |
0x2002_0000 | 0x3_2000 | work RAM | |||||
0x2004_2000 | SRAM_END | ||||||
0x4000_0000 | APB Peripherals | 0x4000_0000 | |||||
0x5000_0000 | AHB-Lite Peripherals | 0x5000_0000 | |||||
0xd000_0000 | IOPORT Registers | 0xd000_0000 | |||||
0xe000_0000 | Cortex-M0+ internal registers | 0xe000_0000 | |||||
- 先頭 0x0000_0000から16KBは内蔵ROM。これは固定。
- 0x1000_0000から2MB(0x20_0000)は外付けFlash。
- 外付けFlashの先頭はboot-k。
- boot-kの先頭0x100(256B)はboot2️。
- その次の0xC0(192B)はboot-kのvector_table
- その後にboot-kの実行コード。全体として0x2_0000(128KB)を割り当てる。
- その後にアプリケーションコード。
- アプリケーションの先頭は自分で定義したimage_header。0x100(256B)。
- その次の0xC0(192B)はapp-blinkyのvector_table
- その後にapp-blinkyの実行コード。全体として0xe_0000(896KB)を割り当てる。
- その後にアプリケーションコードのアップデート領域
- この領域にアップデートコードを書き込むと。本来のアプリケーション領域にコピーされる。
- 残りの外付けFlashの領域 0x2_0000(128KB)はイメージ交換などの作業領域とする。
- 外付けFlashの先頭はboot-k。
- 0x2000_0000から256KB(0x4_0000)は内蔵SRAM。
- ただし、先頭128KB(0x2_0000)は、boot-kがコピーされて動作する領域とする。
- それ以降の領域はboot-k、app-blinky のRAM領域(スタック+ヒープ)。boot-kとapp-blinkyは同時には動作しないので、領域を共用して相互に破壊しても問題ない。
このようなメモリマップを設計して、それに向けてコードをビルドしていく。具体的には、リンカスクリプトでセグメントを作成して、固定アドレスに割り当て、コンパイラはそのセグメントに対してコードを出力すれば良い。
ただし、ブートローダの場合、boot2のコードは0x1000_0000から始まるアドレスに書き込まれ、そのアドレスで動作する。しかし、割り込みベクタとテキスト(実行されるコード)は0x1000_01c0から始まるアドレスに書き込まれるが、0x2000_0000から始まるRAMのアドレスにコピーされ、そこで実行される。
コード生成の方針として、リンカスクリプトで次のように指定する。つまりbootは0x1000_0000から0x100バイト、コードは0x2000_0000から0x1_ff00バイト(boot2の分だけ小さくなる)、RAMは0x2002_0000から0x2_0000バイト(textがコピーされるぶんだけスタック+ヒープの割当ては減る)というようにする。
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
FLASH : ORIGIN = 0x20000000, LENGTH = 0x1ff00
RAM : ORIGIN = 0x20020000, LENGTH = 0x20040000-0x20020000
この状態で、コンパイラの出力(ELF形式)は、.boot2セグメントが0x1000_0000から、コード領域は0x2000_0000から配置されたときに正常に実行されるように内部のアドレスが設定されている。
その後、objcopyで、.boot2とコード領域(.vector_table、.text, .rodata)をバイナリ形式で別々に出力し、catで結合する。
ELF形式は、セグメント情報、アドレス情報などを持っているが、バイナリ形式はメモリ領域をmemcpy
などでベタにコピーしてきた形式をしており、先頭アドレスの情報は別途与えなければならない。
arm-none-eabi-objcopy --only-section=".boot2" \
-O binary ${target_dir}/bootloader ${target_dir}/boot2.bin
arm-none-eabi-objcopy --only-section=".vector_table" \
--only-section=".text" --only-section=".rodata" \
-O binary ${target_dir}/bootloader ${target_dir}/bootloader.bin
cat ${target_dir}/bootloader.bin >> ${target_dir}/boot2.bin
このバイナリ・イメージをprobe-rs
で0x1000_0000から始まるアドレスにベタッと書き込めば良い。
probe-rs download --chip RP2040 --protocol swd --format bin --base-address 0x10000000 --skip 0 ${target_dir}/boot2.bin
probe-rs reset --chip RP2040 --protocol swd
すると、.boot2は0x1000_0000で実行可能なコードであり、0x1000_0000に書き込まれ、コード領域は0x2000_0000で実行可能なコードであり、0x1000_0100から書き込まれる。実行が開始されるとboot2がコード領域を0x2000_0000にコピーして、コードは正常に実行される。
ややこしいので整理すると、次のようなことを行っている。
コンパイラの出力
- 0x1000_0000..0x1000_0100 : boot2(
BOOT_LOADER_RAM_MEMCPY
) - 0x2000_0000.. : bootloader
objdumpで切り出す
- 0x1000_0000..0x1000_0100 : > boot2.bin
- 0x2000_0000.. : > bootloader.bin
結合する。binファイルは先頭からのオフセットのみで、アドレスを持たない
- 0x0000..0x0100 : < boot2.bin
- 0x0100.. : < bootloader.bin
書き込まれるアドレス(--base-address=0x1000_0000
)
- 0x1000_0000..0x1000_0100 : < boot2
- 0x1000_0100.. : < bootloader
boot2がSRAMにコピー(コンパイラの出力が再現されている)
- 0x1000_0000..0x1000_0100 : < boot2
- 0x2000_0000.. : < bootloader
リロケータブル・コード
どのアドレスにコピーしても動作するコード(マシン語)をPosition Independent Code(リロケータブル・コード)という。GCCの場合は-fPIC
をつければそのようなコードが生成される。Rustの場合は、.cargo/config.toml
の[rustflats]
に"-C", "relocation-model=pic"
と書けば良い。
ただし、実際に試してみると、ビルドエラーになる。原因はcortex-m-rt
がPICに対応していない。
cortex-m-rt
のlink.x
で.got
セクションを定義しており(https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in#L176)、.got
セクションがあるとリンクエラーとなる(https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in#L242)。
実際に調べてみたところ、関連クレートには.got
を使っているものはいないみたいだ。
というわけで、cortex-m-rt
を使わずにリロケータブル・コードを書くか、最初からRAMにコピーされる前提で位置固定のコードを書くか、ということになる。
boot2 と boot-k
なぜboot-kをRAMにコピーしてから実行するのか、という理由は、boot-kから外付けフラッシュを書き込みたいから。
RP2040では外付けフラッシュをコードメモリとして扱うことができ、そのアドレスで実行できる。XIP(eXecute In Place)という。ただし、XIPをONにしていると外付けFlashは読み出し専用となってしまう。ブートローダとして、アプリケーション・イメージのアップデートなどフラッシュの書き換えを行いたい場合、外付けフラッシュをXIPに設定せず、そのままのQSPIメモリとして扱う。ブートローダ自身のコードはSRAMにコピーして、その上で実行する。
フラッシュの書き換えを行う場合でも、メモリにマップされていないので、memcpy()
などではなく、QSPI FlashのコマンドをSSIペリフェラルに発行する必要がある。
もともとの boot2_w25q080
の動作
- 内蔵ROMの初期化ルーチン
- 外付けFlashの先頭領域にある
boot2_w25q080
を実行- QSPIの有効化
- XIPを有効化
- boot-kにジャンプ
- boot-kが実行される(Flash上で動作している、この時点ではXIPが有効)
もともとの boot2_ram_memcpy
の動作
- 内蔵ROMの初期化ルーチン
- 外付けFlashの先頭領域にある
boot2_ram_memcpy
を実行- XIPを有効化
- Flash領域からRAMにboot-kをコピー
- XIPを無効化
- boot-kにジャンプ
- boot-kが実行される(RAM上で動作している、この時点ではXIPが無効)
ここで問題となるのが、boot2_ram_memcpy
からboot-kが起動された時にはXIPが無効化されていることだ。
boot2_w25q080
からboot-kが起動されたときはXIPが有効化されているのでapp-blinkyのヘッダをチェックするときに、アドレスを指定してメモリの内容を直接読むことができた。しかし boot2_ram_memcpy
からboot-kが起動されたときはXIPが無効化されているのでapp-blinkyのヘッダをメモリアドレス経由で読み出すことができない。
これに気づかずに、ずいぶんとデバッグにハマってしまった。
こういうふうにしたい。そのために、rp2040-boot2
をローカルツリーにクローンして、XIPを無効化しないように修正してビルド&リンクする。
- 内蔵ROMの初期化ルーチン
- 外付けFlashの先頭領域にある boot2_ram_memcpyを実行
- XIPを有効化
- Flash領域からRAMにboot-kをコピー
- boot-kにジャンプ(この時点ではXIPが有効)
- boot-kの実行(RAM上で動作している)
- app-blinkyのヘッダをチェック(XIPが有効なのでアプリのヘッダをメモリアドレス経由で読める)
- アップデートが不要であれば app-blinkyにジャンプ
- アップデートする必要があれば
- XIPを無効化
- アップデート領域からapp-blinky領域にコピー
- XIPを有効化
- app-blinkyにジャンプ
- app-blinkyのヘッダをチェック(XIPが有効なのでアプリのヘッダをメモリアドレス経由で読める)
- app-blinkyの実行(Flash上で動作している、この時点ではXIPが有効)
イメージヘッダ操作ツール
“app-blinky”にはイメージヘッダが付加される。bootloaderはイメージヘッダを読み込み、必要な情報を得る。
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ImageHeader {
pub header_magic: u32, // 4
pub header_length: u16, // +2 = 6
pub hv_major: u8, // +1 = 7
pub hv_minor: u8, // +1 = 8
pub iv_major: u8, // +1 = 9
pub iv_minor: u8, // +1 = 10
pub iv_patch: u16, // +2 = 12
pub iv_build: u32, // +4 = 16
pub image_length: u32, // +4 = 20
pub signature: [u8; 128], // +128 = 148
pub payload_crc: u32, // +4 = 152
pub padding: [u8; 100], // +100 = 252
pub crc32: u32, // +4 = 256
}
- マジックナンバー
- ヘッダ長さ
- バージョン情報
- イメージのサイズ
- 署名
- CRC
通常のビルドの後プロセスとして”app-blinky”にイメージヘッダを付加する必要がある。イメージヘッダを操作するツール(イメージの長さを求め、CRCを計算し、ヘッダを付加する)も自作する必要がある。
同一のソースコードから、bootloaderに組み込むためのイメージ操作ライブラリ(ターゲット環境: ARM/Thumb2)で動作する、と、イメージヘッダを操作するツール(ホスト環境: x64/Linuxだったりaarch64/MacOSだったり)の両方のバイナリが生成される。
このライブラリは“blxlib”と呼んでいる。
Rust組み込みの#[cfg(test)]
も実装されていて、ホスト環境でcargo test
とすれば、論理的なテストが実施できる。
❯ cargo test
Compiling blxlib v0.1.0 (.../boot-k/blxlib)
Finished `test` profile [optimized + debuginfo] target(s) in 5.17s
Running unittests src/lib.rs (.../boot-k/target/debug/deps/blxlib-bd447e91ed9b4b41)
running 2 tests
test crc32::tests::test_crc32 ... ok
test image_header::tests::test_calc_crc32 ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests blxlib
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
アドレスを指定したメモリアドレスからの直読み
bootloaderからイメージヘッダを読み込む場合、特定のアドレスから、イメージヘッダに相当する部分を読み込んでパースすることになる。
Rustの場合、core::ptr::slice_from_raw_parts()
を使う。
- 第一引数は
*const u8
にキャストした先頭アドレスの即値(32ビット整数) - 第二引数は
usize
にキャストしたメモリの長さ - 返り値は
*const [u8]
というスライス。 core::ptr::slice_from_raw_parts()
自体はunsafe不要。- ただし、戻り値の
*const [u8]
の参照外しをしてスライスを得るときにはunsafe
が必要。
次の例は、メモリアドレス(start_address+image_header::HEADER_LENGTH
:ヘッダの終わり)からimage_length
だけスライスとして読み出して、crc32を計算する例。
pub fn crc32(buf: &[u8]) -> u32 {
let seed = 0u32;
let mut out = !seed;
let mut i = 0usize;
while i < buf.len() {
out = (out >> 8) ^ TABLE[((out & 0xff) ^ (buf[i] as u32)) as usize];
i += 1;
}
!out
}
let slice = core::ptr::slice_from_raw_parts(
(start_address as usize + image_header::HEADER_LENGTH as usize) as *const u8,
ih.image_length as usize,
);
let payload_crc = crc32::crc32(unsafe { &*slice });
次の例は、スライスとして読み出すのではなく、直接ImageHeader
という構造体にマップして読み出す場合。
ptr::read_volatile()
が使える。ptr::read_volatile()
はunsafe
。
pub fn load_from_addr(addr: u32) -> ImageHeader {
unsafe { ptr::read_volatile(addr as *const ImageHeader) }
}
OpenOCD + GDB でデバッグ
- OpenOCDは
brew
でインストール。 - デバッグアダプタは、汎用の
interface/cmsis-dap.cfg
を使えばPicoProbeを駆動することができる。 adapter speed 5000
も追加。- ターゲットは
target/rp2040.cfg
を指定。
❯ sudo openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000"
Password:
Open On-Chip Debugger 0.12.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
adapter speed: 5000 kHz
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E660D4A0A781212F
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: Atomic commands supported
Info : CMSIS-DAP: Test domain timer supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
Info : clock speed 5000 kHz
Info : SWD DPIDR 0x0bc12477, DLPIDR 0x00000001
Info : SWD DPIDR 0x0bc12477, DLPIDR 0x10000001
Info : [rp2040.core0] Cortex-M0+ r0p1 processor detected
Info : [rp2040.core0] target has 4 breakpoints, 2 watchpoints
Info : [rp2040.core1] Cortex-M0+ r0p1 processor detected
Info : [rp2040.core1] target has 4 breakpoints, 2 watchpoints
Info : starting gdb server for rp2040.core0 on 3333
Info : Listening on port 3333 for gdb connections
Info : starting gdb server for rp2040.core1 on 3334
Info : Listening on port 3334 for gdb connections
gdbで接続する場合、ポート3333が core0, 3334が core1となる。
❯ arm-none-eabi-gdb ../target/thumbv6m-none-eabi/debug/bootloader
GNU gdb (GDB) 14.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=aarch64-apple-darwin22.6.0 --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ../target/thumbv6m-none-eabi/debug/bootloader...
(gdb) target remote localhost:3333
Remote debugging using localhost:3333
<signal handler called>
(gdb) monitor reset init
[rp2040.core0] halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
[rp2040.core1] halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
(gdb) disp $pc
1: $pc = (void (*)()) 0xfffffffe
(gdb) continue
Continuing.
[rp2040.core0] clearing lockup after double fault
Program received signal SIGINT, Interrupt.
<signal handler called>
1: $pc = (void (*)()) 0xfffffffe
(gdb)