web-dev-qa-db-ja.com

最初のBIOS命令が0xFFFFFFF0(RAMの「最上部」)にあるのはなぜですか?

私はBIOSが0xFFFFFFF0から最初の命令をロードすることを知っています、しかしなぜこの特定のアドレス?私はたくさんの質問をしていますが、少なくともそれらのうちのいくつかで私を助けることができると思います。

私の質問:

  • 最初のBIOS命令が4 GB RAMの「一番上」にあるのはなぜですか?
  • コンピュータに1 GBのRAMしかないとどうなりますか。
  • 4 GBを超えるRAMがあるシステム(たとえば、8 GB、16 GBなど)はどうですか?
  • スタックがなんらかの値(この場合、0xFFFFFFF0にある値)で初期化されるのはなぜですか?

今日の午後、そのことについて読みましたが、まだわかりません。

50

0xFFFFFFF0は、x86互換CPUが電源投入時に命令の実行を開始する場所です。これはCPUのハードウェアで変更不可能な側面(追加のハードウェアなし)であり、CPUの種類によって動作が異なります。

最初のBIOS命令が4 GB RAMの「一番上」にあるのはなぜですか?

これは、4 GBアドレス空間の「最上部」にあり、BIOSの電源投入時または UEFI ROMはそれらのアドレスの読み込みに応答するように設定されています。

これがなぜなのかについての私の理論:

プログラミングのほぼすべてが、連続したアドレスでうまく機能します。 CPU設計者はシステムビルダーがCPUに対して何をしたいのかわからないので、さまざまな目的のためにスペースの真ん中にあるアドレスを必要とすることはCPUにとっては悪い考えです。アドレス空間の最上部または最下部に「邪魔にならないように」それを保持することをお勧めします。もちろん、この決定は MMU を持っていなかった8086が新しいときになされたことを覚えておいてください。

8086では、割り込みベクタはメモリ位置0以上に存在していました。割り込みベクタは既知のアドレスにある必要があり、柔軟性のためにRAM内にあることが望まれていましたが、CPU設計者がRAMがどのくらいになるかを知ることはできませんでしたシステム。そのため、0から始めて作業することは意味がありません(8086が発明された1978年のシステムでは4GバイトのRAMがないため、RAMを0xFFFFFFF0にすることは期待できませんでした。それから、ROMを上限にする必要があります。

もちろん、少なくとも80286以降では、割り込みベクトルを0以外の別の開始位置に移動することもできますが、最新の64ビットx86 CPUは依然として8086モードで起動するため、互換性のためにすべては依然として古い方法で動作します。それはあなたのx86 CPUがまだDOSを実行することができることを必要とすることが2015年に聞こえるように)。

そのため、割り込みベクタは0から始まり上方向に動作するので、ROMは先頭から始まり下方向に動作する必要があります。

コンピュータのRAMが1 GBしかないとどうなりますか。

32ビットCPUには、0(0x00000000)から4294967295(0xFFFFFFFF)までの番号が付けられた4,294,967,296個のアドレスがあります。 ROMはいくつかのアドレスに存在でき、RAMは他のアドレスに存在できます。 CPUのMMUを使えば、これをオンザフライで切り替えることもできます。 RAMはすべてのアドレスに存在する必要はありません。

RAMが1 GBしかないため、一部のアドレスでは、読み書きを行っても何も応答しません。このようなアドレスにアクセスしたときやシステムがロックアップしたときに、無効なデータが読み取られる可能性があります。

4 GBを超えるRAMがあるシステム(例:8 GB、16 GBなど)はどうですか?

それをいくらか単純にしておく:64ビットCPUはより多くのアドレスを持っています(それはそれらを64ビットにするものの1つです - 例えば0x0000000000000000から0xFFFFFFFFFFFFFFFFまで)ので、余分なRAMは "収まります"。 CPUがロングモードであると仮定します。それまではRAMがあるだけで、アドレス指定はできません。

スタックがなんらかの値(この場合は0xFFFFFFF0にある値)で初期化されるのはなぜですか?

X86が電源投入時にスタックポインタを割り当てるものについてすぐに何かを見つけることはできませんが、そのルーチンがシステム内のRAMがいくらであるかを見つけたら、初期化ルーチンによって再割り当てされる必要があります。 。 (以下のコメントの@Eric Towersは、電源投入時にゼロに設定されていると報告しています。)

55
LawrenceC

RAMの一番上にはありません。これはROMにあり、そのアドレスは、イーサネットコントローラのような拡張カード上のメモリと共にメモリアドレス空間の最上位にあります。少なくとも4 GBがインストールされるまで、RAMと競合しないようにするためです。 4 GB以上のRAMを持つシステムは、競合を解決するために2つのことを実行できます。安いマザーボードはRAMがある場所と衝突するROMの部分を単に無視します。まともな人は、RAMが4 GBを超えるアドレスを持つように見えるように再マップします。

私はあなたがスタックについて何を求めているのかわかりません。 ROMになるように初期化されていません。 CPUがリセットされると、最初は「リアルモード」になっており、元の8086と同じように動作し、16ビットのセグメント化されたアドレス指定を使用して、1 MBのメモリにのみアクセスできます。 BIOSコードはその1 MBの先頭にあります。 BIOSはRAMのどこかを選択してスタックをセットアップし、最初の起動可能ドライブの最初のセクターをロードして実行します。それが引き継ぎ、それ自身のスタックをセットアップすると(タスク/スレッドごとに1つ)、32または64ビットモードに切り替えるのはOS次第です。

25
psusi

まず、これはRAMとは無関係です。ここでは、アドレス空間について話しています。メモリが16 MiBしかない場合でも、32ビットCPUには32ビットのアドレス空間が残っています。

これはすでにあなたの最初の質問にすでに答えています - これが設計された時点では、実世界のPCのメモリ容量は4 GiBにはほとんどありませんでした。それらは、メモリの1-16 MiBの範囲内でした。アドレス空間は、あらゆる目的と目的のために、自由でした。

では、なぜ正確に0xFFFFFFF0なのでしょうか。 CPUはどれだけのBIOSがあるのか​​知りません。 BIOSによっては数キロバイトしか必要としないものもあれば、フルメガバイトのメモリを必要とするものもあります - そして私はさまざまなオプションのRAMにさえ入りません。開始するには、CPUを特定のアドレスに接続する必要があります。CPUを設定する必要はありません。しかし、これはアドレス空間のマッピングにすぎません - アドレスはBIOSのROMチップに直接マッピングされています(そう、これはGiBの4 RAM全部にアクセスできないという意味です)。 ]現時点であなたがそのようなものをたくさん持っているのであれば - しかしそれは特別なことではありません、多くのデバイスはアドレス空間内に独自の範囲を必要とします)。 32ビットCPUでは、このアドレスは非常に基本的な初期化を行うのに十分な16バイトを与えます - セグメントを設定するのに十分で、必要ならアドレスモード(x86は16ビットリアルモードで起動します)平らではありません)そしてreal boot "procedure"へジャンプします。現時点では、RAMはまったく使用していません。すべてマッピングされたROMです。実際のところ、RAMはこの時点で使用する準備ができていません - それがBIOS POSTの仕事の1つです!さて、あなたは考えているかもしれません - 16ビットリアルモードはどのようにアドレス0xFFFFFFF0にアクセスしますか?確かに、セグメントがあるので、20ビットのアドレス空間がありますが、それでもまだ十分ではありません。トリックがあります - 最初のロングジャンプを実行するまでアドレスの上位12ビットが設定され、ハイアドレス空間へのアクセスを許可します(ロングジャンプを実行するまでは0xFFF00000より低いものへのアクセスを拒否します)。 。

これらすべてが現代のオペレーティングシステム上でプログラマからほとんど隠されているものです(ユーザは言うまでもありません)。あなたはたいていそれほど低レベルのものにアクセスすることはできません - あるものはすでに救済を超えています(あなたはCPUモードを何とか切り替えることができません)、あるものはOSカーネルによって排他的に扱われます。

そのため、MS DOS上の旧式のコーディングからより良い見方が得られます。アドレス空間に直接マッピングされているデバイスメモリの別の典型的な例は、ビデオメモリへの直接アクセスです。例えば、あなたが速くディスプレイにテキストを書きたいならば、あなたはアドレスB800:0000に直接書きました(プラスオフセット - 80x25テキストモードで、これは私の記憶が私に正しく役立つなら(y * 80 + x) * 2 - 行ごとに2バイト)。ピクセル単位で描画したい場合は、グラフィックモードとA000:0000の開始アドレス(通常、ピクセルあたり8ビットで320 x 200)を使用しました。高性能なものを実行するということは、通常、デバイスのマニュアルに飛び込んで、それらに直接アクセスする方法を見つけ出すことを意味します。

これは今日まで生き残っています - それはただ隠されています。 Windowsでは、デバイスマネージャでデバイスにマッピングされているメモリアドレスを確認できます - ネットワークカードのようなもののプロパティを開いて、Resourcesタブに移動します - すべてのMemory Range項目は、デバイスメモリからメインアドレススペースへのマッピングです。 32ビットでは、これらのデバイスのほとんどが2 GiB(後の3 GiB)マークより上にマッピングされていることがわかります - これも実際には問題ではありませんが、ユーザー使用可能メモリとの競合を最小限に抑えるためです。仮想メモリ(アプリケーションはreal、hardwareアドレス空間の近くにはありません - 独自の仮想化されたメモリのチャンクを持っています。これはRAM、ROM、デバイス、ページファイルなどにマッピングされるかもしれません。 ).

スタックに関しても、デフォルトではスタックが上から伸びていくことを理解するのに役立ちます。 Pushを実行した場合、新しいスタックポインタは0xFFFFFECになります - 言い換えれば、BIOS initアドレスに書き込もうとしているわけではありません:)もちろん、BIOS initルーチンは安全にスタックを使用できます。どこかに再マッピングするともっと便利です。昔のプログラミングでは、ページングが事実上のデフォルトになる前は、スタックは通常RAMの最後から始まり、アプリケーションメモリを上書きし始めたときに「スタックオーバーフロー」が発生しました。メモリ保護はこれを大きく変えました、しかし一般に、それは可能な限り後方互換性を維持します - 最も最近のx86-64 CPUさえまだMS DOS 5を起動することができる - ページングについて知らない多くのDOSアプリケーション。

13
Luaan

言及された他の点に加えて、アドレスであるものを理解することは役に立つかもしれません。より新しいアーキテクチャでは事が複雑になりますが、歴史的にはマシンは20から32ワイヤで所望のアドレスを出力します(アーキテクチャによりますが、同時に2バイトか4バイトにする必要があるかどうかに注意する特別なトリックがあります)。メモリシステムのさまざまな部分がそれらのワイヤの状態を調べ、それらが高い値と低い値の特定の組み合わせを見たときに自分自身をアクティブにします。

32本のアドレス線を持つマシンで、1MBのRAMと64KBのROMを使用するだけでよい場合[一部の組み込みコントローラにとってはもっともらしい]、先頭アドレスがすべてのアドレスに対してRAMをアクティブにします。ワイヤがローで、ハイであるすべてのアドレスのROM。次に、下位20アドレスワイヤをRAMに接続して1,048,576バイトの1つを選択し、下位16アドレスワイヤもROMに配線して65,536バイトの1つを選択します。残りの11本のアドレス線は単純に何にも接続されていません。

そのようなマシンでは、アドレス0x00100000-0x001FFFFFへのアクセスは、RAMアドレス0x00000000-0x000FFFFFへのアクセスと同等です。アドレス0x000200000-0x0002FFFFF、または0x7FF00000-0x7FFFFFFFFも同様です。 0x80000000を超えるアドレスはすべてROMを読み取り、スペース全体に64Kのパターンが繰り返されます。

プロセッサが4,294,967,296バイトのアドレス空間を持っていても、ハードウェアにその多くの異なるアドレスを認識させる必要はありません。リセットベクタをアドレス空間の最上部近くに配置することは、システムのRAMとROMの量にかかわらず、うまく機能し、アドレス空間を完全にデコードする必要性を回避する設計です。

7
supercat

私の理論は、負論理を使用しているためです。デジタルの1はまったくテンションがない(0ボルト)初期化時に最後の4ビットにテンションをかけるだけでよいので、プログラムカウンタ(または命令ポインタ)は1111 1111でラテートします1111 1111 1111 1111 11110000。ほとんどの(古い)CPUは16ビットで、下位ニブルは1つのアドレスチップでアドレス指定できるため、上位28ビットをアドレス指定する必要はありません。 32ビットと32ビット、16ビットと互換性のある64ビットがあるので、ハードウェアの評価は改善されましたが、方法は変わりません。また、バイアスは常に64ビットまたは32ビットにプログラムされているわけではありません。私の意見はまた、思い出はいつも同じではないので、BIOSは同じ最初のセグメントに配置する必要があります。 BIOSが対処されているのを見る方法は、実際のアドレスではありません。私が教えてくれたのは...

3
Agguro

リセット時に、8088/8086互換のCPUは0FFFF0で命令を実行します。これは1メガバイトの制限より16バイト下です。通常、この場所のROMは(PCの実装では)BIOSになるため、BIOS ROMの最後にはBIOS ROMの先頭にジャンプします。

ここに示されている:開始ベクトルとその背後にある 'date'シグニチャー、IBM 5150 PC 8KB eprom dump bios date:10/19/1981

00001FEE  FF                db 0xff
00001FEF  FF                db 0xff
00001FF0  EA5BE000F0        jmp Word 0xf000:0xe05b
00001FF5  3130              xor [bx+si],si
00001FF7  2F                das
00001FF8  3139              xor [bx+di],di

アドレス指定は8KB $ 2000 ROMで、開始アドレス(この場合は8KB ROM自体内の他の場所への絶対的な遠いJMP)を$ FFFFに配置します。 $ 0セグメントまたは$ FFFF0線形。

互換性に関して:ある「未来の」または現在のプロセッサがアドレスの前にもっとたくさんのFがあると「期待する」なら、それは問題ではありません。古いシステムでの新しいcpusとの互換性のために、追加のアドレスラインは未接続のままであるため、データバス上のデータはまったく同じです。最下位ビットがFFFF0のままである限り。

(たった1MBのRAMとそのRAMの最後にROMが配置されているシステムでは、他の方法では、上位アドレスと通信していてもまったく同じデータが得られることを喜んで「考える」でしょう。 A19より上の住所行)

世界は単なる「PC」ではないことに注意してください。IBMPCは「偶然」でした。これらのプロセッサは「PC」用に特別に設計されたものではなく、単なるPCよりも多くのものに入ります武器システムなど) 32および64ビットプロテクトモードは通常望ましくありません。 (仮想8086モードは、例えば新しい(386+)バージョンを選ぶ理由としてもっとずっと面白いです)。そのため、「下位互換性」には、「dosを実行する」だけではありません。

マザーボードはリセットベクタの命令がBIOSエントリポイントにマッピングされたメモリ位置へのジャンプであることを確認します。このジャンプは、電源投入時に存在する隠しベースアドレスを暗黙的にクリアします。チップセットが保持しているメモリマップのおかげで、これらすべてのメモリ位置はCPUに必要な正しい内容を持っています。この時点でRAMモジュールにはランダムにがらくたがあるので、それらはすべてBIOSを含むフラッシュメモリにマッピングされています。

1
viktorkh