OS起動編⑦ ioapicinit() (Xv6を読む~OSコードリーディング~)
前回
jupiteroak.hatenablog.com
トップページ
jupiteroak.hatenablog.com
main.c(一部抜粋)
https://github.com/mit-pdos/xv6-public/blob/master/main.c#L26
int main(void) { ... ioapicinit(); // another interrupt controller ...
ioapic.c
https://github.com/mit-pdos/xv6-public/blob/master/ioapic.c#L48
void ioapicinit(void) { int i, id, maxintr; ioapic = (volatile struct ioapic*)IOAPIC; maxintr = (ioapicread(REG_VER) >> 16) & 0xFF; id = ioapicread(REG_ID) >> 24; if(id != ioapicid) cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n"); // Mark all interrupts edge-triggered, active high, disabled, // and not routed to any CPUs. for(i = 0; i <= maxintr; i++){ ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i)); ioapicwrite(REG_TABLE+2*i+1, 0); } }
ioapicinit関数では、I/OAPICの初期設定を行なっています。具体的には、I/OAPICが持つ各Redirection Table Entryについて、割り込みベクタ番号と割り込みの無効化を設定しています。
I/O APICのレジスタ
I/O APICのレジスタには、メモリマップドされていて直接アクセスできるレジスタ(APIC Direct レジスタ)とメモリマップドされておらず直接アクセスできないレジスタ(APIC Indirect レジスタ)があります。
IOREGSELレジスタ(indexレジスタ)、IOWINレジスタ(dataレジスタ)はAPIC Direct レジスタに分類され、IOAPIC ID レジスタ、IOAPIC Versionレジスタ、IOAPIC Arbitration IDレジスタ、Redirection TableはAPIC Indirect レジスタに分類されます。
IOREGSELレジスタ(indexレジスタ)とIOWINレジスタ(dataレジスタ)の2つのAPIC Direct レジスタを操作することによって、APIC Indirect レジスタへアクセスすることができるようになります。
処理の内容
- ioapic構造体へのポインタにIOREGSELレジスタ(indexレジスタ)のアドレスを格納する
- I/OAPICが持つRedirection Table Entryの最大数を取得する
- APIC IDが適切な値であることを確認する
- I/OAPICが持つRedirection Table Entryを初期化する
ioapic構造体へのポインタにIOREGSELレジスタ(indexレジスタ)のアドレスを格納する
ioapic = (volatile struct ioapic*)IOAPIC;
IOREGSELレジスタ(indexレジスタ)のアドレスはデフォルトでIOAPIC→0xFEC0 0000、IOWINレジスタ(dataレジスタ)のアドレスはデフォルトで0xFEC0 0010です。
この設定に則るために、メモリマップドされているIOREGSELレジスタ(indexレジスタ)とIOWINレジスタ(dataレジスタ)のデータ構造をまとめて表現したioapic構造体へのポインタに、IOAPIC→0xFEC00000を格納しています。
ioapic構造体へのポインタにIOAPIC→0xFEC0 0000を格納しているので、ioapic構造体のメンバregをIOREGSELレジスタ(indexレジスタ)として、ioapic構造体のメンバdataをIOWINレジスタ(dataレジスタ)として扱うことができます。
ioapic構造体のメンバとアドレスの対応
uint reg | 0xFEC0 0000 |
---|---|
uint pad[0] | 0xFEC0 0004 |
uint pad[1] | 0xFEC0 0008 |
uint pad[2] | 0xFEC0 000c |
uint data | 0xFEC0 0010 |
また、IOREGSELレジスタ(indexレジスタ)やIOWINレジスタ(dataレジスタ)のような、メモリマップドされたレジスタを格納するための変数には、volatileをつけてコンパイラの最適化を抑制します(コンパイラの最適化を抑制しなかった場合、ソースコードの記述で意図したとおりの命令にコンパイルされない可能性があるため)。
volatile struct ioapic *ioapic;
I/OAPICが持つRedirection Table Entryの最大数を取得する
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
ioapicread関数を呼び出してIOAPIC Versionレジスタの値を取得し、その値からRedirection Tableの最大エントリ数-1を取得しています。
REG_VER→0x01は、IOAPIC Versionレジスタを指定するオフセット値です。このオフセット値 REG_VER 0x01 を引数にしてioapicread関数を呼び出すことにより、IOAPIC Versionレジスタの値を取得しています。
取得したIOAPIC Versionレジスタの値を16bit右シフト演算し、0xFFでマスク処理を行うことにより、IOAPIC Versionレジスタのbit23-16 Redirection Entryフィールドの値を取り出しています。IOAPIC Versionレジスタのbit23-16 Maximum Redirection Entryフィールドには、I/O APICが持つRedirection Table Entryの最大数-1の値が格納されています。この値をmaxintrに格納します。
APIC IDが適切な値であることを確認する
id = ioapicread(REG_ID) >> 24; if(id != ioapicid) cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n");
id = ioapicread(REG_ID) >> 24;
ioapicread関数を呼び出してIOAPIC IDレジスタの値を取得し、その値からAPIC IDを取得しています。
REG_ID→0x00は、IOAPIC IDレジスタを指定するオフセット値です。このオフセット値 REG_ID 0x00を引数にしてioapicread関数を呼び出すことにより、IOAPIC IDレジスタの値を取得しています。
取得したIOAPIC IDレジスタの値を24bit右シフト演算することで、IOAPIC IDレジスタのbit27-24 IOAPIC Identificationフィールドの値を取り出しています。IOAPIC IDレジスタのbit27-24 IOAPIC Identificationフィールドには、APIC IDの値が格納されています。
if(id != ioapicid)
cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n");
IOAPIC IDレジスタから取得したAPIC IDの値id と mpinitで取得したAPIC IDの値ioapicid が同じであるかどうかを調べています。
id != ioapicidが真となる場合→id と ioapicid が異なる場合は、cprintf関数を呼び出してメッセージを出力します。
I/OAPICが持つRedirection Table Entryを初期化する
for(i = 0; i <= maxintr; i++){ ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i)); ioapicwrite(REG_TABLE+2*i+1, 0); }
ioapicwriteを呼び出して、各Redirection Table Entryについて、割り込みベクタ番号の設定と割り込み無効化の設定を行なっています。
1つ目のioapicwrite関数について
第一引数0x10+2*iは、Redirection Table Entryのbit31-0(レジスタ)を指定するインデックスです。
第二引数 INT_DISABLED | (T_IRQ0 + i)は、Redirection Table Entryのbit31-0(レジスタ)に設定される値で、割り込みを無効化する値と割り込みベクタ番号の論理和になっています。
2つ目のioapicwrite関数について
第一引数0x10+2*i+1は、Redirection Table Entryのbit63-32(レジスタ)を指定するインデックスです。
第二引数の0は、Redirection Table Entryのbit63-32(レジスタ)に設定される値です。
Redirection Table
入出力装置からI/OAPICが持つIRQピンへ送信される割り込みの設定に関わるレジスタ群です。
Redirection Tableには、IRQピン1つに対してRedirection Table Entry(Redirection Tableのエントリ)が用意されており、このRedirection Table Entry(Redirection Tableのエントリ)に値を設定することで、入出力装置からI/OAPICが持つIRQピンへ送信される割り込みについて設定することができます。
例えば、ピン番号0のIRQピンへ送信される割り込みについて設定したい場合はRedirection Table Entry 0(Redirection Tableのエントリ0)に設定を行い、ピン番号1のRQピンへ送信される割り込みについて設定したい場合はRedirection Table Entry 1(Redirection Tableのエントリ1)に設定を行います。このように、ピン番号iのIRQピンに対してRedirection Table Entry i が対応しています。
Redirection Table Entry(Redirection Tableのエントリ)
Redirection Table Entry(Redirection Tableのエントリ)は64bit幅で、32bit幅のレジスタ2つから構成されています。
ピン番号iのIRQピンに対応しているRedirection Table Entryのオフセット値は、Redirection Table Entryのbit31-0(レジスタ)を指定する0x10+2*iとRedirection Table Entryのbit63-32(レジスタ)を指定する0x10+2*i+1の2つになります。
bit7-0 vectorフィールド
Vector フィールドには、割り込みベクタ番号を設定します。
このフィールドには、割り込みベクタ番号T_IRQ0 + iが設定されるので、ピン番号iのIRQピンへ送信される割り込みには、割り込みベクタ番号T_IRQ0 + iが対応するようになります。
また、Intel 64 IA-32architectureでは0から31までの割り込みベクタ番号は予約済みなので、ユーザーが設定できる割り込みベクタ番号は32から255までとなります。
bit16 maskフラグ
maskフラグ(bit16)は、割り込みの有効化・無効化を指定するフラグです。
このフラグには、INT_DISABLED →0x00010000のbit16が設定されるので、割り込みの受け入れ抑制を指定する1が設定されています。