雑記帳
ここはhideyosiの雑記帳です。テケトーに書き散らしてるだけなので間違っていたりとは普通にしてます。信用度は相当低いことをあらかじめご了承を。またご覧のようにWikiを使ってますが、hideyosi意外は書き込めません。
2: 2007-03-25 (日) 19:50:36 ソース バックアップ No.2 を復元して編集 現: 2024-01-06 (土) 22:39:10 ソース 編集
Line 19: Line 19:
|~範囲|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|
Line 25: Line 26:
''しかし!!'' ''しかし!!''
-これを設定するための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|