APC8750にNetBSD/evbarmを移植するための記録 9/7 その2
割り込み処理がどう呼ばれるのかを追っかけてみる。
comの場合、comintrという関数が割り込み処理で呼ばれる。
それを登録するのは、シリアルドライバのattach処理で呼ばれているintr_establishである。
intr_establishはsys/arch/arm/pic/pic.c#699にあり、同ファイル内のpic_establish_intrを呼ぶ。
is->is_funcを呼び出しているところは、sys/arch/arm/pic/pic.c#pic_dispatchである。
gemini_irq_handlerを呼び出しているのは、検索すると出てこないと思いきや、ヘッダファイルに手掛かりがあった。
geminiであればsys/arch/arm/gemini/gemini_intr.h#6にある。
sys/arch/arm/arm32/irq_dispatch.S#87をみると、ARM_IRQ_HANDLERにジャンプしている。
page0relはどこだろうか。
sys/arch/arm/arm32/arm32_machdep.c#arm32_vector_initで使われている。
VERBOSE_INIT_ARMして、該当箇所を見てみよう。
あれ? vbarっていうのが表示されていないな。ブロックごと飛ばされているって感じに見えるな。ARM_HAS_VBARっていうのが関係している?
ARM_HAS_VBARがRPIはあるけどAPCにはつけてないな。
あらためて、RPIと比べるとstd.rpi, std.apcは違いがある。
__HAVE_CPU_COUNTERとか。これは誰をベースにしたんだっけ?
ARM_HAS_VBARを有効にしたら以下のようになった。
しかし、割り込みハンドラは呼ばれない。さて、なぜでしょうか?
ARMjp-vH.pdfのA2-16、A2.6 例外によるとIRQ(割り込み)は0x00000018, 0xffff001cのアドレスだと書かれている。
本当にそうなのか試してみよう。
ubootのプロンプトを使って割り込みベクタを表示。リセット割込みのアドレスに書かれているea00002cにジャンプしてみる。
VBARが有効ならば、先のc01411c0に飛べばリセットされるはずである。
じゃぁ、リセット割込み以外e59ff2d4が実行されるみたいだ。
ところでnetbsdをobjdump -dしてみると
あれ?変わらない。
obj全部消してやり直そう。-uついてなかったので作り直しているはずなんだけどな。
とおもっていたら、std.rpiを消してた!
うーん、やっぱりだめですね。ffff0000に配置されていません。
と思ったら、違うんですな。よく読むと、ARM_VECTORS_LOWじゃないから以下のルートに入るのですね。
デバッグ文を入れてみると以下のように。
これで、割り込みが来たらジャンプしてくれるはずです。
が、割込みが来ない。なぜだ。割り込み禁止になっていたりするんですかね?
逆に、どこで割り込みを有効にするんでしょうか?
思い起こせば、start.Sを読んだのがいつだったか。
確か、割り込みを禁止していたような。と思いつつrpiのstartを眺めるも、アセンブラは忘却の彼方。
こういう時には別所さん由来の橋本さんソースを読もう。ということでnetwalkerである。
もう。いい感じでコメントが入っているではないか。
同様にrpiでも同じ処理をしておる。むしろ、冒頭である。
さて、割り込みを禁止しているのだから、どこかでそれを解いているはず。
ここまで読んでくると、cpsrがキーワードだとわかる。
cpsrでnxr検索すればヒントが。
enable_interrputしてるやん。
しかし、すべてがそうではない。
src/sys/arch/armだとat91, ep93xx, footbridge, imx, iomd, ixp12x0, mpcore, ofw, omap, s3c2xx0, xscaleの一派がenable_interruptしている。
おっと、レファレンスモデルのrpiがないじゃん。
なんなの。
ははぁ、initarm_commonしてる人は、共通関数でenableしてるんでしょ、と思って、arm32_bootを読むが、それっぽいところなさそうだなー。
それどころか、initarm_common族のomap2がicuのattachでenable_interruptしてるぞ。
もうよくわからん。initarm_common族と、enable_interrupt族を比較するとomap2だけが特殊?
enable_interruptもほとんどintr_initメソッドで呼ばれてるけどね。initarm_common族のomap2は自前initarmで呼べないもんだから、attachで読んでるってわけか。
つまり、あんまりタイミング関係ないってこと?initarmでもデバイスのattachでもいいってことかな?
しっかし、arm splx 割り込み、でgoogleしたら、もしかして: arm cpsr 割り込み、と言われるのはすごいな。
attachでenable_interruptしたら割り込みルーチンが呼ばれた!
割込みレジスタ周りを全部表示するデバッグルーチンを書く。
IC Highest-priority Status registerをみると0x0000000aというのがある。もしかしたら割り込み番号?
調べるとeth0であった。死ぬほど呼ばれるので0x0aあたりを32ビットごと00000000で初期化する。これでeth0の割り込みは来ないはず。
しかし、まだ割り込みルーチン呼ばれる。eth0以外からも割り込みが上がってきているということか。
調べると、どうやら0x00000016つまり22である。pmc_wakeupのようだ。これも0x00000000で周辺ごと初期化する。
これでシリアルの割り込みが来たら、すぐにわかるはず。
しかし、割り込みは来ない。むー。何がいかんのだろうか。
comの場合、comintrという関数が割り込み処理で呼ばれる。
それを登録するのは、シリアルドライバのattach処理で呼ばれているintr_establishである。
intr_establishはsys/arch/arm/pic/pic.c#699にあり、同ファイル内のpic_establish_intrを呼ぶ。
699 intr_establish(int irq, int ipl, int type, int (*func)(void *), void *arg)
700 {
701 KASSERT(!cpu_intr_p());
702 KASSERT(!cpu_softintr_p());
703
704 for (size_t slot = 0; slot < PIC_MAXPICS; slot++) {
705 struct pic_softc * const pic = pic_list[slot];
706 if (pic == NULL || pic->pic_irqbase < 0)
707 continue;
708 if (pic->pic_irqbase <= irq
709 && irq < pic->pic_irqbase + pic->pic_maxsources) {
710 return pic_establish_intr(pic, irq - pic->pic_irqbase,
711 ipl, type, func, arg);
712 }
713 }
714
715 return NULL;
716 }
sys/arch/arm/pic/pic.c#pic_establish_intrでは様々な前処理を行った後、関数ポインタをつかって、機種依存の割り込みドライバに書かれたestablish処理とunblock処理を呼んでいる。
660 (*pic->pic_ops->pic_establish_irq)(pic, is);
661
662 (*pic->pic_ops->pic_unblock_irqs)(pic, is->is_irq & ~0x1f,
663 __BIT(is->is_irq & 0x1f));
機種依存側の処理を見てみると、RPIなどは何もしていない。結局のところ、pic.cに渡されたfuncがpic_sources[]に格納されて、随時呼び出されているということなのだろうか。is->is_funcを呼び出しているところは、sys/arch/arm/pic/pic.c#pic_dispatchである。
255 void
256 pic_dispatch(struct intrsource *is, void *frame)
257 {
258 int rv;
259
260 if (__predict_false(is->is_arg == NULL)
261 && __predict_true(frame != NULL)) {
262 rv = (*is->is_func)(frame);
263 } else if (__predict_true(is->is_arg != NULL)) {
264 rv = (*is->is_func)(is->is_arg);
それを呼び出しているのはsys/arch/arm/pic/pic.c#pic_deliver_irqsである。
277 pic_deliver_irqs(struct pic_softc *pic, int ipl, void *frame)
278 {
279 const uint32_t ipl_mask = __BIT(ipl);
...
335 is = pic->pic_sources[irq_base + irq];
336 if (is != NULL) {
337 cpsie(I32_bit);
338 pic_dispatch(is, frame);
339 cpsid(I32_bit);
さらに、sys/arch/arm/pic/pic.c#pic_list_deliver_irqsに呼ばれており、
430 pic_list_deliver_irqs(register_t psw, int ipl, void *frame)
431 {
432 const uint32_t ipl_mask = __BIT(ipl);
433 struct pic_softc *pic;
434
435 while ((pic = pic_list_find_pic_by_pending_ipl(ipl_mask)) != NULL) {
436 pic_deliver_irqs(pic, ipl, frame);
437 KASSERT((pic->pic_pending_ipls & ipl_mask) == 0);
さらにsys/arch/arm/pic/pic.c#pic_do_pending_intsから呼ばれ、
443 pic_do_pending_ints(register_t psw, int newipl, void *frame)
444 {
445 struct cpu_info * const ci = curcpu();
….
458 pic_set_priority(ci, ipl);
459 pic_list_deliver_irqs(psw, ipl, frame);
460 pic_list_unblock_irqs();
これを呼び出しているのが機種依存割り込みドライバのirq_handerになる。たとえば、geminiであればgemini_irq_handlerである。gemini_irq_handlerを呼び出しているのは、検索すると出てこないと思いきや、ヘッダファイルに手掛かりがあった。
geminiであればsys/arch/arm/gemini/gemini_intr.h#6にある。
6 #define ARM_IRQ_HANDLER _C_LABEL(gemini_irq_handler)
ARM_IRQ_HANDLERというのが怪しい。sys/arch/arm/arm32/irq_dispatch.S#87をみると、ARM_IRQ_HANDLERにジャンプしている。
87 /*
88 * irq_entry:
89 * Main entry point for the IRQ vector. This is a generic version
90 * which can be used by different platforms.
91 */
92 .text
93 .p2align 5
94 ARM_ASENTRY_NP(irq_entry)
95 sub lr, lr, #0x00000004 /* Adjust the lr */
96
97 PUSHFRAMEINSVC /* Push an interrupt frame */
98 ENABLE_ALIGNMENT_FAULTS /* finishes with curcpu() in r4 */
99
100 /*
101 * Increment the interrupt nesting depth and call the interrupt
102 * dispatch routine. We've pushed a frame, so we can safely use
103 * callee-saved regs here. We use the following registers, which
104 * we expect to persist:
105 *
106 * r4 address of current cpu_info
107 * r6 old value of `ci_intr_depth'
108 */
109 ldr r6, [r4, #CI_INTR_DEPTH]
110 add r1, r6, #1
111 str r1, [r4, #CI_INTR_DEPTH]
112
113 mov r0, sp /* arg for dispatcher */
114 bl ARM_IRQ_HANDLER
irq_entryで追っかけると、sys/arch/arm/arm/vectors.S#59に記載がある。
59 _C_LABEL(page0rel):
60 b reset_entry
61 b undefined_entry
62 b swi_entry
63 b prefetch_abort_entry
64 b data_abort_entry
65 b address_exception_entry
66 b irq_entry
なんとなく、page0relは色々なエントリポイントにジャンプするテーブルのようである。page0relはどこだろうか。
sys/arch/arm/arm32/arm32_machdep.c#arm32_vector_initで使われている。
134 arm32_vector_init(vaddr_t va, int which)
135 {
136 #if defined(CPU_ARMV7) || defined(CPU_ARM11) || defined(ARM_HAS_VBAR)
137 /*
138 * If this processor has the security extension, don't bother
139 * to move/map the vector page. Simply point VBAR to the copy
140 * that exists in the .text segment.
141 */
142 #ifndef ARM_HAS_VBAR
143 if (va == ARM_VECTORS_LOW
144 && (armreg_pfr1_read() & ARM_PFR1_SEC_MASK) != 0) {
145 #endif
146 extern const uint32_t page0rel[];
147 vector_page = (vaddr_t)page0rel;
machdepなのできっと機種依存のmachdepから呼ばれているのだろう、と思いつつ、案の定である。initarmの共通関数であるsys/arch/arm/arm32/arm32_boot.c#initarm_commonを代表例として挙げておく。
152 initarm_common(vaddr_t kvm_base, vsize_t kvm_size,
153 const struct boot_physmem *bp, size_t nbp)
154 {
…
173 #ifdef VERBOSE_INIT_ARM
174 printf("vectors");
175 #endif
176 arm32_vector_init(systempage.pv_va, ARM_VEC_ALL);
177 #ifdef VERBOSE_INIT_ARM
178 printf(" %#"PRIxVADDR"\n", vector_page);
179 #endif
ということは、VERVOSE_INIT_ARMされていれば、
147 vector_page = (vaddr_t)page0rel;
なのでpage0relが表示されているはずである。VERBOSE_INIT_ARMして、該当箇所を見てみよう。
vectors 0xffff0000どうやら0xFFFF0000というのがpage0relなのですな。
あれ? vbarっていうのが表示されていないな。ブロックごと飛ばされているって感じに見えるな。ARM_HAS_VBARっていうのが関係している?
ARM_HAS_VBARがRPIはあるけどAPCにはつけてないな。
あらためて、RPIと比べるとstd.rpi, std.apcは違いがある。
__HAVE_CPU_COUNTERとか。これは誰をベースにしたんだっけ?
ARM_HAS_VBARを有効にしたら以下のようになった。
vectors vbar=0xc01411c0 0xc01411c0むむむ。さっきと全然違うな。
しかし、割り込みハンドラは呼ばれない。さて、なぜでしょうか?
- 割込みが発生していない
- 割込みを許可していない(だから割り込みハンドラが呼ばれない)
- 割込み処理にバグがある
- その他の初期化が足りていない(だから割り込みハンドラが呼ばれない)
ARMjp-vH.pdfのA2-16、A2.6 例外によるとIRQ(割り込み)は0x00000018, 0xffff001cのアドレスだと書かれている。
本当にそうなのか試してみよう。
ubootのプロンプトを使って割り込みベクタを表示。リセット割込みのアドレスに書かれているea00002cにジャンプしてみる。
WMT # md.l ffff0000 8 ffff0000: ea00002c e59ff2d4 e59ff2d4 e59ff2d4 ,............... ffff0010: e59ff2d4 e59ff2d4 e59ff2d4 e59ff2d4 ................ WMT # go ea00002c ## Starting application at 0xEA00002C ...リセットされている感じがない。ちがうちがう。ffff0000から実行するのだ。
WMT # go ffff0000 ## Starting application at 0xFFFF0000 ... WonderMedia Technologies, Inc. W-Load Version : 0.23.00.00 uboot set plla cmd ..found wmt.plla.param=1:800:1,2:5,2:3 device clock is disabledethaddr............found 以下、起動メッセージが続くおぉ、リセットされた。
VBARが有効ならば、先のc01411c0に飛べばリセットされるはずである。
WMT # go c01411c0 ## Starting application at 0xC01411C0 ...何も起こらない。つまり、VBARは有効にしてはいけないのではないか?
じゃぁ、リセット割込み以外e59ff2d4が実行されるみたいだ。
WMT # go ffff0004 ## Starting application at 0xFFFF0004 ...何も起こらない。
WMT # md.l ffff0000 ffff0000: ea00002c e59ff2d4 e59ff2d4 e59ff2d4 ,............... WMT # md.l e59ff2d4 128 e59ff2d4:アドレスを読ませてもくれない。
ところでnetbsdをobjdump -dしてみると
c01411c0 <page0rel>: c01411c0: eafb7bea b c0020170 <reset_entry> c01411c4: eafb7c83 b c00203d8 <undefined_entry> c01411c8: eafb7bf4 b c00201a0 <swi_entry> c01411cc: eafb7c18 b c0020234 <prefetch_abort_entry> c01411d0: eafb7c35 b c00202ac <data_abort_entry> c01411d4: eafb7c51 b c0020320 <address_exception_entry> c01411d8: eafc2b30 b c004bea0 <irq_entry> c01411dc: e25ef004 subs pc, lr, #4てな感じである。これはVBARを外してみるとどうなるか。
あれ?変わらない。
obj全部消してやり直そう。-uついてなかったので作り直しているはずなんだけどな。
とおもっていたら、std.rpiを消してた!
うーん、やっぱりだめですね。ffff0000に配置されていません。
と思ったら、違うんですな。よく読むと、ARM_VECTORS_LOWじゃないから以下のルートに入るのですね。
159 #ifndef ARM_HAS_VBAR
160 if (CPU_IS_PRIMARY(curcpu())) {
161 extern unsigned int page0[], page0_data[];
162 unsigned int *vectors = (int *) va;
163 unsigned int *vectors_data = vectors + (page0_data - page0);
164 int vec;
165
166 /*
167 * Loop through the vectors we're taking over, and copy the
168 * vector's insn and data word.
169 */
170 for (vec = 0; vec < ARM_NVEC; vec++) {
171 if ((which & (1 << vec)) == 0) {
172 /* Don't want to take over this vector. */
173 continue;
174 }
175 vectors[vec] = page0[vec];
176 vectors_data[vec] = page0_data[vec];
177 }
178
179 /* Now sync the vectors. */
180 cpu_icache_sync_range(va, (ARM_NVEC * 2) * sizeof(u_int));
181
182 vector_page = va;
183 }
ということでpage0, page0_dataが大事と。デバッグ文を入れてみると以下のように。
Message Buffer: 0x002a7000 0x002aafff 0xc02a7000 0xc02aafff 4
Exception Vectors: 0x002ab000 0x002abfff 0xffff0000 0xffff0fff 1
Free Memory: 0x002ac000 0x1fffffff 130388
Free Memory: 0x00000000 0x00007fff 8
switching to new L1 page table @0x294000...TTBR0=0x294000 OK
done.
nfreeblocks = 2, free_pages = 130396 (0x1fd5c)
bootstrap done.
vectorsvectors = ffff0000, vectors_data = ffff011c
vectors[0] = e59ff114, vectors_data[0] = c00203d0
vectors[1] = e59ff114, vectors_data[1] = c0020638
vectors[2] = e59ff114, vectors_data[2] = c0020400
vectors[3] = e59ff114, vectors_data[3] = c0020494
vectors[4] = e59ff114, vectors_data[4] = c002050c
vectors[5] = e59ff114, vectors_data[5] = c0020580
vectors[6] = e59ff114, vectors_data[6] = c004c320
vectors[7] = e25ef004, vectors_data[7] = 00000000
0xffff0000
ちゃんと、ffff000からe59ff114つまり
c0141a20 <page0>:
c0141a20: e59ff114 ldr pc, [pc, #276] ; c0141b3c <_KERNEL_OPT_ARM_INTR_IMPL+0xc230bbcc>
c0141a24: e59ff114 ldr pc, [pc, #276] ; c0141b40 <_KERNEL_OPT_ARM_INTR_IMPL+0xc230bbd0>
c0141a28: e59ff114 ldr pc, [pc, #276] ; c0141b44 <_KERNEL_OPT_ARM_INTR_IMPL+0xc230bbd4>
c0141a2c: e59ff114 ldr pc, [pc, #276] ; c0141b48 <_KERNEL_OPT_ARM_INTR_IMPL+0xc230bbd8>
c0141a30: e59ff114 ldr pc, [pc, #276] ; c0141b4c <_KERNEL_OPT_ARM_INTR_IMPL+0xc230bbdc>
c0141a34: e59ff114 ldr pc, [pc, #276] ; c0141b50 <_KERNEL_OPT_ARM_INTR_IMPL+0xc230bbe0>
c0141a38: e59ff114 ldr pc, [pc, #276] ; c0141b54 <_KERNEL_OPT_ARM_INTR_IMPL+0xc230bbe4>
c0141a3c: e25ef004 subs pc, lr, #4
...
c0141b3c <page0_data>:
c0141b3c: c0020390 mulgt r2, r0, r3
c0141b40: c00205f8 strdgt r0, [r2], -r8
c0141b44: c00203c0 andgt r0, r2, r0, asr #7
c0141b48: c0020454 andgt r0, r2, r4, asr r4
c0141b4c: c00204cc andgt r0, r2, ip, asr #9
c0141b50: c0020540 andgt r0, r2, r0, asr #10
c0141b54: c004c2e0 andgt ip, r4, r0, ror #5
c0141b58: 00000000 andeq r0, r0, r0
ということですね。これで、割り込みが来たらジャンプしてくれるはずです。
が、割込みが来ない。なぜだ。割り込み禁止になっていたりするんですかね?
逆に、どこで割り込みを有効にするんでしょうか?
思い起こせば、start.Sを読んだのがいつだったか。
確か、割り込みを禁止していたような。と思いつつrpiのstartを眺めるも、アセンブラは忘却の彼方。
こういう時には別所さん由来の橋本さんソースを読もう。ということでnetwalkerである。
もう。いい感じでコメントが入っているではないか。
同様にrpiでも同じ処理をしておる。むしろ、冒頭である。
さて、割り込みを禁止しているのだから、どこかでそれを解いているはず。
ここまで読んでくると、cpsrがキーワードだとわかる。
cpsrでnxr検索すればヒントが。
enable_interrputしてるやん。
しかし、すべてがそうではない。
src/sys/arch/armだとat91, ep93xx, footbridge, imx, iomd, ixp12x0, mpcore, ofw, omap, s3c2xx0, xscaleの一派がenable_interruptしている。
おっと、レファレンスモデルのrpiがないじゃん。
なんなの。
ははぁ、initarm_commonしてる人は、共通関数でenableしてるんでしょ、と思って、arm32_bootを読むが、それっぽいところなさそうだなー。
それどころか、initarm_common族のomap2がicuのattachでenable_interruptしてるぞ。
もうよくわからん。initarm_common族と、enable_interrupt族を比較するとomap2だけが特殊?
enable_interruptもほとんどintr_initメソッドで呼ばれてるけどね。initarm_common族のomap2は自前initarmで呼べないもんだから、attachで読んでるってわけか。
つまり、あんまりタイミング関係ないってこと?initarmでもデバイスのattachでもいいってことかな?
しっかし、arm splx 割り込み、でgoogleしたら、もしかして: arm cpsr 割り込み、と言われるのはすごいな。
attachでenable_interruptしたら割り込みルーチンが呼ばれた!
割込みレジスタ周りを全部表示するデバッグルーチンを書く。
IC Highest-priority Status registerをみると0x0000000aというのがある。もしかしたら割り込み番号?
調べるとeth0であった。死ぬほど呼ばれるので0x0aあたりを32ビットごと00000000で初期化する。これでeth0の割り込みは来ないはず。
しかし、まだ割り込みルーチン呼ばれる。eth0以外からも割り込みが上がってきているということか。
調べると、どうやら0x00000016つまり22である。pmc_wakeupのようだ。これも0x00000000で周辺ごと初期化する。
これでシリアルの割り込みが来たら、すぐにわかるはず。
しかし、割り込みは来ない。むー。何がいかんのだろうか。
コメント(0件)
- TB-URL http://www.tokuda.net/diary/0832/tb/