借り初めのひみつきち

仮ブログです。

最小ステップで作る UEFI OS v0.2

前回は IDT の初期化が終わって割り込みが処理できるようになりましたが、まだ外部割り込み (IRQ) の処理ができなかったので実装します。

github.com

ほぼ全ての OS はタイマー割り込みをサポートし、タスクスイッチなどの重要な役割を持っています。
これらは i8259 (PIC) と i8253 (PIT) という 8bit の時代から存在したチップで実現されていまたが、現代の OS とは相性が悪いためほとんど使われていません。レガシー OS では核となる最重要デバイスのためになかなか置き換えが進まず、最後まで残っているレガシーデバイスのひとつでした。
今後は BIOS を廃止して UEFI の時代がくることが決定しているので、おそらくそのうちなくなる機能だと思います。

というわけで、今回作る OS では PIC/PIT の機能を使わず、より現代的な APIC/HPET を使います。

1. APIC の初期化

APIC は PIC よりも拡張された割り込みコントローラーです。
ACPI から MADT テーブルを取得し、その情報を元に初期化します。

void apic_init() {
    acpi_madt_t* madt = acpi_find_table(ACPI_MADT_SIGNATURE);
    if (madt) {
        ....
    }
}

APICマルチプロセッサと密接な関係にあり、シングルソケットでもマルチコアに対応していて実質2〜4個のマルチプロセッサ構成になっている現代の PC ではマルチプロセッサに対応できない PIC は機能が不十分です。
APIC には IO APIC と Local APIC の2種類あり、これらは全く別物です。 Local APIC は名前の通り各 CPU コアに1つ内蔵されているの対し、 IO APIC は CPU の外にあって IRQ 割り込みを各 CPU の Local APIC に分配するためにあります。

マルチプロセッサのための分配機能、レベルトリガーとエッジトリガーの設定ができるようになったなどなど PIC から拡張されてる面もありますが、シングルプロセッサで使う分には PIC と基本的な概念はあまり変わりません。

その他大きな違いとして、 PIC は I/O 空間にあったので I/O のための専用命令でしかアクセスできませんでしたが、 APICMMIOバイスなので見た目は普通のメモリアクセスと同じようにアクセスします。また、新しい CPU には APIC を拡張した x2APIC 等があり、そちらは MSR 経由でアクセスします。

2. HPET の初期化

HPET は PIT を代替する高精度タイマーです。
ACPI から HPET テーブルを取得し、その情報をもとに初期化します。

void hpet_init() {
    acpi_hpet_t* hpet = acpi_find_table(ACPI_HPET_SIGNATURE);
    if (hpet) {
        ....
    }
}

カウンタが増えたり高精度になったりしましたが、できることは PIT とあまり変わってません。
HPET も APIC 同様に MMIOバイスなので見た目は普通のメモリアクセスと変わりません。

仕様上レベルトリガーでも動作するように見えますが、手元の環境でうまく動作しなかったのでエッジトリガーモードで使うことにします。

3. PS/2 の初期化

>突然のレガシーデバイス!<

APIC と HPET が動いて OS ぽいものの動作に必要な最小限のデバイスの初期化が終わったのでこの辺でユーザーが操作できるものが作りたいと思いましたが、 USB スタックを実装するにはまだまだやることがたくさんあるので簡単に対応できる PS/2 を実装します。

残念ながら PS/2 ポートは現在進行形で消え行く運命のポートで最近の PC だとサポートしていない機種も多いです。
PS/2 ポートの存在は ACPI の FADT テーブルの IAPC_BOOT_ARCH 内のフラグでかんたんに確認できる予定でしたが、いくつかの環境でこのフラグが嘘をついている実装を見つけてしまいました。
より確実な判別方法は AML を辿って PS/2バイスが存在するかどうか見ていけばよいのですが、流石に ACPI の分厚い仕様書の大半を占めている AML を実装するのは大変なので諦めることにします。*1

f:id:neriring16:20181013182935p:plain

とりあえず、一部の機種だけではありますが、ユーザーの入力を受けて何か出力できる最小限の環境 (Minimal Operating Environment) が整ったので手抜きな簡易シェル作ってみました。手抜きシェルなのでいろいろ残念仕様です。

キーボードの入力して表示するだけってここまで何行もかけて PS/2 しか対応できなかったのに、 UEFI なら数行で PS/2 でも USB でも対応してるし、さらにもっといろんな機能サポートしてる UEFI すごいですね。

*1:ACPICAを使うといいです