OS開発
はじめに
本ページは私がOS開発をするにあたって調べた内容をまとめていきます.試行錯誤の上,実装している最中なので,うまくまとまっていなかったり内容に誤りが含まれている可能性がありますのでご了承ください.
OS自作本やかの有名なMINIX本との違いは,x86-64・マルチプロセッサ対応というところでしょうか.理論はMINIX本でまとめられているので,このページでは,OS開発をする上で必要だけど学校では教えてもらえない内容を対象としていこうかと思います.つれづれなるままに.
x86-64(Intel® 64)のプログラミングをする上で,正しい情報を得るにはIntel® 64 and IA-32 Architectures Software Developer Manualsを参照することになります.特にVol. 3の開発者マニュアルは手元に置いておきたい一冊です.x86(IA-32)のマニュアルは日本語版もありますが,64ビット版対応のものは英語のみなので,本ページでも基本的には英語版のものを参照していきます.
ソースコード
ソースコードはgithub repository:scyphus/aos で公開しています.この文章よりもコードの方が先に出るかと思います.
開発環境整備
コンパイラはgcc(とgas)を使っていきます.開発用のOSは何でも良いのですが,私はMac (OS X)を使っています.ただし,Mac OS Xの場合は,標準のgcc(XcodeからCommand Line Toolsとしてインストールするもの)は使えませんので,自分でコンパイル・インストールします.Mac OS Xではなく,BSDやLinuxでインストールされる(されている)gccなら問題無いかと思います.実験用の仮想環境も何でも良いのですが,私はVirtualBoxを使っています.実機はさすがに大変だと思うので,仮想マシンを使うのが良いと思います.
1. BIOS・リアルモード
まずはフロッピーディスクからの起動を扱います.みなさんファイルシステムはあとでゆっくり設計したいでしょうから,とりあえず単純に最初の512バイトはMBRで,そのすぐ後にプログラムが入るという単純な構成にしておきます.とりあえずパーティションも気にしない.今どきフロッピーディスクなんて・・・という状況ですが,そのままハードディスクも読めるはずです.CDの場合,ISO9660ファイルシステムを見るので,少し複雑になります.
BIOSから起動した際のレジスタの値(x86/x86-64)
- %cs:%ip = 0x0000:0x7c00
- %dl:ブートデバイスのドライブ番号
プロテクテッドモード・ロングモード(編集中)
リアルモードからプロテクテッドモードへは,GDTを設定した後に,ロングジャンプ(セグメント間ジャンプ)でコードセグメントを変更(同時にパイプラインキャッシュをクリア)する.プロテクテッドモードからロングモードへは,PAEを有効にし,ページテーブルを設定したEFER MSRレジスタのLMEビットをセットし,ページングとロングモードを有効にした後に,コードセグメントとエントリーポイントのアドレスをスタックに積み,ロングリターンでロングモードのエントリーポイントに遷移する.
入出力(キーボード・ディスプレイ)
- 割り込み:IRQ 1 (i8254),INTIN1(I/O APIC)
- キーボードエンコードバッファからスキャンコードの読み込み:ポート0x0060
割り込み(PIC: Programmable Interrupt Controller)
マルチプロセッサ
Local APICのプロセッサ間割り込みでトランポリンコードを実行する.
- Running code on different processor (x86 assembly)
- Assessing the APIC and creating IPIs in x86 assembly
Destination shorthandは11b (All Excluding Self)を使う.00b (No shorthand)でdestinationを指定した場合,VirtualBoxでは動いたが,実機(ThinkPad T410)だと動かなかった(要プロセッサ・チップセット情報追加).
割り込み(APIC: Advanaced Programmable Interrupt Controller)
I/O APIC
メモリ管理
現状:とりあえずビットマップ. Linuxではバディシステム(Buddy system)によるページ割り当て,スラブアロケータによるページ未満のメモリ割り当てが実装されている.
ネットワーク
ネットワークカード (Ethernet)
はまりどころ
呼び出し規則(Calling Convention)
GCC 4.6.4のx86-64では,cdeclではなく,System V AMD64 Application Binary Interface (ABI) [Wikipedia:X86_calling_conventions].
入出力など(編集中)
- Memory mapped hardware: 特定のメモリ領域がそのままハードウェアの入出力になっているもの.例としては,VGAの80x20テキストモードで0xb8000から始まるビデオRAM.
- MSR (Machine Specific Register): %ecxにMSRのアドレスを指定してrdmsr命令をすると,least significant(いわゆる下位)32ビットが%eax,most significant(いわゆる上位)32ビットが%edxに入る.書き込みは,%ecx, %eax, %edxに値を入れてwrmsrをするとMSRに書き込める.
- I/O命令: in命令とout命令で外部機器(ポート)に対してデータを入出力できる
時間管理(編集中)
クロック
- Programmable Interrupt Timer(i8254):16ビットカウンタで割り込みごとにjiffies加算.解像度が低くなる.シングルプロセッサ環境ではコンテキストスイッチのタイミングに利用.マルチプロセッサ環境ではone-shotで使ってLocal APICタイマのキャリブレーションに使うなど.
- Local APICタイマ:バス速度で周波数が変わるので,正確には測れない.マルチプロセッサ環境でコンテキストスイッチには利用.
- HPET:i8254よりも高精度・高解像度.
- ACPI PMタイマ:24ビット or 32ビットの値を一周する前に定期的に読み込む(Local APICタイマなどを使う)
- Invariant TSC:64ビット.高精度.1命令で読めるので高速.対応していないプロセッサもある(特に仮想環境)
時刻:CMOSから日時を読む(秒精度). これに加えてNTPなどで誤差調整.
x86-64
ToDo
UEFIへの対応.
コンパイラの仕事(__builtin_XXXXX):va_list, va_start, va_arg, va_end, va_copy, alloca