カーネル

**スケジューラは 1 つだけ。同じコードが、5 つのターゲットすべてで動きます。**固定優先度でプリエンプティブ、コアごとに走ります。各タスクは 1 つのコアに固定され、そこから動きません(BMP)。

レジスタフレームを見ないコンテキストスイッチ

スケジューラは、レジスタフレームを見ません。トラップの境界を、たった 1 つの関数に絞ってあるからです。

onTrap(sp: usize) usize

アーキごとのスタブが、全レジスタを中断中のスタックに積みます。そして中断した時点のスタックポインタだけを渡して onTrap を呼ぶ。スケジューラはその値をいまのタスクに保存し、次のタスクの値を返すだけです。あとはスタブが pop して戻ります(復帰命令は x86 が iretq、riscv が sret、aarch64 が eret、Cortex-M は例外リターン)。

レジスタの並びは、アーキ非依存のコードへ一切漏れません。だから、同じスケジューラが 5 つのターゲットで動きます。

コアごとの状態と、固定されたタスク

実行状態——いまのタスク、ラウンドロビンの位置、tick の数——は、詰めた論理コア番号で引きます。実機は APIC 番号を飛び飛びに振ることがあるので、対応表で物理と論理をつなぎます。各タスクは自分のコアを覚えていて、コア間を移りません。

マルチコア起動

5 つのターゲットは、すべて 2 コア目以降を起こします。

アーキコアの起動その後
x86_64INIT-SIPI-SIPI で物理 0x8000 のトランポリンへreal → protected → long mode。ACPI MADT から最大 4 コア
ARMv6-M(RP2040)コア間 SIO FIFO の起動ハンドシェイク2 つ目の Cortex-M0+。コア ID レジスタでコアごとの状態を引く
RV32(RP2350)SIO FIFO の起動ハンドシェイク2 つ目の Hazard3 コアがスケジューラに参加。実機で確認済み
riscv64SBI HSM の hart_start を M-mode ファームウェアへ2 つ目以降のハートが S-mode に入り、スケジューラに参加
aarch64PSCI の CPU_ON をファームウェアへ2 つ目以降のコアが EL1 に入り、スケジューラに参加

x86 では、コア数を決め打ちせず、実際に数えます。応答しないコアは失敗として扱い、そのコアに固定していたタスクは、スケジューラを起こす前に、生きているコアへ移します。

実行時に spawn される新しいアクターは、いちばん空いているコアに置かれます——x86_64・riscv64・aarch64 で。MCU の飛行ビルドは、タスクをコアに明示的に固定します。

メモリモデル

カーネルは、自分のページテーブルを持ちます。中身はアーキごとに違います。

**物理フレームの割り当ては、x86 だけにあります。**ファームウェアのメモリマップが報告する、いちばん大きい空き RAM を管理します。

riscv64 と aarch64 は、中間のページテーブルを小さな固定プールから取ります。

MMU 無し MCU プロファイルには、MMU がそもそもありません。アドレス空間はフラットで物理(VA == PA)、MMU モジュールは何もしないスタブで、隔離はページテーブルでなく PMP / MPU 領域でかけます。

カーネルのペイロードとしての VM

このカーネルは、そもそも Lambda C VM インスタンスをホストするために在ります——スケジュールされる、隔離されたアクターとして。

各タスクは、自分のスタック・グローバル領域・ヒープを持つ、独立した VM です。スケジューラは、コンテキストスイッチのたびに、VM のプロセス全体の実行状態を退避・復元します。だから VM のフレームは、まわりに割り込み禁止区間を持たず、完全にプリエンプトできます。

契約の詳しくは アクター、各アクターを自分のメモリに閉じ込める仕組みは 隔離、スケジューラのハードリアルタイム・プリミティブは リアルタイム にあります。