OSを自作してみる4 ~GDT初期化~



久しぶりの自作OS回。

前回ではキーボード入力が(まぁ、入力だけ)できるところまでやったけど、そういえば割り込み関係やメモリ関係の事をやってなかったのでまずははGDT/IDTの初期化の実装をやっていこうと思う。その前にちょっと用語解説を…

※過去回の記事とソースコードは下記から入手できる
https://github.com/Shadow5523/osdev/blob/master/README.md
また、ここで使用するコードはGitHubからダウンロードできる。
https://github.com/Shadow5523/osdev/releases/tag/version0.3.0

まず、OSを立ち上げるにはまず2つのCPUモードを理解しなければならない。

1.リアルモード すべてのIntel x86アーキテクチャの起動時の状態で、1MBのメモリしか扱えない。
2.プロテクトモード アドレス空間の拡張を行ったモードであり、最大4GBのメモリを扱うことができる。リングプロテクション、メモリの保護機能の追加、、仮想記憶管理、などの機能も追加されている。

またリアルモードではセグメントレジスタと呼ばれるレジスタを使用しており、このレジスタを使用して物理メモリへアクセスする。セグメントレジスタは16ビットの大きさがあるが、下位3ビットが使えない為実質13ビットの領域しか使えない。x86アーキテクチャは以下のようなレジスタを持っている。

SS スタックセグメント。スタックへのポインタ。
CS コードセグメント。コードへのポインタ。
DS データセグメント。データへのポインタ。
ES エクストラセグメント。追加のデータへのポインタ。(EはExtraのEである)
FS Fセグメント。正式な名前がついてないらしい。ESのさらに追加のデータへのポインタである。
GS Gセグメント。正式な名前がついてないらしい。FSのさらに追加のポインタである。

FS, GSはESの次(E → F → G)ている感じで名前が続いている。なんか適当に付けた感がすごいよね。

OSは最初リアルモードの状態で起動し、メモリを1MB以上使うためにはプロテクトモードへ移行しなければならないということで、これに移行するにはGDTと呼ばれる配列の開始位置と終了位置をGDTRとよばれるレジスタへ設定する必要がある。この設定はアセンブラ側からしか行えないらしく(lgdt命令がアセンブリからじゃないといけない)、GASで記述してCから呼び出すようにしなくちゃいけない。

GDT… 1つ64ビットの大きさであるセグメントディスクリプタと呼ばれるテーブルが格納された配列。セグメントディスクリプタはセグメントの開始アドレスや大きさ、属性などの情報を保有し、これとセグメントレジスタを計算して物理アドレスを算出する。細かい仕組みは省くが、この方式を利用することによって最大4GBのメモリを使用することができる。

その他用語集

リニアアドレス 仮想アドレス
セグメントアドレス アドレスを指すためのベース(基準, これをセグメントベースと呼ぶ)となるアドレス。オフセットアドレスと計算することで物理アドレスを割り出すことができる。
オフセットアドレス セグメントアドレスから相対的に表したメモリアドレス。
物理アドレス セグメントベースからオフセットアドレスを足したアドレス。
セレクタ セグメントレジスタへのポインタ。なので0x08, 0x10, 0x18…の値を持つことができる。

以下がコード。kernel.cよりgdt.cのgdt_init()を呼び出しGDTの初期化、その後load_gdtr()でGASで書かれたアセンブラコードを呼び出しlgdt命令を実行。プロテクトモードへ入り、セグメントレジスタを初期化していく。
//gdt.h

 

//gdt.c

 

//gdt.s

 

新たに作成したgdt.hをインクルードするためにkernel.hへ以下のように編集する。
//kernel.h

 

kernel.cにgdtを初期化する関数を呼び出すようにコードを追加する。

 

その後、Makefileにgdtのソースコードをビルドするように編集する。

あとはmakeコマンドでビルドして問題なくqemuで実行できればOK。
ただ実行しても特に前の変わらないです。。。

もう、お腹いっぱいっすね… IDTは次回に回すことにしよう…
因みにcrayonって部分的に色変えるのってどうやんだろ…?

Leave a Reply

Your email address will not be published. Required fields are marked *