メッセージ欄

2013年11月の日記

一覧で表示する

rpi_start.Sを読む (最終回)
2013/11/10(日) 16:14 NetBSD はてブ情報 はてブに登録 はてブ数

さて、第1回から第13回までrpi_start.Sを一通り読むことができました。

まだまだ、わからないところも多いですが、ARMアセンブリ言語も知らず、ARMアーキテクチャもまるで分らない状態からすれば、ずいぶんとやっていることがわかるようになりました。

処理の大まかな内容は次のような感じでしょうか。
  1. 割り込みを禁止する
  2. L1ページ変換テーブルを0に初期化
  3. 最低限のマッピングをL1ページ変換に設定
  4. 各種キャッシュをOFF
  5. L1ページテーブルをセット
  6. コントロールレジスタを調整
  7. MMUをON
  8. スタートルーチンへジャンプ
ARMのアセンブリ言語に対しては、命令もオペランドも種類が豊富すぎで、けっこう苦労しましたが、WebのARMアセンブリ言語の入門ページを見ながら、なんとか読み進められました。

一方、mcr, mrcによって操作される、ステータスレジスタ等々、CPUアーキテクチャに依存する部分はマニュアルを読みつつ、想像力を働かせれば、なんとなーく、ぼやーっと、つかめたかなぁといったところです。

一つのスタートアップルーチンを読んでおけば、ほかのスタートアップルーチンを読むときにも役に立つはずと信じて、今回の短期連載を終わりたいと思います。

rpi_start.Sを読む (第13回)
2013/11/10(日) 15:36 NetBSD はてブ情報 はてブに登録 はてブ数

いよいよMMUもONになり、大詰めに近づいてまいりました。

今回読むのは次の部分です。
    239 	/*
    240 	 * Ensure that the coprocessor has finished turning on the MMU.
    241 	 */
    242 	mrc	p15, 0, r0, c0, c0, 0	/* Read an arbitrary value. */
    243 	mov	r0, r0			/* Stall until read completes. */
    244 
    245 	/*
    246 	 * Jump to start in locore.S, which in turn will call initarm and main.
    247 	 */
    248 	mov	pc, lr
    249 	nop
    250 	nop
    251 	nop
    252 	nop
さっそく進めましょう。
    239 	/*
    240 	 * Ensure that the coprocessor has finished turning on the MMU.
    241 	 */
    242 	mrc	p15, 0, r0, c0, c0, 0	/* Read an arbitrary value. */
    243 	mov	r0, r0			/* Stall until read completes. */
MMUがONになったことをmrcで値を読み込んで確認します。マニュアルによると、このmrc操作はRead Main ID Registerと書いてあります。プロセッサの情報をとる命令のようです。

243行目のmov r0, r0でリードが完了するのを待つ、とコメントにあります。こういうのは慣用句なんでしょうかね。

進みましょう。
    245 	/*
    246 	 * Jump to start in locore.S, which in turn will call initarm and main.
    247 	 */
    248 	mov	pc, lr
いよいよ、このスタートアップルーチンとはお別れです。つまり、リンクレジスタに格納していた、飛び先つまりLstartにあったルーチンのアドレスをプログラムカウンタ(pc)に格納し、そちらに制御を移します。

ついに、rpi_start.Sを読み切りました!

rpi_start.Sを読む (第12回)
2013/11/10(日) 15:21 NetBSD はてブ情報 はてブに登録 はてブ数

さて、今回読むのは次の部分です。冒頭のコメントでMMUを有効にするというのがわかります。
    221 	/*
    222 	 * Enable the MMU, etc.
    223 	 */
    224 	mrc     p15, 0, r0, c1, c0, 0
    225 
    226 	ldr     r1, Lcontrol_wax
    227 	and	r0, r0, r1
    228 	ldr     r1, Lcontrol_clr
    229 	bic	r0, r0, r1
    230 	ldr     r1, Lcontrol_set
    231 	orr	r0, r0, r1
    232 	ldr	lr, Lstart
    233 
    234 	.align 5
    235 	@ turn mmu on!
    236 	mov	r0, r0
    237 	mcr	p15, 0, r0, c1, c0, 0
コメントを読んで盛り上がったところで、読み進めていきます。
    224 	mrc     p15, 0, r0, c1, c0, 0
mrcでコントロールレジスタ設定の値を読み込みます。
マニュアルには「MRC p15, 0, , c1, c0, 0 ; Read Control Register configuration data」とあります。

コントロールレジスタを再掲します。
31-30292827-26252423222120-191817161514131211109876-43210
SBZFATRSBZEEVEXPUFISBZITSBZDTL4RRVIZFRSBSBOWCAM
    226 	ldr     r1, Lcontrol_wax
    227 	and	r0, r0, r1
    228 	ldr     r1, Lcontrol_clr
    229 	bic	r0, r0, r1
    230 	ldr     r1, Lcontrol_set
    231 	orr	r0, r0, r1
ここでは、r0にロードされたコントロールレジスタに対して様々な操作を行います。

まずは、Lcontrol_waxをldrでロードし、r0とANDをとります。そのあと、Lcontrol_clrをロードし、r0のビットをクリア。その後、Lcontrol_setをロードし、r0とORをとります。Lcontorol_setではちゃんとMMU_ENABLEが立っています。

Lcontrol_waxは「/* bits to "write as existing" in the Control Register */」。
Lcontrol_clrは「/* bits to clear in the Control Register */」。
Lcontrol_setは「/* bits to set in the Control Register */」。

Lcontrol_clrは.word 0と定義されています。bicはNOTしてANDなので、すべてのビットが1になりますから、ANDを取ってもすべてのビットがONのまま、なにもクリアされない気がします。

で、下表をみると、最初が何であろうと、Lcontrol_wax, Lcontrol_setの両方に1が立っているビットがないので、結果的にはLcontrol_setしか残らないという気がしてきます。うーん。Lcontrol_setだけでも良かったような。
定義31-30292827-26252423222120-191817161514131211109876-43210
SBZFATRSBZEEVEXPUFISBZITSBZDTL4RRVIZFRSBSBOWCAM
Lcontrol_wax1 1111 1000001 10100000010000000000
Lcontrol_set0 0000 0000000 01010001000101111111
あ、ちがうか、両方が0のやつはクリアされるからLcontrol_waxの意味はあるのか。もしかして、waxって車のワックスのwaxなのかな。ワックスをかけて、きれいにする、余分なもの(ビット)を落として、きれいにする、みたいなニュアンスで。

次に進みます。
    232 	ldr	lr, Lstart
いきなりのlrです。lrはリンクレジスタでr14の別名で、今回はリンクレジスタlrにLstartのアドレスをロードしています。rpi_start.Sの最後で、このアドレスにジャンプするための下準備です。

Lstartでは.word startと定義されており、これはきっとsrc/sys/arch/arm/arm32/locore.Sの以下の部分ではないかと。
  ASENTRY_NP(start)
さて、飛び先も決まったということで次の行に進みましょう。
    234 	.align 5
    235 	@ turn mmu on!
.align 5でアラインメントを整えます。
@から始まるのは一行コメントかな。いよいよMMUをONにします。ってことですね。

しかし、align 5というのは何ビットアラインメントだ? と思って調べると、2の5乗、つまり32ビットアラインメントのようです。たしかに、MMUのようなアドレスを変換するような機能をONにするなら、中途半端な場所にいるとダメそうです。

さて、次に進みます。
    236 	mov	r0, r0
    237 	mcr	p15, 0, r0, c1, c0, 0
これまで色々とビットを操作してきたr0、つまり新しくコントロールレジスタに与えるべき値をmcrで書き込みます。コントロールレジスタの0ビット目に1を立てている、つまりMMUをENABLEにするわけです。

rpi_start.Sを読む (第11回)
2013/11/10(日) 14:08 NetBSD はてブ情報 はてブに登録 はてブ数

さて、今回読むのは次の部分です。
    211 	ldr	r0, Ltemp_l1_table	/* The page table address */
    212 	mcr	p15, 0, r0, c2, c0, 0	/* Set Translation Table Base */
    213 
    214 	mov	r0, #0
    215 	mcr	p15, 0, r0, c8, c7, 0	/* Invalidate TLBs */
    216 
    217 	/* Set the Domain Access register.  Very important! */
    218 	mov     r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT)
    219 	mcr	p15, 0, r0, c3, c0, 0
さっそく始めましょう。
    211 	ldr	r0, Ltemp_l1_table	/* The page table address */
    212 	mcr	p15, 0, r0, c2, c0, 0	/* Set Translation Table Base */
さきほど作成した0x4000のサイズを持つLtemp_l1_tableの先頭アドレスをr0にロードし、TTBをセットしています。Ltemp_l1_tableの先頭アドレスをTTBにするよっ、てことですかね。

マニュアルにも「MCR p15, 0, , c2, c0, 0 ; Write Translation Table Base Register 0」とあります。

次に進みます。
    214 	mov	r0, #0
    215 	mcr	p15, 0, r0, c8, c7, 0	/* Invalidate TLBs */
マニュアルによるとc8, c7, 0の組み合わせは「Invalidate unified TLB unlocked entries」とのこと。c8がTLB関連で、c7がunified TLBを指し、0がInvalidate TLB unlocked entriesを指すようです。
現時点の物理・仮想の変換バッファを無効化するということでいいのかな。

次は、なにやら重要だよというコメントから始まっています。
    217 	/* Set the Domain Access register.  Very important! */
    218 	mov     r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT)
    219 	mcr	p15, 0, r0, c3, c0, 0
久々に定義を追っかける気がしますが、PMAP_DOMAIN_KERNELはpmap.hで次のように定義されています。
  #define	PMAP_DOMAIN_KERNEL	0	/* The kernel uses domain #0 */
DOMAIN_CLIENTはpte.hで次のように定義されています。
  /*
   * Domain Types for the Domain Access Control Register.
   */
  #define	DOMAIN_FAULT	0x00		/* no access */
  #define	DOMAIN_CLIENT	0x01		/* client */
  #define	DOMAIN_RESERVED	0x02		/* reserved */
  #define	DOMAIN_MANAGER	0x03		/* manager */
えーとつまり、
  (0x01 << (0 * 2) | (0x01)
なので、0x01ですよね。最初の(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2))って意味ないような。machdep.cでも似たような書き方をしているのですけど、そういうおまじないってことなんでしょうか。

謎です。

ドメインアクセスコントロールレジスタのフォーマットは次のようになっていますから、ドメイン0に0x01がセットされていると。

313029282726252423222120191817161514131211109876543210
D15D14D13D12D11D10D9D8D7D6D5D4D3D2D1D0
0x01はマニュアルには「Client. Accesses are checked against the access permission bits in the TLB entry.」とあります。TLBエントリのアクセスパーミッションビットに対してチェックを行う、ということかな。

アクセスパーミッション(AP)は10bit,11bitでしたね。
31-20191817161514-1211-1098-543210
セクションのベースアドレス(12bit)SBZ0nGSAPXTEXAPIMPドメインXNCB10
今回読んだ部分では、各種キャッシュを止めて、TTBをセット&無効化し、ドメインアクセスレジスタを設定しました。

rpi_start.Sを読む (第10回)
2013/11/10(日) 12:28 NetBSD はてブ情報 はてブに登録 はてブ数

いよいよ10回目に入りました。

今回読むのは次の部分です。
    194 	/*
    195 	 * In theory, because the MMU is off, we shouldn't need all of this,
    196 	 * but let's not take any chances and do a typical sequence to set
    197 	 * the Translation Table Base.
    198 	 */
    199 	mov	r0, #0			/* SBZ */
    200 	Invalidate_I_cache(r0)
    201 
    202 	mcr	p15, 0, r0, c7, c14, 0	/* Clean and Invalidate Entire Data Cache */
    203 
    204         ldr     r2, Lctl_ID_dis		/* Disable I+D caches */
    205 	mrc	p15, 0, r1, c1, c0, 0	/*  "       "   "     */
    206 	and	r1, r1, r2		/*  "       "   "     */
    207 	mcr	p15, 0, r1, c1, c0, 0	/*  "       "   "     */
    208 
    209 	mcr	p15, 0, r0, c7, c10, 4	/* Drain the write buffers. */
まずはコメントです。
    194 	/*
    195 	 * In theory, because the MMU is off, we shouldn't need all of this,
    196 	 * but let's not take any chances and do a typical sequence to set
    197 	 * the Translation Table Base.
    198 	 */
うーん。どう訳すんでしょう。

理論上、MMUはオフなので、これから行う操作がすべて必要ではないけれど、この辺でやるしかないので、TTB(Translation Table Base)をセットする典型的な手順を実行します、って感じですかねぇ。

まぁ、先に進みましょうか。
    199 	mov	r0, #0			/* SBZ */
    200 	Invalidate_I_cache(r0)
SBZって何かとしらべたところ、SBZ = Should Be Zeroの略なんですね。ちなみに、SBO = Should Be Oneの略のようです。

movでレジスタr0をゼロにしてInvalidate_I_cache(reg)を実行します。

Invalidate_I_cache(reg)はずいぶん最初のほうに記述がありました。
     92 /*
     93  * Workaround Erratum 411920
     94  *
     95  *	- value of arg 'reg' Should Be Zero
     96  */
     97 #define Invalidate_I_cache(reg) \
     98 	.p2align 5;								\
     99 	mcr	p15, 0, reg, c7, c5, 0;	/* Invalidate Entire I cache */		\
    100 	mcr	p15, 0, reg, c7, c5, 0;	/* Invalidate Entire I cache */		\
    101 	mcr	p15, 0, reg, c7, c5, 0;	/* Invalidate Entire I cache */		\
    102 	mcr	p15, 0, reg, c7, c5, 0;	/* Invalidate Entire I cache */		\
    103 	nop;									\
    104 	nop;									\
    105 	nop;									\
    106 	nop;									\
    107 	nop;									\
    108 	nop;									\
    109 	nop;									\
    110 	nop;									\
    111 	nop;									\
    112 	nop;									\
    113 	nop;
Errata 411920に対応するためのI cacheを0にクリアします。
ちなみに、I cacheはInstruction cacheでD cacheはData cacheです。

c7はキャッシュ操作のレジスタです。ARM1176JZ-S™ Technical Reference Manualに、「MCR p15, 0, , c7, c5, 0 Invalidate entire Instruction Cache」とあります。
なんでかわかりませんが、4回ほど連発してますね。

このあたりから、マニュアルと首っ引きで調べる感じです。

それでは、202行目に戻りましょう。
    202 	mcr	p15, 0, r0, c7, c14, 0	/* Clean and Invalidate Entire Data Cache */
これも「MCR p15, 0, , c7, c14, 0 SBZ Clean and Invalidate Entire Data Cache.」とあります。
さっきがインストラクションキャッシュだったので、今度はデータキャッシュですね。

進めましょう。
    204         ldr     r2, Lctl_ID_dis		/* Disable I+D caches */
    205 	mrc	p15, 0, r1, c1, c0, 0	/*  "       "   "     */
    206 	and	r1, r1, r2		/*  "       "   "     */
    207 	mcr	p15, 0, r1, c1, c0, 0	/*  "       "   "     *
mrc, mcr命令にあるc1は Control Registerです。

Lctl_ID_disをr2にロードして。mcrでコントロールレジスタを読んで、Lctl_ID_disとのANDを取って、mrcで書き戻しています。。
I cache, D cacheをinvalidにしていますって、コメントのままですね。

ここからコントロールレジスタがたくさん出てくるので、フォーマットと定義を整理しておきましょう。

まずはレジスタのフォーマットです。
31-30292827-26252423222120-191817161514131211109876-43210
SBZFATRSBZEEVEXPUFISBZITSBZDTL4RRVIZFRSBSBOWCAM
それぞれのフィールドの定義は/sys/arch/arm/include/armreg.hにあります。
  /* CPU control register (CP15 register 1) */
  #define CPU_CONTROL_MMU_ENABLE	0x00000001 /* M: MMU/Protection unit enable */
  #define CPU_CONTROL_AFLT_ENABLE	0x00000002 /* A: Alignment fault enable */
  #define CPU_CONTROL_DC_ENABLE		0x00000004 /* C: IDC/DC enable */
  #define CPU_CONTROL_WBUF_ENABLE 	0x00000008 /* W: Write buffer enable */
  #define CPU_CONTROL_32BP_ENABLE 	0x00000010 /* P: 32-bit exception handlers */
  #define CPU_CONTROL_32BD_ENABLE 	0x00000020 /* D: 32-bit addressing */
  #define CPU_CONTROL_LABT_ENABLE 	0x00000040 /* L: Late abort enable */
  #define CPU_CONTROL_BEND_ENABLE 	0x00000080 /* B: Big-endian mode */
  #define CPU_CONTROL_SYST_ENABLE 	0x00000100 /* S: System protection bit */
  #define CPU_CONTROL_ROM_ENABLE	0x00000200 /* R: ROM protection bit */
  #define CPU_CONTROL_CPCLK		0x00000400 /* F: Implementation defined */
  #define CPU_CONTROL_SWP_ENABLE	0x00000400 /* SW: SWP{B} perform normally. */
  #define CPU_CONTROL_BPRD_ENABLE 	0x00000800 /* Z: Branch prediction enable */
  #define CPU_CONTROL_IC_ENABLE   	0x00001000 /* I: IC enable */
  #define CPU_CONTROL_VECRELOC		0x00002000 /* V: Vector relocation */
  #define CPU_CONTROL_ROUNDROBIN	0x00004000 /* RR: Predictable replacement */
  #define CPU_CONTROL_V4COMPAT		0x00008000 /* L4: ARMv4 compat LDR R15 etc */
  #define CPU_CONTROL_FI_ENABLE		0x00200000 /* FI: Low interrupt latency */
  #define CPU_CONTROL_UNAL_ENABLE	0x00400000 /* U: unaligned data access */
  #define CPU_CONTROL_XP_ENABLE		0x00800000 /* XP: extended page table */
  #define CPU_CONTROL_V_ENABLE		0x01000000 /* VE: Interrupt vectors enable */
  #define CPU_CONTROL_EX_BEND		0x02000000 /* EE: exception endianness */
  #define CPU_CONTROL_NMFI		0x08000000 /* NMFI: Non maskable FIQ */
  #define CPU_CONTROL_TR_ENABLE		0x10000000 /* TRE: */
  #define CPU_CONTROL_AF_ENABLE		0x20000000 /* AFE: Access flag enable */
  #define CPU_CONTROL_TE_ENABLE		0x40000000 /* TE: Thumb Exception enable */
ファイル後半で定義されているコントロールレジスタに関連するのは次の四つ (Lcontrol_set, Lcontrol_clr, Lcontrol_wax, Lctl_ID_dis) になります。waxって何でしょうねぇ。
    268 	/* bits to set in the Control Register */
    269 Lcontrol_set:
    270 	.word CPU_CONTROL_MMU_ENABLE  | \
    271 	      CPU_CONTROL_AFLT_ENABLE | \
    272 	      CPU_CONTROL_DC_ENABLE   | \
    273 	      CPU_CONTROL_WBUF_ENABLE |    /* not defined in 1176 */   \
    274 	      CPU_CONTROL_32BP_ENABLE | \
    275 	      CPU_CONTROL_32BD_ENABLE | \
    276 	      CPU_CONTROL_LABT_ENABLE | \
    277 	      CPU_CONTROL_SYST_ENABLE | \
    278 		(1 << 16) | 	/* SBO - Global enable for data tcm */ \
    279 		(1 << 18) |	/* SBO - Global enable for insn tcm */ \
    280 	      CPU_CONTROL_IC_ENABLE
    281 
    282 
    283 	/* bits to clear in the Control Register */
    284 Lcontrol_clr:
    285 	.word	0
    286 
    287 	/* bits to "write as existing" in the Control Register */
    288 Lcontrol_wax:
    289 	.word	(3 << 30) | \
    290 		(1 << 29) | \
    291 		(1 << 28) | \
    292 		(3 << 26) | \
    293 		(3 << 19) | \
    294 		(1 << 17) | \
    295 	        (1 << 10)
    296 
    297 	/* bits to disable the caches */
    298 Lctl_ID_dis:
    299 	.word	~(CPU_CONTROL_IC_ENABLE|CPU_CONTROL_DC_ENABLE)
それぞれを一覧にしてみます。Lcontrol_clrは全部0なので省略です。
定義31-30292827-26252423222120-191817161514131211109876-43210
SBZFATRSBZEEVEXPUFISBZITSBZDTL4RRVIZFRSBSBOWCAM
Lctl_ID_dis1 1111 1111111 11111110111111111011
Lcontrol_wax1 1111 1000001 10100000010000000000
Lcontrol_set0 0000 0000000 01010001000101111111
209行目に戻り、ふたたび、c7のキャッシュまわりです。
    209 	mcr	p15, 0, r0, c7, c10, 4	/* Drain the write buffers. */
うーん、マニュアルでは「MCR p15,0,,c7,c10,4 ; Data Synchronization Barrier operation.」と書いてあります。どういうことでしょう?

どうやら、昔はDrain the write buffersと呼ばれていたそうです。マニュアルを読んでもまるで自信はないですが、どうやらバリアというだけあって、この命令より前のメモリの操作やらキャッシュやらの操作が完了するまで待ちますよ、ってな感じの動きをするようです。

たしかに、ここまでいろいろキャッシュの操作をしてきたので、それらの操作がちゃんと終わるまで待ちましょう、という命令のようです。

mcr, mrc命令はマニュアルを拾い読みするだけでは理解に至らないことが多くて苦戦します。