Operating Systems Development Series | |
Bootloaders 2
This series is intended to demonstrate and teach operating system development from the ground up. はじめにようこそ!前回のチュートリアルで、多くのことを学びました。電源ボタンを押したときに何が起こるのか、そしてBIOSがどのように起動するのかを正確に見てきました。また、BIOS 割り込み (INT) 0x19 を見て、ブートサイン (0xAA55) を検索し、見つかったら 0x7C00 にあるブートローダをロードして実行することを説明しました。 また、簡単なブートローダを開発し、ビルドプロセス全体を経験することができました。 このチュートリアルでは、ブートローダを拡張していきます。このチュートリアルでは、ブートローダを拡張します。
注意:ここから先は、ブートローダがシステム全体を完全に制御します。これはつまり、私たちがコードを書くことにすべてがかかっているということです。全ては私たち次第なのです。要するに、これからもっとたくさんのコードを書いていくことになります。 これから先、物事はより複雑になっていくでしょう。このシリーズの構成をしっかりさせるために、これから始まるチュートリアルには、ダウンロード可能なデモを用意するつもりです。これは、コンセプトの理解を助けるものです。それでも、このチュートリアルでは、すべてを詳しく説明していきますので、ご安心ください。 準備はいいですか? プロセッサーモードさてさて...この言葉、どこかで聞いたことがあるような?そうですね...すべてのチュートリアルで!プロセッサーモードについては、これまであまり触れてきませんでした。プロセッサのモードを理解することは、私たちにとって非常に重要なことです。なぜでしょうか? 前の2つのチュートリアルでは、x86ファミリーが16ビット環境で起動する方法と理由について説明しました。私たちは32ビットオペレーティングシステム(OS)を開発したいので、プロセッサを16ビットモードから32ビットモードに切り替える必要があります。 モードは2つ以上あります。それぞれについて説明しましょう。 リアルモードご存知のように、x86プロセッサは16ビット環境で起動します。このモードは何なのでしょうか?そうですね :)リアルモードって何?リアルモードは...
もう少し詳しく見てみましょう。 セグメント:オフセット・メモリ・モード - 歴史もう一度、チュートリアル2を見てみましょう。メモリの概念とオペレーティングシステムの使用は1950年代までさかのぼります。この頃のコンピュータはパーソナルコンピュータではなく、大型のメインフレームコンピュータでした。当時、すべてのコンピュータは非常に大きくかさばるハードウェアデバイスを持っていたことを思い出してください。 時を経て(チュートリアル2を振り返って)、オペレーティングシステムだけでなく、コンピュータも進歩していることがわかります。 コンピュータが普及するにつれて、その需要も増えてきました。コンピュータが8ビットだった頃、多くの人が16ビットを欲しがりました。16ビットの時代が来たとき、マイクロソフトはすでに32ビットを考えていた。32ビットの時代が来ると同時に、64ビットが主流になった。しかし、128ビットが登場した。 一番の問題は、コンピュータ業界の進歩が速すぎることです。 Intelが8086プロセッサを設計していたとき、プロセッサは16ビットのレジスタを使い、64KBまでのメモリにしかアクセスできなかった。しかし、問題は、多くのソフトウェアがこれ以上のメモリを必要とすることだった。 8086は、8088と同じ時期に設計されていた。しかし、8088はインテルの「次世代」プロセッサになる予定だったが、予想以上に時間がかかってしまった。そこで、インテルは8086を開発・発売し、8088が発売されるまでの間、他社に対抗しようと考えた。 しかし、ソフトウエアは64KB以上のメモリを要求しており、インテルは8088が出るまでの間、すでに16ビットプロセッサを作っている競合他社に対抗するために、8086というプロセッサを開発したのである。そこで、インテル社は、ある工夫をした。 そこで、8086の設計者は、ある解決策を提案した。8086は16ビットのまま、最大1MBのメモリにアクセスできるようにするのだ。彼らは同意し、インテルも承認した。 セグメント:オフセット・メモリ方式が誕生したのである。 セグメント:オフセット方式を理解するために、まずセグメントとオフセットを分解して見てみよう。 セグメントセグメント(Segment)とは、簡単に言えば、全体の一部分である。この場合、セグメントとはメモリの一部分のことです。 そう、基本的にはそれだけです。メモリをセクションに分割することを想像してください。このセクションがセグメントです。 x86プロセッサーのファミリーは、セグメントの開始位置を格納する4つのプライマリーレジスタを使用します。これはベースアドレスのようなもので、セグメントの開始位置を提供します。 通常、セグメントは64KBの大きさで、自由に移動することができます。 セグメントとは、単にメモリ上のセクションを表すものであることを忘れないでください。この場合、セグメントのベースアドレスが0であれば、バイト0と64KBの間のセグメントを表します。 レジスタは、CS、DS、ES、 SSです。このレジスタは、セグメントのベースアドレスを格納します。 このモードでのアドレッシングを見た後で、詳しく見ていきましょう。 オフセットオフセットとは、基本番号に追加される数値のことです。たとえば、ベースナンバーが3の場合。
オフセット=基準番号(3)+オフセット番号 さて、これがどう関係するのでしょうか。セグメント:オフセットアドレッシングでは、ベースアドレス(セグメントはベースアドレ スを表すことを覚えておいてください)をオフセットアドレスに加えます。 かなり簡単でしょう?さて、すべてをまとめてみましょう。 セグメント:オフセットアドレッシングしかし、前項でReal Modeのセグメントアドレスは16ビットであることを述べた。つまり、セグメントを16(10進数)倍してから、オフセットを加算する必要があります。 これが現在の計算式です。Absolute (Exact) Memory Address = (Segment Address * 16(decimal)) + Offset セグメント:オフセットの規則セグメントとオフセットのアドレスは、通常、コロン(:)で区切られます。 通常は、セグメント :オフセット例えば、次のようになります。上記を絶対アドレス0x7C00に変換するには、次の式を使用します。07C0:0000 < 07C0 is the segment, and 0 is the offset base address = base address * segment size (16) + offset 07C0:0000 = 07C0 * 16 (decimal) + 0 = 07C00 + 0 = 0x7C00 セグメント:オフセットの問題セグメント:オフセットはかなりユニークです。セグメントとオフセットの値を変更することで、異なるセグメントとオフセットのペアが同じ絶対アドレスを生成することを見つけることができます。なぜでしょうか?なぜなら、両方とも同じメモリロケーションを参照しているからです。例えば、以下のアドレスはすべて、0x7C00 にあるブートローダを参照しています。 これらはほんの一部に過ぎません。技術的には、メモリ上の同じバイトを参照できるセグメントとオフセットの組み合わせは、正確には4096通りあります -- これは、メモリ上の各バイトに対してです!0007:7B90 0008:7B80 0009:7B70 000A:7B60 000B:7B50 000C:7B40 0047:7790 0048:7780 0049:7770 004A:7760 004B:7750 004C:7740 0077:7490 0078:7480 0079:7470 007A:7460 007B:7450 007C:7440 01FF:5C10 0200:5C00 0201:5BF0 0202:5BE0 0203:5BD0 0204:5BC0 07BB:0050 07BC:0040 07BD:0030 07BE:0020 07BF:0010 07C0:0000 もし、64KB以内のセグメントアドレスが2つあったらどうでしょうか?セグメントのサイズ(とオフセット)は16ビットであることを思い出してください。そして、セグメントアドレスは、セグメントのベースのみを参照する。 これが、オーバーラップされたセグメントである。 ![]() レイヤーの上にレイヤーがあり、それが他のセグメントの上に重なっていると想像してください。これは問題を引き起こす可能性がある。 つまり、Real Modeでは、メモリ上のすべてのバイトに4,000通り以上のアクセスが可能で、知らないうちにセグメントをオーバーラップさせて、その領域のメモリを破壊する可能性がある。 これが、Real Modeにメモリ保護がないことの意味である。 x86がセグメント参照に使用するレジスタは以下の通りである。
うわー、リアルモードには問題がたくさんある。私たちは、この問題から何を守ればいいのでしょうか? プロテクトモードプロテクトモード(PMode)は、よく耳にする言葉ですが、これからもっと耳にすることになると思います。PModeでは、メモリレイアウトを記述したディスクリプタテーブルを使用することで、メモリ保護を行うことができます。PModeは32bitのプロセッサモードなので、32bitのレジスタも使えるし、最大4GBのRAMにもアクセスできる。Real Modeに比べれば大きな進歩です。 私たちはPModeを使用することになります。そうです、聞かれる前に言っておきますが、WindowsはPMode OSです :) PModeはセットアップとその動作を完全に理解するのが少し難しいです。 PModeについての詳細は後ほど説明します。 アンリアルモードPMモードは、いつでも好きなときにプロセッサモードを切り替えることができます。アンリアルモードとは、PModeのアドレス空間(4GB制限)を持つReal Modeを表現したダジャレです。Unreal Modeを有効にするには、プロセッサをReal ModeからPModeに切り替え、新しいDescriptorをロードしたらまた元に戻すだけでよいのです。 Descriptor Tablesは非常にわかりにくいものです。詳しくはProtected Mode (PMode)の説明の時に説明します。 仮想8086モードVirtual 8086 Mode (v86 Mode) は、Protected Mode を 16bit Real Mode でエミュレートした環境として表現したモードです。これはちょっと奇妙に思えるかもしれませんが、v86は便利なものなのです。すべてのBIOS割り込みはリアルモードでのみ利用可能です!v86モードはPModeの中からBIOS割り込みを実行する方法を提供します。これについては後で詳しく説明します。 プロセッサモードの切り替えここでは、プロセッサのモードを切り替えるためのコードについては、まだ説明しません。その代わりに、一歩下がって、いくつかの重要なコンセプトを説明したいと思います。実際のモードとして組み込まれているのは、Real ModeとPotected Modeの2つだけです。つまり、他のモードであるUnreal Modeとv86 Modeは、この2つのモードを元に作られています。 Unreal ModeはReal Modeだが、Protected Mode (PMode) Addressing Systemを使っていること、Virtual 8086 ModeはPModeだが、16ビットコードを実行するためにReal Modeを使っていることを思い出してほしい。 このように、v86モードもUnrealモードもReal ModeとProtected Modeをベースにしているだけなので、PModeを理解しないとこれらのモードがどのように動作するのか理解するのは難しいかもしれません。 PMode、Unreal Mode、v86 Modeについては近日中に詳しく説明しますので、ご心配なく :) しかし、PModeについて覚えておくべきいくつかの重要なことがあります。
ブートローダの拡張さて、ここまでで多くのことを学びましたね。Protected Mode、Unreal Mode、v86 Modeの基本的な理論について説明しました。しかし、Real Modeについては深く掘り下げて説明しました。なぜか?なぜなら、コンピュータはDOSとの後方互換性のために16ビット環境で起動することを思い出してください。この16ビット環境はReal Modeです。ブートローダが実行されると、リアルモードになります。待って!これってBIOS割り込みが使えるってことだよね? うん :)VGAビデオ割り込みや、ハードウェアから直接マッピングされたその他の割り込みも使えます :) 便利なルーチンとBIOS割り込みOEMパラメータブロックOEMパラメータブロックは、WindowsのMBRとブートレコードの情報を格納します。 その主な目的は、ディスク上のファイルシステムを記述することです。 このテーブルについては、ファイルシステムを見るまで説明しません。しかし、このテーブルがないと先に進めません。これは、Windowsからの "Not formatted "メッセージも修正します。 今のところ、この表は必要なものだけと考えてください。詳しくは、ファイルシステムやディスクからのファイルのロードについて説明するときに、説明します。 以下は、このテーブルを使ったブートローダです。
テキストの印刷 - 割り込み0x10関数0x0EINT 0x10はビデオ割り込みに使用できます。ただし、基本的な割り込みしか動作しないことを忘れないでください。 INT 0x10 - ビデオテレタイプ出力
AH = 0x0E これは、画面上に文字'A'を表示します。 文字列の印刷 - 割り込み 0x10 関数 0x0E同じ割り込みを使って、0終端の文字列を簡単に出力することができます。
RAMの量を取得するこれは簡単すぎです。
INT 0x12 - BIOS GET MEMORY SIZE(メモリサイズ取得 例です。 うわぁ...大変だったでしょう?)実は、プロテクトモード(PMode)では、割り込みが使えないので、とても大変なのです。 注意:BIOSから実際に返されるメモリ量は正確ではないかもしれません!後で他の方法について見てみましょう。 Demo
まとめここまでできたら自分を褒めてあげてください :) このチュートリアルは厄介なものでした。セグメント:オフセットアドレッシングとプロセッサモードを深く掘り下げることなく説明する非常に良い方法を見つけなければなりませんでした。うまくできたと思います :) リアルモード、プロテクトモード、アンリアルモード、v86 などのプロセッサモードについて説明しました。 リアルモードについては、ブートローダを開発するときに使うモードなので、詳しく調べました。 また、セグメントオフセットアドレッシングについても説明しました。これは DOS プログラマにとっては再教育になるかもしれません。さらに、いくつかの BIOS 割り込みについても説明し、最後に完全な例を示しました。 次回のチュートリアルでは、追加した醜いOEMパラメータブロックの解読を行います。 また、基本的なファイルシステムの理論とディスクからのセクタのロードも見ていきます。 では、また次回。 |