1. ホーム
  2. Qt

ARM Linuxの割り込みメカニズム解析

2022-02-15 15:53:12

ARM Linuxの割り込み機構の解析

--ユーザーモードによるirq割り込みの生成例

以下のコードは、kernel linux 2.6.38.3 (trimslice website download)をベースにしています。

ARMにおける割り込み発生時の処理の流れを解析し、usr状態のIRQ、すなわちusr-> irqを例にとって説明します。

1. カーネル例外ベクトルテーブルの初期化

1.1 初期化の一般的な流れ

ARM linuxカーネルが起動すると、最初に実行されるのは linux/arch/arm/kernel/head. 次に main.c->start_kernel() 関数が呼ばれ、trap_init() (または初期化用の early_trap_init()) 、割り込み初期化用の init_IRQ() 、例外ベクタテーブルが続きます。

1.2 例外ベクトルテーブルの作成

例外ベクタテーブルの構築はコピー処理であり、カーネルコードをロケーション非依存で記述するためには、いくつかの注意点がある。

1.2.1 例外ベクトルテーブルのベースアドレス決定

ARM V4およびV4T以降のほとんどのプロセッサでは、割り込みベクタテーブルは、0x00000000と0xffff0000の2つの場所を持つことができます。CP15コプロセッサc1レジスタのVビット(ビット[13])で制御できます。

V=0 ~ 0x00000000~0x0000001C

V=1 ~ 0xffff0000~0xffff001C

注意事項 CP15 コントロールレジスタの詳細については arm armb4-1690 です。

例外ベクトルテーブルを初期化する前に実行するファイル linux/arch/arm/kernel/head. が設定されているのは CP15 レジスタに ~/arch/arm/mm/proc-v7. このファイル __v7_setup 関数)、ここでは CP15 c1 このレジスタは、例外ベクトルテーブルのベースアドレスを決定した( 0xffff0000 ).

1.2.2 例外ベクトルテーブルのコピー処理

カーネルコードのコンパイル・生成後、例外ベクタテーブルを指定した場所(0x00000000または0xffff0000)にコピーする必要があるため、カーネル内の例外ベクタテーブルを場所に依存しないように設計する必要があります。

今回使用したバージョンのカーネルでは、trap_init()の代わりにearly_trap_init()を使用して例外の初期化を行っています。

early_trap_init()はlinux/arch/arm/kernel/traps.cにあり、以下のようなコードになっています。

1. CONFIG_VECTORS_BASE はプロセッサモデルが決定された後に決定され、その値はカーネルが設定され .config ファイルに保存された後に自動生成されます。この記事では、maketrimslice_deconfigの後に自動生成される.configに定義されているカーネルバージョンを使用します。config_vectors_base= 0xffff0000 つまり、例外ベクトルテーブルのベースアドレスは0xffff0000です。

<テーブル

~/arch/arm/kernel/traps.c line783

void __init early_trap_init(void)

{ <未定義

#if defined(CONFIG_CPU_USE_DOMAINS)

         符号なし長さ vectors = CONFIG_VECTORS_BASE; //vectors は割り込みベクターのベースアドレス

#else

         unsigned long vectors = (unsigned long)vectors_page;

#endif

/* 以下はすべて arch/arm/kernel/entry-armv.S の定義では */

         extern char __stubs_start[], __stubs_end[];

         extern char __vectors_start[], __vectors_end[];

         extern char __kuser_helper_start[], __kuser_helper_end[];

         int kuser_sz = __kuser_helper_end - __kuser_helper_start;

         /*

          * ベクター、スタブ、クーサーヘルパーをコピーします(entry-armv.内)。

          * 0xffff0000 にマップされたベクターページに移動し、以下のことを確認します。

          * 命令ストリームから見えるようにします。

          */

/* __vectors_end まで __vectors_start 例外ベクトルテーブルの間です。 __stubs_end から __stubs_start その間に例外処理を行う場所があります。これらの変数は arch/arm/kernel/entry-armv.S */

         memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);

         memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

         memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz) を実行します。

         /*

          * クーサーヘルパーのプロセッサー固有のフィックスアップを行う。

          */

         kuser_get_tls_init(vectors);

         /*

          * シグナルリターンハンドラをベクターページにコピーし

          * sigreturn にこれらへのポインタを設定します。

          */

         memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE)),

                sigreturn_codes, sizeof(sigreturn_codes));

         memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE)),

                syscall_restart_code, sizeof(syscall_restart_code));

         flush_icache_range(vectors, vectors + PAGE_SIZE);

         modify_domain(DOMAIN_USER, DOMAIN_CLIENT);

}


以下は、__vectors_start, __vectors_end, __stubs_end__stubs_start の定義である。

<テーブル

arch/arm/kernel/entry-armv.S

         .グロブル       __vectors_start

__vectors_startです。

 ARM( swi SYS_ERROR0 )

 THUMB( svc #0 )

 THUMB( nop )

         W(b) vector_und + stubs_offset

         W(ldr) pc, .LCvswi + stubs_offset

         W(b) vector_pabt + stubs_offset

         W(b) vector_dabt + stubs_offset

         W(b) vector_addrexcptn + stubs_offset

         W(b) vector_irq + stubs_offset

         W(b) vector_fiq + stubs_offset

         .グロブル       __vectors_end

         .グロブル       __stubs_start

__stubs_startです。

/*

 * 割り込みディスパッチャ

 */

        vector stub irq,IRQ_MODE,4 //v <スパン エクタースタブ は、コードブロックに展開され、その直後にジャンプテーブルが続くマクロです

          .long __irq_usr @ 0 (USR_26 / USR_32)

         .long __irq_invalid @ 1 (FIQ_26 / FIQ_32)

         .long __irq_invalid @ 2 (IRQ_26 / IRQ_32)

         .long __irq_svc @ 3 (SVC_26 / SVC_32)

         .long __irq_invalid @ 4

......

......

......

......

         .グロブル       __stubs_end

__stubs_endです。

.equ stubs_offset, __vectors_start + 0x200 - __stubs_start

vector_stub irq, IRQ_MODE, 4  以下のように展開されます。

// -------------------------------- begin Expand

.align 5                // 例外エントリーを強制的に 2^5 バイトアライメント、すなわち キャッシュライン サイズアライメント、パフォーマンス上の理由

vector_irqです。

       sub lr, lr, 4         <スパン // 調整が必要 ピーシー の場合、戻り値は irq となる例外が発生します。 lr マイナス 4 その他の例外については、別の調整が必要です

       r0、lr_<exception>(親PC)、spsr_<exception>を保存します。

       (親CPSR)

       @

       Stmia sp, {r0, lr}. 保存 r0, lr

       lrさん、spsrさん

       str lr, [sp, #8] @ save spsr 

       SVC32 モードの準備をします。IRQは無効のままです。

       @

       r0さん、cpsrさん

       eor r0, r0, IRQ_MODE ^ SVC_MODE)

       msr spsr_cxsf, r0

       ブランチテーブルは、このコードの直後になければなりません。

       @

       とlr, lr, #0x0f

       mov r0, sp

       ldr lr、[PC、lr、lsl #2] 。

       movs pc, lr @ SVCモードのハンドラへ分岐

       // -------------------------------- end Expand


例外ベクトルテーブルをコピーする処理は、以下のように図にするとより分かりやすく表現できます。

図1 ベクターテーブルの移動とオフセットの計算の説明図

図1の注意点:上2本の横線の方向はアドレスの増加方向、下はCode/Loadビューで、移動前の生成バイナリカーネル内のコード構成、上のExecビューはメモリ上で実行開始後のコード割り当てを表しています。

2. ARMの例外、割り込みに対するLinuxの処理フロー

2.1 IRQ発生時にハードウェアが行う操作

R14_irq= 次に実行される命令のアドレス + 4/* レジスタlr_modeをリターンアドレスに設定 */。

       SPSR_irq = CPSR /* 現在のプロセッサの状態、割り込みマスクビット、各条件フラグビットを保存 */。

       CPSR[4:0] = 0b10010 /* 現在のプログラムステータスレジスタCPSRの対応するビットをIRQモードに設定する */

       CPSR[5] = 0 /* ARM 状態で実行 */

                                                 /*CPSR[6] は変更されません*/。

       CPSR[7] = 1 /* 通常の割り込みを無効にする */。

       ハイベクターが設定されている場合

              PC=0xFFFF0018 /* プログラムカウンタ(PC)の値をこの例外割り込みの割り込みベクタアドレスに設定し、以下の処理を行います。

* ARMv7 のベクタテーブルでは 0xFFFF0018 が一般的で、代わりに適切な例外割り込みハンドラにジャンプします。

*/

       さもなくば

              PC=0x00000018

2.2 コマンドフロージャンプ処理

上記CPUの動作が完了した後、PCは0xFFFF0018にジャンプし、そのアドレスは、命令 W(b) vector_irq + stubs_offset いるアドレスです。その後、次のようにジャンプします。 vector_stub irq,IRQ_MODE, 4 は、その で、適切な例外・割込み処理関数を実行します。

次に、具体的にコードを見てみましょう。

<テーブル

         .globl __vectors_start                       // 例外ベクトルテーブル開始 0xFFFF0000

__vectors_start:

 ARM( swi SYS_ERROR0 )

 THUMB( svc #0 )

 THUMB( nop )

         W(b) vector_und + stubs_offset

         W(ldr) pc, .LCvswi + stubs_offset

         W(b) vector_pabt + stubs_offset

         W(b) vector_dabt + stubs_offset

         W(b) vector_addrexcptn + stubs_offset

<スパン          W(b) vector_irq + stubs_offset //. 割り込み発生後のジャンプアドレス 0xFFFF0018

         W(b) vector_fiq + stubs_offset

         .globl __vectors_end

__vectors_endです。

stubs_offsetはジャンプアドレスを固定するためのオフセットで、主な処理はvector_irqで行われます。 vector_irqはマクロvector_stub irq,IRQ_MODE,4( IRQ_MODEはincludeasmanthus.hで定義されています。0x12 ) が生成されます。以下は、vector_irq生成後のコードです。 ( アセンブリコードでは @ で始まるステートメント // , /**/ どちらもコメントを表す ) :

<テーブル

.アライン 5

vector_irq。

       sub lr, lr, 4                                         <スパン // という例外が発生するため cpu は、その ピーシー <スパン 住所 <スパン +4 に値を割り当てる。 lr こちらで訂正します。

       r0、lr_<exception>(親PC)、spsr_<exception>を保存してください。

       (親CPSR)

       @

       STMIA SP、{R0、LR}。       // 保存 r0, lr <スパン になります。 イルク をスタックに格納します(各例外はそれぞれスタックを持ちます)。

       LRさん、SPSRさん                                 <スパン //LR

<スパン 保存 spsr_irq の値は ユーザー <スパン の状態です。 cpsr 値(参照 2.1 )

       str lr、[sp、#8]。             // 保存 エスエスアール <スパン 次のページへ <スパン [sp+8]です。 <スパン 場所

       SVC32 モードの準備をします。IRQは無効のままです。

       @

       R0さん、CPSRさん

       eor r0, r0,#( IRQ_MODE ^ SVC_MODE| PSR_ISETSTATE)     <スパン // psr_isetstate
: 選択 <スパン ARM/サム <スパン 命令セット <スパン

       msr spsr_cxsf, r0                             <スパン  // 以下は cxsf の数を示す。 4 個別 <スパン 8ビット のデータフィールドは

iso-or 演算は位置の入れ替えが可能で、つまり A^B^C は A^C^B と等価です。つまり、ここではr0^( IRQ_MODE ^ SVC_MODE| PSR_ISETSTATE)はr0^ IRQ_MODE ^SVC_MODEと同等で、r0のモードビット下位5ビットはIRQ_MODEと同じなのでr0^ IRQ_MODEの演算結果の下位5ビットは全て0クリアされて、^SVC_MODEとなる。 という低い 5 ビットに設定されます。 SVC_MODE は、その その他のビットは変更しない。

<テーブル

       ブランチテーブルの直後には、このコードが必要です。

       とlr, lr, #0x0f               // 例外が発生する前のプロセッサパターンを抽出する ここにある ユーザー モード

       mov r0, sp              

       ldr lr、[PC、lr、lsl #2] 。

<スパン   movs pc, lr  <スパン


sp は SVC32 モードでのスタックポインタです。ここでは r0 に移動しており、C 関数の第 1 引数 pt_regs として使用することができます。

pcは現在地+8で、このコードの直後のジャンプテーブルのベースアドレスです。lrはジャンプテーブルのインデックスに使われ、1エントリーが4バイトなので、lrを2つ左にシフトしたものが*4と等価です。usrモードからirqモードに入る場合は、lr=pc+4*0、svcモードからirqに入る場合は、lr=pc+4*3、つまり __irq_svc のアドレス、他のアドレスは __irq_invalid エラー処理、他のモードからはirq例外には入れないためです。

ここでは、irqへのエントリーがusrからだと仮定して、ジャンプテーブルの最初の命令を実行する。ARMv4は3レベルパイプライン構造なので、ジャンプのベースアドレスは現在のpcであり、常に現在の命令の次の2命令のアドレスを指します。後のバージョンで命令パイプラインは5レベル、8レベルに拡張されたが、この機能は常に互換性があるものとして扱われてきた。すなわち、pc(escape) = pc(fetch) + 8、ここで: pc(fetch) は現在実行中の命令で、前に命令をフェッチしたときのPCの値、 pc(execute) は現在実行中の命令、計算で pc が使われていれば、このときのPCの値である。

いつ 動く 命令のターゲット・レジスタは PC で始まり、コマンドは S が終了すると、その時点で spsr の値は cpsr というのは、現在のspsrはr0の値を保持しており、これがsvcモードであることは前述したとおりです。つまり、この命令はプロセッサのモードをsvcモードに変更しながら__irq_usrにジャンプしていることになります。 例外処理は svc このパターンの理由は、例外処理は必ず PL1 特権レベル。ネストされた割り込みを有効にするもう一つの理由。 . 正確な理由は質問4で説明しています。__irq_svc と __irq_invalid については、ここでは触れません。

<テーブル

/*

 * 割り込みディスパッチャ の直後には、以下のジャンプ表が必要です。 vector_irq

 */

vector_stub irq, IRQ_MODE, 4 //. 生成する vector_irq

 /* ユーザステートから割り込みに入るためのハンドラ機能 */

.long __irq_usr @ 0 (USR_26 / USR_32)

.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)

.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)

  /*  から SVC 割り込みに入るためのハンドラ機能  */

.long __irq_svc @ 3 (SVC_26 / SVC_32)

.long __irq_invalid @ 4

.long __irq_invalid @ 5

.long __irq_invalid @ 6


図 2 IRQ 割り込み処理ジャンプ回路図

なお、以下の動作はすべて svc パターンを借用する必要があるため SVC モード ISP の処理を行うので、すべての SVC にモード中のレジスタを 対向車線 スタックに格納し、最後に割込みサービスルーチン( ISP ) irq_handler .

2.2.1 __irq_usr

<テーブル

         .アライン 5

__irq_usr:

         usr_entry // ユーザモードでの割り込み発生時に、割り込み処理スタックを初期化するために使用します。 SVC スタックへの状態登録。

         kuser_cmpxchg_check // です。 の下位バージョンでは ARM コアの場合、ユーザーステートはアトミックな比較スワップを実装できない。もし、ユーザー状態がオリジナルの

                        // サブコンパリゾン交換時に割り込みが発生し、特別な処理が必要なため省略

         get_thread_info tsk //. 現在の sp ポインターを使用すると、一番右の 13 ビットクリア 0 を取得し、現在のタスクの スレッド情報

#ifdef CONFIG_PREEMPT   // 先取りが可能な場合、タスクの先取りカウントをインクリメントする。

         LDR R8, [TSK、#TI_PREEMPT]を指定します。           //T は次のように定義されます。 offsetof(struct thread_info, preempt_count) です。 preempt_count は、どうやら ちっ ただ

<スパン プロセスを取得するのは簡単です プリエンプトカウント メンバーのアドレスは現在

         add r7, r8, #1 @ インクリメントしてください。

         str r7, [tsk, #TI_PREEMPT].

#endif

irq_handler // 割り込みサービスルーチン、後述

#ifdef CONFIG_PREEMPT 

         ldr r0, [tsk、#TI_PREEMPT]。   // 現在の先取り回数を取得する

         str r8, [tsk, #TI_PREEMPT].  // となります。 r8 の値をセーブバックする。 これは、前のステップでインクリメントした先取りカウントを引き戻すことと同じである。

         teq r0, r7                                  //r0,r7 irq_handler ここで比較した、先取り前と先取り後のカウントは、ドライバが インターネットリレーチャージ

                            // プログラムがカウントを取得するためのペア操作を行わなかったため、システムエラーが発生しました。 .

 ARM( strne r0, [r0, -r0] ) // 先取りカウントが途切れたら、強制的に書き込みを行い 0.

 THUMB( movne r0, #0 )

 THUMB( strne r0, [r0] )

#endif

         mov why, #0

         b ret_to_user // ユーザー状態に戻る

 UNWIND(.fnend )

ENDPROC(__irq_usr)


次に、各機能を個別に見てみましょう

<テーブル

arch/arm/include/asm/ptrace.h

構造体 pt_regs { <未定義

         無記号長uregs[18]。

};

#endif /* __KERNEL__ */

#define ARM_cpsr uregs[16] です。

#define ARM_pc uregs[15] です。

#define ARM_lr uregs[14]です。

#define ARM_sp uregs[13] です。

#define ARM_ip uregs[12]です。

#define ARM_fp uregs[11] です。

#define ARM_r10 uregs[10]です。

#define ARM_r9 uregs[9]です。

......

#define ARM_ORIG_r0 uregs[17] です。

pt_regs 構造体の定義で、後で使用する。

<テーブル

.マクロ      usr_entry   //usr_entry マクロの定義

 UNWIND(.fnstart )

 UNWIND(.cantunwind ) @ ユーザースペースを巻き戻さない

sub sp, sp, #S_FRAME_SIZE          S_FRAME_SIZE で定義されています。 trimslice-kernel-archarm-offsets.c S_FRAME_SIZE は次のように定義されます。 <スパン sizeof(struct pt_regs) のサイズを指定します。 =18*4=72 となるバイト svc32 スタックポインタが1つ移動する pt_regs 保持するための構造体の大きさ サービス モードでの登録サイトです。

ARM( stmib sp, {r1 - r12} )  @ は、その svc32 レジスタサイトをスタックに保存

 THUMB( stmia sp, {r0 - r12} )

         ldmia r0, {r3 - r5}.      @ フロント r0 に格納される。 アイラック モードのスタックポインタ sp の値は r0-r2 に格納します。 r3-r5

         add r0, sp, #S_PC            インターロック回避のためここに <スパン インターロック回避のため、ここに S_PC として定義されています。 offsetof(struct pt_regs, ARM_pc) ここで

<スパン ここから次の例を挙げますvia 追加 ディレクティブは r0 を指します。 ARM_pc

         mov r6, #-1 @.  r6 で保存 <スパン -1

         str r3, [sp] @ コピーされたquot;real" r0を保存します。 割り込みスタックからrealを削除 r0 保存先 <スパン pt_regs->r0 での

                                            例外スタックから

2.2.2 usr_entry

<テーブル

         これでスタックに残っている空白を埋める準備ができました。

         @

         r4 - lr_<exception>, 正しい復帰/再スタートのために既に修正されています。

         r5 - spsr_<exception>

         r6 - orig_r0 (ptrace.h の pt_regs の定義を参照してください)

         @

         また、sp_usrとlr_usrを別々に保存します。

         @

         stmia r0, {r4 - r6}.   //stmia は、その svc モードでの登録 R4~R6 <スパン 保存先 ARM_pc は、その ARM_cpsr

<スパン                                                              ARM_ORIG_r0。 明確に ARM_ORIG_r0 <スパン 保存 <スパン -1 <スパン (その r6 ) この定数

 ARM( stmdb r0, {sp, lr}^ ) //stmdb 命令の <スパン ^ フラグは、ストレージで中断が発生したモードを示す <スパン sp,lr <スパン レジスタ

<スパン <スパン 次のページへ <スパン ARM_sp ARM_lr にしています。

 THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )

         カーネルモード時にアライメントトラップを有効化する。

         alignment_trap r0           // alignment_trap 設定において config_alignment_trap は、選択範囲をオンにしたときに有効です。

                                                     // 項目で、割り込み処理でアライメント・トラッキングがサポートされます。

         最初のスタックフレームをマークするためのFPクリア

         ゼロエフピー                              //zero_fp を設定するために使用します。 エフピー スタックフレームレジスタは 0

#ifdef CONFIG_IRQSOFF_TRACER

         BL TRACE_HARDIRQS_OFF

#endif

         .endm                        エントリ マクロ定義の終了

上記の命令をまとめると以下のようになり、一般レジスタr1~r12がARM_r1~ARM_r12にセーブされ、これは割り込み発生時にレジスタr1~r12をセーブすることと等価です。次に、r0、lr_irq、spsr_irqは割り込み発生時にr1〜r3にセーブされ、r4には次に使用する値-1が代入されます。次にr0をARM_r0に、lr_irq、spsr_irq、-1をARM_pc ARM_cpsr ARM_ORIG_R0に保存します。stmdb命令中の"^"に注目すると、sp_usrとlr_usrをそれぞれARM_sp、ARM_lrに保存しますが、明らかにすべての割り込みは、割り込み時にここにレジスタ保存されていることがわかります。

<イグ

図 3 割り込みスタックの保存

2.2.3 get_thread_info

get_thread_info マクロは、現在の sp の値に基づいて、13 ビットをそれぞれ lsr と lsl だけ右に、左にシフトします。これは sp を 8K アライメントに切り下げることと同じです。これは、thread_info が配置されるアドレスでもあります。

<テーブル

arch/arm/kernel/entry-header.S

  .マクロ  get_thread_info, rd

  mov \rd, sp, lsr #13

  mov \rd, lsl #13

  .エンドム

<テーブル

linux/arch/arm/kernel/entry-armv.S

/*

 * r7, r8, r9 を保持する。

 */

         .マクロ     irq_handler

#ifdef CONFIG_MULTI_IRQ_HANDLER

         ldr r5, =handle_arch_irq

         mov r0, sp

         ldr r5, [r5]の場合

         adr lr, BSYM(9997f)

         teq r5, #0

         movne pc, r5

#endif

arch_irq_handler_default

9997:

         .endm

2.2.4 irq_handler

linux/arch/arm/kernel/entry-armv.S

/*

 * r7, r8, r9 を保持する。

 */

         .マクロ     irq_handler

#ifdefCONFIG_MULTI_IRQ_HANDLER

         ldr r5,=handle_arch_irq

         mov r0,sp

         ldr r5,[r5] (※)。

         adr lr,BSYM(9997f)

         teq r5,#0

         movne pc,r5

#endif

arch_irq_handler_default

9997:

         .endm

2.2.5 arch_irq_handler_default

irq_handler は実際の IRQ 割り込み処理エントリで、割り込み処理では r7, r8, r9 レジスタを予約する必要があります。これらはカーネルプリエンプションを処理するために使用されます。MULTI_IRQ_HANDLER が設定されていない場合、irq_handler のロジックは単純で、単に arch_irq_handler_default を呼び出すだけです。

このオプションが設定されている場合、プラットフォームコードはグローバル変数: handle_arch_irq を変更することができ、ここではデフォルトの実装についてのみ説明します。

<テーブル

arch/arm/include/asm/entry_macro_multi.

/*

 * r7, r8, r9 を保持する。

 */

         .マクロ     arch_irq_handler_default // です。 get_irqnr_preamble 割り込みステータスレジスタのベースアドレスを取得するために使用します

         get_irqnr_preamble r5, lr   <スパン //
割り込みコントローラのステータスレジスタのアドレスを r5

1: get_irqnr_and_base r0, r6, r5, lr // により割り込み番号を決定する。 r0 戻る

         r1, sp を移動  // まだ割り込みがある場合は sp 第2引数として asm_do_IRQ . スプ 現在ポイントしている場所 pt_regs

         @

         r0 = irq番号、r1 = struct pt_regs *で呼び出される@ルーチン。

         @

         アドルネ lr, BSYM(1b) <スパン // <スパン ここでは lr に設定します。 get_irqnr_and_base なぜなら、2番目のループは最初の命令を実行する必要がないからです。 ( ロードレジスタのベースアドレス )

bne asm_do_IRQ //. 割り込み番号である pt_regs( 割り込み前のサイト登録 ) に渡します。 asm_do_IRQ . asm_do_IRQ 戻り値では

// に戻ることになります。 get_irqnr_and_base すべての割り込みが処理されるまで、ループは終了しません。

#ifdef CONFIG_SMP  // に対応して SMP 取り扱いについて

         /*

          * このマクロは、irqstat (r6) と base (r5) を、以下のように仮定しています。

          * 上記のget_irqnr_and_baseから保存されます。

          */

         ALT_SMP(test_for_ipi r0、r6、r5、lr) <スパン // ここでは、レジスタから読み出しています ipi <スパン ロゴマーク

         ALT_UP_B(9997f)

         movne r1, sp

         adrne lr, BSYM(1b)  <スパン // 同様に、ここではリターンアドレス