8259A 初期化

マクロから類推できる範囲で解析。ペンディングにしている APIC マワリな部分についても早めに見とく必要がありそうな気が。
init_8259A()#arch/i386/kernel/i8259.c の以下の部分について

	outb(0xff, PIC_MASTER_IMR);
	outb(0xff, PIC_SLAVE_IMR);

	outb_p(0x11, PIC_MASTER_CMD);
	outb_p(0x20 + 0, PIC_MASTER_IMR);	
	outb_p(1U << PIC_CASCADE_IR, PIC_MASTER_IMR);
	if (auto_eoi)
		outb_p(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
	else
		outb_p(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);

	outb_p(0x11, PIC_SLAVE_CMD);
	outb_p(0x20 + 8, PIC_SLAVE_IMR);
	outb_p(PIC_CASCADE_IR, PIC_SLAVE_IMR);
	outb_p(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR);
	if (auto_eoi)
		i8259A_irq_type.ack = disable_8259A_irq;
	else
		i8259A_irq_type.ack = mask_and_ack_8259A;

	udelay(100);

	outb(cached_master_mask, PIC_MASTER_IMR);
	outb(cached_slave_mask, PIC_SLAVE_IMR);

ざっくりベースで上記処理は

  1. master、slave の割り込み停止 (マスク)
  2. master の初期化
  3. slave の初期化
  4. master、slave のマスク解除

では、順に。まずマスク処理。

	outb(0xff, PIC_MASTER_IMR);
	outb(0xff, PIC_SLAVE_IMR);

マクロ PIC_MASTER_IMR、PIC_SLAVE_IMR は include/asm-i386/io_ports.h にて定義。

#define PIC_MASTER_IMR		0x21
#define PIC_SLAVE_IMR		0xa1

IMR はマニュアルによれば、Interrupt Mask Register との事。8259A には他にレジスタが二つある。In-Service Register (ISR)、Interrupt Request Register (IRR) とマニュアルには記述されている。

で、IMR ですが、フラグが立った IRQ は割込みを受け付けない状態になる、と。マニュアルにもamazon:Linux カーネル解析入門にも OCW1 (Operation Command Words 1) と記述されている。

次からは master に対する初期化手続き。初期化手続きは 4 つのフェイズ (ICW1 から ICW4) に分かれているとの事。ICW は Initialization Command Words の略、とマニュアルにあり。

  • ICW1 初期化手順の決定
  • ICW2 「割り込みベクタ」と「IRQ」の関連づけ
  • ICW3 カスケード接続する IRQ の指定
  • ICW4 EOI のモード設定

amazon:Linux カーネル解析入門より引用
マニュアルによると、ICW1 において ICW3 および ICW4 が必要かどうかの指定をしているとの事。

	outb_p(0x11, PIC_MASTER_CMD);

上記では ICW3 も ICW4 も必要、との意味になっている。又、PIC_MASTER_CMD マクロは include/asm-i386/io_ports.h によると以下のように定義されている。

#define PIC_MASTER_CMD		0x20

master の ICW1 はポート 0x20 に出力する必要があるらしい。

で、次は ICW2 でコメントにある通り、IRQ 0〜7 を 0x20〜0x27 にマップするよう指示している。根拠は不明。コメントが仕様、としか言いようが無いかも。(弱

	outb_p(0x20 + 0, PIC_MASTER_IMR);

ICW2 は PIC_MASTER_IMR (0x21) に出力という事か。ちなみにマニュアルには A0 という input signal が 1 か 0 かという指定はある。ICW1 は 0 で、ICW2 は 1 となっていたが、これとポートの関係がどうなのかは微妙。

次。ICW1 でカスケードします、と言っているので ICW3 が必要。

	outb_p(1U << PIC_CASCADE_IR, PIC_MASTER_IMR);

ちなみに PIC_CASCADE_IR マクロは include/asm-i386/io_ports.h にて定義。

#define PIC_CASCADE_IR		2

0x04 を PIC_MASTER_IMR (0x21) に出力。IRQ2 を slave に接続するという指示をしている模様。コメントにもそう書いてありますな。

で、最後の ICW4 ですが以下。

	if (auto_eoi)
		outb_p(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
	else
		outb_p(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);

init_8259A() の引数 auto_eoi の値によって条件分岐している。呼出し元は init_ISA_irqs()#arch/i386/kernel/i8259.c で以下のように呼び出している。

	init_8259A(0);

というコトで、ここでは else なブロックの処理が実行される。MASTER_ICW4_DEFAULT マクロは include/asm-i386/io_ports.h によると以下。

#define MASTER_ICW4_DEFAULT	0x01

amazon:Linux カーネル解析入門によれば、EOI は自分でヤリますよ、という指示を主に通知している模様。

これで一旦 master 向けの初期化は終了。slave の初期化処理はまとめて以下に。

	outb_p(0x11, PIC_SLAVE_CMD);
	outb_p(0x20 + 8, PIC_SLAVE_IMR);
	outb_p(PIC_CASCADE_IR, PIC_SLAVE_IMR);
	outb_p(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR);

上記コードの関連マクロは include/asm-i386/io_ports.h にて定義。

#define PIC_SLAVE_CMD		0xa0
#define PIC_SLAVE_IMR		0xa1
#define PIC_CASCADE_IR		2
#define SLAVE_ICW4_DEFAULT	0x01

master と異なる点は以下。

  • 出力ポート ICW1 が 0xa0 で ICW2〜4 が 0xa1 になっている。
  • IRQ 0〜7 を 0x28〜0x2f にマップするよう指示している。
  • slave の場合、ICW3 における出力はフラグ (ってかビットマップ) ではなく値になる模様。

もう少し後処理が残っていますが、とりあえずここで一旦ストップ。

TODO

  • IRQ と割込みベクタの根拠
  • master および slave の入出力ポートの根拠

根拠が微妙だととても気になるのは悪いクセかも。