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

日々勉強中。。。

0から作るOS開発

環境準備

環境設定

ブートローダ

カーネルローダ

GRUB

カーネル

ドライバーその他

0から作るOS開発 物理メモリ管理その1 物理メモリとマルチブート仕様

前回までの内容

これまでで、 ことがわかりました。それではカーネルを0から開発していきましょう!

今回は物理メモリ管理とマルチブート仕様について説明します

メモリ

一口にメモリと言っていますが、メモリには多くの種類があります

例えば、フラッシュメモリと呼ばれるメモリはROM(リードオンリーメモリ:Read Only Memory)で、

読み出し専用のメモリです

マイコンによく使われるメモリでOSのコード、変数の初期値などが格納されています

(BIOSのファームウェアもROMに格納されています)

その他にもPROM(Programmable ROM)のEEPROM、フラッシュメモリもメモリです

読み出し専用のROMがある一方で、RAM(ランダムアクセスメモリ:Random Access Memory)と呼ばれる

読み書きができるメモリがあります。ここで取り上げる物理メモリがRAMとなります

やはり、RAMにも多くの種類があります。代表的なRAMとしてパソコンでも使用されている

DRAM(ダイナミックランダムアクセスメモリ:Dynamic Random Access Memory)があります

DRAMは各ビットをキャパシタ(コンデンサ)で記憶しています

キャパシタは、電圧を保持できる(記憶できる)のは一定時間だけですので、

周期的にリフレッシュ(再書き込み)する必要があります

その他にもSRAM(スタティックランダムアクセスメモリ:Static Random Access Memory)が

あります。SRAMはフリップフロップで各ビットを記憶するので、通電しておけば

記憶し続けるのでリフレッシュの必要ありません

リフレッシュ機構が無いので、SRAMは高速にアクセスできるためパソコンのキャッシュメモリとして

使用されています

CPUとメモリの間にはアドレスバス、データバス、制御バスがあります(パソコンの場合にはシステムバス)

そして、CPUからメモリを制御するためのメモリコントローラが必要となります

メモリコントローラにより、CPUからメモリのデータを読み込んだり書き込んだりすることができます

(DRAMの場合は、リフレッシュもメモリコントローラが勝手にやってくれます)

メモリコントローラ

CPUがアクセスできるメモリ空間は32ビット(64ビット)と広大なため

メモリ空間上に複数のメモリをマッピングすることができます

メモリ空間に複数のメモリをマッピング

空白の所はメモリがありませんので、アクセスしても値は不定です

(上図は空想上のメモリ空間で、ただの例です)

CPUがメモリにアクセスする最小単位は8ビット(1バイト=char型)です

ですので、メモリのアドレスも最小単位は1バイトとなります

メモリのアドレスはアドレスバスで指定します

アドレッシングモード

アドレッシングモードとはソフトウェアがどうやってメモリにアクセスするかを決めたルールです

ソフトウェアはCPUのレジスタを設定してメモリにアクセスします

(ブートローダで見てきましたように、セグメントセレクタとオフセットを指定してメモリにアクセスしました)

CPUがメモリにアクセスするAPI(Application Program Interface)を持っていて

そのAPIをソフトウェアが利用している、と言えます

メモリの制御

CPUがメモリを制御するときに、メモリ自身との間に、MMU(Memory Management Unit)、

TLB(Translation Lookaside Buffer)、DDRコントローラ(メモリコントローラ)があります

メモリの制御。CPUとメモリの間にはMMU、TLB、メモリコントローラがあります

CPU(MMU)とTLBとメモリコントローラはデータバス、アドレスバス、制御バスをもったシステムバスで

つながっています。TLBはページングを有効にしたときに動作します

データをメモリに書き込みときには、制御バスのRWピンをHighにするとメモリコントローラは

データバスにデータが書き込まれるのを待ちます。CPUはIO制御ラインをLowにします

これにより、CPUがコマンドを書き込んでも、各IOデバイス(メモリコントローラ以外)が無視するようになります

(CPUがメモリにデータを書き込む場合IN/OUT命令は使用しません)

次にCPUはプログラムが指定するアドレスに従って、アドレスバスの各ピンをHigh/Lowにし、

書き込むデータをデータバスに書き込みます。アドレスバスを読み込んでメモリコントローラは

RAM上のアドレスにデコードし、データバス上のデータをRAMに記憶させます

メモリコントローラは、その次のクロック周期でDRAMをリフレッシュします


データをメモリから読み出すときは、書き込む場合と似たような制御となります

制御バスのRWピンをLowにし、IO制御ラインをLowにします。CPUはアドレスバスにアドレスを出力し

メモリコントローラはDRAM上のデータを取り出しデータバスに出力し、CPUはデータバスを読み込みます

メモリコントローラは、その次のクロック周期でDRAMをリフレッシュします


制御バスのCLK信号はCPUとメモリコントローラが、アドレスとデータをやりとりするときに同期を

とるために使用します。CPUがCLKをHighにすることで、メモリコントローラとのデータのやりとりを開始します

アドレスバス上にアドレスデータを出力し、RWピンは書き込み時はHigh、読み込み時はLowにします

メモリコントローラがアドレスを読み込む一定時間はCLKをHighのままにし、RWピンもHigh/Lowのままにします

次にデータバスにデータを出力します。データの書き込み/読み込み時にはCLKラインをHighとLowに

交互に出力し、クロックを出します


ページングが無効となっている場合はTLBはデータをスルーします

物理メモリ管理

プログラムを実行するときには、RAM上にデータとプログラムコードが書き込まれている必要があります

プログラムやデータをRAMにロードしたり、必要なくなったデータを消したりするにはメモリ上のどの領域が

使用されていて、どの領域がフリーなのかを把握しておく必要があります

物理メモリを管理するために、まず必要となるのが、どのメモリを使用できるかの情報が必要となります

メモリ情報の取得

まず最初にパソコンに積まれているメモリの大きさを取得します

メモリのサイズを取得するには色々な方法があります。組み込みなどでは設計段階でメモリサイズが

分かっている場合や、 OS自作入門でやっているように

メモリにデータを書き込んで、読み出して、書き込んだ値が読み出せるアドレスまでがメモリのサイズ

とする方法もあります(この方法は汎用的でどのシステムでも使えます)。

パソコンでは起動時にBIOSが調べてくれているので、今回はそれを利用したいと思います

BIOSからメモリ情報を取得する

BIOSからメモリの情報を取得します。32ビットのプロテクティッドモードに移行した後ではBIOSの

割り込み処理が利用できません。そこで、16ビットのリアルモードで動作する カーネルローダ

BIOSからメモリ情報を取得します

BIOSから取得できるメモリ情報には”従来のメモリ領域(Conventional Memory)”

(ローメモリ:Low Memoryとも言います)と”拡張メモリ領域(Extended Memory)”があります

ローメモリは16ビットCPU時代に使用していた領域で1MB以下のメモリ領域となります

拡張メモリ領域は1MB以降のメモリとなります

メモリ情報を取得するにはINT命令を使用します

ローメモリのサイズ取得 INT 0x12命令

INT 0x12命令でローメモリのサイズを取得します

INT 0x12命令を実行した後の戻り値は

<戻り値> となります。INT 0x12命令の割り込み処理では、取得できるメモリサイズは

AXレジスタ分(16ビットサイズ)の0xFFFF(65535)までとなります

そのため、64kB以上のメモリを積んでいる場合には正確なサイズが取得できません

拡張メモリサイズ取得(0x88機能) INT 0x15命令

INT 0x15命令の引数に0x88を指定すると1MB以上のアドレスにある

拡張メモリ領域のメモリサイズが取得できます

INT 0x15命令の引数と戻り値は次のようになります

<入力(引数)> <戻り値> INT 0x15命令(0x88機能)の割り込み処理では、取得できるメモリサイズは

AXレジスタ分(16ビットサイズ)の0xFFFF(65535)までとなります

メモリサイズは1kB単位ですので、合計64MBまでのメモリサイズが取得できます

現在のパソコンでは64MB以上あるのが普通ですので、別のBIOS処理を利用する必要があります

拡張メモリサイズ取得(0xE801、0xE881機能) INT 0x15命令

更に大きなメモリサイズを取得するには、BIOS分野で世界トップメーカの Phoenix Technologies

発行している BIOSユーザーズマニュアル で決められているINT 0x15命令を使用します

(興味の有る方はマニュアルがダウンロードできますので、見てみてください)

INT 0x15命令の引数に0xE801(16ビット)または0xE881(32ビット)を指定すると

64MBを超えるメモリサイズを取得できます

また、Tipsのビッグメモリサービスのビッグメモリサイズ取得(32ビット)にも 説明を載せていますので随時参照してください

<入力(引数)> <戻り値> ”設定済み”メモリはマザーボードのジャンパ(スイッチ)を変えることでメモリを切り替える

ことができるメモリ領域のことを言います。EAXとECX、EBXとEDXは同じ値が入ると

思っていただいて問題はありせん

このBIOS割り込み処理は古いマザーボードでOSを動かすときに使用します

(1994年頃に実装されたためそれ以前のマザーボードではサポートされていません)

後述するメモリマップ取得機能(0xE820)をサポートしていないBIOSのときに

代替手段として使えます。Windows、Linuxでもサポートしています

このBIOS割り込み処理を使用するときの注意点があります

BIOSは戻り値をEAXとEBXまたは、ECXとEDXに格納しますが、どちらのレジスタに格納されるか

または両方のレジスタに格納するのかはBIOSのファームによって動作が異なるようです

ですので、あるBIOSはEAXとEBXに値を格納しますが、ECXとEDXは0を入れたり(変更しなかったり)

します。また別のBIOSでは逆の組み合わせでECXとEDXに値を格納したりするものもあります

このような場合を想定して、EAX(ECX)が0かどうかを判断し、有効なメモリのサイズを取得します

アセンブラの例を見ていきます


;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Get Big Memory Size
;	return : AX : size of memory between 1MB and 16MB (1KB unit)
;		 BX : size of memory above 16MB (64KB unit)
;		 CX : size of memory between 1MB and 16MB (1KB unit)
;		 DX : size of memory above 16MB (64KB unit)
;
;		 AX : -1 if error occurs
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
BIOSGetBigMemorySize:
	PUSH	EDX
	PUSH	ECX
	
	XOR	ECX, ECX
	XOR	EDX, EDX
	MOV	AX, 0xE801
	INT	0x15		; INT 0x15命令
	JC	.ERROR		; CFが0の場合はエラー
	
	CMP	AH, 0x86	; エラーステータスあるか調べる
	JE	.ERROR		; エラーステータスがあればエラー
	
	CMP	AH, 0x80
	JE	.ERROR
	
	JCXZ	.RETURN_SIZE	; EXCが0なら.RETURN_SIZEへ
	MOV	AX, CX		; メモリサイズをAXへ
	MOV	BX, DX		; メモリサイズをBXへ
	
.RETURN_SIZE			; メモリサイズはAX、BXに格納されている
	POP	ECX				
	POP	EDX
	RET			
	
.ERROR				; エラー処理
	MOV	AX, -1		; AXに-1を格納
	MOV	BX, 0		: BXにを格納
	POP	ECX
	POP	EDX
	RET



メモリマップ取得(0xE820機能) INT 0x15命令

ACPI(Advanced Configuration and Power Interface)の仕様

決められている特殊なINT 0x15命令を使用します

(興味の有る方は仕様がダウンロードできますので、見てみてください)

また、Tipsのビッグメモリサービスのメモリマップ情報取得にも 説明を載せていますので随時参照してください

<入力(引数)> <戻り値>

メモリマップ情報(Address Range Descriptor)

格納されるメモリマップ情報(Address Range Descriptor)は下記フォーマットになります

(下記図はビット単位ではなく0−20バイトです)

メモリマップ情報(Address Range Descriptor)

メモリマップ情報(Address Range Descriptor)
バイト ラベル名 サイズ
(バイト)
説明
0-7 Base Address 8 メモリのベースアドレスが格納されます
8-15 Length 8 メモリのサイズがバイト単位で格納されます
16-20 Type 4 メモリのタイプが格納されます
0x0001:OSが使用できるメモリです
0x0002:利用できないメモリです(ROMやメモリマップドデバイスなど)
0x0003:ACPI用のメモリです(ACPI情報を読みだした後はOSで利用できます)
0x0004:利用できないメモリです(ACPI NVSセッション用)
上記以外:利用できないメモリです(未定義のタイプ)


メモリマップ情報を定義する

カーネルローダにメモリマップ情報を定義します


struc	MemoryMapEntry
	.baseAddress	resq	1
	.length		resq	1
	.type		resd	1
	.acpi_null	resd	1
endstruc



strucは構造体を宣言します。endstrucまでが構造体となります

resqは8バイトのデータを宣言します(qはクワッドワードの意)

resdは4バイトのデータを宣言します(dはダブルワードの意)

(ここでは最後の4バイトを予約領域として余分に定義しています)

メモリマップ情報の取得

INT 0x15命令の0xE820機能を使ってメモリマップ情報を取得していきます

0xE820を使用する際に少し注意点があります。BIOSのなかにはEAXレジスタの

上位16ビットを0にしておく必要があるBIOSがあります。全てのメモリ情報を取得するため

0xE820を繰り返し使用することになります。この際に、EDXは必ず”SMAP”を入力しておく

必要がありますが、BIOSのなかには0xE820の処理でEDXの値を変えるものもあります


;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; get available memory space
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
[BITS 16]

BiosGetMemoryMap:
	PUSHAD
	XOR	EBX, EBX
	XOR	BP, BP		; メモリマップ情報のエントリ数をBPに入れる
	MOV	EDX, 'PAMS'	; ”SMAP”
	MOV	EAX, 0xE820
	MOV	ECX, 24		; MemoryMapEntryのバイトサイズ
	INT	0x15		; メモリマップ情報を取得
	JC	.ERROR		; エラー時は.ERRORへ
	CMP	EAX, 'PAMS'	; BIOSが”SMAP”をしたか調べる
	JNE	.ERROR		; ”SMAP”ではないときは.ERRORへ
	TEST	EBX, EBX	; 戻り値EBXが0のときはメモリ情報は無し
	JE	.ERROR
	JMP	.START		; メモリマップ情報を格納する
.NEXT_ENTRY:
	MOV	EDX, 'PAMS'	; EDSにもう一度”SMAP”入れておく
	MOV	ECX, 24		; MemoryMapEntryのバイトサイズ
	MOV	EAX, 0xE820
	INT	0x15		; 次のメモリマップ情報を取得
.START:
	JCXZ	.SKIP_ENTRY	; CFが0のときは失敗なので戻り値を格納しない
.NOTNEXT:
	MOV	ECX, [ES:DI + MemoryMapEntry.length]	; BIOSが格納した
							; メモリマップ情報のLengthを取得
							; (下位4バイト分)
	TEST	ECX, ECX	; Length(下位4バイト)が0かどうか調べる
	JNE	SHORT .GOOD_ENTRY	; 0じゃなければ.GOOD_ENTRYへ
	MOV	ECX, [ES:DI + MemoryMapEntry.length + 4]; BIOSが格納した
							; メモリマップ情報のLengthを取得
							; (上位4バイト分)
	JECXZ	.SKIP_ENTRY	; Length(上位4バイト)が0なら.SKIP_ENTRYへ
.GOOD_ENTRY:
	INC	BP		; メモリマップ情報のエントリ数をインクリメント
	ADD	DI, 24		; 格納先ポインタを進める(MemoryMapEntryは24バイト)
.SKIP_ENTRY:
	CMP	EBX, 0		; EBXが0ならこれ以上メモリマップ情報は無し
	JNE	.NEXT_ENTRY	; 次候補がある場合は.NEXT_ENTRYへ
	JMP	.MM_DONE	; 終了
.ERROR:
	STC			; CFフラグをセット
	
.MM_DONE:
	POPAD
	RET



この関数をStarting.asm(カーネルローダ)で呼び出してメモリマップ情報を取得します


;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Starting Kernel Procedure
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
MAIN2:
	; MAIN2の続きでプロテクティッドモードに以降する前
	
GET_MEMORY_MAP:
	XOR	AX, AX		; メモリマップ情報取得で使用するレジスタを初期化
	MOV	ES, AX
	MOV	DS, AX
	PUSH DI			; DIを退避しておく
	MOV	EAX, DWORD mme	; MemoryMapEntryの先頭アドレスを取得
	MOV	DI, AX		; DIに入れておく
	CALL BiosGetMemoryMap	; メモリマップ情報取得

	POP	DI	
	

	; プロテクティッドモードへ以降していく
	
;---------------------------------------------------------------------------------
;
; Memory Map Entry
;
;---------------------------------------------------------------------------------
struc	MemoryMapEntry
	.baseAddress	resq	1
	.length		resq	1
	.type		resd	1
	.acpi_null	resd	1
endstruc

mme:
istruc MemoryMapEntry
	at MemoryMapEntry.baseAddress,	dd 0
	at MemoryMapEntry.length,	dd 0
	at MemoryMapEntry.type,		dd 0
	at MemoryMapEntry.acpi_null,	dd 0

iend



ここでNASMの構造体に2つの宣言がでてきました

最初の

struc	MemoryMapEntry
	.baseAddress	resq	1
	.length		resq	1
	.type		resd	1
	.acpi_null	resd	1
endstruc



は構造体の型を定義しています。C言語で言えば

typedef struct
{
	unsigned long long baseAddress;
	unsigned long long length;
	unsigned long type;
	unsigned long acpi_null;
} MemoryMapEntry;



ような感じになります。型定義だけですので、メモリ上には存在していません

(インスタンス化する前です)。次の


mme:
istruc MemoryMapEntry
	at MemoryMapEntry.baseAddress,	dd 0
	at MemoryMapEntry.length,	dd 0
	at MemoryMapEntry.type,		dd 0
	at MemoryMapEntry.acpi_null,	dd 0
iend



は実際にMemoryMapEntryをインスタンス化しています

このアドレス以降に見つかったメモリ情報を順次格納していきます)

C言語で言えば


static MemoryMapEntry mme;



となります。インスタンス化したMemoryMapEntryのアドレスを取得してDIに入れています


	PUSH DI			; DIを退避しておく
	MOV	EAX, DWORD mme	; MemoryMapEntryの先頭アドレスを取得←
	MOV	DI, AX		; DIに入れておく
	CALL BiosGetMemoryMap	; メモリマップ情報取得



ここで取得したメモリマップ情報をカーネル起動時にマルチブート仕様に従って

カーネルに渡します

マルチブート仕様

マルチブート仕様 GNU が発行している、複数のOSを起動させるときのカーネルローダの仕様を

規定したものです。今回はマルチブート仕様とタイトルをつけておりますが

仕様に完全に従うのではなく、実際にはカーネルにメモリマップ情報を渡すだけとなります

GRUBからカーネルを起動させるときには仕様を満たしたカーネルを作る必要がありますので

その時に作成します

(マルチブート仕様の詳細については Tipsのマルチブート仕様 を参照してください。)

カーネル起動時のCPUのレジスタ

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

ブート情報

マルチブート仕様では、ブート情報のアドレスはカーネル起動時にEBXレジスタに格納されています

ブート情報は下記フォーマットになっています

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

今回は必要な部分のみの説明になります

ブート情報のフィールド全部については Tipsのマルチブート仕様 をご確認ください。

ブート情報
ラベル名 オフセット
(バイト)
サイズ
(バイト)
説明
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 メモリマップ情報が格納されているアドレスが格納されています


ブート情報にBIOSから取得したメモリサイズなどを格納します

0xE820機能が使用できない古いマザーボードであれば、0xE881で取得した

ローメモリのサイズと拡張メモリサイズをそれぞれmem_lowerとmem_upperに格納しておきます

0xE820機能が使用できるマザーボードではメモリマップ情報のサイズをmmap_lenghtに

メモリマップが格納されているアドレスをmmap_addrに格納します

カーネルローダでブート情報の定義

カーネルローダでブート情報を定義します


;multi boot info
struc multiboot_info
		.flags			resd	1
		.mem_lower		resd	1
		.mem_upper		resd	1
		.bootDevice		resd	1
		.cmdline		resd	1
		.mods_count		resd	1
		.mods_addr		resd	1
		.syms0			resd	1
		.syms1			resd	1
		.syms2			resd	1
		.reserved		resw	1
		.mmap_length		resd	1
		.mmap_addr		resd	1
		.drives_length		resd	1
		.drives_addr		resd	1
		.config_table		resd	1
		.bootloader_name	resd	1
		.apm_table		resd	1
		.vbe_control_info	resd	1
		.vbe_mode_info		resd	1
		.vbe_mode		resw	1
		.vbe_interface_seg	resw	1
		.vbe_interface_off	resw	1
		.vbe_interface_len	resw	1
endstruc
	
boot_info:
istruc multiboot_info
	at multiboot_info.flags,		dd 0
	at multiboot_info.mem_lower,		dd 0
	at multiboot_info.mem_upper,		dd 0
	at multiboot_info.boot_device,		dd 0
	at multiboot_info.cmdline,		dd 0
	at multiboot_info.mods_count,		dd 0
	at multiboot_info.mods_addr,		dd 0
	at multiboot_info.syms0,		dd 0
	at multiboot_info.syms1,		dd 0
	at multiboot_info.syms2,		dd 0
	at multiboot_info.reserved,		dw 0
	at multiboot_info.mmap_length,		dd 0
	at multiboot_info.mmap_addr,		dd 0
	at multiboot_info.drives_length,	dd 0
	at multiboot_info.drives_addr,		dd 0
	at multiboot_info.config_table,		dd 0
	at multiboot_info.bootloader_name,	dd 0
	at multiboot_info.apm_table,		dd 0
	at multiboot_info.vbe_control_info,	dd 0
	at multiboot_info.vbe_mode_info,	dd 0
	at multiboot_info.vbe_mode,		dw 0
	at multiboot_info.vbe_interface_seg,	dw 0
	at multiboot_info.vbe_interface_off,	dw 0
	at multiboot_info.vbe_interface_len,	dw 0
iend



カーネルローダでメモリマップ情報を取得する

INT 0x15命令の0x8E20機能を呼出すBiosGetMmoryMap関数で

メモリマップ情報を取得した後にブート情報に値を格納します


;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; get available memory space
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
[BITS 16]

BiosGetMemoryMap:
	; 0xE820機能でメモリマップ情報を取得します

.done:
	MOV	[boot_info+multiboot_info.mmap_length], BP
						; BPはメモリマップ情報の
						; エントリ数が入っています
						; boot_infoのmmap_lengthに格納します
	POPAD
	RET




カーネルローダStarting.asmメイン処理でメモリマップ情報のアドレスを格納します

;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; get available memory space
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
GET_MEMORY_MAP:
	XOR	AX, AX
	MOV	ES, AX
	MOV	DS, AX
	PUSH DI
	MOV	EAX, DWORD mme
	MOV	DI, AX
	CALL BiosGetMemoryMap
	
	POP	DI
	MOV	EAX, DWORD mme			; メモリマップ情報のアドレスを取得
	MOV	[boot_info+multiboot_info.mmap_addr], EAX
						; メモリマップ情報のアドレスを
						; mmap_addrに格納



カーネルにメモリマップ情報を渡す

カーネルを実行する前に、シグネチャとブート情報を格納したアドレスをカーネルに渡します

	

;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Starting Protected Mode
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
[BITS 32]
Pmode_start:
	; プロテクティッドモード移行後の処理

EXECUTE:

	;-------------------------------------------
	; make boot info
	;-------------------------------------------
	;flags : flags[0] = 1, flgas[6] = 1
	MOV	EAX, 0x00000021			; flagsのビット0とビット6を1にして
						; flagsに格納します
	MOV	[boot_info+multiboot_info.flags], EAX

	;-------------------------------------------
	; Execute Kernel
	;-------------------------------------------
	MOV	EBX, IMAGE_PMODE_BASE
	MOV	EBP, EBX
	
	MOV	EAX, 0x2BADB002
	XOR	EBX, EBX
	CLI
	PUSH	DWORD boot_info			; 引数としてboot_infoをカーネルに渡します
	CALL	EBP
	ADD	ESP, 4				; 念のためスタック上の引数を除去しておく
	JMP	Failure2



カーネルでブート情報を引数として受け取ります

ここで関数呼び出しとスタックの関係をほんの少し

例えば次のような引数int aを持つ関数functionがあったときに、

	

void function( int a )
{
	// process of the function
}

int main( void )
{
	function( 1000 );
}



コンパイルを行うと関数functionを呼出すアセンブラコードは

(概念的なコードとなります。実際にコンパイラが出力するコードではありません)

	

	PUSH	DWORD a
	CALL	function



となります。CALL命令をする前に、スタックにPUSHしたものが引数となります

(引数が1つだけの場合となります)

カーネルでメモリマップ情報を引数として受け取る

まずはマルチブート仕様のブート情報をC言語で定義します

	

/*********************************************************************************
 File:bootinfo.h
 Description:definition of bootinfo

*********************************************************************************/
#ifndef __BOOTINFO__H
#define __BOOTINFO__H

/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/

	Description : Boot Information

_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
typedef struct
{
	unsingned int	flags;
	unsingned int	mem_lower;
	unsingned int	mem_upper;
	unsingned int	boot_device;
	unsingned int	cmdline;
	unsingned int	mods_count;
	unsingned int	mods_addr;
	unsingned int	syms1;
	unsingned int	syms2;
	unsingned int	syms3;
	unsingned int	mmap_length;
	unsingned int	mmap_addr;
	unsingned int	drives_length;
	unsingned int	drives_addr;
	unsingned int	config_table;
	unsingned int	boot_loader_name;
	unsingned int	apm_table;
	unsingned int	vbe_control_info;
	unsingned int	vbe_mode_info;
	unsingned short	vbe_mode;
	unsingned short	vbe_interface_seg;
	unsingned short	vbe_interface_off;
	unsingned short	vbe_interrace_len;
} BOOTINFO;

#endif	/* __BOOTINFO__H */



次にカーネルのソースコードを変更して、引数にブート情報を受け取ります

	
	
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	Funtion     :_kernel_entry
	Input       :BOOTINFO* info
	Output      :void
	Return      :void
	Description :Kernel Core
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
int _kernel_entry( BOOTINFO* info )
{
	for( ;; )
	{
	}
}	



これでカーネルローダで取得したメモリマップ情報をカーネルが受け取ることができます

次回はいよいよ物理メモリの管理方法について説明します

inserted by FC2 system