前回書いたとおり、MP Configuration tableは5種類あって、ENTRY TYPEと呼ばれるテーブルがあるので、それを見ていく。
ENTRY TYPEの種類。
Entry Description Entry Type Code Processor 0 Bus 1 I/O APIC 2 I/O Interrupt Assignment 3 Local Interrupt Assignment 4
minix2smpのソースはここ。
http://gsd.unex.es/projects/minixsmp/
まずは、Processor Entriesから。
これは、大きくわけて8個のフィールドがあり、内訳はこんな感じ。
・ENTRY TYPE
・LOCAL APIC ID
・LOCAL APIC VERSION
・CPU FLAGS
・CPU SIGNATURE
・FEATURE FLAGS
・RESERVED
・RESERVED
これを構造体で表しているのがこちら。
struct cpu_entry { u8_t ce_type; /* 0 for this type */ u8_t ce_local_apic_id; /* local APIC id number */ u8_t ce_local_apic_ver; /* local APIC version */ u8_t ce_flags; /* CPU enabled and CPU is bootstrap */ u32_t ce_signature; /* CPU type */ u32_t ce_features; /* features of this CPU */ char reserved[8]; /* reserved */ };
フィールドの意味は基本的にコメントに書かれているとおりなんだけど、補足として以下があります。
構造体の先頭フィールドはENTRY TYPEを表現していて、この値が0の場合、このENTRY TYPEがProcessor Entriesということを示す。
CPU FLAGSフィールド(ce_flags)は2ビットのフィールドで、ENビットとBPビットという各1bitのフィールドが2個ある。
ENビットが0の場合、このCPUは使えないので、OSは絶対にこのcpuにアクセスしないでねと仕様書に書かれている。
もうひとつのBPビットはこのエントリーがBSPのものかを示していて、値が1ならBSPということを示す。
次のCPU SIGNATUREフィールド(CPU SIGNATURE)は12bitのフィールドで、STEPPINGビット、MODELビット、FAMILYビットの3種類・各4bitのフィールドで構成されている。
minix2smpの場合、下のような感じでENTRY TYPEをthis_entryに読み込んで、switch-case文で判断する。
phys_copy(next, vir2phys(&this_entry), 1); switch (this_entry) { ・・・ }
さて、ENTRY TYPEがPROESSOR ENTRIESの場合はどういう処理をするかというと、
case CPU_ENTRY_ID : if (++cpu_count > 2) ADDS_MP_STATUS("MP PANIC: only 2 cpus currently supported!\n"); phys_copy(next,vir2phys(&ce),sizeof(struct cpu_entry)); next+=sizeof(struct cpu_entry); PRINT_CPU_ENTRY(cpu); if ((ce.ce_flags & CE_EN_FLAG_MASK)==0) ADDS_MP_STATUS("MP PANIC: disabled apics not currently supported\n"); break;
こんな感じです。++cpu_countのところは、minix2smpの実装は2個以上のcpuはサポートしていないというだけなので、
やる気があればこの制限を外せます(∩´∀`)∩ワーイ
実際にProcessor Entriesを読み込むのはphys_copy()のところ。
Processor Entriesの処理に関しては、ENビットを見てcpuが利用可能かどうかだけ見てます。
if ((ce.ce_flags & CE_EN_FLAG_MASK)==0) ADDS_MP_STATUS("MP PANIC: disabled apics not currently supported\n");
そして、次にBus Entriesですが、これは3つしかフィールドありません。
・ENTRY TYPE
・BUS ID
・BUS TYPE STRING
構造体はこうなります。
struct bus_entry { u8_t be_type; /* 1 for this type */ u8_t be_id; /* BUS type ID */ char be_type_str[6]; /* manufacturer's description */ };
Bus EntriesのENTRY TYPEは1です。
BUS IDは0から始まって、エントリー毎に1個ずつ増えていくようです。このIDの割り当てを担当するのはBIOS。
BUS TYPE STRING(be_type_str)は、ISA、PCIやPCMCIAといった名称が入ります。
実装上ではこのフィールドは使っていません。
case BUS_ENTRY_ID : bus_count++; next+=sizeof(struct bus_entry); break;
I/O APIC Entriesは以下の5フィールド。
・ENTRY TYPE
・I/O APIC ID
・I/O APIC VERSION
・I/O APIC FLGAS
・I/O APIC ADDRESS
struct io_apic_entry { u8_t ae_type; /* 2 for this type */ u8_t ae_id; /* IO APIC identification */ u8_t ae_version; /* IO APIC version number */ u8_t ae_flags; /* bit 0 means enabled */ u32_t ae_address; /* IO APIC memory address */ };
これのENTRY TYPEは2。
I/O APICとは?ということに関してはウィキペディアに任せるとしてw
(簡単にまとめると、I/Oデバイスから受け取った割り込みをCPUに通知するための機構。)
I/O APIC FLGASもいくつかのビット、というか1bitのENビットと予約済みフィールドに分かれていて、
ENビットが0の場合、このI/O APICは使えないということを表す。
コメントだと、/* bit 0 means enabled */ってなってるけどdisabledの間違いでしょう。
コードではこんな処理をする。
case IO_APIC_ENTRY_ID : if (++apic_count > 1) ADDS_MP_STATUS("MP PANIC: only 1 I/O APIC currently supported!\n"); phys_copy(next,vir2phys(&ae),sizeof(struct io_apic_entry)); next+=sizeof(struct io_apic_entry); /*PRINT_IO_APIC_ENTRY(ae);*/ if ((ae.ae_flags & AE_EN_FLAG_MASK)==0) ADDS_MP_STATUS("MP PANIC: disabled apic not currently supported!"); io_apic_base=ae.ae_address; break;
最初のif文は単なる実装上の制限。
そして、phys_copyでエントリーを読み込んで、ENビットを調べる。
その後、io_apic_baseにIO APIC memory addressを代入。この値は、IO_APIC_WRITE()とIO_APIC_READ()の2個の関数で、APICへの読み書きに使う。
次は、I/O Interrupt AssignmentでENTRY TYPEは3で、内容はこんな感じ。
・ENTRY TYPE
・INTERRUPT TYPE
・I/O INTERRUPT FLAG
・SOURCE BUS ID
・SOURCE BUS IRQ
・DESTINATION I/O APIC ID
・DESTINATION I/O APIC INTIN
構造体では、
struct io_int_entry { u8_t ie_type; /* 3 for this type */ u8_t ie_int_type; /* iinterupt type */ u16_t ie_flags; /* bits 0-1 are PO and 2-3 are EL */ u8_t ie_bus_id; /* source bus */ u8_t ie_bus_irq; /* IRQ in source bus */ u8_t ie_apic_id; /* destination I/O APIC */ u8_t ie_apic_int; /* INTN in destination I/O APIC */ };
I/O INTERRUPT FLAG(ie_flags)は2bitのフィールドを2個持っていて(PO、EL)
POビットは「Polarity of AIPC I/O input signals.」と説明されていて、割り込み入力の特性を示しているよう。
ELビットは「Trigger mode of AIPC I/O input signals.」と説明されているので、この割り込みのトリガーってことではないでしょうか
実装では使われていないエントリーなので、使わなくてもとりあえずは支障がないと言うことなんでしょう( ;゜Д゜)y─┛~~
case IO_INT_ENTRY_ID : ioint_count++; phys_copy(next,vir2phys(&ioie),sizeof(struct io_int_entry)); next+=sizeof(struct io_int_entry); /*PRINT_IO_INT_ENTRY(ioie);*/ break;
そして、最後のLocal Interrupt Assignment。こいつのENTRY TYPEは4。
これは、I/O Interrupt Assignmentと似たような形。
・ENTRY TYPE
・INTERRUPT TYPE
・I/O INTERRUPT FLAG
・SOURCE BUS ID
・SOURCE BUS IRQ
・DESTINATION LOCAL APIC ID
・DESTINATION LOCAL APIC LINTIN
I/O INTERRUPT FLAGがPOビット、ELビット持っていることも一緒。
構造体は以下の形。
struct l_int_entry { u8_t lie_type; /* 4 for type type */ u8_t lie_int_type; /* interrupt type */ u16_t lie_flags; /* bits 0-1 are PO and 2-3 are EL */ u8_t lie_bus_id; /* source bus */ u8_t lie_bus_irq; /* IRQ in source bus */ u8_t lie_apic_id; /* destination local APIC */ u8_t lie_apic_int; /* INTN in destination local APIC */ };
実装の方もとくにこのエントリーは使っていません。
case LOCAL_INT_ENTRY_ID : lint_count++; phys_copy(next,vir2phys(&lie),sizeof(struct l_int_entry)); next+=sizeof(struct l_int_entry); /*PRINT_LOCAL_ENTRY(lie);*/ break;
以上で、各エントリーを見てきた訳だけど、
Processor EntriesとI/O APIC Entriesの2エントリーの一部のフィールドさえ見ておけば、cpuを2個使うことは可能ということがわかったのでおk