ARM GICv2を使ってみる
これは自作osアドベントカレンダー 2021の9日目の記事です。
要約
mikanOS をQEMU aarch64 向けに移植しています。
day06あたりまで動くようになりましたが、day07の割り込みハンドリングのところで放置状態になっています。
ちょっとがんばって割り込みのハンドリングを行ってみたいと思います。
ARM Cortex-Aプロセッサの割り込み
(しっかり調べ切っていないので、一部不正確な内容があるかもしれません)
ARM Cortex-Aプロセッサの外部割り込みは基本的にIRQ
とFIQ
の二つです。
しかし、二つしかないのでは非常に不便で実用になりません。
なので、一般的にARM Cortex-Aを搭載したシステムには「割り込みコントローラー」というCPUとは別のコントローラーが搭載されており、この割り込みコントローラーがCPUと連携して割り込みの調停を行っています。
ARMは割り込みコントローラーを設計していますが、すべてのARM搭載システムにARMが設計した割り込みコントローラーが搭載されているわけではありません。
そして、各割り込みコントローラーごとに制御方法が違います。
というわけでARMのシステムで割り込みハンドリングを行う場合、まず最初に知るべきは
「開発ターゲットに搭載されている割り込みコントローラーは何か?」
となります。
qemu-system-aarch64の割り込みコントローラー
qemu-system-aarch64の割り込みコントローラーはオプションでGICv2, GICv3のどちらかを選べます。
私がmikanOSの移植で使っているqemuは4.2.1で、デバイス構成オプションは下記になります。このオプションでの割り込みコントローラーはGICv2になります。
qemu-system-aarch64 \
-machine virt \
-cpu cortex-a53 \
-m 1G \
-drive media=disk,index=0,format=raw,file=$DISK_IMG \
-drive if=pflash,format=raw,readonly,file=$DEVENV_DIR/$ARCH/OVMF_CODE.fd \
-device nec-usb-xhci,id=xhci \
-device usb-mouse \
-device usb-kbd \
-monitor stdio \
-device ramfb
また、デバイスツリー情報はこちらにあります。
GICv2
GICv2の情報は検索するといっぱい出てきます。
@tnishinagaさんのBareMetalで遊ぶ Raspberry Pi 4 - GIC v2編は非常によくまとまっていて、これを読んでから公式ドキュメントを見ればだいたいOKだと思います。
上記資料によるとラズパイ4はEL2で動作しているとのことですが、Tiano CoreでUEFI起動したqemu-system-aarch64はEL1のノンセキュアで動いています。
制御するにあたり、qemu-system-aarch64上でのGICv2レジスタ群のアドレスを知る必要があります。これはデバイスツリーから知ることができます。
qemu-system-aarch64のGICv2に関する情報は下記になります。
intc@8000000 {
phandle = <0x8001>;
reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000>;
compatible = "arm,cortex-a15-gic";
ranges;
#size-cells = <0x02>;
#address-cells = <0x02>;
interrupt-controller;
#interrupt-cells = <0x03>;
v2m@8020000 {
phandle = <0x8002>;
reg = <0x00 0x8020000 0x00 0x1000>;
msi-controller;
compatible = "arm,gic-v2m-frame";
};
};
レジスタ群 | 先頭アドレス |
---|---|
GICD(ディストリビューター)レジスタ | 0x08000000 |
GICC(CPUインターフェース)レジスタ | 0x08010000 |
GICH(ハイパーバイザ)レジスタ | (ない) |
GICV(バーチャルCPUインターフェース)レジスタ | (ない) |
GICv2m(MSI制御用) | 0x08020000 |
GICv2m
という見慣れないものがあります。これはMSI (Message Signaled Interrupt)のためのGICv2の拡張機能みたいなものです。
MSIとGICv2m
MSIはPCIやPCIeで使われる割り込み発生の方法です。
太古の昔から一般的に使われている割り込みは、外部デバイスの端子と割り込みコントローラーが物理的に結線されていて、この端子の電圧変化で割り込みを駆動する方法をとっています。
MSIはそうではなく、特定のメモリアドレスに特定の値を書き込むことによって発生させる割り込みです。
GICv2mは特定のメモリ(というかGICv2mのレジスタ)への書き込みを検知して、GICv2へ端子経由で割り込みが発生したかのように見せかけるものです。これによりCPUは端子経由の割り込みと同じようにMSIを扱うことができるようになります。
ちなみにGICv2mの仕様はGICv2のドキュメントには記載されていません。
ARMのサイトからダウンロードできるServer_Base_System_Architecture_v3_1_ARM_DEN_0029A.pdf
の末尾にちょこっと記載があり、これが唯一の資料のようです。
仕様自体はシンプルでレジスタも3つしかありません。
レジスタ | オフセット | |
---|---|---|
MSI_TYPER | +0x008 | GICv2の何番からの割り込み番号を使うかなどの情報 |
MSI_SETSPI_S | +0x040 | ここに割り込み番号を書くとGICv2側の割り込みが発生する |
MSI_IIDR | +0xFCC | プロダクトIDなどの情報 |
PCI/PCIeのMSI設定としては、「MSI_SETSPI_S
レジスタに割り込み番号を書く」でよいです。
mikanOSでの実装
書いているうちに0時をすぎて9日になりました。
アドベントカレンダーを公開する日になってしまったので実装の説明は別途書きたいと思います。GICv2mの話を少し書いただけになってしまいました。ほんとにすみません。
osbook_day07a相当まで実装して割り込みでマウス情報を取得できたmikanos-aarch64はこのブランチです。
aarch64に特化した実装はarch/aarch64-linux-gnu/以下にまとまっています。
GICv2とGICv2mに関するコードはgicv2.cppです。
とりあえず今回はここまで
デバイス初期化の部分以外はUSBドライバを含めmikanOSのコードをほぼそのまま使っています。
mikanOSは移植性の高いコードで素晴らしいなあと感心しています。
Discussion