Raspberry Pi で bare metal をやっているこのコードを読んで理解したいと思います。README に殆ど書いてありますが、ちょっとよくわからないところがあったのでさらに詳しくしてみます。
前提として、ld スクリプト の通り、このプログラムの冒頭は 0x8000 からはじまっています。
割込みハンドラの実行
まず _start の最初には割込みハンドラの定義が書いてあります。
_start:
ldr pc,reset_handler
ldr pc,undefined_handler
ldr pc,swi_handler
ldr pc,prefetch_handler
ldr pc,data_handler
ldr pc,unused_handler
ldr pc,irq_handler
ldr pc,fiq_handler
reset_handler: .word reset
undefined_handler: .word hang
swi_handler: .word hang
prefetch_handler: .word hang
data_handler: .word hang
unused_handler: .word hang
irq_handler: .word irq
fiq_handler: .word fiq
これは、README によるとちょっとしたハックになっていて、若干トリッキーな動きをします。
まず最初に _start が実行されはじめると、最初にあるのが ldr pc,reset_handler なので、すぐに reset: に実行が移り、後続のコードは実行されません。
そして reset: の冒頭では以下のようになっています (コメントはこちらでつけたものです)
reset:
/* Set interrupt handler to radical address from 0x8000
*/
mov r0,#0x8000
mov r1,#0x0000
/* copy machine code 32bytes bytes at once */
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9} /* load machine code from 0x8000 */
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9} /* store code to 0x0000 */
/* more 32 bytes */
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
割込みハンドラは本来、0x0000 から初まるアドレスに指定通り配置する必要があります (すなわり、割込みが入ると、決め打ちのアドレスが実行される)。これは ARM のアーキテクチャマニュアルに書いてあるマジックナンバーです。なので、0x0000 へ、割込みハンドラをコピーする必要があり、それがこの部分になっています。
ldmia は LDM 命令 + IA (インクリメントアフター) という命令で、指定されたメモリアドレス (ここでは r0!) から、指定したレジスタリストに値をロードします。r0 はロードされるごとにインクリメントされます。
そして stmia は STM 命令 + IA という命令で、指定したメモリアドレス (ここでは r1!) に、指定したレジスタリストを書きこみます。ここでは一括して1度にレジスタ8個に対し処理が行われているので、8 * 4bytes = 32bytes がコピーされます。1セット目の ldmia/stmia で割込みハンドラ8個分がコピーされ、2セット目でその後に続くハンドラのアドレスリストがコピーされています。
C的には memcpy ですかね。
なぜこのようなハックが必要か?というと、プログラムが必ず 0x8000 にロードされて、直接 0x0000 には書きこめないからみたいです。
スタックポインタの初期化
続いて以下のような似たような3つの塊がでてきます。
/* IRQ Mode (0b11010010), */
mov r0,#0xD2
msr CPSR_c,r0
mov sp,#0x8000
/* FIQ Mode (0b11010001) */
mov r0,#0xD1
msr CPSR_c,r0
mov sp,#0x4000
/* Supervisor Mode (0b11010011) */
mov r0,#0xD3
msr CPSR_c,r0
mov sp,#0xF000000
msr 命令はプログラムステータスレジスタに値を書きこむ命令で、ここでは特にモードの切り替えを行っています。CPSR_c は カレントプログラムステータスレジスタの下位 8bit に書きこむという意味です。
カレントプログラムステータスレジスタの下位 8bit は以下のような構造になっています。
/*
* CPSR register lowest 8bit: (page A2-11)
* I [7] -> IRQ disabled (set 1 to disable)
* F [6] -> FIQ disabled (set 1 to disable)
* T [5] -> Always set 0 in ARM state
* MODE [4:0] -> Mode bit
*/
下位4bitでモードを指定するようになっており、それぞれコメントにあるようなモードに遷移します。
ARM では例外モードと呼ばれるモードそれぞれに対して sp レジスタは別々に存在しています。ここでは sp をそれぞれ別のアドレスを示すように初期化しているようです。
notmain の呼び出し
その後、スーパーバイザーモードのまま、C で定義された notmain 関数が呼ばれて終わりです。
本来、.bss の初期化などがあるはずですが、この例では使ってないので書いてないようです。
- トップ
-
tech
-
Raspberry Pi で bare metal している blinker05 の ARM ブートコードを読む