5

Intelx86でプロテクトモードに切り替えようとしています。

gdtにlgdtをロードし、cr0のPフラグを1に設定し、すべてのセグメントセレクターを設定しましたが、関数呼び出しから戻ると、他の関数を呼び出すことができないか、このエラーが発生します

qemu: fatal: Trying to execute code outside RAM or ROM at 0xfeeb7c5b

これが私のswitch_to_pmode関数です:

gdtr:
.short      23  // limit
gdtr_base:
.long       0   // base

switch_to_pmode:
    movl $null_segment, %eax        // Address of the first byte of the GDT
    movl %eax, gdtr_base

    cli             // disable interrupts

    lgdt (gdtr)

    movl %cr0, %eax
    or $0x1, %eax
    movl %eax, %cr0         // Set the PE flag

    push $0x8
    push $reload_segments
    lret

reload_segments:
    movl $0x10, %eax
    movl %eax, %ds
    movl %eax, %ss
    movl %eax, %es
    movl %eax, %fs
    movl %eax, %gs

    ret

foo:
    ret

そして私の電話

_start:
    call switch_to_pmode
    call foo // <----- Ouch!

ありがとうございました

4

2 に答える 2

3

.code32アセンブラがプロテクトモードスイッチに続くコードを(またはuse32nasmの)ディレクティブを使用して32ビットコードとして変換することを確認する必要があります。

さらに、プロテクトモードルーチン後の差出人住所は無効になります。その後は本当に何にも戻れません。代わりに、espを何か便利なものに設定して続行してください。

于 2012-02-06T17:49:19.527 に答える
3

PEを設定またはクリアするCR0に移動した直後%espに、PCをリロードするためにファージャンプを実行する必要があります。次に、すべてのセグメントレジスタと同様にリロードする必要があります。スタックにアクセスしたり、割り込みを有効にしたりする前に、これらすべてを実行する必要があります。また、(drhirschが言うように)リターンアドレスはリアルモードアドレスであるため、リアルモードスタックを無効にする前にリターンアドレスをポップオフしても、この操作から戻ることはできません。

PCをリロードし、同時に割り込みを再度有効にしようとしているようですがlret、スタックポインタが無効であるため、機能しません。正しいコードは次のようになります。

switch_to_pmode:
    # ... what you have ...

    movl %eax, %cr0
.code32
    ljmpl reload_segments

reload_segments:
    # ... what you have ...
    movl $pm_stack, %esp
    sti # perhaps

    # and then just go on with your startup code here
    call foo

インテルのシステムプログラミングガイド、特に第9章(マシンの初期化)、特にプロテクトモード切り替えの方法を詳細に説明しているセクション9.9をお読みください。

于 2012-02-06T18:19:51.037 に答える