Help us understand the problem. What is going on with this article?

GDT、IDTを軽く説明(メモ)

More than 1 year has passed since last update.

はじめに

OSを作る際に、必ずしも躓くポイント----それが、GDTとIDTに対する理解ではないでしょう?os関連の本、特に「30日で作る os自作入門」等といった本では、序盤の方に出てきます。GDTとIDTはosを動かす為に必要不可欠な設定項目です。よって、自作本と言った説明しながら作る本ではどうしてもはじめの方で説明しなければなりません。それ故に早い段階で躓いてしまう人もおおいでしょう。そこで、今回は簡単にGDTの概要だけを紹介したいと思います。

GDTとは

GDTとは、分かりやすくいえばメモリの整理整頓です。家では子供はお母さんに、遊んだ後はちゃんと整理整頓しなさいと言われますよね?それは、洋服を決められた場所に置かなければ後で探すのに困るからです。メモリも同じで、どのメモリにアクセスするか、どの番地にあるコードを実行するか等、整理整頓しなければならないのです。

GDTの仕組み

それでは、GDTの仕組みの説明に入りたいと思います。
とあるサイトから、以下の写真を借りて説明します。
logical_address.png
図1
図1の写真には、左側にGDT、右側にメモリ(memory)と書かれてありますね。この図を本に例えれば分かりやすいでしょう。左がわのGDTは本を読む際の目次だと考えればいいでしょう。
目次には、何ページ目にどんな内容が書いてあるか等を記載しています。

.png
図2
図2の資料は(リンク先から借りた)wordでの目次の例です。ご覧の通り、目次には何ページ目に何の内容が書いてあるかを伝える役割を担ってます(図2で言うと、「目次作成にはwordが便利」と書かれた内容は3ページ目にあり、6ページ目にまとめがあります)。図1のGDTも同じ役割を担っているのです。

図1の左のGDTと書かれたエリアの0x08番目(segment_discripter2)に、base_addrss=0x00000500と書かれてあります。これは、0x08番目の目次が指す場所は物理アドレスの0x500番地にある事を意味します。0x08のsegment_discripter2の矢印の指す場所はsegment2になってます。つまり、segment_discripter2はsegment2(0x500番地以降のアドレス)を参照する為の目次だったのです。

追記1:図1には書いてありませんが、GDTにはメモリのサイズ(limit_size)を指定する必要があります。0x08番目のアドレスのlimit_sizeを0x1500と指定すれば、矢印の先にあるsegment2のアドレスの範囲は0x500からの0x1500先ビットまで(0x500 ~ 0x500 + 0x1500)となります。要するに、segment_discripter2は(0x500 ~ 0x500 + 0x1500)のメモリを参照する際の目次だったという事です。


追記2:図1のGDTのアドレスが0x08ビット毎に(segment_discripter1は0x00, segment_discripter1は0x08, segment_discripter3は0x10)になってるのは、base_addrssと追記1にあるlimit_sizeと権限(ここでは説明しない)を設定する必要があるからです。それらの情報は64ビットで表せます。番地は1ビット毎に8ビットのvalueを格納してるので、64➗8=8ビット毎にGDTのsegment_discripterを保存してるのが分かります。

それでは、仕上げといきます。
まず、GDTを指定しただけでは具体的なメモリ番地にアクセスする事はできません。図1だと、base_addressから離れた、0x1500のアドレスのアドレスにアクセスできません。例えるならば、紙の辞書の目次に「S ---- 100p」と書いてあるからといって、「study」が100ページ丁度にある事なんてほぼありません。目次だけではなく、その「S」から数えた何ページ目にあるかを調べないといけないでしょう。そこで役に立つのがoffsetなのです。

図1の右上にoffset = 0x00001000と書かれた四角形があります。このオフセットは、「GDT(目次)」の指してるbase_addressから数えて何番目の番地にアクセスしたいかを表しています。また図1に戻りましょう。図1のsegment_discripter2は(0x500 ~ 0x500 + 0x1500)の範囲のアドレスを指してる事を追記1から指摘しました。しかし、具体的に0x1500番地のアドレスにアクセスしたいとしましょう。GDTだけでは、アクセスしたいメモリの範囲は分かっても、その範囲内のどこにアクセスすればいいかまでは分かりません。そこで、offsetで0x00001000と指定します。すると、0x500 + 0x00001000 = 0x1500なので、0x1500にジャンプできるのです。

アセンブラでは、LGDT(次の省で説明)
JMP 0x08:0x00001000
と指定すれば0x1500のアドレスにjmpできます。
JMP命令にある0x08は、(図1参照)segment_discripterの開始アドレスが最初から数えて0x08番目にあるからです。

GDTの設定

それでは、設定に入りましょう。

「30日で出来るos自作入門」のasmhead.nasから以下のコードを抜き出します(改変あり)。

GDT0:
        RESB    8               ; ヌルセレクタ
        DW      0xffff,0x0000,0x9200,0x00cf ; 読み書き可能セグメント32bit
        DW      0xffff,0x0000,0x9a28,0x0047 ; 実行可能セグメント32bit(bootpack用)
        DW      0
GDT0_end:
GDTR0:
        DW      GDT0_end - GDT0 - 1
        DD      GDT0

まずはGDTR0をご覧ください。最初にDW GDT0_end - GDT0 - 1と書かれてありますね。そして、次に DD GDT0と書かれています。これは、GDT(つまりメモリの目次)を格納してるのはGDTR0にあるという意味です。そして、目次はGDT0とGDT0_endの間にあるので、サイズはGDT0_end - GDT0 - 1となるのです。

次はGDT0を見てみましょう。
まず最初に RESB 8がありました。GDTを指定する際に最初の8番地分は0にするのが決まりみたいです。次の部分は以下のようになります。

    DW      0xffff,0x0000,0x9200,0x00cf

上記の最初の最初の0xffffはlimit_size(前節--GDTの原理を参照)を表しています。つまり、この番地の指す物理アドレスは0xffffビットの大きさになります。次の0x0000,0x9200,0x00cfの部分ですが、そこはbase_addressが2000000である事を表しています。実は、0x0000,0x0200,0x0000がベースアドレスをあらわしていて、その上にある0x0000,0x9000,0x00cfは読み書き可能を表しています。何故そうなるかは、とあるサイトにお任せします。読み書き可能(実行不可)というのは、mov命令でセグメントにデータいれたり、データを引き出す事ができるからです。ただし、実行不可の為にjmp命令で0x2000000番地に飛んでコードを実行する事はできません。

    DW      0xffff,0x0000,0x9a28,0x0047

そして、その下にある上のコマンドは、セグメントのベースアドレスが0x280000、サイズが0xffff、0x9aと0x0047なので(ここもとあるサイト参照)実行可能セグメントとなります。

そして、

LGDT GDTR0
を使えば指定した領域がGDTになるのです。
0x280000番地は実行可能なので、jmp 0x08:0x00000000でコードセグメントのアドレスにジャンプできます。逆に0x200000番地は読み書き番地なので、mov [0x08:0x00000000], EAXとデータを格納する事ができます。

IDTとは

IDTとは、簡単に言えば外部装置との繋がりです(正確ではないのですが、最初はそう考えても構わないでしょう。詳しくはグーグルで割り込みと調べれば出てきます。)。例えば、キーボードからの入力は一旦IDTを経由(割り込み--詳しくはグーグル先生で)してからパソコンの中の送り込まれます。実は、IDTを使わなくてもアセンブラでキーボードの入力を受け付けられるのですが、IDTを経由した方が早くなります。また、パソコン内のタイマーもIDTを通して繋げる事ができるので、IDTでパソコンの時間を取得して、目覚まし時計が作れたりします。

IDTの設定

IDTの構文は以下のようになります。

IDT0:
irq0:
        dw isr0
        dw 0x0008
        db 0x00
        db 10101110b
        dw 0x0000
irq1:
        dw isr1
        dw 0x0008
        db 0x00
        db 10101110b
        dw 0x0000
irq2:
        dw isr2
        dw 0x0008
        db 0x00
        db 10101110b
        dw 0x0000
IDT0_end:
IDTR0:
        DW      IDT0_end - IDT0 - 1
        DD      IDT0

とある海外サイトからパクリました。
まずはIDTRからです。

IDTR0:
        DW      IDT0_end - IDT0 - 1
        DD      IDT0

ここはGDTと同じで、サイズがIDT0_end - IDT0 - 1、ベースアドレスがIDT0です。
次にIDT0を見てみましょう。

IDT0:
irq0:
        dw isr0
        dw 0x0008
        db 0x00
        db 10101110b
        dw 0x0000
irq1:
        dw isr1
        dw 0x0008
        db 0x00
        db 10101110b
        dw 0x0000
irq2:
        dw isr2
        dw 0x0008
        db 0x00
        db 10101110b
        dw 0x0000
IDT0_end:

それぞれirq0、irq1、irq2と分かれています。
そして、それぞれ一番最初にisr0、isr1、isr2と書かれています。これらは実はc言語で言う関数ポインターみたいなものです。その下にある数々のパラメーターは、各種設定を表していますが、そこもグーグル先生にお預けします。
実は、IDTの場合はハードウェアや使いたい性能がそれぞれ番地が決まっているのです。上のコードで言うと、例えば、irq0番はタイマー(時間関連)、irq1はキーボードに繋がってます。今回はキーボードを例に説明したいので、irq1を以下のように定義します。

isr1:
        call  test
        iret

見てのとおり、test関数を呼び出すだけの関数です(test関数はc言語やなんやで自分で作ろう)。この関数はIDTの2番に登録されてる為、キーボードを押すと自動的に実行されます(前に実行してるプログラムは一次中断される)。最後のiretはretと同じ意味で、IDTに登録されてる関数はこちらを使います(一般のret命令は通用しない)。そして、前回中断されたとこからプログラムがまた始まるのです。

IDTを実行させるには、picを初期化する必要がありますが、話が長くなりますので「30日で出来るos自作入門」等の本を参照していただけたらなと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした