rpi_start.Sを読む (第3回)
2013/11/04(月) 22:16 NetBSD はてブ情報 はてブに登録 はてブ数

続きです。

まずは、コメントです。
    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	1b
bneの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が全部ゼロクリアされた状態になりました。

名前:  非公開コメント   

  • TB-URL  http://www.tokuda.net/diary/0805/tb/