0から作るソフトウェア開発

日々勉強中。。。

0から作るOS開発

環境準備

環境設定

ブートローダ

カーネルローダ

GRUB

カーネル

ドライバーその他

0から作るOS開発 Grubその3 GRUBから起動できる自作OSを作成する

前回までの内容

これまでで、 についてわかりました。それではGRUBから自作OSを作成していきましょう!

今回は前回みてきましたCygwindeでインストールしたGRUBを使用して

起動できる自作OSを作成していきます。



それでは、順番に見ていきます。

推奨

カーネルから開発してみたいという方もいらっしゃるかと存じますが、一度ブートローダーを

アセンブラで作ってみることをおすすめ致します。CPU周りの制御の仕方の基本的なことが

わかるようになりますので、ぜひ一度最初から作ってみてください。

GRUBから起動できる自作OS(カーネル)の作成

GRUBからブートできる自作OSを作るには、 マルチブート仕様 に対応したカーネルを

作る必要があります。が、仕様の全部を適用したカーネルを作成するのは時間が

かかるので、今回は自作カーネルをブートするのに必要なところだけなるべく適用していきます。

(仕様の詳細については マルチブート仕様 をご確認ください)。

マルチブート仕様(ベーシック)

では、マルチブート仕様について必要な部分を見ていきます。

マルチブート仕様の概略

マルチブート仕様は現在GNUプロジェクトで発行している仕様となります。

この仕様は、複数のOSを起動ときのブートローダーとOSとのインターフェースを規定したものです。

マルチブートに対応したOSは下記3点について実装しておく必要があります。

マルチブートヘッダー

マルチブートヘッダーはOSのイメージの先頭から8KB以内に付けます。

イメージとしては下記のようになります。

OSイメージ

そしてマルチブートヘッダーは次のような構造となっています。

(下記図表のオフセットはマルチブートヘッダーの先頭から何バイト目に配置するかを

意味しています。また詳細な情報については マルチブート仕様 をご確認ください)。

マルチブートヘッダーの構造

マルチブートヘッダー
マジックフィールド
オフセット 型式 フィールド名 説明
0 u32 magic マルチブートヘッダーを特定するためのマジックナンバーです。 マジックナンバーとして”必ず0x1BADB002を設定します”。
4 u32 flags このフィールドにはOSが必要な機能とブートローダーが必要な情報を ヘッダーに格納しているかどうかを示すフラグです。

ビット0:1に設定している場合には、GRUBは、 OSと一緒にロードするモジュールをページ境界(4KB境界)に配置します。 (OSのなかには起動時に用意しているページに、 モジュールがロードされることを前提としているものがあるためです。)

ビット1:1に設定している場合には、GRUBは OSに引数として渡す ブート情報構造体 のmem_*フィールドに、システムで利用可能なメモリー情報を セットします。

ビット2:1に設定している場合、OSは ブート情報構造体 のビデオモードテーブルを使用しますので、GRUBは テーブル値をセットする必要があります。

ビット16:1に設定している場合には、 マルチブートヘッダーのオフセット12から28が有効となります。 セットされている場合、GRUBは ELFフォーマット以外の実行形式のカーネルをロードすることができます。
8 u32 checksum チェックサムを設定します。 magicフィールドの値とflagsフィールドの値の合計と checksumフィールドの値を足すと0になるような32ビットのチェックサムの値を設定する 必要があります。

    checksum = - ( magic + flags )

アドレスフィールド
flagsのビット16が1の場合に、アドレスフィールドの全部のフィールドが 有効となります。アドレスは物理アドレスです。
12 u32 header_addr マルチブートヘッダーの先頭が配置される物理アドレスを設定します。 (magicフィールドの値が配置されるメモリーの物理アドレスです)。
16 u32 load_addr OSイメージのテキストセグメント(.text)の開始物理アドレスを 設定します。GRUBはOSの実行バイナリだけをロードするために、 OSイメージファイルの先頭から何バイト目に実行バイナリが あるのかを計算するために使用します。
20 u32 load_end_addr OSイメージの初期値付きデータセグメント(.data)の最終物理アドレスを 設定します。(load_end_addr - load_addr)は ロードする初期値有りデータのバイト数となります。 初期値有りデータセグメントとテキストセグメントは連続しているものとして GRUBは処理します。
24 u32 bss_end_addr 初期値無し変数のデータセグメント(.bss)の最終物理アドレス を設定します。GRUBはこのエリアをゼロで初期化します。 (ANSI C規格に準拠するためなど)。 このフィールドが0に設定されている場合には、GRUBは 初期値無し変数のデータセグメント(.bss)が無いものとして (初期化しません)処理します。
28 u32 entry_addr OSをロードした後にジャンプする物理アドレスを設定します。 (カーネルのメイン関数のアドレスを設定します)。
グラフィックフィールド
flagsのビット2が1の場合に、グラフィックフィールドの全部のフィールドが 有効となります。OS起動時にカーネルが要求するグラフィックモードを設定します。
32 u32 entry_addr 0:ブートローダーはグラフィックモードに設定します
1:ブートローダーはEGA標準のテキストモードに設定します
その他の値:将来仕様のため予約されています
(値が0の場合でもブートローダーはOS起動時にテキストモードに 設定してもよいです。)
36 u32 width グラフィックの列数を設定します。
グラフィックモード:ピクセル単位で設定します。
テキストモード:文字数単位で設定します。
(値を0に設定した場合、OSはどのように設定されるかは気にしません)
40 u32 height グラフィックの行数を設定します。
グラフィックモード:ピクセル単位で設定します。
テキストモード:文字数単位で設定します。
(値を0に設定した場合、OSはどのように設定されるかは気にしません)
44 u32 depth 0以外:グラフィックモードの1ピクセル毎のビット数を設定します
0:ビット数をどのように設定されてもOSには問題ありません


ここで出てくる型式については次のように定義しています。

マルチブート仕様で使用する変数の型式
型式 説明
u8 符号なし(unsigned)の8ビットデータです
u16 符号なし(unsigned)の16ビットデータです
u32 符号なし(unsigned)の32ビットデータです
u64 符号なし(unsigned)の64ビットデータです


マルチブートヘッダーについてみてきましたが、ヘッダーとして必ず必要なものは

magic、flags、checksumの3つとなります。最低限この3つの

フィールドを設定しておくとカーネルをブートできます。(ELFフォーマットの場合)

マルチブートヘッダーの実装

マルチブートヘッダーについてOSで使用するために、定義をしていきます。

カーネルをロードする物理アドレスの定義

最初の定義は、GRUBがマルチブートヘッダーを読み込むアドレスを定義します。

この定義はELFフォーマットのカーネルの場合には必要ありません。

今回最初はELFフォーマットのカーネルを作っていこうと思います。

次に、バイナリイメージのカーネルの作っていこうと思いますが、

(あくまで参考程度です。カーネルはELFフォーマットで作った方が簡単だと思います)。

その時にカーネルをロードするメモリーアドレスをGRUBに指示するために定義しておきます。

カーネルは カーネルローダーその1 メモリマップ で見て来ました

ように、GRUBに物理アドレス0x00100000に読み込んでもらいます。



今回のバイナリイメーのカーネルは、マルチブートヘッダー+バイナリカーネルイメージと

しております。マルチブートヘッダーとバイナリイメージを結合してカーネルイメージと

しています。


/* Physical address where kernel is loaded    */
#define DEF_MM_KERNEL_PHY_BASEADDR    0x00100000



magicフィールドの定義

マルチブートヘッダーの一番先頭にはマジックフィールドを置きます。

仕様通り値0x1BADB002を設定します。この値意外では、GRUBはカーネルを

起動してくれません。magicフィールドは必ず必要です。


/*
----------------------------------------------------------------------------------

    Magic of Multiboot Header

----------------------------------------------------------------------------------
*/
#define DEF_MBH_MAGIC            0x1BADB002



flagsフィールドの定義

flagsフィールドのビット0、1、2、16を定義します。近い将来用に

今回作成するELFフォーマットのカーネルでは、ビット0、ビット1をセットしておきます。

ビデオモードについては、GRUBがカーネルを起動したときにはテキストモードになって

いますので、とりあえずはビット2は0のままでいいと思います。将来的にグラフィック

ドライバーを作った後で、グラフィックモードにしたいといったときにビット2をセットします。

ビット16についてはEFLフォーマットでは使用しませんので、0にしておきます。

後で紹介するバイナリのカーネルの時にはビット16をセットしておく必要があります。

flagsフィールドは必ず必要です。

/*
----------------------------------------------------------------------------------

    Flags of Multiboot Header

----------------------------------------------------------------------------------
*/
#define	DEF_MBH_FLAGS_PAGE_ALIGN    0x00000001  /* page (4KB) boundaries        */
#define	DEF_MBH_FLAGS_MEMORY_INFO   0x00000002  /* memory information           */
#define	DEF_MBH_FLAGS_VIDEO_MODE    0x00000004  /* video mode                   */
#define	DEF_MBH_FLAGS_AOUT_KLUDGE   0x00010000  /* use address fields           */



そしてこの定義を使って、実際にFlagsフィールドに設定する値を作っていきます。

DEF_MBH_FLAGSはEFLフォーマット用のFlagsフィールド値です。

DEF_MBH_FLAGS_PAGE_ALIGNとDEF_MBH_FLAGS_MEMORY_INFOのビットを設定します。

DEF_MBH_FLAGS_BINはバイナリフォーマット用のFlagsフィールド値です。

ビット16をセットしますのでDEF_MBH_FLAGS_AOUT_KLUDGEを追加します。

定義は次のようになります。


/* Flags for elf kernel image                          */
#define	DEF_MBH_FLAGS           ( DEF_MBH_FLAGS_PAGE_ALIGN | DEF_MBH_FLAGS_MEMORY_INFO )
/* Flags for binary kernel image                       */
#define	DEF_MBH_FLAGS_BIN       ( DEF_MBH_FLAGS_PAGE_ALIGN | DEF_MBH_FLAGS_MEMORY_INFO | DEF_MBH_FLAGS_AOUT_KLUDGE )



checksumフィールド

チェックサムはマジックフィールドの値とフラッグフィールドの値の合計の

マイナスを定義します。(checksumフィールドは必ず必要です)。


#define	DEF_MBH_CHECKSUM        ( - ( DEF_MBH_MAGIC + DEF_MBH_FLAGS     ) )
#define	DEF_MBH_CHECKSUM_BIN    ( - ( DEF_MBH_MAGIC + DEF_MBH_FLAGS_BIN ) )



header_addrフィールド

ヘッダーアドレスフィールドを定義します。先ほど出てきた定義を使用します。

このフィールドはバイナリカーネルイメージの起動時のみ使用します。

今回はマルチブートヘッダー+カーネルイメージのように結合しますので、

GRUBにマルチブートヘッダーを0x00100000に読み込んでもらうように

します。


#define	DEF_MBH_HEADER_ADDR     DEF_MM_KERNEL_PHY_BASEADDR



残りのアドレスフィールドについて

その他のフィールドについてはカーネルをコンパイル(リンク)したときに

アドレスが決まりますので、リンケージエディターが計算したアドレスを

使用します。ですので、特に定義はしていません。

グラフィックフィールドの定義

OS起動時のグラフィックについて定義します。今回は使用しませんが、

とりあえず定義だけしております。この例では、テキストモードで、

80x40の表示としております。

				
#define	DEF_MBH_MODE_TYPE       0x00000001        /* text mode                  */
#define	DEF_MBH_WIDTH           0x00000050        /* width  = 80                */
#define	DEF_MBH_HEIGHT          0x00000028        /* height = 40                */
#define	DEF_MBH_DEPTH           0x00000000



C言語用のマルチブートヘッダー定義

今回はマルチブート仕様に載っている例と同様に、マルチブートヘッダーを

アセンブラで、カーネルをC言語で実装する例となります。カーネルで使用する

マルチブートヘッダーの定義をしておきます。

				
/*
=================================================================================

    The followings are for only C

=================================================================================
*/
#ifndef __MULTIBOOT__S
#ifndef __MULTIBOOT_HEADER_H
#define	__MULTIBOOT_HEADER_H

typedef unsigned int    UINT32

/*
=================================================================================

    Multiboot Header

=================================================================================
*/
typedef struct
{
    UINT32  magic;
    UINT32  flags;
    UINT32  checksum;
    /* The followings are only valid if MULTIBOOT_AOUT_KLUDGE is set	*/
    UINT32  header_addr;
    UINT32  load_addr;
    UINT32  load_end_addr;
    UINT32  bss_end_addr;
    UINT32  entry_addr;
    /* The followings are only valid if MULTIBOOT_VIDEO_MODE is set		*/
    UINT32  mode_type;
    UINT32  width;
    UINT32  height;
    UINT32  depth;
}MULTIBOOT_HEADER;

#endif	/* __MULTIBOOT_HEADER_H	*/
#endif	/* __MULTIBOOT__S		*/



とりあえずマルチブートヘッダーの定義ができました。

次にOSが起動したときのマシン状態について見ていきます。

OS起動時のマシン状態

マルチブート仕様で作られたGRUBはカーネル起動時にCPUのレジスタを次のように設定します

となっています。今回は特に、カーネルに渡すマジックナンバー(0x2BADB002)をEAXに

格納し、OSに渡す処理とマルチブート情報を渡す処理について後ほど見ていきます。

とりあえず、カーネルに渡すマジックナンバーナンバーを定義します。

カーネルはこの値を確認することで、マルチブート仕様のブートローダーから起動されたことを

確認することができます。

				
#define	DEF_MBH_BL_MAGIC         0x2BADB002	/* The magic should be in EAX   */



マルチブート情報

マルチブート情報はGRUBがカーネルを起動する前にBIOSから取得した

メモリーマップなどの情報となります。このマルチブート情報を次のような

フォーマットでカーネルに渡します。(簡略化していますので、詳細は

マルチブート仕様 をご確認ください)。

今回はこの情報をカーネルに渡すだけの例となりますので、特に重要な

メモリーに関するフィールドのみ見ていきます。

マルチブート情報のフォーマット

マルチブート情報
ラベル名 オフセット
(バイト)
サイズ
(バイト)
説明
flags 0 4 ブート情報のフィールドが有効/無効(有り/無し)のフラグです
各ビットが1になっていれば有効(有り)で、0であれば無効(無し)となります
ビット0:1であればmem_*フィールドが有効になります
ビット1:1であればboot_deviceフィールドが有効になります
ビット2:1であればcmdlineフィールドが有効になります
ビット3:1であればmods_*フィールドが有効になります
ビット4:1であればsymフィールドがa.outカーネルイメージ用の
            フォーマットになります
ビット5:1であればsymフィールドがELFカーネルイメージ用の
            フォーマットになります
ビット6:1であればmmap_*フィールドが有効になります
ビット7:1であればdrives_*フィールドが有効になります
ビット8:1であればconfig_tableフィールドが有効になります
ビット9:1であればboot_loader_nameフィールドが有効になります
ビット10:1であればapm_tableフィールドが有効になります
ビット11:vbe_*フィールドが有効になります
mem_lower 4 4 ローメモリのメモリサイズが格納されています
mem_upper 8 4 拡張メモリ(1MB以降のメモリ領域)のメモリサイズが格納されています
mmap_length 44 4 メモリマップ情報のデータ長が格納されています
mmap_addr 48 4 メモリマップ情報が格納されているアドレスが格納されています


マルチブート情報の定義

マルチブート情報はカーネルで使用するのでC言語で定義します。

UINT32 syms1とsyms2とsyms3は簡略化しています。

正確な定義は マルチブート仕様 をご確認ください。

				
/*
=================================================================================

    Multiboot Info

=================================================================================
*/
typedef struct
{
    UINT32	flags;          /* multiboot info version number     */
    /* available memory from BIOS       */
    UINT32	mem_lower;
    UINT32	mem_upper;
    UINT32	boot_device;    /* "root" partition                  */
    UINT32	cmdline;        /* kernel command line               */
    /* Boot-Module list                 */
    UINT32	mods_count;
    UINT32	mods_addr;

    UINT32  syms1;
    UINT32  syms2;
    UINT32  syms3;

    /* memory mapping buffer            */
    UINT32	mmap_length;
    UINT32	mmap_addr;
    /* drive info buffer                */
    UINT32	drives_length;
    UINT32	drives_addr;
    /* ROM configuration table          */
    UINT32	config_table;
    /* bootloader name                  */
    UINT32	boot_loader_name;
    /* Video                            */
    UINT32	vbe_control_info;
    UINT32	vbe_mode_info;
    UINT32	vbe_mode;
    UINT32	vbe_interface_seg;
    UINT32	vbe_interface_off;
    UINT32	vbe_interface_len;
}MULTIBOOT_INFO;



マルチブート仕様のヘッダー定義まとめ

ここまでで、マルチブート仕様のカーネルを作るための定義を一通り見て来ました。

ここまでの定義をまとめたmultiboot.hヘッダーファイルを載せております。

			
/*****************************************************************************
 File:multiboot.h
 Description:Definition of Multiboot header for C and Assembler

*****************************************************************************/
#ifndef __MULTIBOOT__H
#define __MULTIBOOT__H

/* Physical address where kernel is loaded    */
#define DEF_MM_KERNEL_PHY_BASEADDR    0x00100000

/*
==================================================================================

    DEFINES of Multiboot header for C and Assember

==================================================================================
*/
/*
----------------------------------------------------------------------------------

    Magic of Multiboot Header

----------------------------------------------------------------------------------
*/
#define DEF_MBH_MAGIC            0x1BADB002     /* magic                        */

/*
----------------------------------------------------------------------------------

    Flags of Multiboot Header

----------------------------------------------------------------------------------
*/
#define DEF_MBH_FLAGS_PAGE_ALIGN    0x00000001  /* page (4KB) boundaries        */
#define DEF_MBH_FLAGS_MEMORY_INFO   0x00000002  /* memory information           */
#define DEF_MBH_FLAGS_VIDEO_MODE    0x00000004  /* video mode                   */
#define DEF_MBH_FLAGS_AOUT_KLUDGE   0x00010000  /* use address fields           */

/*
----------------------------------------------------------------------------------

    Other fileds of Multiboot Header

----------------------------------------------------------------------------------
*/
/* Flags for elf kernel image                          */
#define DEF_MBH_FLAGS           ( DEF_MBH_FLAGS_PAGE_ALIGN | DEF_MBH_FLAGS_MEMORY_INFO )
/* Flags for binary kernel image                       */
#define DEF_MBH_FLAGS_BIN       ( DEF_MBH_FLAGS_PAGE_ALIGN | DEF_MBH_FLAGS_MEMORY_INFO | DEF_MBH_FLAGS_AOUT_KLUDGE )
#define DEF_MBH_CHECKSUM        ( - ( DEF_MBH_MAGIC + DEF_MBH_FLAGS     ) )
#define DEF_MBH_CHECKSUM_BIN    ( - ( DEF_MBH_MAGIC + DEF_MBH_FLAGS_BIN ) )
/* The following fields are unnecessory for elf format */
#define DEF_MBH_HEADER_ADDR     DEF_MM_KERNEL_PHY_BASEADDR
#define DEF_MBH_MODE_TYPE       0x00000001        /* text mode                  */
#define DEF_MBH_WIDTH           0x00000050        /* width  = 80                */
#define DEF_MBH_HEIGHT          0x00000028        /* height = 40                */
#define DEF_MBH_DEPTH           0x00000000

#endif  /* __MULTIBOOT__H */


/*
=================================================================================

    The followings are for only C

=================================================================================
*/
#ifndef __MULTIBOOT__S
#ifndef __MULTIBOOT_HEADER_H
#define __MULTIBOOT_HEADER_H

typedef unsigned int    UINT32;

/*
=================================================================================

    Multiboot Header

=================================================================================
*/
typedef struct
{
    UINT32  magic;
    UINT32  flags;
    UINT32  checksum;
    /* The followings are only valid if MULTIBOOT_AOUT_KLUDGE is set	*/
    UINT32  header_addr;
    UINT32  load_addr;
    UINT32  load_end_addr;
    UINT32  bss_end_addr;
    UINT32  entry_addr;
    /* The followings are only valid if MULTIBOOT_VIDEO_MODE is set		*/
    UINT32  mode_type;
    UINT32  width;
    UINT32  height;
    UINT32  depth;
}MULTIBOOT_HEADER;

/*
=================================================================================

    Multiboot Info

=================================================================================
*/
typedef struct
{
    UINT32  flags;          /* multiboot info version number     */
    /* available memory from BIOS       */
    UINT32  mem_lower;
    UINT32  mem_upper;
    UINT32  boot_device;    /* "root" partition                  */
    UINT32  cmdline;        /* kernel command line               */
    /* Boot-Module list                 */
    UINT32  mods_count;
    UINT32  mods_addr;

    UINT32  syms1;
    UINT32  syms2;
    UINT32  syms3;

    /* memory mapping buffer            */
    UINT32  mmap_length;
    UINT32  mmap_addr;
    /* drive info buffer                */
    UINT32  drives_length;
    UINT32  drives_addr;
    /* ROM configuration table          */
    UINT32  config_table;
    /* bootloader name                  */
    UINT32  boot_loader_name;
    /* Video                            */
    UINT32  vbe_control_info;
    UINT32  vbe_mode_info;
    UINT32  vbe_mode;
    UINT32  vbe_interface_seg;
    UINT32  vbe_interface_off;
    UINT32  vbe_interface_len;
}MULTIBOOT_INFO;

#endif	/* __MULTIBOOT_HEADER_H  */
#endif	/* __MULTIBOOT__S        */



GRUBから起動するカーネルを作成する

ここまでで定義してきました、multiboot.hヘッダーファイルの定義を使用して、

マルチブート仕様を適用したカーネルと作成していきます。



まずはマルチブートヘッダーを作ります。

マルチブートヘッダーを作成する

マルチブートヘッダーはアセンブラで作成します。ここの例ではmultiboot.Sと

します。multiboot.hをインクルードするために、拡張子は大文字のSにします。

multiboot.S

少ししかないないのでソース全体を見てみます。


/*********************************************************************************
 File:multiboot.s
 Description:Multiboot Header

*********************************************************************************/
#define __MULTIBOOT__S	1
#include "multiboot.h"

/* kernel entry */
.extern _kernel_entry

/*
==================================================================================

    Prototype Statement

==================================================================================
*/
/* Open Functions */

/* Local Functions */

/*
==================================================================================

    DEFINE 

==================================================================================
*/

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < open functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
.text
.code32
.globl start, _start

/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :start, _start
    Input       :void
    Output      :void
    Return      :void
    Description :void
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
start:
_start:
    jmp     _multi_boot_entry

/* align 32 bits boundary	*/
.align 4

/*
==================================================================================

    multiboot header

==================================================================================
*/
multiboot_header:
    .long   DEF_MBH_MAGIC
    .long   DEF_MBH_FLAGS
    .long   DEF_MBH_CHECKSUM

/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :_multi_boot_entry
    Input       :void
    Output      :void
    Return      :void
    Description :void
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
_multi_boot_entry:

    /* ------------------------------------------------------------------------ */
    /* reset eflags                                                             */
    /* ------------------------------------------------------------------------ */
    pushl   $0
    popf

    /* ------------------------------------------------------------------------ */
    /* push the pointer to the multiboot information                            */
    /* ------------------------------------------------------------------------ */
    pushl   %ebx    # 1st argument
    pushl   %eax    # 2nd argument

    call    _kernel_entry

loop_infinite:
    hlt
    jmp     loop_infinite

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Local Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
==================================================================================
    Funtion     :void
    Input       :void
    Output      :void
    Return      :void
    Description :void
==================================================================================
*/



最初の定義__MULTIBOOT__Sはmultiboot.hをインクルードしたときに

C言語の構造体定義などをインクルードしないように定義しています。

multiboot.hでは、構造体の定義は__MULTIBOOT__Sの定義があれば、

インクルードしないように#ifndefで場合分けしておきます。


#define	__MULTIBOOT__S	1
#include "multiboot.h"



次にC言語で作るカーネルのメイン関数をextern宣言します。

リンキングするときにリンケージエディターに外部関数名であることを

知らせます。この関数名を使って、最後にカーネルのメイン関数を呼び出します。


/* kernel entry */
.extern	_kernel_entry



次は.textディレクティブを指定します。.textを指定しておくと以下は

以下のコードは.textセクションに配置されます。次は.code32です、

アセンブラで32ビットコードを出力させるために指定します。

.globl start, _startです。start, _startは関数名となります。

.globlを指定してリンケージエディターにグローバルな関数であることを宣言しています。


.text
.code32
.globl start, _start



次が最初の命令となります。GRUBからカーネルが起動されたときに一番最初に

この命令が実行されます。_multi_boot_entry関数に分岐します。

マルチブートヘッダーはカーネルイメージの8KB以内に置いておく必要があります。

マルチブートヘッダーをこの命令の直後に配置します。ヘッダーをなるべくカーネル

イメージの最初のほうに置くために、まずは分岐させます。


start:
_start:
    jmp		_multi_boot_entry



分岐命令の直後にマルチブートヘッダーのインスタンスを作成します。

multiboot.hで定義した値そのものをカーネルイメージに埋め込みます。

マルチブート仕様に従って、magicフィールド、flagsフィールド、checksumフィールドの

順番通りに配置します。ここで.longは32ビットコードとなりますので、32ビット型の

データ宣言となります。


multiboot_header:
    .long   DEF_MBH_MAGIC
    .long   DEF_MBH_FLAGS
    .long   DEF_MBH_CHECKSUM



次はいよいよカーネル本体を起動する処理です。最初にEFLAGSレジスターの値を

初期化します。初期化方法として、 PUSH命令 pushl でスタックに0x00000000をプッシュして、

直ぐに POPF命令 でスタックにプッシュしておいた0x00000000の値をEFLAGSにポップします。

ここでpushlは32ビットの値をアセンブラに明示的に指示するために、

push命令にl(long)をつけています。


_multi_boot_entry:

    /* ------------------------------------------------------------------------ */
    /* reset eflags                                                             */
    /* ------------------------------------------------------------------------ */
    pushl   $0
    popf



そして、今回はマルチブート仕様に対応したカーネルを作成しますので、

カーネルの引数としてマジックナンバーとマルチブート情報を渡します。

仕様ではGRUbはEBXレジスターにマジックナンバーを、EAXレジスターに

マルチブート情報のアドレスが格納されいます。この値を関数の引数として

渡すために、スタックに順番に格納しておきます。


    /* ------------------------------------------------------------------------ */
    /* push the pointer to the multiboot information                            */
    /* ------------------------------------------------------------------------ */
    pushl   %ebx    # 1st argument
    pushl   %eax    # 2nd argument



そして、やっとカーネルのメイン関数を CALL命令 を使って呼び出します。


    call    _kernel_entry



後は、もうカーネルから戻ってくることはありませんが、フェール処理として

無限ループを作っておきます。


loop_infinite:
    hlt
    jmp	    loop_infinite



カーネル本体を作成する

簡単なカーネル本体を作成します。今回はマルチブート仕様を端折って

ますので、実際の実装例は マルチブート仕様 をご確認ください。



まずは全体を見てみます。


/*********************************************************************************
 File:kernel.c
 Description:Kernel entry

*********************************************************************************/
#include "multiboot.h"

/*
==================================================================================

    Prototype Statement

==================================================================================
*/
/* Open Functions */

/* Local Functions */
void displaySample( char c,
                    unsigned char  foreColor,
                    unsigned char backColor,
                    int x,
                    int y );

/*
==================================================================================

    DEFINE 

==================================================================================
*/
#define VRAM_TEXTMODE       0x000B8000
#define DEF_COLOR_BLACK     0x00
#define DEF_COLOR_WHITE     0x0F
#define MAX_Y               25
#define MAX_X               80
#define	MAX_XY              (80*25)

/*
==================================================================================

	Management 

==================================================================================
*/
char message[ ] = "Hello world!";

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
	< open functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :_kernel_entry
    Input       :UINT32 magic
                 < magic number of multiboot >
                 MULTIBOOT_INFO *info
                 < multiboot information structure >
    Output      :void
    Return      :void
    Description :kernel entry
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
void _kernel_entry( UINT32 magic, MULTIBOOT_INFO *info )
{
    int strlen;
    int i;

    strlen = sizeof( message );

    for( i = 0 ; i < strlen ; i++ )
    {
        displaySample( message[ i ], DEF_COLOR_WHITE, DEF_COLOR_BLACK, i, 0 );
    }
    
    for( ; ; );
}

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Local Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
==================================================================================
    Funtion     :displaySample
    Input       :char c
                 < a character to print >
                 unsigned char foreColr
                 < foreground color >
                 unsigned char backColor
                 < background color >
                 int x
                 < x-axis position where a character is printed >
                 int y
                 < y-axis position where a character is printed >
    Output      :void
    Return      :void

    Description :print a character with setting color in text mode
==================================================================================
*/
void displaySample( char c,
                    unsigned char  foreColor,
                    unsigned char backColor,
                    int x,
                    int y )
{
    unsigned short  *vram_TextMode;
    unsigned short  color;

    vram_TextMode  = ( unsigned short *)VRAM_TEXTMODE;

    color          = ( backColor << 4 ) | ( foreColor & 0x0F );

    vram_TextMode += x + y * MAX_X;

    *vram_TextMode = ( color << 8 ) | c;
}



ここでカーネルのメイン関数はmultiboot.Sで呼出す_kernel_entry関数と

しています。カーネルのメイン処理では、messageに格納されている文字列

”Hello world!”を1文字ずつ画面に表示していきます。1文字の表示には

シンプルビデオドライバ で作成しましたdisplaySample関数を使用しています。

(表示の仕組みについては上記のシンプルビデオドライバをご確認ください。)

カーネルをコンパイルする

では、multiboot.Sとkernel.cをコンパイルしていきます。

サンプル用意しました。lzh形式で圧縮してあります。)

multiboot.Sをコンパイルします。multiboot.hヘッダーファイルは

コンパイル時に参照できるように同じディレクトリにおいておきます。

[multiboot.S]


i686-pc-linux-gnu-gcc -c -o multiboot.o multiboot.S



次にkernel.cをコンパイルします。

[kernel.c]


i686-pc-linux-gnu-gcc -c -o kernel.o kernel.c



これでmultiboo.oとkernel.oができましたので、リンケージエディタで

マルチブートヘッダーをカーネルイメージに結合します。

マルチブートヘッダーとカーネルイメージの結合


$ i686-pc-linux-gnu-ld -Ttext=0x100000 multiboot.o kernel.o -o KImage



ここではカーネルイメージの名前をKImageとしました。

環境準備その2 クロスコンパイラ でインストールしたコンパイラは

特にオプションを指定しない場合はELFフォーマットのバイナリを生成します。

また、カーネルは物理アドレス0x00100000で実行開始できるように、

.textセクションの開始アドレスを-Tオプションで0x00100000に指定します。

このアドレスはEFLフォーマットに記録されます。またGRUBはEFLフォーマットの

このアドレスを参照して、0x00100000にカーネルをロードしてくれます。

(そのためEFLフォーマットのマルチブートヘッダーには物理アドレスが要りません)

(補足)ELFフォーマット以外のカーネルイメージの作成

ELFフォーマット以外のカーネルイメージはマルチブートヘッダーのflagsのビット16を

セットします。マルチブートヘッダーmultiboot.Sは次のように作成します。

(ヘッダーのインスタンス化する部分のみを抜粋します。)



ファイル名を変えてmultiboot_bin.Sとしました。

(このファイルもダウンロードできるように準備しました

[multiboot_bin.S]

/*
==================================================================================

    multiboot header

==================================================================================
*/
multiboot_header:
    .long   DEF_MBH_MAGIC
    .long   DEF_MBH_FLAGS_BIN
    .long   DEF_MBH_CHECKSUM_BIN
    .long   multiboot_header
    .long   _start
    .long   _data_end
    .long   _bss_end
    .long   _multi_boot_entry



ここで、マルチブート仕様に従って次のように設定しています。

そして、上記で使用するリンカースクリプトを作ります。


SECTIONS
{
    . = 0x100000;
    .text :
    {
        _text_start = .;
        *(.text)
        _text_end = .;
    }

    .rodata :
    {
        _rodata_start = .;
        *(.rodata)
        _rodata_end = .;
    }

    .data :
    {
        _data_start = .;
        *(.data)
        _data_end = .;
    }

    .bss :
    {
        _bss_start = .;
        *(.bss)
        _bss_end = .;
    }
}



マルチブートヘッダーで使用する_data_end、_bss_endを定義します。

そして、コンパイルします。


$ i686-pc-linux-gnu-gcc -c -o multiboot_bin.o multiboot_bin.S



リンカースクリプトを使用してリンキングします。

		
i686-pc-linux-gnu-ld -T linkerscript --oformat=binary multiboot_bin.o kernel.o -o KImagebin



ここでは、KImagebinを作成しました。

後は同じようにGRUBを起動して、カーネルを指定して起動します。



長かったですが、ようやくGRUBで起動できる自作OSを作ることができました。

(表示するだけで特に何の処理もしませんが。。。。)

次回はいよいよ、自作OSをGRUBから起動してみます。

inserted by FC2 system