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_HANDLERirq_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/