読者です 読者をやめる 読者になる 読者になる

acpi_parse_madt_lapic_entries()とMultiple APIC Description Table

kernel linux

今日はacpi_parse_madt_lapic_entries()を見ていこう。と言ってもコードよりもACPIの仕様書読むほうがメインかも。。

901 static int __init acpi_parse_madt_lapic_entries(void)
902 {
903         int count;
904         int x2count = 0;
905 
906         if (!cpu_has_apic)
907                 return -ENODEV;
908 
909         /*
910          * Note that the LAPIC address is obtained from the MADT (32-bit value)
911          * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value).
912          */
913 
914         count =
915             acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE,
916                                   acpi_parse_lapic_addr_ovr, 0);
917         if (count < 0) {
918                 printk(KERN_ERR PREFIX
919                        "Error parsing LAPIC address override entry\n");
920                 return count;
921         }

この範囲ではacpi_table_parse_madt()の実行が主要処理なのでここを調べる。
コメントにLAPICのアドレスは32bitもしくは64bitの模様。ちなみにACPI_MADT_TYPE_LOCAL_APIC_OVERRIDEはinclude/acpi/actbl1.hによると以下のように定義されている。

650 /* Values for MADT subtable type in struct acpi_subtable_header */
651 
652 enum acpi_madt_type {
653         ACPI_MADT_TYPE_LOCAL_APIC = 0,
~略~
657         ACPI_MADT_TYPE_LOCAL_APIC_NMI = 4,
658         ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE = 5,
659         ACPI_MADT_TYPE_IO_SAPIC = 6,
~略~
666         ACPI_MADT_TYPE_RESERVED = 13    /* 13 and greater are reserved */
667 };

ACPIの資料にあるMADTのテーブルリスト(Table 5-43 Multiple APIC Description Table (MADT) Format)ではこのような要素があるので32bitと言っている部分はここかな。

Field Byte Length Byte Offset Description
Local Interrupt Controller Address 4 36 The 32-bit physical address at which each processor can access its local interrupt controller.

では64bitのほうはというと同じくMADTなんだけど、これはMADTの44バイト目から始まる可変部分にある「Local APIC Address Override」ですかね。

Field Byte Length Byte Offset Description
Interrupt Controller Structure[n]  --  44

Descriptionでは「ここには I/O APIC, I/O SAPIC, Local APIC, Local SAPIC, Interrupt Source Override, Non-maskable Interrupt Source, Local APIC NMI Source, Local APIC Address Override, Platform Interrupt Sources, Local x2APIC, Local x2APIC NMI,GIC and GICDなどのデータを入れておく」なんでことが書いてある。

また「Table 5-45 APIC Structure Types」に「Local APIC Address Override」は5とあるので間違いなさそう。
ということで、これのデータ構造を探すと「5.2.12.8 Local APIC Address Override Structure」にあって、内容は以下のとおり。

Field Byte Length Byte Offset Description
Type 1 0 これのタイプは5
Length 1 1 このテーブルのサイズで12
Reserved 2 2 予約済み
Local APIC Address 8 4 Local APICの物理アドレス

ということで、「Local APIC Address Override Structure」があればLocal APICのアドレスは64bitで扱って、なければMADTの「Local Interrupt Controller Address」にある32bitのアドレスという感じかな。

そしてacpi_parse_madt_lapic_entries()に戻って

923         register_lapic_address(acpi_lapic_addr);
924 
925         count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_SAPIC,
926                                       acpi_parse_sapic, MAX_LOCAL_APIC);

register_lapic_address()はまあ飛ばしても良いかな。次にまたacpi_table_parse_madt()を呼ぶけど、今度のタイプは7の「Local SAPIC」。SAPIC? 仕様書にある用語集によるとこの用な説明。

A local Streamlined Advanced Programmable Interrupt Controller receives interrupts from the I/O SAPIC.

これだけだと「お、おう」としか言いようがないのでググってみると我らがIntel® Itanium® Processorのマニュアル(Intel® Itanium® Processor Family Interrupt Architecture Guide)に辿り着く( ´∀`)bグッ!
これの「1.3 Objectives of SAPIC」によると

The SAPIC architecture minimizes software context switching overhead, which is inherent with the APIC architecture. ~

というような説明が。
Local SAPIC Structureのテーブル構造はACPIの仕様書「5.2.12.10 Local SAPIC Structure」にあるので、そちらを参照。

で、またacpi_parse_madt_lapic_entries()に戻って、Local SAPICが見つからなければ次はタイプが9の「Processor Local x2APIC」&タイプ0の「Processor Local APIC」を探しにいく。

928         if (!count) {
929                 x2count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_X2APIC,
930                                         acpi_parse_x2apic, MAX_LOCAL_APIC);
931                 count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC,
932                                         acpi_parse_lapic, MAX_LOCAL_APIC);
933         }

単なるエラーチェック。

934         if (!count && !x2count) {
935                 printk(KERN_ERR PREFIX "No LAPIC entries present\n");
936                 /* TBD: Cleanup to allow fallback to MPS */
937                 return -ENODEV;
938         } else if (count < 0 || x2count < 0) {
939                 printk(KERN_ERR PREFIX "Error parsing LAPIC entry\n");
940                 /* TBD: Cleanup to allow fallback to MPS */
941                 return count;
942         }
943 

そして最後はタイプ0x0aの「Local x2APIC NMI」とタイプ4の「Local APIC NMI」をチェック。

944         x2count =
945             acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_X2APIC_NMI,
946                                   acpi_parse_x2apic_nmi, 0);
947         count =
948             acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC_NMI, acpi_parse_lapic_nmi, 0);
949         if (count < 0 || x2count < 0) {
950                 printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
951                 /* TBD: Cleanup to allow fallback to MPS */
952                 return count;
953         }
954         return 0;
955 }

ここまでで使用しているPCのAPICの構成がつかめた感じですかね。

ああ、そういえばacpi_table_parse_madt()は2番目の引数に関数ポインタを渡していて acpi_table_parse_entries()で対象のテーブルが見つかった場合にこの関数で何かしらの処理させているので次はacpi_parse_x2apic()とかacpi_parse_x2apic_nmi()が何をやっているのかを調べようφ( ̄ー ̄ )メモメモ

Debug Hacks -デバッグを極めるテクニック&ツール

Debug Hacks -デバッグを極めるテクニック&ツール