rpi_start.Sを読む (最終回)
さて、第1回から第13回までrpi_start.Sを一通り読むことができました。
まだまだ、わからないところも多いですが、ARMアセンブリ言語も知らず、ARMアーキテクチャもまるで分らない状態からすれば、ずいぶんとやっていることがわかるようになりました。
処理の大まかな内容は次のような感じでしょうか。
一方、mcr, mrcによって操作される、ステータスレジスタ等々、CPUアーキテクチャに依存する部分はマニュアルを読みつつ、想像力を働かせれば、なんとなーく、ぼやーっと、つかめたかなぁといったところです。
一つのスタートアップルーチンを読んでおけば、ほかのスタートアップルーチンを読むときにも役に立つはずと信じて、今回の短期連載を終わりたいと思います。
まだまだ、わからないところも多いですが、ARMアセンブリ言語も知らず、ARMアーキテクチャもまるで分らない状態からすれば、ずいぶんとやっていることがわかるようになりました。
処理の大まかな内容は次のような感じでしょうか。
- 割り込みを禁止する
- L1ページ変換テーブルを0に初期化
- 最低限のマッピングをL1ページ変換に設定
- 各種キャッシュをOFF
- L1ページテーブルをセット
- コントロールレジスタを調整
- MMUをON
- スタートルーチンへジャンプ
一方、mcr, mrcによって操作される、ステータスレジスタ等々、CPUアーキテクチャに依存する部分はマニュアルを読みつつ、想像力を働かせれば、なんとなーく、ぼやーっと、つかめたかなぁといったところです。
一つのスタートアップルーチンを読んでおけば、ほかのスタートアップルーチンを読むときにも役に立つはずと信じて、今回の短期連載を終わりたいと思います。
コメント(0件)
- TB-URL http://www.tokuda.net/diary/0816/tb/
rpi_start.Sを読む (第13回)
いよいよMMUもONになり、大詰めに近づいてまいりました。
今回読むのは次の部分です。
243行目のmov r0, r0でリードが完了するのを待つ、とコメントにあります。こういうのは慣用句なんでしょうかね。
進みましょう。
ついに、rpi_start.Sを読み切りました!
今回読むのは次の部分です。
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を読み切りました!
- TB-URL http://www.tokuda.net/diary/0815/tb/
rpi_start.Sを読む (第12回)
さて、今回読むのは次の部分です。冒頭のコメントでMMUを有効にするというのがわかります。
マニュアルには「MRC p15, 0,, c1, c0, 0 ; Read Control Register configuration data」とあります。
コントロールレジスタを再掲します。
まずは、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だけでも良かったような。
あ、ちがうか、両方が0のやつはクリアされるからLcontrol_waxの意味はあるのか。もしかして、waxって車のワックスのwaxなのかな。ワックスをかけて、きれいにする、余分なもの(ビット)を落として、きれいにする、みたいなニュアンスで。
次に進みます。
Lstartでは.word startと定義されており、これはきっとsrc/sys/arch/arm/arm32/locore.Sの以下の部分ではないかと。
@から始まるのは一行コメントかな。いよいよMMUをONにします。ってことですね。
しかし、align 5というのは何ビットアラインメントだ? と思って調べると、2の5乗、つまり32ビットアラインメントのようです。たしかに、MMUのようなアドレスを変換するような機能をONにするなら、中途半端な場所にいるとダメそうです。
さて、次に進みます。
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,
コントロールレジスタを再掲します。
| 31-30 | 29 | 28 | 27-26 | 25 | 24 | 23 | 22 | 21 | 20-19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6-4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| SBZ | FA | TR | SBZ | EE | VE | XP | U | FI | SBZ | IT | SBZ | DT | L4 | RR | V | I | Z | F | R | S | B | SBO | W | C | A | M |
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-30 | 29 | 28 | 27-26 | 25 | 24 | 23 | 22 | 21 | 20-19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6-4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| SBZ | FA | TR | SBZ | EE | VE | XP | U | FI | SBZ | IT | SBZ | DT | L4 | RR | V | I | Z | F | R | S | B | SBO | W | C | A | M | |
| Lcontrol_wax | 1 1 | 1 | 1 | 1 1 | 0 | 0 | 0 | 0 | 0 | 1 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 000 | 0 | 0 | 0 | 0 |
| Lcontrol_set | 0 0 | 0 | 0 | 0 0 | 0 | 0 | 0 | 0 | 0 | 0 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 111 | 1 | 1 | 1 | 1 |
次に進みます。
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にするわけです。- TB-URL http://www.tokuda.net/diary/0814/tb/
rpi_start.Sを読む (第11回)
さて、今回読むのは次の部分です。
マニュアルにも「MCR p15, 0,, c2, c0, 0 ; Write Translation Table Base Register 0」とあります。
次に進みます。
現時点の物理・仮想の変換バッファを無効化するということでいいのかな。
次は、なにやら重要だよというコメントから始まっています。
謎です。
ドメインアクセスコントロールレジスタのフォーマットは次のようになっていますから、ドメイン0に0x01がセットされていると。
0x01はマニュアルには「Client. Accesses are checked against the access permission bits in the TLB entry.」とあります。TLBエントリのアクセスパーミッションビットに対してチェックを行う、ということかな。
アクセスパーミッション(AP)は10bit,11bitでしたね。
今回読んだ部分では、各種キャッシュを止めて、TTBをセット&無効化し、ドメインアクセスレジスタを設定しました。
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,
次に進みます。
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がセットされていると。
| 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| D15 | D14 | D13 | D12 | D11 | D10 | D9 | D8 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | ||||||||||||||||
アクセスパーミッション(AP)は10bit,11bitでしたね。
| 31-20 | 19 | 18 | 17 | 16 | 15 | 14-12 | 11-10 | 9 | 8-5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| セクションのベースアドレス(12bit) | SBZ | 0 | nG | S | APX | TEX | AP | IMP | ドメイン | XN | C | B | 1 | 0 |
- TB-URL http://www.tokuda.net/diary/0813/tb/
rpi_start.Sを読む (第10回)
いよいよ10回目に入りました。
今回読むのは次の部分です。
理論上、MMUはオフなので、これから行う操作がすべて必要ではないけれど、この辺でやるしかないので、TTB(Translation Table Base)をセットする典型的な手順を実行します、って感じですかねぇ。
まぁ、先に進みましょうか。
movでレジスタr0をゼロにしてInvalidate_I_cache(reg)を実行します。
Invalidate_I_cache(reg)はずいぶん最初のほうに記述がありました。
ちなみに、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行目に戻りましょう。
, c7, c14, 0 SBZ Clean and Invalidate Entire Data Cache.」とあります。
さっきがインストラクションキャッシュだったので、今度はデータキャッシュですね。
進めましょう。
Lctl_ID_disをr2にロードして。mcrでコントロールレジスタを読んで、Lctl_ID_disとのANDを取って、mrcで書き戻しています。。
I cache, D cacheをinvalidにしていますって、コメントのままですね。
ここからコントロールレジスタがたくさん出てくるので、フォーマットと定義を整理しておきましょう。
まずはレジスタのフォーマットです。
それぞれのフィールドの定義は/sys/arch/arm/include/armreg.hにあります。
209行目に戻り、ふたたび、c7のキャッシュまわりです。
,c7,c10,4 ; Data Synchronization Barrier operation.」と書いてあります。どういうことでしょう?
どうやら、昔はDrain the write buffersと呼ばれていたそうです。マニュアルを読んでもまるで自信はないですが、どうやらバリアというだけあって、この命令より前のメモリの操作やらキャッシュやらの操作が完了するまで待ちますよ、ってな感じの動きをするようです。
たしかに、ここまでいろいろキャッシュの操作をしてきたので、それらの操作がちゃんと終わるまで待ちましょう、という命令のようです。
mcr, mrc命令はマニュアルを拾い読みするだけでは理解に至らないことが多くて苦戦します。
今回読むのは次の部分です。
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,
なんでかわかりませんが、4回ほど連発してますね。
このあたりから、マニュアルと首っ引きで調べる感じです。
それでは、202行目に戻りましょう。
202 mcr p15, 0, r0, c7, c14, 0 /* Clean and Invalidate Entire Data Cache */
これも「MCR p15, 0, さっきがインストラクションキャッシュだったので、今度はデータキャッシュですね。
進めましょう。
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-30 | 29 | 28 | 27-26 | 25 | 24 | 23 | 22 | 21 | 20-19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6-4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| SBZ | FA | TR | SBZ | EE | VE | XP | U | FI | SBZ | IT | SBZ | DT | L4 | RR | V | I | Z | F | R | S | B | SBO | W | C | A | M |
/* 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-30 | 29 | 28 | 27-26 | 25 | 24 | 23 | 22 | 21 | 20-19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6-4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| SBZ | FA | TR | SBZ | EE | VE | XP | U | FI | SBZ | IT | SBZ | DT | L4 | RR | V | I | Z | F | R | S | B | SBO | W | C | A | M | |
| Lctl_ID_dis | 1 1 | 1 | 1 | 1 1 | 1 | 1 | 1 | 1 | 1 | 1 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 111 | 1 | 0 | 1 | 1 |
| Lcontrol_wax | 1 1 | 1 | 1 | 1 1 | 0 | 0 | 0 | 0 | 0 | 1 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 000 | 0 | 0 | 0 | 0 |
| Lcontrol_set | 0 0 | 0 | 0 | 0 0 | 0 | 0 | 0 | 0 | 0 | 0 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 111 | 1 | 1 | 1 | 1 |
209 mcr p15, 0, r0, c7, c10, 4 /* Drain the write buffers. */
うーん、マニュアルでは「MCR p15,0,どうやら、昔はDrain the write buffersと呼ばれていたそうです。マニュアルを読んでもまるで自信はないですが、どうやらバリアというだけあって、この命令より前のメモリの操作やらキャッシュやらの操作が完了するまで待ちますよ、ってな感じの動きをするようです。
たしかに、ここまでいろいろキャッシュの操作をしてきたので、それらの操作がちゃんと終わるまで待ちましょう、という命令のようです。
mcr, mrc命令はマニュアルを拾い読みするだけでは理解に至らないことが多くて苦戦します。
- TB-URL http://www.tokuda.net/diary/0812/tb/
rpi_start.Sを読む (第9回)
今回読むのは次の部分です。
これで終わりです。だと面白くないので、ちょっと振り返ってみましょう。
第6回から第8回までは、第3回でゼロに初期化したL1テーブルを設定する一連の処理でした。
処理は、
mmu_init_tableは、次のようなテーブルでした。
これが、L1テーブルに格納されるころには次のようになり、
L1テーブルは次のように設定されたということになります。
186 .unreq va
187 .unreq pa
188 .unreq n_sec
189 .unreq attr
190 .unreq itable
191 .unreq l1table
192 .unreq l1sfrm
これは、157行目からの
157 l1table .req r0
158 va .req r1
159 pa .req r2
160 n_sec .req r3
161 attr .req r4
162 itable .req r5
163 l1sfrm .req r6
に対して、反対のことをしているわけですね。これで終わりです。だと面白くないので、ちょっと振り返ってみましょう。
第6回から第8回までは、第3回でゼロに初期化したL1テーブルを設定する一連の処理でした。
処理は、
- mmu_init_tableに設定された三つのva, pa, n_sec, attrによるループ
- n_secによるループ
mmu_init_tableは、次のようなテーブルでした。
| va | pa | nsec | attr |
|---|---|---|---|
| 0x0 | 0x0 | 0x643033 | 0x412 |
| 0xC0000000 | 0x0 | 0x643033 | 0x041E |
| 0xF2000000 | 0x20000000 | 0x010FFFFF | 0x0412 |
| va | pa | n_sec |
|---|---|---|
| 0x0000 | 0x00000412 | 0x06 |
| 0x2000 | 0x0000041E | 0x06 |
| 0x3C80 | 0x20000412 | 0x10 |
| L1 offset | L1 entry |
|---|---|
| 0000 | 0x00000412 |
| 0004 | 0x00000412 |
| 0008 | 0x00000412 |
| 000C | 0x00000412 |
| 0010 | 0x00000412 |
| 0014 | 0x00000412 |
| 0018 | 0x00000000 |
| 001C | 0x00000000 |
| (中略) | |
| 1FF8 | 0x00000000 |
| 1FFC | 0x00000000 |
| 2000 | 0x0000041E |
| 2004 | 0x0000041E |
| 2008 | 0x0000041E |
| 200C | 0x0000041E |
| 2010 | 0x0000041E |
| 2014 | 0x0000041E |
| 2018 | 0x00000000 |
| 201C | 0x00000000 |
| (中略) | |
| 3C78 | 0x00000000 |
| 3C7C | 0x00000000 |
| 3C80 | 0x20000412 |
| 3C84 | 0x20000412 |
| 3C88 | 0x20000412 |
| 3C8C | 0x20000412 |
| 3C90 | 0x20000412 |
| 3C94 | 0x20000412 |
| 3C98 | 0x20000412 |
| 3C9C | 0x20000412 |
| 3CA0 | 0x20000412 |
| 3CA4 | 0x20000412 |
| 3CA8 | 0x20000412 |
| 3CAC | 0x20000412 |
| 3CB0 | 0x20000412 |
| 3CB4 | 0x20000412 |
| 3CB8 | 0x20000412 |
| 3CBC | 0x20000412 |
| 3CC0 | 0x00000000 |
| 3CC4 | 0x00000000 |
| (以下略) | |
- TB-URL http://www.tokuda.net/diary/0811/tb/
rpi_start.Sを読む (第8回)
前回の終わりに169行目にジャンプしたところから再開します。
今回読むのは以下の部分です。
[Rn, Rm]はMem[Rn + Rm]ですから、
次に進みます。
セクションから1減算し、ゼロでなければラベル2にジャンプする、という処理になりますね。つまり、今回読んでいるソースはn_secでループしているわけですね。
ループを抜けると、再びラベル3の処理に入ります。そして、新しいMMU_INITマクロで設定したva, pa, n_sec, attrを読み込んで、vaとpaを適切に変換して、また、今回読んだループに入るというわけです。二重ループをこうやって実現しているんですね。
しかし、よくわからないのが173行目にあるbhiのhiです。
これは、キャリーフラグが1かつゼロフラグが0である、という意味になります。
キャリーフラグが1とは桁あふれが発生した場合で、ゼロフラグが0とは命令の結果が0でない(0ならフラグは1)である、という意味です。
なるほど、n_secから1を引いたときにゼロになればゼロフラグが1になりそうです。
一方で、キャリーフラグはn_secつまり0x06や0x10からマイナス1されるだけなので、ちっとも桁あふれなど起きなさそうです。
良く考えると、マイナス1を2の補数で表すと全部の桁が1になります。全部の桁が1に対して正の数を足せば、桁あふれが起きそうな気がします。もしかすると、それでキャリーフラグが立つのかもしれません。
だったら、ゼロフラグだけ見ていればよい気もします。すっきりしないなぁ。
すっきりしませんが、今回読んだ部分をCっぽく書いて終わりにしましょう。
今回読むのは以下の部分です。
169 2: str pa, [l1table, va]
170 add va, va, #4
171 add pa, pa, #(L1_S_SIZE)
172 adds n_sec, n_sec, #-1
173 bhi 2b
まずは169行目から。
169 2: str pa, [l1table, va]
strはストア命令です。無印なので32ビットストアですね。レジスタpaをアドレス[l1table, va]にストアします。角かっこがいやらしいですね。[Rn, Rm]はMem[Rn + Rm]ですから、
*(l1table + va) = pa;ということになります。Ll1_tableは.word 0x4000の空間でしたね。
170 add va, va, #4
171 add pa, pa, #(L1_S_SIZE)
addは加算ですね。L1_S_SIZEはセクションのサイズ、つまり1MBですからva += 4; pa += 0x00100000; /* L1_S_SIZEは1M */となります。
次に進みます。
172 adds n_sec, n_sec, #-1
173 bhi 2b
addsのaddは加算命令で、のこりのsはステータスレジスタの更新あり、でしたね。セクションから1減算し、ゼロでなければラベル2にジャンプする、という処理になりますね。つまり、今回読んでいるソースはn_secでループしているわけですね。
ループを抜けると、再びラベル3の処理に入ります。そして、新しいMMU_INITマクロで設定したva, pa, n_sec, attrを読み込んで、vaとpaを適切に変換して、また、今回読んだループに入るというわけです。二重ループをこうやって実現しているんですね。
しかし、よくわからないのが173行目にあるbhiのhiです。
これは、キャリーフラグが1かつゼロフラグが0である、という意味になります。
キャリーフラグが1とは桁あふれが発生した場合で、ゼロフラグが0とは命令の結果が0でない(0ならフラグは1)である、という意味です。
なるほど、n_secから1を引いたときにゼロになればゼロフラグが1になりそうです。
一方で、キャリーフラグはn_secつまり0x06や0x10からマイナス1されるだけなので、ちっとも桁あふれなど起きなさそうです。
良く考えると、マイナス1を2の補数で表すと全部の桁が1になります。全部の桁が1に対して正の数を足せば、桁あふれが起きそうな気がします。もしかすると、それでキャリーフラグが立つのかもしれません。
だったら、ゼロフラグだけ見ていればよい気もします。すっきりしないなぁ。
すっきりしませんが、今回読んだ部分をCっぽく書いて終わりにしましょう。
do {
*(l1table + va) = pa;
va += 4;
pa += 0x00100000; /* L1_S_SIZEは1M */
} while (--n_sec > 0)
- TB-URL http://www.tokuda.net/diary/0810/tb/