MP Configuration Table Entriesの内容

前回書いたとおり、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