1

私は楽しみと教育のためのおもちゃのカーネルに取り組んでいます (クラスのプロジェクトではありません)。INT 0x15, EAX=E820メモリ マネージャの作業を開始しているので、リアル モードのまま呼び出しを使用して BIOS からメモリ マップを取得しようとしています。私はosdev wikiから自分の機能を適応させています(ここでは、「E820メモリマップの取得」セクションにあります)。ただし、これを C コードから呼び出すことができる関数にしたいので、少し変更しようとしています。マップ エントリを格納する場所へのポインタと、テーブル内のエントリの数だけインクリメントされる整数へのポインタです。

ウィキによると、ES:DIはデータを保存する場所を指す必要があるため、最初の引数を 2 つ (セグメント セレクターpointer_to_map / 16とオフセットpointer_to_map % 16) に分割しました。C コードの一部を次に示します。

typedef struct SMAP_entry {
    unsigned int baseL; // Base address, a QWORD
    unsigned int baseH;
    unsigned int lengthL; // Length, a QWORD
    unsigned int lengthH;
    unsigned int type; // entry type
    unsigned int ACPI; // extra data from ACPI 3.0
} SMAP_entry_t;

SMAP_entry_t data[100];
kprint("Pointer: ");
kprint_int((int) data, 16);
kprint_newline();

int res = 0;
read_mem_map(((int) data) / 16, ((int) data) % 16, &res);
kprint("res: ");
kprint_int(res, 16);
kprint_newline();

これが私の ASM コードの一部です。

; performs a INT 0x15, eax=0xE820 call to find the memory map
; inputs: the pointer to the data table / 16, the pointer % 16, a pointer to an dword (int) which will be
;       incremented by the number of entries after this function returns.
; preserves: no registers except esi
read_mem_map:
    mov es, [esp + 4]           ; set es to the value of the first argument
    mov di, [esp + 8]           ; set di to the value of the second argument

プログラムがトリプル フォールトを起こし、そこで VM をシャットダウンするため、ここに貼り付けているのはそれだけです。コマンドを移動するretと、最初の行で関数がクラッシュすることがわかりました。C で呼び出しをコメントアウトすると、すべてが期待どおりに機能します。

直接設定する理由はほとんどないことをGoogleで読んだことがありES:DIます。私が見つけたコードでは、それをリテラルに設定しています。どのように設定ES:DIすればよいですか? また、直接設定しない場合は、C と ASM が正しく対話するようにするにはどうすればよいですか?

4

1 に答える 1

2

各セグメント レジスタ (80x86 上) には、可視部分といくつかの非表示フィールド (セグメント ベース、セグメント制限、およびセグメントの属性 - 読み取り/書き込み、特権レベルなど) があります。

保護モードで。セグメント レジスタをロードすると、CPU は可視部分を GDT または LDT へのインデックスとして使用し、その記述子からセグメントの非表示フィールドを (GDT または LDT で) ロードします。

リアルモードで; CPU はまったく異なることを行います。セグメント ベースを「可視部分 * 16」に設定するだけで、(GDT、LDT) テーブルは使用しません。

データ テーブルへの 32 ビット ポインターと 32 ビット スタック ポインター (例: mov es, [esp + 4])を使用しているという事実を考えると、あなたの C コードは 32 ビット プロテクト モードになっていると思います。これは、リアル モードとは完全に互換性がありません。これは、セグメント ロードの動作がまったく異なるため、また、デフォルトのオペランド/アドレス サイズが 16 ビットではなく 32 ビットであるためです。

すべての BIOS 機能は、リアル モード用に設計されています。プロテクト モードでは使用できません。

基本的; 私はお勧めします:

  • データ テーブルへのポインターをアセンブリに 32 ビット整数/ポインターとして渡します (2 つの別個の 16 ビット整数ではありません)。
  • 「リアル モードに移行」関数を呼び出します (32 ビット スタックから 16 ビット スタックに切り替えることもあり、16 ビットでは「32 ビット リターン命令」が必要になるため、これは少し注意が必要です)。コード)。
  • データ テーブルへのポインターをアセンブリのセグメントとオフセットに分割し、セグメントを読み込みます (リアル モードになっているため、正しく動作するはずです)。
  • BIOS 関数を呼び出します (現在リアル モードになっているため、正しく動作するはずです)。
  • 「プロテクト モードに移行」関数を呼び出します (32 ビット コードの「16 ビット リターン命令」を含め、これもまた少しトリッキーになります)。
  • (32 ビット プロテクト モード) 呼び出し元に戻る

リアル モードからプロテクト モードへの切り替え、およびプロテクト モードからリアル モードへの切り替えの手順は、Intel のシステム プログラマーズ ガイドに含まれています。:)

于 2014-12-21T18:19:56.697 に答える