2: 2007-03-25 (日) 19:50:36 |
現: 2024-01-06 (土) 22:39:10 |
| |~範囲|0x0〜0xFFFFFFFF|0x0〜0xFFFFFFFF|0x0〜0xFFFF| | | |~範囲|0x0〜0xFFFFFFFF|0x0〜0xFFFFFFFF|0x0〜0xFFFF| |
| |~必要ビット数|32|32|16| | | |~必要ビット数|32|32|16| |
| + | |~必要バイト数|4|4|2| |
| |~例|0x00230000|0x0000C000|0xA4FD| | | |~例|0x00230000|0x0000C000|0xA4FD| |
| | | |
| ''しかし!!'' | | ''しかし!!'' |
| | | |
- | これを設定するためのLGDTというレジスタが、なんと48ビットしかないんですよ!!!。 | + | GDTという表が、一行をこのとおり80ビット(10バイト)とってくれていれば単なる当て込みでいいのですが、なぜかi386では一行は64ビット(8バイト)ということになっています。 |
- | ~(これはインテルがそう設計したんだからどうしようもありません) | + | |
| | | |
- | そのため、上記の表の「例」の部分みたいに素直に設定できないんです。そのために、値を押し込む(一種の圧縮?)必要があるのです。 | + | なので、全部で10バイト必要なパラメータを8バイトに押し込む(一種の圧縮?)必要があるのです。ここはそのための処理なのです。 |
| | | |
- | LGDTの48ビット。48ビットってことは、2進数だと0x000000000000000000000000000000000000000000000000とこうなります。(48桁ですね) | + | 解りやすくこんなことを考えてみます。 |
| + | セグメント0を、 |
| + | ・大きさ0x11223344(278MB) |
| + | ・開始番地0x55667788 |
| + | ・属性を0xAABB |
| + | として定義したいとします。 |
| | | |
- | この48桁に上記のデータを分解・圧縮して収めるわけです。こういうふうになります。 | + | セグメントの開始番地は本でとりあえず0x00270000と決めています。これをそのまま使いましょう。 |
| | | |
- | #ref(ldtr1.jpg) | + | ようするにGDTは表です。なので、こんな表を妄想できますね? |
| + | |アドレス|セグメントNo|大きさ|開始番地|属性|h |
| + | |0x00270000|0|0x11223344|0x55667788|0xAABB| |
| + | |CENTER:??|1|:||| |
| + | ||2|:||| |
| + | |||:||| |
| + | ||8191|||| |
| + | |
| + | さて、この表の「??」の部分の番地。いくつになると思いますか? |
| + | |
| + | 0x00270000から始まって4バイト+4バイト+2バイト分進むのですから、0x00270010になるはずです。「??」のアドレスは0x00270010。つまりここから次のセグメント(セグメント1)の設定が始まると。ばんざーい!! |
| + | |
| + | ''ダメなのです!!!'' |
| + | |
| + | 正解は0x00270008。そう。正しくは、GDT表の一行は8バイトなのです!(インテルが決めた) |
| + | |
| + | -COLOR(blue){「まてこのやろう!10バイト必要なのは明白だろう!なんで一行が8バイトなんだよ!2バイト分足りないじゃないかよ!!!」} |
| + | -COLOR(red){はいそのとおりです! インテルに変わってお詫びします!。でももう決まっているので、なんとかヤリクリしてください。あしからず!} |
| + | -COLOR(blue){・・・いっちゃった・・・} |
| + | |
| + | ''そういうわけで''P124のコード。押し込み(圧縮?)作業が必要になると! |
| + | |
| + | さて、実際に押し込む作業を追ってみましょう。 |
| + | |
| + | 一行はとにかく8バイトなので、本では SEGMENT_DESCRIPTOR という構造体を定義しています。なるほど。構造体の中身をバイト単位で表してみると・・・ |
| + | |
| + | |0000|0000|00|00|00|00| |
| + | |limit_low|base_low|base_mid|access_right|limit_heigh|base_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を使用します。) |
| + | |
| + | *** sd->base_low = base & 0xffff; [#jcb197e5] |
| + | 0x55667788 & 0xffffですね。 |
| + | |
| + | これはアセンブラでよく使う、「[[特定のビットをゼロにしたい!:http://thebbl.hideyosi.com/modules/bwiki/index.php?NASK%2F%C4%EA%C8%D6%A4%CE%BC%EA%CB%A1#ccf582da]]」という処理です。 |
| + | |
| + | ようするにここは、「下2バイトを切り出す」のではなく、 |
| + | 逆に「頭2バイトを全部0にしちゃう」という処理です。 |
| + | 2進数にするとかえってわかりやすいかも。 |
| + | |
| + | 01010101011001100111011110001000 ←0x55667788の2進数 |
| + | AND 00000000000000001111111111111111 ←0x0000ffffの2進数 |
| + | ──────────────────── |
| + | 00000000000000000111011110001000 ←0x00007788になる |
| + | |
| + | なるほど! これでbase_low(2バイト)に代入できます! |
| + | |
| + | |
| + | |
| + | *** sd->base_mid = (base >> 16) & 0xff; [#wae51ffd] |
| + | ( 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 が取り出せました。 |
| + | |
| + | |
| + | |
| + | *** sd->base_heigh = (base >> 24) & 0xff; [#j65f29e4] |
| + | 上の処理とほぼ同じですね。 |
| + | |
| + | 01010101011001100111011110001000 ←0x55667788の2進数 |
| + | ↓ |
| + | 24個分右にシフト |
| + | ↓ |
| + | 00000000000000000000000001010101 ←0x00000055の2進数 |
| + | |
| + | なお、この時点ですでに頭2バイトだけの状態になっていますが、あえて0xffでANDしていますね。おそらくこれは、「念のため」の処理でしょう。 |
| + | |
| + | さて、これで、「開始番地」を三つに分割することができましたね。本のコードでは処理してすぐに構造体に当て込んでいます。今は構造体はどんな状態でしょうか? |
| + | |
| + | |0000|7788|66|00|00|55| |
| + | |limit_low|base_low|base_mid|access_right|limit_heigh|base_heigh| |
| + | |
| + | こうなっています。 |
| + | |
| + | |
| + | **大きさ(limit) [#od1732a8] |
| + | さて、セグメントの大きさです。これは本に書いてある通り、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に関する演算です。最初から見てみましょう。 |
| + | |
| + | *** if(limit > ・・・・ [#cba68586] |
| + | これはなにをやっているかというと、P125に書いてある通りです。 |
| + | |
| + | 大きさがもし1MB以上ならG_bitを1にしてx4kbモード?にします。当然limitの値もあらかじめ4kbで割り算しておきます。 |
| + | |
| + | ・・・しかし、G_bitを1にするのはわかりますが、なんで ar |= 0x80000 なんて処理をしているのでしょう? arはそもそも属性の値じゃないですか・・・ |
| + | |
| + | この表をもう一度見てください。 |
| + | |
| + | |
| + | |0000|7788|66|00|00|55| |
| + | |limit_low|base_low|base_mid|access_right|limit_heigh|base_heigh| |
| + | |
| + | これは構造体の定義にそって変数を宣言しています。C言語では、設定(というか、指定?)できる変数はchar(8ビット)、short(16ビット)、int(32ビット)等等がありますが、残念ながら20ビットなんて単位の変数はありません。やむを得ず16ビットや8ビットでごまかしていますが、本当は上記の表の区切りはまちがっているのです。本当はこうなります。(色が付いている部分はそこだけ2進数です。ご注意) |
| + | |
| + | | | | |>|↓区切る所が厳密には正しくない|| |
| + | |0000|7788|66|~00000000|~00000000|55| |
| + | |limit_low|base_low|base_mid|access_right|limit_heigh|base_heigh| |
| + | |
| + | |
| + | |
| + | ~~ |
| + | |
| + | | | | |>|↓正しい区切り位置|| |
| + | |0000|7788|66|~000000000000|~0000|55| |
| + | |limit_low|base_low|base_mid|access_right|limit_heigh|base_heigh| |