最小マルチブートブートローダー

この記事では、マルチブート標準を使用して最小限のオペレーティングシステムカーネルを作成する方法について説明します。 実際、画面にロードしてOKを印刷するだけOK 。 将来の記事では、 Rustプログラミング言語を使用して拡張します。


私はすべてを詳細に説明し、コードをできる限りシンプルにしようとしました。 質問、提案、または問題がある場合は、 GitHubコメントを残すか、タスク作成GitHub 。 ソースコードはリポジトリで利用可能です



復習


コンピュータの電源を入れると、特別なフラッシュメモリからBIOSがロードされます。 BIOSはセルフテストとハードウェア初期化テストを実行し、起動可能なデバイスを検索します。 少なくとも1つが見つかった場合、制御はブートローダーに転送されます。ブートローダーは、ストレージデバイスの先頭に格納されている実行可能コードのごく一部です。 ブートローダーは、デバイス上のカーネルイメージの場所を特定し、それをメモリにロードします。 また、x86プロセッサはデフォルトで非常に限られたリアルモードで起動するため(1978年のプログラムと互換性があるため)、プロセッサをいわゆるプロテクトモードに切り替える必要があります。


これはそれ自体が複雑なプロジェクトであるため、ローダーを作成しません(実際にこれを実行したい場合は、 ここ読んでください )。 代わりに、 多くのテスト済みブートローダーの 1つを使用して、CD-ROMからカーネルをブートします。 しかし、どれですか?


マルチブート


幸いなことに、ブートローダーの標準、マルチブート仕様があります。 コアは、仕様をサポートし、互換性のあるブートローダーがそれをロードできることを示すだけです。 Multiboot 2仕様を使用します( PDF
有名なブートローダーGRUB 2とともに。


ブートローダーに Multiboot 2サポートを知らせるには、カーネルを次の形式の で開始する必要があります。


野原種類価値
マジックナンバーu320xE85250D6
建築u32i386の場合は0 、MIPSの場合は4
ヘッダー長u32タグを含む合計ヘッダーサイズ
チェックサムu32-( + + )
タグ可変
終了タグ(u16、u16、u32)(0, 0, 8)

x86アセンブラーに変換すると、次のようになります( Intel構文):


 section .multiboot_header header_start: dd 0xe85250d6 ;   (multiboot 2) dd 0 ;  0 (  i386) dd header_end - header_start ;   ;   dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) ;   `multiboot`   ;    dw 0 ;  dw 0 ;  dd 8 ;  header_end: 

x86アセンブラーがわからない場合は、簡単な紹介を次に示します。



nasmを使用して、このファイル( multiboot_header.asmと呼びます)を既にコンパイルできます。


`archlinux`にnasmをインストールする
 [loomaclin@loomaclin ~]$ yaourt nasm 1 extra/nasm 2.13.02-1 An 80x86 assembler designed for portability and modularity 2 extra/yasm 1.3.0-2 A rewrite of NASM to allow for multiple syntax supported (NASM, TASM, GAS, etc.) 3 aur/intel2gas 1.3.3-7 (3) (0.20) Converts assembly language files between NASM and GNU assembler syntax 4 aur/nasm-git 20150726-1 (1) (0.00) 80x86 assembler designed for portability and modularity 5 aur/sasm 3.9.0-1 (18) (0.61) Simple crossplatform IDE for NASM, MASM, GAS, FASM assembly languages 6 aur/yasm-git 1.3.0.r30.g6caf1518-1 (0) (0.00) A complete rewrite of the NASM assembler under the BSD License ==> Enter n° of packages to be installed (eg, 1 2 3 or 1-3) ==> --------------------------------------------------------- ==> 1 [sudo] password for loomaclin: resolving dependencies... looking for conflicting packages... Packages (1) nasm-2.13.02-1 Total Download Size: 0.34 MiB Total Installed Size: 2.65 MiB :: Proceed with installation? [Y/n] :: Retrieving packages... nasm-2.13.02-1-x86_64 346.0 KiB 1123K/s 00:00 [#############################################################################] 100% (1/1) checking keys in keyring [#############################################################################] 100% (1/1) checking package integrity [#############################################################################] 100% (1/1) loading package files [#############################################################################] 100% (1/1) checking for file conflicts [#############################################################################] 100% (1/1) checking available disk space [#############################################################################] 100% :: Processing package changes... (1/1) installing nasm [#############################################################################] 100% :: Running post-transaction hooks... (1/1) Arming ConditionNeedsUpdate... [loomaclin@loomaclin ~]$ nasm --version NASM version 2.13.02 compiled on Dec 10 2017 [loomaclin@loomaclin ~]$ 

次のコマンドはフラットバイナリファイルを生成し、結果のファイルには24バイトが含まれます(x86マシンで作業している場合はlittle endian )。


 [loomaclin@loomaclin ~]$ cd IdeaProjects/ [loomaclin@loomaclin IdeaProjects]$ mkdir a_minimal_multiboot_kernel [loomaclin@loomaclin IdeaProjects]$ cd a_minimal_multiboot_kernel/ [loomaclin@loomaclin a_minimal_multiboot_kernel]$ nano multiboot_header.asm [loomaclin@loomaclin a_minimal_multiboot_kernel]$ nasm multiboot_header.asm [loomaclin@loomaclin a_minimal_multiboot_kernel]$ hexdump -x multiboot_header 0000000 50d6 e852 0000 0000 0018 0000 af12 17ad 0000010 0000 0000 0008 0000 0000018 [loomaclin@loomaclin a_minimal_multiboot_kernel]$ 

ブートコード


カーネルをロードするには、ローダーが呼び出すことができるコードを追加する必要があります。 boot.asmファイルを作成しましょう。


 global start section .text bits 32 start: ;  `OK`   mov dword [0xb8000], 0x2f4b2f4f hlt 

ここにはいくつかの新しいコマンドがあります:



組み立て、表示、および分解した後、プロセッサのオペコードが動作しているのがわかります。


 [loomaclin@loomaclin a_minimal_multiboot_kernel]$ nano boot.asm [loomaclin@loomaclin a_minimal_multiboot_kernel]$ nasm boot.asm [loomaclin@loomaclin a_minimal_multiboot_kernel]$ hexdump -x boot 0000000 05c7 8000 000b 2f4f 2f4b 00f4 000000b [loomaclin@loomaclin a_minimal_multiboot_kernel]$ ndisasm -b 32 boot 00000000 C70500800B004F2F mov dword [dword 0xb8000],0x2f4b2f4f -4B2F 0000000A F4 hlt [loomaclin@loomaclin a_minimal_multiboot_kernel]$ 

実行可能ファイルを作成する


後でGRUBを介して実行可能ファイルをダウンロードするには、 ELF実行可能ファイルでなければなりません。 したがって、単純なバイナリの代わりにnasmを使用してELFオブジェクトファイルを作成する必要があります。 これを行うには、単に引数に-f elf64を追加します。


ELF実行可能コード自体を作成するには、オブジェクトファイルをリンクする必要があります。 linker.ldと呼ばれるリンク用のカスタムスクリプトを使用します。


 ENTRY(start) SECTIONS { . = 1M; .boot : { /*      */ *(.multiboot_header) } .text : { *(.text) } } 

人間の言語で書かれたものを翻訳しましょう:



ELFオブジェクトファイルを作成し、上記のリンカースクリプトを使用してリンクします。


 [loomaclin@loomaclin a_minimal_multiboot_kernel]$ nasm -f elf64 multiboot_header.asm [loomaclin@loomaclin a_minimal_multiboot_kernel]$ nasm -f elf64 boot.asm [loomaclin@loomaclin a_minimal_multiboot_kernel]$ ld -n -o kernel.bin -T linker.ld multiboot_header.o boot.o [loomaclin@loomaclin a_minimal_multiboot_kernel]$ 

-n (または--nmagic )フラグをリンカーに渡すことが非常に重要です。これにより、実行可能ファイル内の自動セクションアライメントが無効になります。 そうしないと、リンカは実行可能ファイルの.bootセクションのページを整列させる場合があります。 これが発生すると、 GRUBはマルチブートヘッダーを見つけることができなくなります。これは、先頭にないためです。


objdumpコマンドを使用して、生成された実行可能ファイルのセクションを表示し、 .bootセクションにファイル内の最小オフセットがあることを確認します。


 [loomaclin@loomaclin a_minimal_multiboot_kernel]$ objdump -h kernel.bin kernel.bin: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .boot 00000018 0000000000100000 0000000000100000 00000080 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .text 0000000b 0000000000100020 0000000000100020 000000a0 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE [loomaclin@loomaclin a_minimal_multiboot_kernel]$ 

注: ldおよびobjdumpコマンドはプラットフォームに依存しています。 x86_64アーキテクチャで作業していない場合、 binutilsクロスコンパイルする必要があります 。 その後、 x86_64‑elf‑ldおよびx86_64‑elf‑objdump代わりに、それぞれx86_64‑elf‑ldおよびx86_64‑elf‑objdump使用します。

ISOイメージの作成


すべてのBIOSベースのパーソナルコンピューターはCD-ROMから起動する方法を知っているため、カーネルとGRUBブートローダーファイルを含むブート可能なCD-ROMイメージをISOと呼ばれる単一のファイルに作成する必要があります。 次のディレクトリ構造を作成し、 kernel.binbootディレクトリにコピーしboot


 isofiles └── boot ├── grub │ └── grub.cfg └── kernel.bin 

grub.cfgは、カーネルファイル名とmultiboot 2との互換性を示します。 次のようになります。


 set timeout=0 set default=0 menuentry "my os" { multiboot2 /boot/kernel.bin boot } 

コマンドを実行します。


 [loomaclin@loomaclin a_minimal_multiboot_kernel]$ mkdir isofiles [loomaclin@loomaclin a_minimal_multiboot_kernel]$ mkdir isofiles/boot [loomaclin@loomaclin a_minimal_multiboot_kernel]$ mkdir isofiles/boot/grub [loomaclin@loomaclin a_minimal_multiboot_kernel]$ cp kernel.bin isofiles/boot/ [loomaclin@loomaclin a_minimal_multiboot_kernel]$ nano grub.cfg [loomaclin@loomaclin a_minimal_multiboot_kernel]$ cp grub.cfg isofiles/boot/grub/ 

次のコマンドを使用して、起動可能なイメージを作成できます。


 [loomaclin@loomaclin a_minimal_multiboot_kernel]$ grub-mkrescue -o os.iso isofiles xorriso 1.4.8 : RockRidge filesystem manipulator, libburnia project. Drive current: -outdev 'stdio:os.iso' Media current: stdio file, overwriteable Media status : is blank Media summary: 0 sessions, 0 data blocks, 0 data, 7675m free Added to ISO image: directory '/'='/tmp/grub.jN4u6m' xorriso : UPDATE : 898 files added in 1 seconds Added to ISO image: directory '/'='/home/loomaclin/IdeaProjects/a_minimal_multiboot_kernel/isofiles' xorriso : UPDATE : 902 files added in 1 seconds xorriso : NOTE : Copying to System Area: 512 bytes from file '/usr/lib/grub/i386-pc/boot_hybrid.img' ISO image produced: 9920 sectors Written to medium : 9920 sectors at LBA 0 Writing to 'stdio:os.iso' completed successfully. 

注:一部のプラットフォームでは、 grub-mkrescue呼び出すと問題が発生する場合があります。 うまくいかなかった場合は、次の手順を試してください。
  • --verboseてコマンドを実行し、
  • xorrisoライブラリxorrisoインストールされていることを確認してxorrisoxorrisoまたはlibisoburnパッケージ)。


`Archlinuxに` libisoburn`を置かなければならなかった

[loomaclin @ loomaclin a_minimal_multiboot_kernel] $ yaourt xorriso
1個の追加/ libisoburn 1.4.8-2
ライブラリlibburnおよびlibisofsのフロントエンド
==>インストールするパッケージのn°を入力します(例:1 2 3または1-3)
==>-==> 1


[sudo] loomaclinのパスワード:
依存関係の解決...
競合するパッケージを探しています...


パッケージ(3)libburn-1.4.8-1 libisofs-1.4.8-1 libisoburn-1.4.8-2


合計ダウンロードサイズ:1.15 MiB
合計インストールサイズ:3.09 MiB


::インストールを続行しますか? [Y / n]
::パッケージの取得...
libburn-1.4.8-1-x86_64 259.7 KiB 911K / s 00:00 [################################## ##################################################] 100%
libisofs-1.4.8-1-x86_64 237.8 KiB 2.04M / s 00:00 [################################ ###################################################]] 100%
libisoburn-1.4.8-2-x86_64 683.8 KiB 2.34M / s 00:00 [################################ ###################################################]] 100%
(3/3)キーリングのキーをチェックする[############################################# ###########################################] 100%
(3/3)パッケージの整合性のチェック[############################################ #########################################] 100%
(3/3)パッケージファイルの読み込み[############################################# #########################################] 100%
(3/3)ファイルの競合のチェック[############################################# ###########################################] 100%
(3/3)利用可能なディスク容量[############################################ ###########################################] 100%
::パッケージ変更の処理...
(1/3)libburn [#############################################のインストール######################################] 100%
(2/3)libisofs [###############################################のインストール######################################] 100%
(3/3)libisoburnのインストール



読み込み中


OSをダウンロードします。 これを行うには、 QEMUを使用します。


 [loomaclin@loomaclin a_minimal_multiboot_kernel]$ qemu-system-x86_64 -cdrom os.iso (qemu-system-x86_64:10878): Gtk-WARNING **: Allocating size to GtkScrollbar 0x7f2337e5a280 without calling gtk_widget_get_preferred_width/height(). How does the code know the size to allocate? (qemu-system-x86_64:10878): Gtk-WARNING **: Allocating size to GtkScrollbar 0x7f2337e5a480 without calling gtk_widget_get_preferred_width/height(). How does the code know the size to allocate? (qemu-system-x86_64:10878): Gtk-WARNING **: Allocating size to GtkScrollbar 0x7f2337e5a680 without calling gtk_widget_get_preferred_width/height(). How does the code know the size to allocate? 

エミュレータウィンドウが表示されます:
エミレータウィンドウ


左上隅にある緑色のテキストOKに注意してください。 これがうまくいかない場合は、コメントのセクションをご覧ください。


何が起こったのかをまとめます:


  1. BIOSは、仮想CD-ROM(ISO)からブートローダー(GRUB)をロードします。
  2. ブートローダーはカーネル実行可能コードを読み取り、マルチブートヘッダーを見つけました。
  3. .bootおよび.text .bootをメモリにコピーしました( 0x100000および0x100020 )。
  4. エントリポイントに移動しました( 0x100020 、これはobjdump -f呼び出すことで見つけることができます)。
  5. カーネルはテキストOK緑色で表示し、プロセッサを停止しました。

これを実際のハードウェアでテストすることもできます。 結果のイメージをディスクまたはUSBドライブに書き込み、そこから起動する必要があります。


ビルドの自動化


ここで、ファイルを変更するたびに4つのコマンドを正しい順序で呼び出す必要があります。 これは悪いです。 Makefileを使用してこのプロセスを自動化しましょう。 ただし、最初に、適切なディレクトリ構造を作成して、アーキテクチャ依存のファイルを分離する必要があります。


 … ├── Makefile └── src └── arch └── x86_64 ├── multiboot_header.asm ├── boot.asm ├── linker.ld └── grub.cfg 

私たちは作成します:


 [loomaclin@loomaclin a_minimal_multiboot_kernel]$ mkdir -p src/arch/x86_64 [loomaclin@loomaclin a_minimal_multiboot_kernel]$ cp multiboot_header.asm src/arch/x86_64/ [loomaclin@loomaclin a_minimal_multiboot_kernel]$ cp boot.asm src/arch/x86_64/ [loomaclin@loomaclin a_minimal_multiboot_kernel]$ cp linker.ld src/arch/x86_64/ [loomaclin@loomaclin a_minimal_multiboot_kernel]$ cp grub.cfg src/arch/x86_64/ [loomaclin@loomaclin a_minimal_multiboot_kernel]$ nano Makefile 

メイクファイルは次のようになります。


 arch ?= x86_64 kernel := build/kernel-$(arch).bin iso := build/os-$(arch).iso linker_script := src/arch/$(arch)/linker.ld grub_cfg := src/arch/$(arch)/grub.cfg assembly_source_files := $(wildcard src/arch/$(arch)/*.asm) assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \ build/arch/$(arch)/%.o, $(assembly_source_files)) .PHONY: all clean run iso all: $(kernel) clean: @rm -r build run: $(iso) @qemu-system-x86_64 -cdrom $(iso) iso: $(iso) $(iso): $(kernel) $(grub_cfg) @mkdir -p build/isofiles/boot/grub @cp $(kernel) build/isofiles/boot/kernel.bin @cp $(grub_cfg) build/isofiles/boot/grub @grub-mkrescue -o $(iso) build/isofiles 2> /dev/null @rm -r build/isofiles $(kernel): $(assembly_object_files) $(linker_script) @ld -n -T $(linker_script) -o $(kernel) $(assembly_object_files) # compile assembly files build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm @mkdir -p $(shell dirname $@) @nasm -felf64 $< -o $@ 

コメント(以前にmakeを使用したことがない場合は、 makefileチュートリアルをご覧ください ):



これでmakeを呼び出すmakeができ、更新されたすべてのアセンブラファイルがコンパイルおよびリンクされます。 make isoコマンドもISOイメージを作成し、 make runがQEMUを起動します。


次は?


次の記事では、ページテーブルを作成し、プロセッサ構成を実行して64ビットロングモードモードに切り替えます。


注釈


  1. 表の式-(magic + architecture + header_length)は、32ビットに収まらない負の値を作成します。 0x100000000から減算することにより、減算した値を変更せずに値を正のままにします。 その結果、追加の符号ビットなしで、結果は32ビットに配置され、コンパイラは満足です:)
  2. メモリの多くの特定の領域は1メガバイトマークまで配置できるため、オフセット0x0でカーネルをロードする必要はありません(たとえば、画面にOKを表示するために使用する0xb8000いわゆるVGAバッファー)。


Source: https://habr.com/ru/post/J351568/


All Articles