2013-05-03 13 views
6

ポストブート環境(OSなし)では、AP(他のすべてのコア/プロセッサ)のIPIを作成するために、どのようにBSP(最初のコア/プロセッサ)を使用しますか?本質的に、起動時に他のコアの命令ポインタをどのように起動し、設定しますか?APICを使用してIPを作成し、x86アセンブリでSMPのAPをスリープ状態にする方法は?

+0

[この質問](http://stackoverflow.com/questions/1622388/running-code-on-different-processor-x86-assembly)は、別のプロセッサを起動するための基本的なアルゴリズムを示しています。それはあなたが探しているものですか、具体的にIPIを送信する方法の詳細が必要ですか? (いずれにしても、私は有用な情報がたくさんある[OSDev wiki](http://wiki.osdev.org)と[forums](http://forum.osdev.org)を指しています) – ughoavgfhw

+0

最小限例:http://stackoverflow.com/a/33651438/895245 –

答えて

9

警告:ここでは80x86と仮定しています。それが80x86ではない場合、私は分かりません:-)

まず、いくつの他のCPUが存在し、どのAPIC IDがあるかを調べ、ローカルAPICの物理アドレスを特定する必要があります。これを行うには、ACPIテーブルを解析します(ACPI仕様のMADT/APICを参照)。有効なACPIテーブルが見つからない場合(コンピュータが古すぎるなど)、同じ情報を持つ独自のテーブルを定義する古い「マルチプロセッサ仕様」があります。 「MultiProcessor Specification」は廃止されました(ダミーのMultiProcessorテーブルを持つコンピュータがいくつかあります)。そのため、まずACPIテーブルを確認する必要があります。

次のステップは、使用しているローカルAPICのタイプを判断することです。 3つのケースがあります - 古い外部 "82489DX"ローカルAPIC(CPU自体に組み込まれていない)、xAPICとx2APIC。

まず、CPUIDを調べて、ローカルAPICがx2APICであるかどうかを確認します。 2つの選択肢がある場合は、x2APICを使用するか、「xAPIC互換モード」を使用できます。 「xAPIC互換モード」では、8ビットのAPIC IDしか使用できず、CPUが多い(たとえば255以上のCPU)コンピュータをサポートすることはできません。 x2APICを使用することをお勧めします(たとえあなたがたくさんのCPUを搭載したコンピュータを気にしていなくても)。 x2APICモードを使用する場合は、ローカルAPICをこのモードに切り替える必要があります。

x2APICでない場合は、ローカルAPICのバージョン・レジスタを読んでください。ローカルAPICのバージョンが0x10以上でxAPICで、0x0F以下であれば、それは外部 "82489DX"ローカルAPICです。

古い外部 "82489DX"ローカルAPICは、80486以前のコンピュータで使用されていましたが、これは非常にまれです(20年前は非常にまれで、ほとんどが死亡してしまいました。異なるシーケンスを使用して他のCPUを起動するため、これらのローカルAPICを搭載したコンピュータは非常にまれであるため(たとえば、コードをテストできないなど)、これらのコンピュータのサポートを気にしなくても大丈夫です。これらの古いコンピュータをまったくサポートしていれば、ローカルAPICが "82489DX"の場合、それらを「シングルCPUのみ」として扱い、他のCPUを起動しないことをお勧めします。この理由から、私はここでそれらを始める方法を説明しません(それは、あなたが興味があるなら、Intelの "MultiProcess Specification"に記述されています)。

xAPICとx2APICの場合、別のCPUを起動するシーケンスは本質的に同じです(ローカルAPIC - MSRまたはマップされたメモリにアクセスする異なる方法)。これらの違いを隠すために関数ポインタを使用することをおすすめします。後のコードがを介して "IPI送信"機能を呼び出すことができます。ローカルAPICがx2APICまたはxAPICである場合には、関数ポインタは気にする必要はありません。

実際に別のCPUを起動するには、一連のIPI(プロセッサ間割り込み)を送信する必要があります。インテルの方法は次のようになります。

Send an INIT IPI to the CPU you're starting 
Wait for 10 ms 
Send a STARTUP IPI to the CPU you're starting 
Wait for 200 us 
Send another STARTUP IPI to the CPU you're starting 
Wait for 200 us 
Wait for started CPU to set a flag (so you know it started) 
    If flag was set by other CPU, other CPU was started successfully 
    Else if time-out, other CPU failed to start 

インテルの方法には2つの問題があります。多くの場合、他のCPUは最初のSTARTUP IPIによって起動され、場合によってはこれが問題になることがあります(例えば、他のCPUのスタートアップコードがtotal_CPUs++;のようなものであれば、各CPUが2回実行する可能性があります。 (例えば、他のCPUが、「あなたが開始したことを知っている」というフラグが最初のCPUによって設定されるのを待つ)、インテルの方法の第2の問題はそれらの遅延を測定することです。通常、OSは他のCPUを起動し、CPUがどの機能をサポートしているか、その後に存在するハードウェアを把握し、200μsの遅延を正確に測定するための正確なタイマーを設定していません。

これらの問題を回避するには、私はこのような別の方法を使用します:

Send an INIT IPI to the CPU you're starting 
Wait for 10 ms 
Send a STARTUP IPI to the CPU you're starting 
Wait for started CPU to set a flag (so you know it started) with a short timeout (e.g. 1 ms) 
    If flag was set by other CPU, other CPU was started successfully 
    Else if time-out 
     Send another STARTUP IPI to the CPU you're starting 
     Wait for started CPU to set a flag with a long timeout (e.g. 200 ms) 
      If flag was set by other CPU, other CPU was started successfully 
      Else if time-out, other CPU failed to start 
If CPU started successfully 
    Set flag to tell other CPU it can continue 

また、CPUを個別に起動する必要があることにも注意してください。私は、「IPIをすべて自己にブロードキャストする」機能を使用して、すべてのCPUを同時に起動する人たちを見てきました.-これは間違っていて、壊れていて、恐ろしいものです(ファームウェアを作成していない限りしないでください)。これに伴う問題は、一部のCPUが故障している(BIST /組み込みセルフテストに失敗したなど)、一部のCPUが無効になっている可能性がある(ハイパースレッディングがファームウェアで無効になっているとハイパースレッディングなど)。 「IPIをすべて自己にブロードキャストする」方法では、決して開始してはならないCPUを起動することができます。

最後に、多数のCPUを搭載したコンピュータでは、一度に1つずつ起動する場合は、すべての起動に比較的長い時間がかかります。たとえば、各CPUを起動するのに11ミリ秒かかり、128 CPUがある場合、1.4秒かかります。速く起動したい場合は、これを避ける方法があります。例えば、第1CPUは第2CPUを起動し、第1CPUと第2CPUは第3CPUと第4CPUを起動することができ、その4CPUは次の4CPUを起動することができる。このようにして、77msで128CPUを起動することができる1.4秒の代わりに。

注:CPUを一度に1つずつ起動し、「並列起動」を試みる前に必ず動作させることをお勧めします(残っていることが分かってから心配することがあります)。

他のCPUが実行を開始するアドレスは、STARTUP IPIの「ベクトル」フィールドにエンコードされます。 CPUはCS = vector * 256IP = 0のコードの実行を開始します(リアルモード)。ベクタフィールドは8ビットなので、使用できる最上位の開始アドレスは0x000FF000(リアルモードでは0xFF00:0x0000)です。しかし、これはレガシーROM領域です(実際には、開始アドレスはより低くなければなりません)。通常、スタートアップコードの一部を適切なアドレスにコピーします。スタートアップコードが同期を処理する場所(例えば、別のCPUが見ることができ、継続することを確認するのを待っている「I started」フラグを設定する)し、保護/ロングモードを有効にしてエントリにジャンプする前にスタックを設定するOSの通常のコードを参照してください。この小さなスタートアップコードは、「AP CPUスタートアップトランポリン」と呼ばれています。これはまた、「並列起動」を少し複雑にするものです。起動されている各CPUが独自の/個別の同期フラグとスタックを必要とするため、これらのことは通常、トランポリン(例えばmov esp,[cs:stackTop])の変数で実装されるため、複数のトランポリンで終わることになります。

+0

一定の時間を待つ方法は?関連するhttp://stackoverflow.com/questions/9971405/assembly-display-in-screen-and-system-sleep –

関連する問題