APC8750にNetBSD/evbarmを移植するための記録 9/7 その2
2014/01/13(月) 22:04 NetBSD はてブ情報 はてブに登録 はてブ数

割り込み処理がどう呼ばれるのかを追っかけてみる。

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で周辺ごと初期化する。

これでシリアルの割り込みが来たら、すぐにわかるはず。

しかし、割り込みは来ない。むー。何がいかんのだろうか。

名前:  非公開コメント   

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