12

現在、低レベルのプログラミング スキルを磨くために、x86 アセンバーをいじっています。現在、32 ビット プロテクト モードでのアドレッシング スキームに関する小さな問題に直面しています。

状況は次のとおりです。

CPU を保護モードに切り替え、コード内の対応するラベルにジャンプするプログラムを 0x7e0 にロードしました。

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

jmp ProtectedMode


[...]

bits 32

ProtectedMode:
    .halt:
        hlt
        jmp .halt

これまでのところ、これはまったく問題なく機能します。「jmp ProtectedMode」は、プリフェッチ キューをクリアするための明示的な far jump なしで機能します。このプログラムはオフセット 0 (最初は org 0) でロードされるため、コード セグメントが正しい場所を指すようになります。

私の現在の問題は、「ProtectedMode」ラベル内で、0x8000 にロードされている他のプログラムにジャンプしたいということです (これをメモリ ダンプで確認したところ、ロード機能は正しく機能し、プログラムは 0x8000 に正しくロードされました)。 .

CPU が RealMode ではなく ProtectedMode になったため、アドレス指定スキーマが異なります。ProtectedMode は記述子セレクターを使用して記述子テーブルのベース アドレスと制限を検索し、指定されたオフセットを追加して物理アドレスを取得します (私が理解しているように)。そのため、ProtectedMode に入る前に GDT をインストールする必要がありました。

私は次のようになっています:

%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 レジスタにロードされます。

lgdt [gdtr]

これまで理解できなかったのは、GDT を使用して ProtectedMode の物理アドレス 0x8000 にジャンプするにはどうすればよいかということです。

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

jmp CODE_DESC:0x200

しかし、これはうまくいきません。

jmp 0x7e0:0x200 

どちらも機能しません...

ここで何が欠けているのか分かりますか? 32 ビット ProtectedMode アドレッシング スキームと GDT の使用法に不可欠な要素を理解していなかったのかもしれません。

[編集] 完全なコード:

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
4

2 に答える 2

14

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

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

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

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

これはうまくいくはずです:

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

セグメント制限は、セグメント サイズから 1 を引いた値に等しいことに注意してください。

さらにいくつかのポイント... すべてのセグメント レジスタに有効なセレクタまたは 0 をロードします。また、スタックをセットアップします。そこにガベージ (またはリアル モードからの古い値) がある場合、割り込み/例外をいじり始めると、クラッシュがさらに発生します。

最後に、elf64 が何かはわかりませんが、org他のモジュールのことを処理し、生成されたすべてのアドレスがロード アドレスに対応していることを確認する必要があります。また、64 ビット モードを有効にする場合は、やるべきことが山ほどあります。比較的単純なことにつまずいているので、まだ64ビットモードに急いではいけないことをお勧めします.

于 2012-02-04T10:32:43.207 に答える
3

いくつかのこと。まず、現在のコードは技術的に保護モードに入りません。csGDTから記述子をロードして、プロテクトモードに入ります。レジスタを直接設定することはできないためcs、これを行う最も簡単な方法は、ファージャンプを使用することです。現在のジャンプを次のように置き換えます。

jmp (CODE_DESC-NULL_DESC):ProtectedMode

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

これらの両方を実行したら、通常のジャンプ命令を使用してプログラムにジャンプできます。GDTをそのままにしておく場合は、完全なアドレスを使用します。

jmp 0x8000

コードセグメントのベースを変更する場合は、それに関連するアドレスを使用する必要があります。

jmp 0x200

GDTに関する
詳細情報保護モードへの移行に関する詳細情報

于 2012-02-04T02:51:02.197 に答える