Pro Yearly is on sale from $80 to $50! »

KOZOS/RasPi移植してみた

 KOZOS/RasPi移植してみた

2017/07/02に第7回自作OSもくもく会で発表したKOZOSをRasPiに移植した発表資料です。

Bdd3fb3269e67bbc512f2530c409a926?s=128

Toshifumi NISHINAGA

July 02, 2017
Tweet

Transcript

  1. KOZOS/RasPi 移植してみた Toshifumi NISHINAGA(@tnishinaga) 2017/07/02 第7回自作OSもくもく会

  2. 自己紹介 • なまえ ◦ Toshifumi NISHINAGA(@tnishinaga) • さいきんやったこと ◦ セキュリティキャンプ講師

    (2016 - 2017) ◦ Google Sumber of Code 2016(Linux Foundation) ▪ STM32F7マイコンでブートローダー (U-Boot)移植してLinux動くようにしました • すきなこと ◦ 組み込みいじり(ARM64勉強中) ◦ アニメ、ゲーム ◦ ロードバイク 2
  3. cpsie if @ IRQ, FIQ interrupt enable 3

  4. 目次 • KOZOSとは • 移植作戦 4

  5. KOZOSとは • 非常に小さな教育用組み込みOS ◦ ブートローダー&OS本体で2700行程度(書籍版) • 対応アーキテクチャ ◦ H8(書籍版) ◦

    移植版:PowerPC/SH2/Arduino(AVR) • 持っている機能 ◦ スレッド管理 ◦ メモリ管理 ◦ I/O管理 5
  6. KOZOSとは • 非常に小さな教育用組み込みOS ◦ ブートローダー&OS本体で2700行程度(書籍版) • 対応アーキテクチャ ◦ H8(書籍版) ◦

    移植版:PowerPC/SH2/Arduino(AVR) • 持っている機能 ◦ スレッド管理 ◦ メモリ管理 ◦ I/O管理 6
  7. Raspberry Piでも動かしたいっ!! 7

  8. 移植しちゃいました • URL ◦ https://github.com/tnishinaga/kozos-rpi • 作業期間 ◦ 2017年4月に2週間程度 ◦

    (1日1時間程度作業) • とりあえず以下は動く ◦ スレッドの切り替え動作 ◦ シリアル出力 8
  9. 移植作戦 • 方針 ◦ とりあえず少しずつ動くようにしていく • 手順 ◦ 全体をなんとなく把握する ◦

    ARMクロス環境でビルド(だけ)できるようにする ◦ JTAGデバッグできるようにする ◦ main関数まで動くようにする ◦ 割り込みハンドラを作ってみる ◦ ディスパッチャを作ってみる ◦ Serialを動くようにする ◦ バグ潰し 9
  10. 移植作戦 • 方針 ◦ とりあえず少しずつ動くようにしていく • 手順 ◦ 全体をなんとなく把握する ◦

    ARMクロス環境でビルド(だけ)できるようにする ◦ JTAGデバッグできるようにする ◦ main関数まで動くようにする ◦ 割り込みハンドラを作ってみる ◦ ディスパッチャを作ってみる ◦ Serialを動くようにする ◦ バグ潰し 10
  11. 全体をなんとなく把握する • 「12ステップで作る 組み込みOS自作入門」を読む • コードをなんとなく眺める 画像は以下より引用 http://kozos.jp/books/makeos/ 11

  12. なんとなく把握したこと • ブートローダーとOS本体(KOZOS)がある • 起動時はFLASHに書かれたブートローダーが立ち上がる • ブートローダーはシリアル経由でKOZOSをRAM読み込む ◦ ※直接KOZOS起動するようにすればブートローダーは必要なさそう •

    KOZOSにはスレッドと優先度スケジューリング、メモリ管理がある • KOZOSのコンテクストスイッチは以下の理由で起こる ◦ シリアル受信などの外部割り込み ◦ システムコール関数呼び出しによるソフトウェア割り込み ◦ 謎の例外 • echoコマンドが動けばKOZOSの移植は完了 ◦ シリアル割り込みによるスレッド切り替えの動作が確認できる 12
  13. 移植作戦 • 方針 ◦ とりあえず少しずつ動くようにしていく • 手順 ◦ 全体をなんとなく把握する ◦

    ARMクロス環境でビルド(だけ)できるようにする ◦ JTAGデバッグできるようにする ◦ main関数まで動くようにする ◦ 割り込みハンドラを作ってみる ◦ ディスパッチャを作ってみる ◦ Serialを動くようにする ◦ バグ潰し 13
  14. ARMクロス環境でビルド(だけ)できるようにする • とりあえずARMのクロスコンパイラでビルドが全部走るようにする • Makefileを書き換える ◦ ARCHの部分(クロスコンパイラのprefix)をarm-none-eabiにする ◦ CFLAGSのarm gccでは無いオプションを消す

    • アセンブリ部はコメントアウトしたり、nopに変えたり • コミットはこのあたり ◦ https://github.com/tnishinaga/kozos-rpi/commit/0f11825d4e81f2af3c311d595292d6617d32 b7a1 14
  15. 移植作戦 • 方針 ◦ とりあえず少しずつ動くようにしていく • 手順 ◦ 全体をなんとなく把握する ◦

    ARMクロス環境でビルド(だけ)できるようにする ◦ JTAGデバッグできるようにする ◦ main関数まで動くようにする ◦ 割り込みハンドラを作ってみる ◦ ディスパッチャを作ってみる ◦ Serialを動くようにする ◦ バグ潰し 15
  16. JTAGデバッグできるようにする • デバッグできないと辛いのでJTAGデバッグできるようにする • RasPiのJTAGは特定のGPIOポートのモードをAlternativeモードにして有効化する • JTAGデバッグソフトはOpenOCDを利用 • JTAGアダプタは以下を利用 ◦

    http://akizukidenshi.com/catalog/g/gM-06545/ • コミットはこのあたり ◦ https://github.com/tnishinaga/kozos-rpi/commit/0 31f8dcd9a39ba41e9d7e6fa5fa4387b9314948f ◦ https://github.com/tnishinaga/kozos-rpi/commit/5 de931245aec2a55b99bee2740bb789a11049615 16
  17. 動作確認 • JTAGアダプタとRaspberry Piを接続する ◦ 接続先等は「BareMetalで遊ぶ RaspberryPi」等を参照 • OpenOCDを立ち上げる ◦

    openocd -f raspi_with_ngxtech.cfg • GDBを立ち上げる ◦ arm-none-eabi-gdb kozos.elf • GDBをOpenOCDのGDB Serverと接続する ◦ target remote localhost:3333 • 一旦プログラムを走らせてから止める ◦ continue してから Ctrl + C • プログラムをロードして実行再開し、動けばOK ◦ load して continue 17
  18. 移植作戦 • 方針 ◦ とりあえず少しずつ動くようにしていく • 手順 ◦ 全体をなんとなく把握する ◦

    ARMクロス環境でビルド(だけ)できるようにする ◦ JTAGデバッグできるようにする ◦ main関数まで動くようにする ◦ 割り込みハンドラを作ってみる ◦ ディスパッチャを作ってみる ◦ Serialを動くようにする ◦ バグ潰し 18
  19. main関数まで動くようにする • リンカスクリプトを修正してramの先頭を0x8000にする ◦ 0x8000はRasPi起動時のエントリポイント • スタートアップコード(_start)を書く ◦ CPUのモードを設定する ▪

    cps命令で割り込み不可にする ◦ BSSの初期化をする ◦ スタックポインタの初期値を設定する ▪ RasPiはGPUがRAMがマップされているアドレスの末尾から 64MB使うのに注意 ◦ main関数を呼ぶ ◦ sleepさせる ▪ ARMの場合はwfi(割り込み待ち)でsleepできる 19
  20. 動作確認 • gdbでmain関数にbreakpointを仕込む ◦ break main • プログラムをloadし、breakpointまでたどり着くか調べる 20

  21. 移植作戦 • 方針 ◦ とりあえず少しずつ動くようにしていく • 手順 ◦ 全体をなんとなく把握する ◦

    ARMクロス環境でビルド(だけ)できるようにする ◦ JTAGデバッグできるようにする ◦ main関数まで動くようにする ◦ 割り込みハンドラを作ってみる ◦ ディスパッチャを作ってみる ◦ Serialを動くようにする ◦ バグ潰し 21
  22. 割り込みハンドラ(だけ)を作ってみる • KOZOSで使う割り込み要因のハンドラだけ作る ◦ IRQ割り込み(外部割り込み) ◦ SVC割り込み(ソフトウェア割り込み) • 割り込みハンドラに飛ばすベクタテーブルを作成する ◦

    IRQとSVC以外はsleepするようにする 22
  23. ベクタテーブル • CPUが例外発生時に適切な例外ハンドラに処理を移すための命令やアドレス 等を記録しておくテーブル • 基本的にはメモリの先頭(0x00000000)に配置する Reset Undefined SVC Prefetch

    Abort Data Abort Reserved IRQ FIQ 0x00 0x04 0x08 0x0C 0x10 0x14 0x18 0x1C 23
  24. • ベクタテーブルは再配置が必要 ◦ RasPiのプログラムは0x8000に読み込まれるので • スタートアップコード内でプログラムの何処か に置かれたVector Tableをメモリの先頭にコ ピーする ◦

    set_vector_table関数が担当 ベクタテーブルの設置 _start Vector Table メモリ 0x0000 0x8000 24
  25. 一般的なIRQ割り込みの動き Vector Table main IRQ Handler IRQ interrupt 1. ベクタテーブルの

    IRQ欄の命令を実行 2. IRQハンドラに飛ぶ 3. 割り込み処理が終 わったら元の場所に戻る メモリ 25
  26. 移植作戦 • 方針 ◦ とりあえず少しずつ動くようにしていく • 手順 ◦ 全体をなんとなく把握する ◦

    ARMクロス環境でビルド(だけ)できるようにする ◦ JTAGデバッグできるようにする ◦ main関数まで動くようにする ◦ 割り込みハンドラを作ってみる ◦ ディスパッチャを作ってみる ◦ Serialを動くようにする ◦ バグ潰し 26
  27. ディスパッチャを作ってみる • ディスパッチャ ◦ タスクやスレッドを切り替え CPU資源を与える機構のこと • KOZOSのディスパッチャ ◦ スレッド状態(現在のレジスタ値たち

    )の保存 ▪ 割り込みハンドラ内で行う ◦ スレッド状態の復元 ▪ dispatch関数で行う 27
  28. KOZOSのディスパッチャ(H8の場合) • スレッド状態の保存 ◦ 現在のスレッドのスタックにレジスタをすべて 保存 ◦ 割り込み要因とスタックポインタ(er7)の値(保 存したスレッド状態の先頭アドレス)を interrupt関数に渡す

    • スレッド状態の復元(dispatch関数) ◦ 与えられたスタックポインタの値をスタックポイ ンタ(er7)にセット ◦ スタックポインタからレジスタの状態を復元 レジスタ スタック レジスタ Push sp interrupt(type, sp) dispatch(sp) スタック レジスタ レジスタ Pop sp 28
  29. ARM移植の課題 • ARMはバンクレジスタがあるためH8のように単一のスタックにスレッド状態を保存 するのが難しい 29

  30. バンクレジスタ(レジスタバンク) • ARMには複数のCPUモードがある • 一部レジスタはCPUのモードが変わると勝手に退避される(バンク切り替え) ◦ この退避される一部レジスタのことをバンクレジスタという 図はhttp://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211k/ch02s08s01.html より引用 30

  31. 割り込み応答とバンク切り替えの例 (SystemからIRQ) 1. SystemのCPSRがIRQのSPSRにコピーされ る 2. CPUモードを切り替える a. この例だとSystemからIRQへ 3.

    バンクレジスタを切り替える a. SP, LR, SPSRがIRQのものになる 4. 割り込みを無効化する 5. PC(戻りアドレス)がIRQのLRにセットされる 6. PCに例外ベクタアドレスをセットする 31 R0 - R12 SP_sys LR_sys PC CPSR SPSR_sys System R0 - R12 SP_irq LR_irq PC CPSR SPSR_irq IRQ CPSR: CPUフラグ等の状態が入るレジスタ
  32. 割り込み応答とバンク切り替えの例 (SystemからIRQ) 1. SystemのCPSRがIRQのSPSRにコピーされ る 2. CPUモードを切り替える a. この例だとSystemからIRQへ 3.

    バンクレジスタを切り替える a. SP, LR, SPSRがIRQのものになる 4. 割り込みを無効化する 5. PC(戻りアドレス)がIRQのLRにセットされる 6. PCに例外ベクタアドレスをセットする 32 R0 - R12 SP_sys LR_sys PC CPSR SPSR_sys System R0 - R12 SP_irq LR_irq PC CPSR SPSR_irq IRQ CPSR: CPUフラグ等の状態が入るレジスタ
  33. 割り込み応答とバンク切り替えの例 (SystemからIRQ) 1. SystemのCPSRがIRQのSPSRにコピーされ る 2. CPUモードを切り替える a. この例だとSystemからIRQへ 3.

    バンクレジスタを切り替える a. SP, LR, SPSRがIRQのものになる 4. 割り込みを無効化する 5. PC(戻りアドレス)がIRQのLRにセットされる 6. PCに例外ベクタアドレスをセットする 33 R0 - R12 SP_sys LR_sys PC CPSR SPSR_sys System R0 - R12 SP_irq LR_irq PC CPSR SPSR_irq IRQ CPSR: CPUフラグ等の状態が入るレジスタ
  34. 割り込み応答とバンク切り替えの例 (SystemからIRQ) 1. SystemのCPSRがIRQのSPSRにコピーされ る 2. CPUモードを切り替える a. この例だとSystemからIRQへ 3.

    バンクレジスタを切り替える a. SP, LR, SPSRがIRQのものになる 4. 割り込みを無効化する 5. PC(戻りアドレス)がIRQのLRにセットされる 6. PCに例外ベクタアドレスをセットする 34 R0 - R12 SP_sys LR_sys PC CPSR SPSR_sys System R0 - R12 SP_irq LR_irq PC CPSR SPSR_irq IRQ CPSR: CPUフラグ等の状態が入るレジスタ Change
  35. 割り込み応答とバンク切り替えの例 (SystemからIRQ) 1. SystemのCPSRがIRQのSPSRにコピーされ る 2. CPUモードを切り替える a. この例だとSystemからIRQへ 3.

    バンクレジスタを切り替える a. SP, LR, SPSRがIRQのものになる 4. 割り込みを無効化する 5. PC(戻りアドレス)がIRQのLRにセットされる 6. PCに例外ベクタアドレスをセットする 35 R0 - R12 SP_sys LR_sys PC CPSR SPSR_sys System R0 - R12 SP_irq LR_irq PC CPSR SPSR_irq IRQ CPSR: CPUフラグ等の状態が入るレジスタ
  36. 割り込み応答とバンク切り替えの例 (SystemからIRQ) 1. SystemのCPSRがIRQのSPSRにコピーされ る 2. CPUモードを切り替える a. この例だとSystemからIRQへ 3.

    バンクレジスタを切り替える a. SP, LR, SPSRがIRQのものになる 4. 割り込みを無効化する 5. PC(戻りアドレス)がIRQのLRにセットされる 6. PCに例外ベクタアドレスをセットする 36 R0 - R12 SP_sys LR_sys PC CPSR SPSR_sys System R0 - R12 SP_irq LR_irq PC: 0x18 CPSR SPSR_irq IRQ CPSR: CPUフラグ等の状態が入るレジスタ
  37. ARM移植の課題 • ARMはバンクレジスタがあるためH8のように単一のスタックにスレッド状態を保存 するのが難しい ◦ そのままではソフトウェア割り込み (SVCモード)とIRQ割り込み(IRQモード)で異なるスタックに保存さ れてしまう ◦ 単一のスタックに保存するにはどうすれば?

    37
  38. ARM移植の課題 • ARMはバンクレジスタがあるためH8のように単一のスタックにスレッド状態を保存 するのが難しい ◦ そのままではソフトウェア割り込み (SVCモード)とIRQ割り込み(IRQモード)で異なるスタックに保存さ れてしまう ◦ 単一のスタックに保存するにはどうすれば?

    38 A. KOZOSを常にSystemモードで動かす。 どちらの割り込みが入ってもすぐにSystemモードに切り替え、 Systemモードのスタックにスレッドを保存する (H8のようにスタックを一元化する)
  39. KOZOS/RasPiのスレッド状態保存(IRQ割り込み) 1. R0-R3までをIRQのスタックに保存 2. SP_irqをR0に、SPSR_irqをR1に、LR_irqをR3に退避 3. cps命令でSystemモードに変更(バンク切り替えが起こる) 4. SP_sysをR2に保存(SP_bak) 5.

    R3(LR_irq)をスタックにpush 6. R4-R12をスタックにpush 7. SP_irqに保存したR0-R3をR4-R7に復元 8. R0-R3をスタックにpush 9. R1(SPSR_irq)をスタックにpush 10. R2(SP_bak)とLR_sysをスタックにpush 39
  40. 保存された状態の構造 40 SPSR R0-R3 R4-R12 LR_irq SP_bak, LR_sys sp SP_bak

  41. スレッド状態の復元 • SVCモードに切り替え、コンテキストを逆順にもどしていく • ldmfd sp!, {pc}^ のように^を付けるとCPUが勝手にSPSRからCPSRを復元してから PCの命令に戻ってくれる 41

  42. 移植作戦 • 方針 ◦ とりあえず少しずつ動くようにしていく • 手順 ◦ 全体をなんとなく把握する ◦

    ARMクロス環境でビルド(だけ)できるようにする ◦ JTAGデバッグできるようにする ◦ main関数まで動くようにする ◦ 割り込みハンドラを作ってみる ◦ ディスパッチャを作ってみる ◦ Serialを動くようにする ◦ バグ潰し 42
  43. シリアルが動くようにする • やるだけ。 • 「BareMetalで遊ぶ Raspberry Pi」のコードからMMIOのアドレス定義だけもらってく ると楽 • FIFOを切るとH8と同じ使い方ができるので移植しやすい

    • IRQ割り込みの設定するのも忘れずに ◦ 割り込みに関する説明は「 BareMetalで遊ぶ Raspberry Pi」参照 43
  44. 移植作戦 • 方針 ◦ とりあえず少しずつ動くようにしていく • 手順 ◦ 全体をなんとなく把握する ◦

    ARMクロス環境でビルド(だけ)できるようにする ◦ JTAGデバッグできるようにする ◦ main関数まで動くようにする ◦ 割り込みハンドラを作ってみる ◦ ディスパッチャを作ってみる ◦ Serialを動くようにする ◦ バグ潰し 44
  45. ばぐつぶし • BSS領域のアライメントがあって無くてBSSクリアに失敗してた ◦ リンカスクリプトにアライン設定を追加 • GPUから謎のIRQ割り込みが入りまくり処理できずに落ちてた ◦ UART以外のIRQ割り込みをさせないよう修正 •

    そのほか色々 ◦ 未処理 45
  46. デモ echoコマンドがうごくよ 46

  47. TODO • コミットを勉強用に整理する • コードをもう少しきれいにする • FBSD等のコンテクストスイッチを参考に改善する • 既知のバグを何とかする ◦

    これはセキュキャン受講生がやってくれそう 47
  48. まとめ • Raspberry PiでKOZOSが動くようになった • 使用するモードをSystemモードに限定してH8と動作をあわせて問題を解決 • とりあえずechoが動きます 48