APIC

「アドバンスド割り込みコントローラ」の実装の部分をペンディングにせず調べてみる事に。ちなみに

  • init_IRQ()#arch/i386/kernel/i8259.c から
    • pre_intr_init_hook()#arch/i386/mach-default/setup.c が呼ばれてさらにそこから
      • init_ISA_irqs()#arch/i386/kernel/i8259.c が呼ばれてるんですが途中は略。

ただ、init_IRQ() な後処理は若干手落ちな部分があるんで別途チェックが必要と考えとります。
早速ですが init_ISA_irqs()#arch/i386/kernel/i8259.c にて

#ifdef CONFIG_X86_LOCAL_APIC
    init_bsp_APIC();
#endif

な命令が冒頭にある。amazon:Linuxカーネル解析入門によると

最初に「init_ISA_irqs()」から呼ばれる「init_bsp_APIC()」で、いちばんはじめの初期設定が行なわれます。
下記のコードを見て分かるように、カーネル・コンフィグレーションで「APIC」が有効になっていないと組み込まれません。
amazon:Linuxカーネル解析入門より引用

との事にて上記のコードらへんが掲載されている。で、今使用している PC の設定を見てみるに

$ grep 'APIC' /boot/config-2.4.18
CONFIG_X86_IO_APIC=y
CONFIG_X86_LOCAL_APIC=y
$

との出力だった (ちなみにカーネルのバージョンは 2.4.18 なんで少々微妙かも)。
それは良いとして init_bsp_APIC() を掘ってみると関数の定義は arch/i386/kernel/apic.c との事にて以下にコードの一部を引用。

    /*
     * Don't do the setup now if we have a SMP BIOS as the
     * through-I/O-APIC virtual wire mode might be active.
     */
    if (smp_found_config || !cpu_has_apic)
        return;

smp_found_config は SMP 環境であると見做した時にカーネルが 1 を設定する変数らしい。amazon:Linuxカーネル解析入門によると以下の記述あり。

「smp_found_config」変数は OS が「MP(Multi Processor)テーブル」を見付けたときに「1」が立ちます。
amazon:Linuxカーネル解析入門より引用

又、ググった所、以下の文書を発見。2.6 ではどうなっているのか、別途見てみたいな、と。

で、もひとつの cpu_has_apic なんですが、これはマクロな模様。include/asm-i386/cpufeature.h にて定義されている。

#define cpu_has_apic        boot_cpu_has(X86_FEATURE_APIC)

ワケワカんねぇのでどんどん掘りまくる。boot_cpu_has マクロも X86_FEATURE_APIC も同一ヘッダ内にて定義。一部引用。

#define X86_FEATURE_APIC    (0*32+ 9) /* Onboard APIC */
(中略)
#define boot_cpu_has(bit)    test_bit(bit, boot_cpu_data.x86_capability)

まだまだ掘る。test_bit って何だ、とゆー事で。include/asm-i386/bitops.h にあるとの事にて定義は以下。

#define test_bit(nr,addr) \
(__builtin_constant_p(nr) ? \
 constant_test_bit((nr),(addr)) : \
 variable_test_bit((nr),(addr)))

__builtin_constant_p ってどっかで見たな、と思いブログ検索してみたら hit。ペンディング事項三点に記述があった。引数がコンパイル時に定数かどうかを評価して真偽値を返却との事。
定数だったら constant_test_bit でそうでなかったら variable_test_bit を実行するワケですな。で、このケースでは定数なんで constant_test_bit のみを掘ってみる。と、同一ファイル内にて定義されていました。

static inline int constant_test_bit(int nr, const volatile unsigned long *addr) {

    return ( (1UL << (nr & 31) ) & (addr[nr >> 5]) ) != 0;

}

なななんだコレわー、という事で一つ一つを見てみるに

(1UL << (nr & 31))

という記述は nr の値は 0 から 31 までが前提なんだけど、前提を覆すケースを事前に回避している風に読めます。あ、仮引数の定義によると、addr は unsigned long な配列のアドレスなのか。てコトは nr の意味てきに

  • (右から) 0 bit から 5 ビット (0 〜 31 までを表現可能) を使ってビットの位置を指定
  • 6 bit 以降を使って配列の添字を指定

してるんだな、と。それで X86_FEATURE_APIC な定義が (0*32+ 9) みたいな形になってるのか。なるほどねー。慣れればアレなんでしょうが、可読性が良いと言えるんだろうか。ま、ハカー的にはこれくらいすーぐに分かんねぇと駄目だろ、的なナニですか??

で、ちょっと気になるのは

  • variable_test_bit はどうなっているのか (これは別途)
  • 64 bit な環境ってポインタは 64 bit だとして、int とか long int のサイズってどうなってたんだっけ

という事か。

こんなチマチマやってたらいつまで経っても終わらんぞ、と。
# いつが終わりなんだろうか。(わら

追記

可読性もクソもチマチマと配列の何番目の何ビットが立ってますかどうですか、なナニを悠長にやってるアレではないんだな、とゆー事に気がついた次第で。(とほほ

追記2

上記については基本的に i386 なアレなので、64bit なナニってのは考慮しなくて良い事に随分経って気がついた。どうもいかん。

追記3

なんか微妙な追記が続くなぁ (しかもエントリ投入中に追記を書いてしまう微妙さ加減)。cpu_has_apic についての記述をamazon:Linuxカーネル解析入門に発見。smp_found_config の直下だった。ソース追い掛けるのに夢中で本をスルーだった模様。
上記記述では、boot_cpu_data.x86_capability の状態を確認しているんだけど、これについてのナニが省略されてるな、という事で補足を。
まず、boot_cpu_data ですが arch/i386/kernel/setup.c にて定義されている。

struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 };

で、cpuinfo_x86 構造体ですが、どこで定義されているか、というと include/asm-i386/processor.h との事。メンバとして x86_capability も以下のように定義されていた。

	unsigned long	x86_capability[NCAPINTS];

とりあえず NCAPINTS については別途。あと、boot_cpu_data の初期状態についても別途かなぁ。
で、amazon:Linuxカーネル解析入門には

「CPUID」命令を実行して「Local APIC」が存在する場合は「1」が立ちます。
amazon:Linuxカーネル解析入門より引用

とある。_CPUID 命令_についても調べとく必要あり。おそらくは CPU の状態によって起動処理において x86_capability の値が設定されていくのだろう、と。