雑記帳
ここはhideyosiの雑記帳です。テケトーに書き散らしてるだけなので間違っていたりとは普通にしてます。信用度は相当低いことをあらかじめご了承を。またご覧のようにWikiを使ってますが、hideyosi意外は書き込めません。

OS自作本の6日目。P124。

ここは難しくはありませんが、とても煩雑でややこしい部分。

初めて読む方でもしうまく理解できなかったら、「とにもかくにも合計80ビット分くらい必要なデータを、むりやり64ビットに押し込んでいる」程度に理解して、先に進んじゃうのがいいかも。(それで後でもう一回よく読んでみれば意外とスルスルっと解ることも多いので)

・・・まあでも、これじゃちょっとあんまりなので、オイラなりの解説〜♪

GDTへの設定に必要なもの anchor.png

  • とにかく、GDTには 8,192個分の設定ができる。(セグメントを 8,192種類設定できる)
  • 各1個の設定には、大きさ・開始番地・属性の三つを設定する必要がある。

まずはこれだけ。属性はちょっと特殊なのでちょっと置いておく。とりあえずは16ビット必要ということで。

そう考えると、こういう設定パラメータが必要になると。

C大きさC開始番地C属性
範囲0x0〜0xFFFFFFFF0x0〜0xFFFFFFFF0x0〜0xFFFF
必要ビット数323216
必要バイト数442
0x002300000x0000C0000xA4FD

さて、これを見ると、一つのセグメントに設定しなくちゃいけないパラメータの合計は32+32+16で、合計80ビットが必要になるわけです。

しかし!!

GDTという表が、一行をこのとおり80ビット(10バイト)とってくれていれば単なる当て込みでいいのですが、なぜかi386では一行は64ビット(8バイト)ということになっています。

なので、全部で10バイト必要なパラメータを8バイトに押し込む(一種の圧縮?)必要があるのです。ここはそのための処理なのです。

解りやすくこんなことを考えてみます。

セグメント0を、
   ・大きさ0x11223344(278MB)
   ・開始番地0x55667788
   ・属性を0xAABB
として定義したいとします。

セグメントの開始番地は本でとりあえず0x00270000と決めています。これをそのまま使いましょう。

ようするにGDTは表です。なので、こんな表を妄想できますね?

アドレスセグメントNo大きさ開始番地属性
0x0027000000x112233440x556677880xAABB
??1
2
8191

さて、この表の「??」の部分の番地。いくつになると思いますか?

0x00270000から始まって4バイト+4バイト+2バイト分進むのですから、0x00270010になるはずです。「??」のアドレスは0x00270010。つまりここから次のセグメント(セグメント1)の設定が始まると。ばんざーい!!

ダメなのです!!!

正解は0x00270008。そう。正しくは、GDT表の一行は8バイトなのです!(インテルが決めた)

  • 「まてこのやろう!10バイト必要なのは明白だろう!なんで一行が8バイトなんだよ!2バイト分足りないじゃないかよ!!!」
  • はいそのとおりです! インテルに変わってお詫びします!。でももう決まっているので、なんとかヤリクリしてください。あしからず!
  • ・・・いっちゃった・・・

そういうわけでP124のコード。押し込み(圧縮?)作業が必要になると!

さて、実際に押し込む作業を追ってみましょう。

一行はとにかく8バイトなので、本では SEGMENT_DESCRIPTOR という構造体を定義しています。なるほど。構造体の中身をバイト単位で表してみると・・・

0000000000000000
limit_lowbase_lowbase_midaccess_rightlimit_heighbase_heigh

さてさっそく本のように、ここにデータを押し込んで見ましょう。まずは開始アドレス(0x55667788)から。

本によると、開始アドレスは32ビット(4バイト)が必要です。GDTの場合はこれをbase_low(2バイト)・base_mid(1バイト)・base_heigh(1バイト)の三つに割ってバラバラに格納しなければいけないようです。そのための処理が、

sd->base_low    = base & 0xffff;
sd->base_mid    = (base >> 16) & 0xff;
sd->base_heigh  = (base >> 24) & 0xff;

これらです。ひとつひとつ見てみましょう。(ちなみにこのサイトの例として、baseは0x55667788を使用します。)

Page Top

sd->base_low = base & 0xffff; anchor.png

0x55667788 & 0xffffですね。

これはアセンブラでよく使う、「特定のビットをゼロにしたい!」という処理です。

ようするにここは、「下2バイトを切り出す」のではなく、 逆に「頭2バイトを全部0にしちゃう」という処理です。 2進数にするとかえってわかりやすいかも。

       01010101011001100111011110001000     ←0x55667788の2進数
 AND   00000000000000001111111111111111     ←0x0000ffffの2進数
────────────────────
       00000000000000000111011110001000     ←0x00007788になる

なるほど! これでbase_low(2バイト)に代入できます!

Page Top

sd->base_mid = (base >> 16) & 0xff; anchor.png

( 0x55667788 >> 16) & 0xff ですね。

まず括弧の中身。これはビットシフト命令ですね。(本のP117に図が載ってます)

これも、かえって2進数にしたほうがわかりやすいかもしれません。

       01010101011001100111011110001000     ←0x55667788の2進数
                       ↓
                16個分右にシフト
                       ↓
       00000000000000000101010101100110     ←0x00005566の2進数

そしてさらに、これを0xffでAND演算しています。

       00000000000000000101010101100110     ←0x00005566の2進数
  AND  00000000000000000000000011111111     ←0x000000ffの2進数
 ───────────────────
       00000000000000000000000001100110     ←0x00000066になる

これで base_mid が取り出せました。

Page Top

sd->base_heigh = (base >> 24) & 0xff; anchor.png

上の処理とほぼ同じですね。

       01010101011001100111011110001000     ←0x55667788の2進数
                       ↓
                24個分右にシフト
                       ↓
       00000000000000000000000001010101     ←0x00000055の2進数

なお、この時点ですでに頭2バイトだけの状態になっていますが、あえて0xffでANDしていますね。おそらくこれは、「念のため」の処理でしょう。

さて、これで、「開始番地」を三つに分割することができましたね。本のコードでは処理してすぐに構造体に当て込んでいます。今は構造体はどんな状態でしょうか?

0000778866000055
limit_lowbase_lowbase_midaccess_rightlimit_heighbase_heigh

こうなっています。

Page Top

大きさ(limit) anchor.png

さて、セグメントの大きさです。これは本に書いてある通り、0から4GBまで指定できないとおかしいですよね?。なので、32ビット(4バイト)のデータになります。

上記の開始アドレスは幸いただ割ればよかったのですが、limitは20ビットしか入れ物が用意されていません。なので、特殊な方法で指定します。

例として、大きさは0x11223344(278MB)を使います。

if (limit > 0xffff) {
     ar |= 0x80000; /* G_bit = 1 */
     limit /= 0x10000;
}

sd->limit_low    = limit & 0xffff;

sd->limit_heigh  = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0x0f);

これがlimitに関する演算です。最初から見てみましょう。

Page Top

if(limit > ・・・・ anchor.png

これはなにをやっているかというと、P125に書いてある通りです。

大きさがもし1MB以上ならG_bitを1にしてx4kbモード?にします。当然limitの値もあらかじめ4kbで割り算しておきます。

・・・しかし、G_bitを1にするのはわかりますが、なんで ar |= 0x80000 なんて処理をしているのでしょう? arはそもそも属性の値じゃないですか・・・

この表をもう一度見てください。

0000778866000055
limit_lowbase_lowbase_midaccess_rightlimit_heighbase_heigh

これは構造体の定義にそって変数を宣言しています。C言語では、設定(というか、指定?)できる変数はchar(8ビット)、short(16ビット)、int(32ビット)等等がありますが、残念ながら20ビットなんて単位の変数はありません。やむを得ず16ビットや8ビットでごまかしていますが、本当は上記の表の区切りはまちがっているのです。本当はこうなります。(色が付いている部分はそこだけ2進数です。ご注意)

↓区切る所が厳密には正しくない
0000778866000000000000000055
limit_lowbase_lowbase_midaccess_rightlimit_heighbase_heigh


↓正しい区切り位置
0000778866000000000000000055
limit_lowbase_lowbase_midaccess_rightlimit_heighbase_heigh

Last-modified: 2012-10-22 (Mon) 22:05:03 (GMT) (2818d) by