rpi_start.Sを読む (第3回)
続きです。
まずは、コメントです。
MMUに最低限の準備をします、ぐらいにとらえておけばよいのでしょうかねぇ。
さて、今回のメインはこちらです。
先に進みます。
まぁ、とにかくL1変換テーブルの先頭をr0に代入したという感じです。
次に進みます。
あとでr0を使うので温存しておいて、r1のほうを作業用に使うという感じですかね。
次に進みます。
先に進みましょう。
ここから、ループに入ります。
ということで、r1つまりL1_temp_tableの先頭からゼロを代入した8つのレジスタをどんどんメモリに転送していきます。
次に進みます。
L1_TABLE_SIZE(つまり0x4000)が格納されたr2から4*4*8を引きます。レジスタは32ビットなので4, stmiaが4回なので4, レジスタは8個転送されているので4*4*8ですね。
次に進みます。
bneのneはNot Equalつまりbneはr2がゼロでなければ1bに飛べと言っています。1bとは149行目の1:というラベルです。1bのbはbackのbでしょうか?
今回の処理をCっぽく書いてみると次のような感じでしょうか。
さてこれで、Ltemp_l1_tableが全部ゼロクリアされた状態になりました。
まずは、コメントです。
132 /* 133 * Set up a preliminary mapping in the MMU to allow us to run 134 * at KERNEL_BASE with caches on. 135 */キャッシュをONにしてKERNEL_BASEで動かすため、MMUに準備的なマッピングをセットアップします、とでも訳すのでしょうか? なんのこっちゃ。
MMUに最低限の準備をします、ぐらいにとらえておけばよいのでしょうかねぇ。
さて、今回のメインはこちらです。
136 /* Build page table from scratch */ 137 ldr r0, Ltemp_l1_table /* The page table address - entered into TTB later */ 138 mov r1, r0 /* Start address to clear memory. */ 139 /* Zero the entire table so all virtual addresses are invalid. */ 140 mov r2, #L1_TABLE_SIZE /* in bytes */ 141 mov r3, #0 142 mov r4, r3 143 mov r5, r3 144 mov r6, r3 145 mov r7, r3 146 mov r8, r3 147 mov r10, r3 148 mov r11, r3 149 1: stmia r1!, {r3-r8,r10-r11} 150 stmia r1!, {r3-r8,r10-r11} 151 stmia r1!, {r3-r8,r10-r11} 152 stmia r1!, {r3-r8,r10-r11} 153 subs r2, r2, #(4 * 4 * 8) /* bytes per loop */ 154 bne 1bちょっと長いですが、似た命令が多いので、大丈夫でしょう。
136 /* Build page table from scratch */ページテーブルをスクラッチからつくりますというコメントです。なるほど、以降の処理はページテーブルに関する処理というわけですね。
先に進みます。
137 ldr r0, Ltemp_l1_table /* The page table address - entered into TTB later */Ltemp_l1_tableのアドレスをr0にロードします。TTBとはTransration Table Baseの略ですね。Ltemp_l1_tableのところは、
258 Ltemp_l1_table: 259 /* Put the temporary L1 translation table just below the kernel. */ 260 .word 0x4000と書かれています。kernelのすぐ後ろにテンポラリのL1変換テーブルを置く、と読めます。0x4000の大きさのテーブルが確保されているってことですかね。
まぁ、とにかくL1変換テーブルの先頭をr0に代入したという感じです。
次に進みます。
138 mov r1, r0 /* Start address to clear memory. */これは、先ほどのr0つまりL1変換テーブルの先頭をr1にも代入しています。
あとでr0を使うので温存しておいて、r1のほうを作業用に使うという感じですかね。
次に進みます。
139 /* Zero the entire table so all virtual addresses are invalid. */テーブル全体をゼロにして、すべての仮想アドレスをinvalidにします。というコメントです。なんだかよくわかりませんが、ゼロにするようです。
140 mov r2, #L1_TABLE_SIZE /* in bytes */L1_TABLE_SIZEはarch/arm/include/arm32/pte.hで
#define L1_TABLE_SIZE 0x4000 /* 16K */と定義されていますので、
r2 = 0x4000; /* L1_TABLE_SIZE */ということになりますね。そういえば、さっきのL1_temp_tableは
260 .word 0x4000でしたね。なるほど、関係ありそうです。
先に進みましょう。
141 mov r3, #0 142 mov r4, r3 143 mov r5, r3 144 mov r6, r3 145 mov r7, r3 146 mov r8, r3 147 mov r10, r3 148 mov r11, r3レジスタr3に0を代入し、それをレジスタr4, r5, r6, r7, r8, r10, r11に代入しています。つまり、8つのレジスタをぜんぶゼロにしているようです。
ここから、ループに入ります。
149 1: stmia r1!, {r3-r8,r10-r11} 150 stmia r1!, {r3-r8,r10-r11} 151 stmia r1!, {r3-r8,r10-r11} 152 stmia r1!, {r3-r8,r10-r11}stmiaです。stmはレジスタ-メモリ転送命令の複数版で、iaはIncrement Afterということで、メモリアクセス後にポインタを増やす、でしたね。
ということで、r1つまりL1_temp_tableの先頭からゼロを代入した8つのレジスタをどんどんメモリに転送していきます。
次に進みます。
153 subs r2, r2, #(4 * 4 * 8) /* bytes per loop */subsのsubは減算命令です。subsの後ろのsはステータスレジスタを更新するためののsです。
L1_TABLE_SIZE(つまり0x4000)が格納されたr2から4*4*8を引きます。レジスタは32ビットなので4, stmiaが4回なので4, レジスタは8個転送されているので4*4*8ですね。
次に進みます。
154 bne 1bbneのbは分岐命令です。先のsubsの末尾のsで更新されたステータスフラグをみて、分岐します。見るステータスフラグはZ、つまりゼロかどうかです。
bneのneはNot Equalつまりbneはr2がゼロでなければ1bに飛べと言っています。1bとは149行目の1:というラベルです。1bのbはbackのbでしょうか?
今回の処理をCっぽく書いてみると次のような感じでしょうか。
r0 = &(Ltemp_l1_table); r1 = r0; r2 = 0x4000; /* L1_TABLE_SIZE */ r3 = 0; r4 = r3; r5 = r3; r6 = r3; r7 = r3; r8 = r3; r10 = r3; r11 = r3; do { for (int i = 0; i < 4; i++) { *(r1++) = r3; *(r1++) = r4; *(r1++) = r5; *(r1++) = r6; *(r1++) = r7; *(r1++) = r8; *(r1++) = r10; *(r1++) = r11; } r2 -= (4 * 4 * 8); } while (r2 != 0)素朴に、r3に0をセットして0x1000回のループにすればよいと思ったのですが、stmiaで8レジスタ一気に転送して、0x4000/(4*4*8)回のループのほうが効率的なんでしょうね。
さてこれで、Ltemp_l1_tableが全部ゼロクリアされた状態になりました。
コメント(0件)
- TB-URL http://www.tokuda.net/diary/0805/tb/
rpi_start.Sを読む (第2回)
つづきです。
今回読むのはここです。
read status, store registerの頭文字でしょうか。
cpsrは現在のステータスレジスタです(Current Program Status Register)。
現在のステータスレジスタをr0にロードする、ということですね。
次に進みます。
次に進みます。
先ほど下位5ビットをクリアしたr0にビットを立てています。I32_bit, F32_bit,PSR_SVC32_MODEのそれぞれです。これまたarm/include/armreg.hに定義されています。
ということで、ここでは割り込みを禁止する一連の処理をしていたようです。
今回読むのはここです。
127 mrs r0, cpsr 128 bic r0, r0, #PSR_MODE 129 orr r0, r0, #(I32_bit | F32_bit | PSR_SVC32_MODE) 130 msr cpsr, r0まず127行目。
127 mrs r0, cpsrmrsはステータスレジスタのシステムコールです。mrsはロード、msrはストア。
read status, store registerの頭文字でしょうか。
cpsrは現在のステータスレジスタです(Current Program Status Register)。
現在のステータスレジスタをr0にロードする、ということですね。
次に進みます。
128 bic r0, r0, #PSR_MODEbicはビットクリアです。#PSR_MODEはarm/include/armreg.hで定義されていて下位5ビットに1が立っています。
#define PSR_MODE 0x0000001f /* mode mask */ということで、r0(ステータスレジスタと同値)の下位5ビットをクリアする、ということですね。
次に進みます。
129 orr r0, r0, #(I32_bit | F32_bit | PSR_SVC32_MODE)orrは文字どおりorつまり論理和をとります。
先ほど下位5ビットをクリアしたr0にビットを立てています。I32_bit, F32_bit,PSR_SVC32_MODEのそれぞれです。これまたarm/include/armreg.hに定義されています。
62 #define I32_bit (1 << 7) /* IRQ disable */ 63 #define F32_bit (1 << 6) /* FIQ disable */ 77 #define PSR_SVC32_MODE 0x00000013コメントから想像するに、割り込みを禁止するビットのようですね。
130 msr cpsr, r0最初のmrs(リード)に対するmsr(ストア)ですね。ここまでr0を操作してきた内容をステータスレジスタにストアしています。
ということで、ここでは割り込みを禁止する一連の処理をしていたようです。
- TB-URL http://www.tokuda.net/diary/0804/tb/
rpi_start.Sを読む (第1回)
ARMアセンブリ言語を勉強するため、CPUのスタートアップを題材にソースコードを読んでみようと思います。
といっても、ARMアセンブリ言語の知識は皆無なので、Webで一命令ずつ調べながら、わからないところは大胆に飛ばしながら読んでいくという無責任企画です。
今話題のRaspberry Piをターゲットに、少しづつ勉強していきます。
src/sys/arch/evbarm/rpi/rpi_start.Sの1.7がターゲットになります。コメント含めて328行の小さなプログラムですが、自分にとっては楽しめそうです。
さて、はじめましょう。
一番最初に実行されるエントリーポイントから読んでいきます。ソースの最初の部分は少し飛ばして、122行目からスタートです。
rpi_boot_regsには
ということで、ラベルrpi_boot_regsのアドレスをr8に読み込むという命令ですね。
adrという疑似命令は、ARMでは命令の長さが4バイトに固定されるため、32ビット(4バイト)の整数 やラベルの値(メモリアドレス)を直接レジスタに転送することができません。 とのこと。
さて次に進みます。
rpi_boot_regがr0からr3に割り当てられたということですね。
しかし、rpi_boot_regは、どこにも使っていそうな場所がありませんね。うーん。
といっても、ARMアセンブリ言語の知識は皆無なので、Webで一命令ずつ調べながら、わからないところは大胆に飛ばしながら読んでいくという無責任企画です。
今話題のRaspberry Piをターゲットに、少しづつ勉強していきます。
src/sys/arch/evbarm/rpi/rpi_start.Sの1.7がターゲットになります。コメント含めて328行の小さなプログラムですが、自分にとっては楽しめそうです。
さて、はじめましょう。
一番最初に実行されるエントリーポイントから読んでいきます。ソースの最初の部分は少し飛ばして、122行目からスタートです。
122 .global _C_LABEL(rpi_start) 123 _C_LABEL(rpi_start):ラベルですね。_CとついているとCのプログラムから呼べるんでしたっけ? 先に進みましょう。
124 adr r8, rpi_boot_regsいきなりのadrはGNU asの疑似命令です。指定したレジスタにラベル(ここだとrpi_boot_regs)のアドレスをロードします。プログラムカウンタr15にadd or subして実現されているそうです。r15なんて書かれてないから不思議な感じですね。
rpi_boot_regsには
327 rpi_boot_regs: 328 .space 4 * 4と書かれています。ブート用のレジスタ(?)の領域を確保ということかな? 4*4なので32bitのアドレスを4つ確保と読むのかな。
ということで、ラベルrpi_boot_regsのアドレスをr8に読み込むという命令ですね。
adrという疑似命令は、ARMでは命令の長さが4バイトに固定されるため、32ビット(4バイト)の整数 やラベルの値(メモリアドレス)を直接レジスタに転送することができません。 とのこと。
さて次に進みます。
125 stmia r8!, {r0-r3}stmはレジスタ-メモリ転送命令の複数版です。stmはストアマルチ、ldmはロードマルチかな。iaはIncrement Afterということで、メモリアクセス後にポインタを増やす。つまり、以下のようなイメージでしょうか。
addr = r8; r0 = *(addr++); r1 = *(addr++); r2 = *(addr++); r3 = *(addr++);r8!の!は最後にr8に書き戻すという意味なので
r8 = addr;ということなのでしょう。
rpi_boot_regがr0からr3に割り当てられたということですね。
しかし、rpi_boot_regは、どこにも使っていそうな場所がありませんね。うーん。
- TB-URL http://www.tokuda.net/diary/0803/tb/