Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

自己紹介 ● なまえ ○ Toshifumi NISHINAGA(@tnishinaga) ● さいきんやったこと ○ セキュリティキャンプ講師 (2016 - 2017) ○ Google Sumber of Code 2016(Linux Foundation) ■ STM32F7マイコンでブートローダー (U-Boot)移植してLinux動くようにしました ● すきなこと ○ 組み込みいじり(ARM64勉強中) ○ アニメ、ゲーム ○ ロードバイク 2

Slide 3

Slide 3 text

cpsie if @ IRQ, FIQ interrupt enable 3

Slide 4

Slide 4 text

目次 ● KOZOSとは ● 移植作戦 4

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Raspberry Piでも動かしたいっ!! 7

Slide 8

Slide 8 text

移植しちゃいました ● URL ○ https://github.com/tnishinaga/kozos-rpi ● 作業期間 ○ 2017年4月に2週間程度 ○ (1日1時間程度作業) ● とりあえず以下は動く ○ スレッドの切り替え動作 ○ シリアル出力 8

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

全体をなんとなく把握する ● 「12ステップで作る 組み込みOS自作入門」を読む ● コードをなんとなく眺める 画像は以下より引用 http://kozos.jp/books/makeos/ 11

Slide 12

Slide 12 text

なんとなく把握したこと ● ブートローダーとOS本体(KOZOS)がある ● 起動時はFLASHに書かれたブートローダーが立ち上がる ● ブートローダーはシリアル経由でKOZOSをRAM読み込む ○ ※直接KOZOS起動するようにすればブートローダーは必要なさそう ● KOZOSにはスレッドと優先度スケジューリング、メモリ管理がある ● KOZOSのコンテクストスイッチは以下の理由で起こる ○ シリアル受信などの外部割り込み ○ システムコール関数呼び出しによるソフトウェア割り込み ○ 謎の例外 ● echoコマンドが動けばKOZOSの移植は完了 ○ シリアル割り込みによるスレッド切り替えの動作が確認できる 12

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

ARMクロス環境でビルド(だけ)できるようにする ● とりあえずARMのクロスコンパイラでビルドが全部走るようにする ● Makefileを書き換える ○ ARCHの部分(クロスコンパイラのprefix)をarm-none-eabiにする ○ CFLAGSのarm gccでは無いオプションを消す ● アセンブリ部はコメントアウトしたり、nopに変えたり ● コミットはこのあたり ○ https://github.com/tnishinaga/kozos-rpi/commit/0f11825d4e81f2af3c311d595292d6617d32 b7a1 14

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

動作確認 ● 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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

main関数まで動くようにする ● リンカスクリプトを修正してramの先頭を0x8000にする ○ 0x8000はRasPi起動時のエントリポイント ● スタートアップコード(_start)を書く ○ CPUのモードを設定する ■ cps命令で割り込み不可にする ○ BSSの初期化をする ○ スタックポインタの初期値を設定する ■ RasPiはGPUがRAMがマップされているアドレスの末尾から 64MB使うのに注意 ○ main関数を呼ぶ ○ sleepさせる ■ ARMの場合はwfi(割り込み待ち)でsleepできる 19

Slide 20

Slide 20 text

動作確認 ● gdbでmain関数にbreakpointを仕込む ○ break main ● プログラムをloadし、breakpointまでたどり着くか調べる 20

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

割り込みハンドラ(だけ)を作ってみる ● KOZOSで使う割り込み要因のハンドラだけ作る ○ IRQ割り込み(外部割り込み) ○ SVC割り込み(ソフトウェア割り込み) ● 割り込みハンドラに飛ばすベクタテーブルを作成する ○ IRQとSVC以外はsleepするようにする 22

Slide 23

Slide 23 text

ベクタテーブル ● CPUが例外発生時に適切な例外ハンドラに処理を移すための命令やアドレス 等を記録しておくテーブル ● 基本的にはメモリの先頭(0x00000000)に配置する Reset Undefined SVC Prefetch Abort Data Abort Reserved IRQ FIQ 0x00 0x04 0x08 0x0C 0x10 0x14 0x18 0x1C 23

Slide 24

Slide 24 text

● ベクタテーブルは再配置が必要 ○ RasPiのプログラムは0x8000に読み込まれるので ● スタートアップコード内でプログラムの何処か に置かれたVector Tableをメモリの先頭にコ ピーする ○ set_vector_table関数が担当 ベクタテーブルの設置 _start Vector Table メモリ 0x0000 0x8000 24

Slide 25

Slide 25 text

一般的なIRQ割り込みの動き Vector Table main IRQ Handler IRQ interrupt 1. ベクタテーブルの IRQ欄の命令を実行 2. IRQハンドラに飛ぶ 3. 割り込み処理が終 わったら元の場所に戻る メモリ 25

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

ディスパッチャを作ってみる ● ディスパッチャ ○ タスクやスレッドを切り替え CPU資源を与える機構のこと ● KOZOSのディスパッチャ ○ スレッド状態(現在のレジスタ値たち )の保存 ■ 割り込みハンドラ内で行う ○ スレッド状態の復元 ■ dispatch関数で行う 27

Slide 28

Slide 28 text

KOZOSのディスパッチャ(H8の場合) ● スレッド状態の保存 ○ 現在のスレッドのスタックにレジスタをすべて 保存 ○ 割り込み要因とスタックポインタ(er7)の値(保 存したスレッド状態の先頭アドレス)を interrupt関数に渡す ● スレッド状態の復元(dispatch関数) ○ 与えられたスタックポインタの値をスタックポイ ンタ(er7)にセット ○ スタックポインタからレジスタの状態を復元 レジスタ スタック レジスタ Push sp interrupt(type, sp) dispatch(sp) スタック レジスタ レジスタ Pop sp 28

Slide 29

Slide 29 text

ARM移植の課題 ● ARMはバンクレジスタがあるためH8のように単一のスタックにスレッド状態を保存 するのが難しい 29

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

割り込み応答とバンク切り替えの例 (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フラグ等の状態が入るレジスタ

Slide 32

Slide 32 text

割り込み応答とバンク切り替えの例 (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フラグ等の状態が入るレジスタ

Slide 33

Slide 33 text

割り込み応答とバンク切り替えの例 (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フラグ等の状態が入るレジスタ

Slide 34

Slide 34 text

割り込み応答とバンク切り替えの例 (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

Slide 35

Slide 35 text

割り込み応答とバンク切り替えの例 (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フラグ等の状態が入るレジスタ

Slide 36

Slide 36 text

割り込み応答とバンク切り替えの例 (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フラグ等の状態が入るレジスタ

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

ARM移植の課題 ● ARMはバンクレジスタがあるためH8のように単一のスタックにスレッド状態を保存 するのが難しい ○ そのままではソフトウェア割り込み (SVCモード)とIRQ割り込み(IRQモード)で異なるスタックに保存さ れてしまう ○ 単一のスタックに保存するにはどうすれば? 38 A. KOZOSを常にSystemモードで動かす。 どちらの割り込みが入ってもすぐにSystemモードに切り替え、 Systemモードのスタックにスレッドを保存する (H8のようにスタックを一元化する)

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

保存された状態の構造 40 SPSR R0-R3 R4-R12 LR_irq SP_bak, LR_sys sp SP_bak

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

シリアルが動くようにする ● やるだけ。 ● 「BareMetalで遊ぶ Raspberry Pi」のコードからMMIOのアドレス定義だけもらってく ると楽 ● FIFOを切るとH8と同じ使い方ができるので移植しやすい ● IRQ割り込みの設定するのも忘れずに ○ 割り込みに関する説明は「 BareMetalで遊ぶ Raspberry Pi」参照 43

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

ばぐつぶし ● BSS領域のアライメントがあって無くてBSSクリアに失敗してた ○ リンカスクリプトにアライン設定を追加 ● GPUから謎のIRQ割り込みが入りまくり処理できずに落ちてた ○ UART以外のIRQ割り込みをさせないよう修正 ● そのほか色々 ○ 未処理 45

Slide 46

Slide 46 text

デモ echoコマンドがうごくよ 46

Slide 47

Slide 47 text

TODO ● コミットを勉強用に整理する ● コードをもう少しきれいにする ● FBSD等のコンテクストスイッチを参考に改善する ● 既知のバグを何とかする ○ これはセキュキャン受講生がやってくれそう 47

Slide 48

Slide 48 text

まとめ ● Raspberry PiでKOZOSが動くようになった ● 使用するモードをSystemモードに限定してH8と動作をあわせて問題を解決 ● とりあえずechoが動きます 48