2012-02-04 6 views
10

現在、私は低レベルのプログラミングスキルを磨くためにx86 Assemberで遊んでいます。現在、私は32ビット保護モードのアドレス指定方式に少し問題があります。アセンブラがGDTで保護モードでジャンプする

状況は以下の通りです:

私が保護モードにCPUを切り替えて、コードに応じてラベルにジャンプ0x7e0でロードされたプログラムを持っている:

[...] 
code to switch CPU in Protected Mode 
[...] 

jmp ProtectedMode 


[...] 

bits 32 

ProtectedMode: 
    .halt: 
     hlt 
     jmp .halt 

これには絶対に正常に動作します遠い"jmp ProtectedMode"は、このプログラムにオフセット0(最初はorg 0)がロードされているため、プリフェッチキューをクリアするために明示的な遠方ジャンプを行わずに動作し、コードセグメントが正しい位置を指し示すようにします。

現在の問題は、 "ProtectedMode"ラベル内で、0x8000で読み込まれた他のプログラムにジャンプしたいということです(これをメモリダンプでチェックしました。ローディング機能が正しく動作し、プログラムがロードされました0x8000に正しく)。

CPUは現在保護モードであり、もはやRealModeではないため、アドレス指定スキーマは異なります。 ProtectedModeは、ディスクリプタセレクタを使用してディスクリプタテーブルのベースアドレスと制限を検索し、指定されたオフセットを追加し、物理アドレスを取得します(これはわかっています)。したがって、ProtectedModeに入る前にGDTをインストールする必要がありました。私はこれまでであることを理解していない何

lgdt [gdtr] 

は、どのように私は今、物理的にジャンプしない経由で登録

%ifndef __GDT_INC_INCLUDED__ 
%define __GDT_INC_INCLUDED__ 

;********************************* 
;* Global Descriptor Table (GDT) * 
;********************************* 
NULL_DESC: 
    dd 0   ; null descriptor 
    dd 0 

CODE_DESC: 
    dw 0xFFFF  ; limit low 
    dw 0   ; base low 
    db 0   ; base middle 
    db 10011010b ; access 
    db 11001111b ; granularity 
    db 0   ; base high 

DATA_DESC: 
    dw 0xFFFF  ; data descriptor 
    dw 0   ; limit low 
    db 0   ; base low 
    db 10010010b ; access 
    db 11001111b ; granularity 
    db 0   ; base high 

gdtr: 
    Limit dw 24   ; length of GDT 
    Base dd NULL_DESC ; base of GDT 

%endif ;__GDT_INC_INCLUDED__ 

とGDTにロードされます。

鉱山は、次のように探していますProtectedModeのアドレス0x8000はGDTを使用していますか?

最初の考えは、0x7e00(現在のプログラムがロードされていた)を指すコード記述子(CODE_DESC)を選択し、0x8000(512バイト)に到達するのに必要なオフセットを使用して、ジャンプ命令:

jmp CODE_DESC:0x200 

これは機能しません。

jmp 0x7e0:0x200 

...どちらか動作しません

あなたは私がここで行方不明です何任意のアイデアを持っていますか?たぶん、私は32ビットProtectedModeアドレッシングスキームとGDTの使用法に不可欠な何かを理解していないかもしれません。

[EDIT]コンプリートコード:

bits 16 
org 0      ; loaded with offset 0000 (phys addr: 0x7e00) 

jmp Start 

Start: 
    xor ax, ax 
    mov ax, cs 
    mov ds, ax    ; update data segment 

    cli      ; clear interrupts 

    lgdt [gdtr]    ; load GDT from GDTR (see gdt_32.inc) 

    call OpenA20Gate  ; open the A20 gate 

    call EnablePMode  ; jumps to ProtectedMode 

;****************** 
;* Opens A20 Gate * 
;****************** 
OpenA20Gate: 
    in al, 0x93   ; switch A20 gate via fast A20 port 92 

    or al, 2   ; set A20 Gate bit 1 
    and al, ~1   ; clear INIT_NOW bit 
    out 0x92, al 

    ret 

;************************** 
;* Enables Protected Mode * 
;************************** 
EnablePMode: 
    mov eax, cr0 
    or eax, 1 
    mov cr0, eax 

    jmp ProtectedMode ; this works (jumps to label and halts) 
    ;jmp (CODE_DESC-NULL_DESC):ProtectedMode ; => does not work 
    ;jmp 08h:ProtectedMode , => does not work 

;*************** 
;* data fields * 
;* &includes * 
;*************** 
%include "gdt_32.inc" 

;****************** 
;* Protected Mode * 
;****************** 
bits 32 

ProtectedMode: 
    ;here I want to jump to physical addr 0x8000 (elf64 asm program) 

    .halt: 
     hlt 
     jmp .halt 

答えて

11

コード内の複数の問題があります。

最初にGDTR.Baseには、コードが0から始まるようにコンパイルされているため(org 0のため)、オフセットの先頭にGDTが含まれています。ベースアドレスは、相対アドレスではなく物理アドレスでなければなりません。 IOW、これを保持する場合org 0CS * 16(= 0x7e00)をBaseに追加する必要があります。

ので、同じorg 0の第二に、あなたのコード内の32ビットオフセット(bits 32ProtectedMode:後)は、それらが対応する物理アドレスに等しくない、彼らは物理アドレスより0x7e00少ないです。 OTOHの場合、GDTで定義されたセグメントは物理アドレス0から始まります(GDTエントリのベース部分は0なので0x7e00ではないためです)。これは、コード/データでこれらのセグメントを使用しようとすると、アドレスが0x7e00で失われることを意味します。 org 0のままにしたい場合は、GDTのベースアドレスを0x7e00に設定する必要があります。

org 0org 0x7e00に変更して、GDTのベースを0にすることができます.GDTR.Baseを0x7e00で調整する必要はありません。

この動作するはずです:セグメント制限がセグメントサイズから1を引い

いくつかのより多くのポイントに等しいことを

bits 16 
org 0x7e00     ; loaded at phys addr 0x7e00 
          ; control must be transferred with jmp 0:0x7e00 

    xor ax, ax 
    mov ds, ax    ; update data segment 

    cli      ; clear interrupts 

    lgdt [gdtr]    ; load GDT from GDTR (see gdt_32.inc) 

    call OpenA20Gate  ; open the A20 gate 

    call EnablePMode  ; jumps to ProtectedMode 

;****************** 
;* Opens A20 Gate * 
;****************** 
OpenA20Gate: 
    in al, 0x93   ; switch A20 gate via fast A20 port 92 

    or al, 2   ; set A20 Gate bit 1 
    and al, ~1   ; clear INIT_NOW bit 
    out 0x92, al 

    ret 

;************************** 
;* Enables Protected Mode * 
;************************** 
EnablePMode: 
    mov eax, cr0 
    or eax, 1 
    mov cr0, eax 

    jmp (CODE_DESC - NULL_DESC) : ProtectedMode 

;*************** 
;* data fields * 
;* &includes * 
;*************** 
;%include "gdt_32.inc" 
;********************************* 
;* Global Descriptor Table (GDT) * 
;********************************* 
NULL_DESC: 
    dd 0   ; null descriptor 
    dd 0 

CODE_DESC: 
    dw 0xFFFF  ; limit low 
    dw 0   ; base low 
    db 0   ; base middle 
    db 10011010b ; access 
    db 11001111b ; granularity 
    db 0   ; base high 

DATA_DESC: 
    dw 0xFFFF  ; limit low 
    dw 0   ; base low 
    db 0   ; base middle 
    db 10010010b ; access 
    db 11001111b ; granularity 
    db 0   ; base high 

gdtr: 
    Limit dw gdtr - NULL_DESC - 1 ; length of GDT 
    Base dd NULL_DESC ; base of GDT 

;****************** 
;* Protected Mode * 
;****************** 
bits 32 

ProtectedMode: 
    mov  ax, DATA_DESC - NULL_DESC 
    mov  ds, ax ; update data segment 

    .halt: 
     hlt 
     jmp .halt 

を注...、また、有効なセレクタまたは0で全てのセグメントレジスタをロードスタックを設定します。そこにゴミがある場合(またはリアルモードの古い値)、割り込み/例外で再生を開始すると、より多くのクラッシュが発生します。

最後に、私はelf64が何であるかはわかりませんが、他のモジュールの場合はorgの世話をして、生成されたすべてのアドレスがロードアドレスに対応していることを確認する必要があります。また、64ビットモードを有効にする予定がある場合は、やるべきことがたくさんあります。私は、あなたが比較的シンプルなものを乗り越えているので、まだ64ビットモードに突入しないことをお勧めします。

+0

説明をお寄せいただきありがとうございます...これは私がこれをやっている理由です。そして、x86のリファレンスを読むだけで最初からこれをやったほうが簡単です! Btw:これらのトピックを正確に扱うアドバイスができる素晴らしい本はありますか? –

+1

私は良い本を知らない。インテルとAMDの公式ドキュメントにはすべての情報があります。あなたは簡単に読んですぐに理解できる典型的な本や教科書ではありません(btw、インテルのドキュメントには多くの誤植や間違いがあります)。オンラインで多くの記事やチュートリアルがあります。そして、あなたはいつも実験することができます。または、誰かのコードを見て質問してください。 [alt.os.development](http://groups.google.com/group/alt.os.development/topics)、[comp.lang.asm.x86](http://groups.google .com/group/comp.lang.asm.x86/topics)。 –

+0

あなたのアドバイスありがとうございます!私はそれを見てみましょう! –

3

カップルの事。まず、現在のコードは技術的に保護モードに入っていません。保護モードに入るには、csにGDTの記述子をロードします。 csレジスタを直接設定することはできませんので、これを実行する最も簡単な方法は遠くのジャンプを使用することです。あなたの現在のジャンプを置き換えます

jmp (CODE_DESC-NULL_DESC):ProtectedMode 

第二に、あなたのコード・セグメントのベースは0ではない0x7e00です。単語 "base"でラベル付けされた4バイトを見ると、それらはすべて0です。あなたは2つのオプションがあります。 GDTのベースを0x7e00に変更するか、ディレクティブを追加して、0のベースのすべての保護モードコードのロードアドレスを変更します。

これらの両方を実行したら、通常のジャンプ命令。あなたはそれがあるとして、あなたのGDTを残すことを選択した場合は、完全なアドレスを使用します:

jmp 0x8000 

を、あなたのコードセグメントのベースを変更することを選択した場合、あなたはそれに相対アドレスを使用する必要があります。

jmp 0x200 

More information about the GDT
More information about entering protected mode

+0

あなたの答えをありがとう。OK - ジャンプ命令 "jmp CODE_DESC:ProtectedMode"を使用すると、CPUのトリプルフォールトとリセットが発生します(このジャンプ方向はどこかにジャンプするようです)。 "jmp ProtectedMode"は正しいラベルにジャンプし、システムを停止します。これはGDTベースの問題に関連している可能性があるので、私はGDTを変更して再試行します。あなたの速い応答に感謝します! –

+0

@ughoavgfhwあなたは 'jmp(CODE_DESC-NULL_DESC):ProtectedMode'を意味しますか? – Nayuki

+0

@NayukiMinaseこれをキャッチしてくれてありがとう、私は彼らがすでにオフセットだったと思った。 – ughoavgfhw

関連する問題